Add custom renderer documentation

This commit is contained in:
Dominik Chrastecky 2023-06-09 18:50:41 +02:00
parent db5d6131c4
commit 53fedea25c
5 changed files with 790 additions and 1 deletions

View File

@ -502,3 +502,39 @@ $model = new ScadModel(facetsConfiguration: new FacetsNumber(30));
This topic is covered in [separate documentation](doc/creating-shapes.md).
**This documentation is a work in progress**
## Rendering output
By default, the `ScadModel` uses a `ScadFileRenderer` class which, as the name implies, renders the content into
a SCAD file which you can then open in OpenSCAD.
There are other built-in renderers available:
- `PngPreviewRenderer`
- `StlRenderer`
Unlike the `ScadFileRenderer`, these two need to find the OpenSCAD binary. If you have OpenSCAD installed in your path
or you use the flatpak version, it works out of the box:
```php
<?php
use Rikudou\PhpScad\ScadModel;
use Rikudou\PhpScad\Renderer\PngPreviewRenderer;
use Rikudou\PhpScad\Renderer\StlRenderer;
use Rikudou\PhpScad\Shape\Cube;
$model = (new ScadModel())
->withRenderable(new Cube(5, 5, 5))
;
$model->withRenderer(new PngPreviewRenderer())->render(__DIR__ . '/preview.png');
$model->withRenderer(new StlRenderer())->render(__DIR__ . '/output.stl');
// you can also provide some settings to the renderers:
$previewRenderer = new PngPreviewRenderer(width: 512, height: 512, openScadPath: '/custom/path/to/openscad');
$stlRenderer = new StlRenderer(openScadPath: '/custom/path/to/openscad');
```
> Tip: You can also create your own reusable renderer, follow the [tutorial here](doc/custom-renderer.md)

457
doc/custom-renderer.md Normal file
View File

@ -0,0 +1,457 @@
# Creating a custom renderer
What is a renderer? It transforms the `ScadModel` object, which houses all the defined `Renderable` objects, into
something usable outside of this library.
The built-in examples include a SCAD file (for opening in OpenSCAD), a PNG file with model preview and an STL model
file for using with 3D viewers, slicers and so on.
Today we're going to create a renderer that creates a GCode for 3D printers directly.
## Prerequisites
We'll need `PrusaSlicer` and `OpenSCAD` installed on our system. It can either be installed locally or from flatpak.
On Ubuntu, you can install by using `apt install prusa-slicer openscad`. If your system supports flatpaks install using
`flatpak install com.prusa3d.PrusaSlicer org.openscad.OpenSCAD`.
## Creating a renderer
Creating a renderer starts by creating a class implementing the `Renderer` interface:
```php
<?php
namespace Rikudou\PhpScad\Renderer;
final class GCodeRenderer implements Renderer
{
public function render(string $outputFile, string $scadContent): void
{
// TODO: Implement render() method.
}
}
```
As you can see, the `Renderer` defines a single method `render()` which accepts the output file path as a parameter
and the SCAD content.
### Flow
We're going to take the `$scadContent` and let the built-in `StlRenderer` convert it into a STL 3D model,
then we're going to take the STL file and slice it using PrusaSlicer into GCode.
We'll be making it as easy to use as possible, which means we'll try to find where PrusaSlicer and OpenSCAD is located
but we'll also need to provide some way to use a custom path:
```php
<?php
namespace Rikudou\PhpScad\Renderer;
final class GCodeRenderer implements Renderer
{
public function __construct(
?string $prusaSlicerPath = null,
private readonly StlRenderer $stlRenderer = new StlRenderer(),
) {
}
public function render(string $outputFile, string $scadContent): void
{
// TODO: Implement render() method.
}
}
```
We allow providing of custom PrusaSlicer and custom `StlRenderer` instance which could have a manual path set.
The STL renderer has its own logic (which we'll be pretty much replicating for PrusaSlicer in our class)
for locating OpenSCAD, so we can skip writing code for that part in our code.
We'll be checking in constructor that we can find a path if we're not provided one:
```php
<?php
namespace Rikudou\PhpScad\Renderer;
final class GCodeRenderer implements Renderer
{
private string $prusaSlicerPath;
public function __construct(
?string $prusaSlicerPath = null,
private readonly StlRenderer $stlRenderer = new StlRenderer(),
) {
$this->prusaSlicerPath = $prusaSlicerPath ?? $this->locatePrusaSlicer();
}
// ...
private function locatePrusaSlicer(): string
{
// TODO: find the binary or throw an exception
}
}
```
For locating the binary I'm pretty much just going to copy & paste from the `AbstractOpenScadBinaryRenderer` library:
```php
<?php
namespace Rikudou\PhpScad\Renderer;
use RuntimeException;
final class GCodeRenderer implements Renderer
{
private string $prusaSlicerPath;
public function __construct(
?string $prusaSlicerPath = null,
private readonly StlRenderer $stlRenderer = new StlRenderer(),
) {
$this->prusaSlicerPath = $prusaSlicerPath ?? $this->locatePrusaSlicer();
}
// ...
private function locatePrusaSlicer(): string
{
// try finding the binary in $PATH
exec('which prusa-slicer', $output, $exitCode);
if ($exitCode === 0) { // in Unix, 0 means success
return "'{$output[0]}'"; // the path might contain a space, enclose it in a single quote
}
$exception = new RuntimeException('Cannot find PrusaSlicer in path.');
exec('which flatpak', result_code: $exitCode);
if ($exitCode !== 0) { // if flatpak doesn't exist, no need to check further
throw $exception;
}
// if the flatpak is not installed, throw an exception
exec('flatpak info com.prusa3d.PrusaSlicer', result_code: $exitCode);
if ($exitCode !== 0) {
throw $exception;
}
// we will need the --file-forwarding flag if we want the Slicer to have access to the files we'll be creating
return 'flatpak run --file-forwarding com.prusa3d.PrusaSlicer';
}
private function isFlatpak(): bool
{
return str_starts_with($this->prusaSlicerPath, 'flatpak ');
}
}
```
Let's also add one helper method:
```php
<?php
namespace Rikudou\PhpScad\Renderer;
use RuntimeException;
final class GCodeRenderer implements Renderer
{
/**
* @var array<string>
*/
private array $temporaryFiles = [];
// ...
public function __destruct()
{
foreach ($this->temporaryFiles as $temporaryFile) {
if (is_file($temporaryFile)) {
unlink($temporaryFile);
}
}
}
private function createTemporaryStlFile(): string
{
$file = tempnam(sys_get_temp_dir(), 'PhpScadGCodeRenderer');
rename($file, "{$file}.stl");
$this->temporaryFiles[] = $file;
return "{$file}.stl";
}
}
```
This will help us create temporary files which will be automatically deleted when our script ends.
Now let's get started by creating the STL file as a temporary file:
```php
public function render(string $outputFile, string $scadContent): void
{
$stlFile = $this->createTemporaryStlFile();
$this->stlRenderer->render($stlFile, $scadContent);
}
```
And let's construct and execute the command:
```php
public function render(string $outputFile, string $scadContent): void
{
$stlFile = $this->createTemporaryStlFile();
$this->stlRenderer->render($stlFile, $scadContent);
// we specify that we want to export a gcode
$command = "{$this->prusaSlicerPath} --gcode -o ";
// in previous part you can see '-o' which means output file, we need to check whether we're running a flatpak
// or not to give the correct path
if ($this->isFlatpak()) {
// this is a special syntax that tells the flatpak system to forward real filesystem files to the app
$command .= "@@ '{$outputFile}' @@";
} else {
$command .= "'{$outputFile}'";
}
$command .= ' ';
// we do the same for input file
if ($this->isFlatpak()) {
$command .= "@@ '{$stlFile}' @@";
} else {
$command .= "'{$stlFile}'";
}
// this tells the shell that error output should be redirected to standard output
$command .= " 2>&1";
exec($command, $output, result_code: $exitCode);
if ($exitCode !== 0) {
throw new RuntimeException('There was an error while slicing your model: ' . implode("\n", $output));
}
}
```
This is the simplest form of the command and isn't actually that great because the sliced GCode will probably be wrong
regardless of what printer you use.
To mitigate that let's add support for loading custom options! PrusaSlicer accepts an ini file with configurations
(which you can export from the GUI by going to `File -> Export -> Export config` or pressing `CTRL+E`) and we'll
add a parameter that allows us to load such a config file!
> You could also add support for the hundreds of individual parameters, but that would be way too out of scope of this
> tutorial.
```php
<?php
namespace Rikudou\PhpScad\Renderer;
use RuntimeException;
final class GCodeRenderer implements Renderer
{
/**
* @var array<string>
*/
private array $temporaryFiles = [];
private string $prusaSlicerPath;
public function __construct(
private readonly ?string $configFile = null,
?string $prusaSlicerPath = null,
private readonly StlRenderer $stlRenderer = new StlRenderer(),
) {
$this->prusaSlicerPath = $prusaSlicerPath ?? $this->locatePrusaSlicer();
}
// ...
public function render(string $outputFile, string $scadContent): void
{
$stlFile = $this->createTemporaryStlFile();
$this->stlRenderer->render($stlFile, $scadContent);
// we specify that we want to export a gcode
$command = "{$this->prusaSlicerPath} --gcode -o ";
// in previous part you can see '-o' which means output file, we need to check whether we're running a flatpak
// or not to give the correct path
if ($this->isFlatpak()) {
// this is a special syntax that tells the flatpak system to forward real filesystem files to the app
$command .= "@@ '{$outputFile}' @@";
} else {
$command .= "'{$outputFile}'";
}
$command .= ' ';
// we do the same for input file
if ($this->isFlatpak()) {
$command .= "@@ '{$stlFile}' @@";
} else {
$command .= "'{$stlFile}'";
}
if ($this->configFile) {
$command .= " --load ";
if ($this->isFlatpak()) {
$command .= "@@ '{$this->configFile}' @@";
} else {
$command .= "'{$this->configFile}'";
}
}
// this tells the shell that error output should be redirected to standard output
$command .= " 2>&1";
exec($command, $output, result_code: $exitCode);
if ($exitCode !== 0) {
throw new RuntimeException('There was an error while slicing your model: ' . implode("\n", $output));
}
}
// ...
}
```
The whole command then might look something like this:
`flatpak run --file-forwarding com.prusa3d.PrusaSlicer --gcode -o @@ '/home/user/PhpScad/output.gcode' @@ @@ '/tmp/PhpScadGCodeRendererdafabR.stl' @@ --load @@ '/home/user/PhpScad/doc/prusa-mini.ini' @@ 2>&1`
You can check the [example config file](prusa-mini.ini) to see what it looks like.
Now let's try it!
```php
<?php
use Rikudou\PhpScad\FacetsConfiguration\FacetsNumber;
use Rikudou\PhpScad\Renderer\GCodeRenderer;
use Rikudou\PhpScad\ScadModel;
use Rikudou\PhpScad\Shape\Cube;
require __DIR__ . '/vendor/autoload.php';
(new ScadModel(facetsConfiguration: new FacetsNumber(60)))
->withRenderable(
new Cube(5, 10, 15),
)
->withRenderer(new GCodeRenderer(configFile: __DIR__ . '/prusa-mini.ini'))
->render(__DIR__ . '/output.gcode');
```
And sure enough, the generated gcode looks good!
![Rendered GCode](img/renderer-gcode.png)
Just in case, this is the whole class:
```php
<?php
namespace Rikudou\PhpScad\Renderer;
use RuntimeException;
final class GCodeRenderer implements Renderer
{
/**
* @var array<string>
*/
private array $temporaryFiles = [];
private string $prusaSlicerPath;
public function __construct(
private readonly ?string $configFile = null,
?string $prusaSlicerPath = null,
private readonly StlRenderer $stlRenderer = new StlRenderer(),
) {
$this->prusaSlicerPath = $prusaSlicerPath ?? $this->locatePrusaSlicer();
}
public function __destruct()
{
foreach ($this->temporaryFiles as $temporaryFile) {
if (is_file($temporaryFile)) {
unlink($temporaryFile);
}
}
}
public function render(string $outputFile, string $scadContent): void
{
$stlFile = $this->createTemporaryStlFile();
$this->stlRenderer->render($stlFile, $scadContent);
// we specify that we want to export a gcode
$command = "{$this->prusaSlicerPath} --gcode -o ";
// in previous part you can see '-o' which means output file, we need to check whether we're running a flatpak
// or not to give the correct path
if ($this->isFlatpak()) {
// this is a special syntax that tells the flatpak system to forward real filesystem files to the app
$command .= "@@ '{$outputFile}' @@";
} else {
$command .= "'{$outputFile}'";
}
$command .= ' ';
// we do the same for input file
if ($this->isFlatpak()) {
$command .= "@@ '{$stlFile}' @@";
} else {
$command .= "'{$stlFile}'";
}
if ($this->configFile) {
$command .= " --load ";
if ($this->isFlatpak()) {
$command .= "@@ '{$this->configFile}' @@";
} else {
$command .= "'{$this->configFile}'";
}
}
// this tells the shell that error output should be redirected to standard output
$command .= " 2>&1";
exec($command, $output, result_code: $exitCode);
if ($exitCode !== 0) {
throw new RuntimeException('There was an error while slicing your model: ' . implode("\n", $output));
}
}
private function locatePrusaSlicer(): string
{
// try finding the binary in $PATH
exec('which prusa-slicer', $output, $exitCode);
if ($exitCode === 0) { // in Unix, 0 means success
return "'{$output[0]}'"; // the path might contain a space, enclose it in a single quote
}
$exception = new RuntimeException('Cannot find PrusaSlicer in path.');
exec('which flatpak', result_code: $exitCode);
if ($exitCode !== 0) { // if flatpak doesn't exist, no need to check further
throw $exception;
}
// if the flatpak is not installed, throw an exception
exec('flatpak info com.prusa3d.PrusaSlicer', result_code: $exitCode);
if ($exitCode !== 0) {
throw $exception;
}
// we will need the --file-forwarding flag if we want the Slicer to have access to the files we'll be creating
return 'flatpak run --file-forwarding com.prusa3d.PrusaSlicer';
}
private function isFlatpak(): bool
{
return str_starts_with($this->prusaSlicerPath, 'flatpak ');
}
private function createTemporaryStlFile(): string
{
$file = tempnam(sys_get_temp_dir(), 'PhpScadGCodeRenderer');
rename($file, "{$file}.stl");
$this->temporaryFiles[] = $file;
return "{$file}.stl";
}
}
```

BIN
doc/img/renderer-gcode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

292
doc/prusa-mini.ini Normal file
View File

@ -0,0 +1,292 @@
# generated by PrusaSlicer 2.5.2 on 2023-06-09 at 16:16:29 UTC
avoid_crossing_perimeters = 0
avoid_crossing_perimeters_max_detour = 0
bed_custom_model =
bed_custom_texture =
bed_shape = 0x0,180x0,180x180,0x180
bed_temperature = 60
before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\n\n
between_objects_gcode =
bottom_fill_pattern = monotonic
bottom_solid_layers = 4
bottom_solid_min_thickness = 0.5
bridge_acceleration = 1000
bridge_angle = 0
bridge_fan_speed = 100
bridge_flow_ratio = 0.95
bridge_speed = 25
brim_separation = 0.1
brim_type = outer_only
brim_width = 0
clip_multipart_objects = 1
color_change_gcode = M600
colorprint_heights =
compatible_printers_condition_cummulative = "printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4";"nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)"
complete_objects = 0
cooling = 1
cooling_tube_length = 5
cooling_tube_retraction = 91.5
default_acceleration = 1000
default_filament_profile = "Prusament PLA"
default_print_profile = 0.15mm QUALITY @MINI
deretract_speed = 40
disable_fan_first_layers = 1
dont_support_bridges = 0
draft_shield = disabled
duplicate_distance = 6
elefant_foot_compensation = 0.2
end_filament_gcode = "; Filament-specific end gcode"
end_gcode = G1 E-1 F2100 ; retract\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F720 ; Move print head up{endif}\nG1 X178 Y178 F4200 ; park print head\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)} F720 ; Move print head further up{endif}\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM221 S100 ; reset flow\nM900 K0 ; reset LA\nM84 ; disable motors\n; max_layer_z = [max_layer_z]
ensure_vertical_shell_thickness = 1
external_perimeter_acceleration = 800
external_perimeter_extrusion_width = 0.45
external_perimeter_speed = 30
external_perimeters_first = 0
extra_loading_move = -2
extra_perimeters = 0
extruder_clearance_height = 20
extruder_clearance_radius = 35
extruder_colour = ""
extruder_offset = 0x0
extrusion_axis = E
extrusion_multiplier = 1
extrusion_width = 0.45
fan_always_on = 1
fan_below_layer_time = 100
filament_colour = #FF8000
filament_cooling_final_speed = 3.4
filament_cooling_initial_speed = 2.2
filament_cooling_moves = 4
filament_cost = 36.29
filament_density = 1.24
filament_deretract_speed = nil
filament_diameter = 1.75
filament_load_time = 0
filament_loading_speed = 28
filament_loading_speed_start = 3
filament_max_volumetric_speed = 15
filament_minimal_purge_on_wipe_tower = 15
filament_notes = "Affordable filament for everyday printing in premium quality manufactured in-house by Josef Prusa"
filament_ramming_parameters = "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6"
filament_retract_before_travel = nil
filament_retract_before_wipe = nil
filament_retract_layer_change = nil
filament_retract_length = nil
filament_retract_lift = nil
filament_retract_lift_above = nil
filament_retract_lift_below = nil
filament_retract_restart_extra = nil
filament_retract_speed = nil
filament_settings_id = "Prusament PLA"
filament_soluble = 0
filament_spool_weight = 201
filament_toolchange_delay = 0
filament_type = PLA
filament_unload_time = 0
filament_unloading_speed = 90
filament_unloading_speed_start = 100
filament_vendor = Prusa Polymers
filament_wipe = nil
fill_angle = 45
fill_density = 15%
fill_pattern = gyroid
first_layer_acceleration = 600
first_layer_acceleration_over_raft = 0
first_layer_bed_temperature = 60
first_layer_extrusion_width = 0.42
first_layer_height = 0.2
first_layer_speed = 20
first_layer_speed_over_raft = 30
first_layer_temperature = 215
full_fan_speed_layer = 4
fuzzy_skin = none
fuzzy_skin_point_dist = 0.8
fuzzy_skin_thickness = 0.3
gap_fill_enabled = 1
gap_fill_speed = 40
gcode_comments = 0
gcode_flavor = marlin2
gcode_label_objects = 1
gcode_resolution = 0.0125
gcode_substitutions =
high_current_on_filament_swap = 0
host_type = octoprint
infill_acceleration = 1500
infill_anchor = 2.5
infill_anchor_max = 12
infill_every_layers = 1
infill_extruder = 1
infill_extrusion_width = 0.45
infill_first = 0
infill_only_where_needed = 0
infill_overlap = 10%
infill_speed = 80
interface_shells = 0
ironing = 0
ironing_flowrate = 15%
ironing_spacing = 0.1
ironing_speed = 15
ironing_type = top
layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z]
layer_height = 0.2
machine_limits_usage = emit_to_gcode
machine_max_acceleration_e = 5000
machine_max_acceleration_extruding = 2000
machine_max_acceleration_retracting = 1250
machine_max_acceleration_travel = 2500
machine_max_acceleration_x = 2500
machine_max_acceleration_y = 2500
machine_max_acceleration_z = 400
machine_max_feedrate_e = 80
machine_max_feedrate_x = 180
machine_max_feedrate_y = 180
machine_max_feedrate_z = 12
machine_max_jerk_e = 10
machine_max_jerk_x = 8
machine_max_jerk_y = 8
machine_max_jerk_z = 2
machine_min_extruding_rate = 0
machine_min_travel_rate = 0
max_fan_speed = 100
max_layer_height = 0.25
max_print_height = 180
max_print_speed = 150
max_volumetric_extrusion_rate_slope_negative = 0
max_volumetric_extrusion_rate_slope_positive = 0
max_volumetric_speed = 0
min_bead_width = 85%
min_fan_speed = 100
min_feature_size = 25%
min_layer_height = 0.07
min_print_speed = 15
min_skirt_length = 4
mmu_segmented_region_max_width = 0
notes =
nozzle_diameter = 0.4
only_retract_when_crossing_perimeters = 0
ooze_prevention = 0
output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode
overhangs = 1
parking_pos_retraction = 92
pause_print_gcode = M601
perimeter_acceleration = 900
perimeter_extruder = 1
perimeter_extrusion_width = 0.45
perimeter_generator = arachne
perimeter_speed = 40
perimeters = 2
physical_printer_settings_id =
post_process =
print_host =
print_settings_id = 0.20mm QUALITY @MINI
printer_model = MINI
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MINI\n
printer_settings_id = Original Prusa MINI & MINI+
printer_technology = FFF
printer_variant = 0.4
printer_vendor =
printhost_apikey =
printhost_cafile =
raft_contact_distance = 0.2
raft_expansion = 1.5
raft_first_layer_density = 90%
raft_first_layer_expansion = 3
raft_layers = 0
remaining_times = 1
resolution = 0
retract_before_travel = 1.5
retract_before_wipe = 70%
retract_layer_change = 1
retract_length = 3.2
retract_length_toolchange = 4
retract_lift = 0.2
retract_lift_above = 0
retract_lift_below = 179
retract_restart_extra = 0
retract_restart_extra_toolchange = 0
retract_speed = 70
seam_position = aligned
silent_mode = 0
single_extruder_multi_material = 0
single_extruder_multi_material_priming = 1
skirt_distance = 2
skirt_height = 3
skirts = 1
slice_closing_radius = 0.049
slicing_mode = regular
slowdown_below_layer_time = 10
small_perimeter_speed = 25
solid_infill_acceleration = 1500
solid_infill_below_area = 0
solid_infill_every_layers = 0
solid_infill_extruder = 1
solid_infill_extrusion_width = 0.45
solid_infill_speed = 80
spiral_vase = 0
standby_temperature_delta = -5
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0"
start_gcode = M862.3 P "[printer_model]" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM204 T1250 ; set travel acceleration\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM204 T[machine_max_acceleration_travel] ; restore travel acceleration\nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0\nG1 Y-2 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110 E8 F900\nG1 X40 E10 F700\nG92 E0\n\nM221 S95 ; set flow
support_material = 0
support_material_angle = 0
support_material_auto = 1
support_material_bottom_contact_distance = 0
support_material_bottom_interface_layers = 0
support_material_buildplate_only = 0
support_material_closing_radius = 2
support_material_contact_distance = 0.2
support_material_enforce_layers = 0
support_material_extruder = 0
support_material_extrusion_width = 0.35
support_material_interface_contact_loops = 0
support_material_interface_extruder = 0
support_material_interface_layers = 2
support_material_interface_pattern = rectilinear
support_material_interface_spacing = 0.2
support_material_interface_speed = 80%
support_material_pattern = rectilinear
support_material_spacing = 2
support_material_speed = 40
support_material_style = grid
support_material_synchronize_layers = 0
support_material_threshold = 50
support_material_with_sheath = 0
support_material_xy_spacing = 60%
temperature = 215
template_custom_gcode =
thick_bridges = 0
thin_walls = 0
threads = 20
thumbnails = 16x16,220x124
thumbnails_format = PNG
toolchange_gcode =
top_fill_pattern = monotoniclines
top_infill_extrusion_width = 0.4
top_solid_infill_acceleration = 800
top_solid_infill_speed = 40
top_solid_layers = 6
top_solid_min_thickness = 0.7
travel_speed = 150
travel_speed_z = 12
use_firmware_retraction = 0
use_relative_e_distances = 1
use_volumetric_e = 0
variable_layer_height = 1
wall_distribution_count = 1
wall_transition_angle = 10
wall_transition_filter_deviation = 25%
wall_transition_length = 100%
wipe = 1
wipe_into_infill = 0
wipe_into_objects = 0
wipe_tower = 0
wipe_tower_bridging = 10
wipe_tower_brim_width = 2
wipe_tower_no_sparse_layers = 0
wipe_tower_rotation_angle = 0
wipe_tower_width = 60
wipe_tower_x = 170
wipe_tower_y = 140
wiping_volumes_extruders = 70,70
wiping_volumes_matrix = 0
xy_size_compensation = 0
z_offset = 0

View File

@ -21,6 +21,10 @@ abstract class AbstractOpenScadBinaryRenderer implements Renderer
$skipQuotes = false;
if ($argument === self::TARGET_FILE_PLACEHOLDER) {
$argument = $outputFile;
if ($this->isFlatpak()) {
$argument = "@@ '{$argument}' @@";
$skipQuotes = true;
}
}
if ($argument === self::SCAD_FILE_PLACEHOLDER) {
$renderer = new ScadFileRenderer();
@ -29,7 +33,7 @@ abstract class AbstractOpenScadBinaryRenderer implements Renderer
$argument = $file;
if ($this->isFlatpak()) {
$argument = "@@ {$argument} @@";
$argument = "@@ '{$argument}' @@";
$skipQuotes = true;
}
}