Fixed conflicts after merge with master
This commit is contained in:
commit
064bc63b23
181 changed files with 4345 additions and 3577 deletions
|
@ -139,7 +139,6 @@ sub mesh {
|
|||
|
||||
my $mesh = Slic3r::TriangleMesh->new;
|
||||
$mesh->ReadFromPerl($vertices, $facets);
|
||||
$mesh->repair;
|
||||
$mesh->scale_xyz(Slic3r::Pointf3->new(@{$params{scale_xyz}})) if $params{scale_xyz};
|
||||
$mesh->translate(@{$params{translate}}) if $params{translate};
|
||||
return $mesh;
|
||||
|
|
17
resources/icons/exclamation_manifold.svg
Normal file
17
resources/icons/exclamation_manifold.svg
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#808080" d="M8,11c-0.55,0-1-0.45-1-1V7c0-0.55,0.45-1,1-1s1,0.45,1,1v3C9,10.55,8.55,11,8,11z"/>
|
||||
</g>
|
||||
<g>
|
||||
<circle fill="#808080" cx="8" cy="13" r="1"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#808080" d="M15,15.5H1c-0.18,0-0.34-0.09-0.43-0.24c-0.09-0.15-0.09-0.34-0.01-0.49l7-13c0.17-0.32,0.71-0.32,0.88,0
|
||||
l7,13c0.08,0.16,0.08,0.34-0.01,0.49C15.34,15.41,15.18,15.5,15,15.5z M1.84,14.5h12.33L8,3.05L1.84,14.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 781 B |
17
resources/icons/white/exclamation_manifold.svg
Normal file
17
resources/icons/white/exclamation_manifold.svg
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M8,11c-0.55,0-1-0.45-1-1V7c0-0.55,0.45-1,1-1s1,0.45,1,1v3C9,10.55,8.55,11,8,11z"/>
|
||||
</g>
|
||||
<g>
|
||||
<circle fill="#FFFFFF" cx="8" cy="13" r="1"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M15,15.5H1c-0.18,0-0.34-0.09-0.43-0.24c-0.09-0.15-0.09-0.34-0.01-0.49l7-13c0.17-0.32,0.71-0.32,0.88,0
|
||||
l7,13c0.08,0.16,0.08,0.34-0.01,0.49C15.34,15.41,15.18,15.5,15,15.5z M1.84,14.5h12.33L8,3.05L1.84,14.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 781 B |
|
@ -1,4 +1,5 @@
|
|||
min_slic3r_version = 2.4.0-alpha0
|
||||
1.4.0-alpha8 Added material profiles for Prusament Resin. Detect bridging perimeters enabled by default.
|
||||
1.4.0-alpha7 Updated brim_separation value. Updated Prusa MINI end g-code. Added Filamentworld filament profiles.
|
||||
1.4.0-alpha6 Added nozzle priming after M600. Added nozzle diameter checks for 0.8 nozzle printer profiles. Updated FW version. Increased number of top solid infill layers (0.2 layer height).
|
||||
1.4.0-alpha5 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S).
|
||||
|
@ -11,9 +12,11 @@ min_slic3r_version = 2.4.0-alpha0
|
|||
1.3.0-alpha1 Added Prusament PCCF. Increased travel acceleration for Prusa MINI. Updated start g-code for Prusa MINI. Added multiple add:north and Extrudr filament profiles. Updated Z travel speed values.
|
||||
1.3.0-alpha0 Disabled thick bridges, updated support settings.
|
||||
min_slic3r_version = 2.3.2-alpha0
|
||||
1.3.2 Added material profiles for Prusament Resin.
|
||||
1.3.1 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S).
|
||||
1.3.0 Added SL1S SPEED profiles.
|
||||
min_slic3r_version = 2.3.0-rc1
|
||||
1.2.9 Added material profiles for Prusament Resin.
|
||||
1.2.8 Added multiple add:north and Extrudr filament profiles.
|
||||
1.2.7 Updated "Prusament PC Blend Carbon Fiber" profile for Prusa MINI.
|
||||
1.2.6 Added filament profile for "Prusament PC Blend Carbon Fiber".
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
name = Prusa Research
|
||||
# Configuration version of this file. Config file will only be installed, if the config_version differs.
|
||||
# This means, the server may force the PrusaSlicer configuration to be downgraded.
|
||||
config_version = 1.4.0-alpha7
|
||||
config_version = 1.4.0-alpha8
|
||||
# Where to get the updates from?
|
||||
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
|
||||
changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
|
||||
|
@ -120,7 +120,7 @@ technology = SLA
|
|||
family = SL1
|
||||
bed_model = sl1_bed.stl
|
||||
bed_texture = sl1.svg
|
||||
default_materials = Prusa Orange Tough @0.05
|
||||
default_materials = Prusa Orange Tough @0.05; Prusament Resin Tough Prusa Orange @0.05
|
||||
|
||||
[printer_model:SL1S]
|
||||
name = Original Prusa SL1S SPEED
|
||||
|
@ -129,7 +129,7 @@ technology = SLA
|
|||
family = SL1
|
||||
bed_model = sl1s_bed.stl
|
||||
bed_texture = sl1s.svg
|
||||
default_materials = Prusa Orange Tough @0.05 SL1S
|
||||
default_materials = Prusa Orange Tough @0.05 SL1S; Prusament Resin Tough Prusa Orange @0.05 SL1S
|
||||
|
||||
# All presets starting with asterisk, for example *common*, are intermediate and they will
|
||||
# not make it into the user interface.
|
||||
|
@ -181,7 +181,7 @@ max_volumetric_extrusion_rate_slope_positive = 0
|
|||
max_volumetric_speed = 0
|
||||
min_skirt_length = 4
|
||||
notes =
|
||||
overhangs = 0
|
||||
overhangs = 1
|
||||
only_retract_when_crossing_perimeters = 0
|
||||
ooze_prevention = 0
|
||||
output_filename_format = {input_filename_base}_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode
|
||||
|
@ -395,6 +395,7 @@ top_solid_min_thickness = 1.2
|
|||
bottom_solid_min_thickness = 0.8
|
||||
single_extruder_multi_material_priming = 0
|
||||
thick_bridges = 1
|
||||
overhangs = 0
|
||||
|
||||
[print:*soluble_support*]
|
||||
overhangs = 1
|
||||
|
@ -460,10 +461,10 @@ bridge_flow_ratio = 1
|
|||
bridge_speed = 20
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1
|
||||
layer_height = 0.1
|
||||
perimeter_acceleration = 800
|
||||
perimeter_acceleration = 600
|
||||
top_solid_layers = 9
|
||||
support_material_contact_distance = 0.17
|
||||
raft_contact_distance = 0.17
|
||||
raft_contact_distance = 0.15
|
||||
|
||||
[print:*0.15mm*]
|
||||
inherits = *common*
|
||||
|
@ -619,6 +620,7 @@ support_material_contact_distance = 0.1
|
|||
raft_contact_distance = 0.2
|
||||
top_solid_infill_speed = 40
|
||||
thick_bridges = 1
|
||||
overhangs = 0
|
||||
|
||||
## MMU1 specific
|
||||
[print:0.15mm OPTIMAL SOLUBLE FULL]
|
||||
|
@ -704,7 +706,7 @@ small_perimeter_speed = 15
|
|||
solid_infill_speed = 40
|
||||
top_solid_infill_speed = 30
|
||||
support_material_contact_distance = 0.08
|
||||
raft_contact_distance = 0.08
|
||||
raft_contact_distance = 0.07
|
||||
|
||||
## MK2 - 0.6mm nozzle
|
||||
|
||||
|
@ -772,6 +774,7 @@ single_extruder_multi_material_priming = 0
|
|||
inherits = 0.35mm FAST
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4
|
||||
single_extruder_multi_material_priming = 0
|
||||
overhangs = 0
|
||||
|
||||
## MK2.5 - MMU2 specific
|
||||
|
||||
|
@ -1013,7 +1016,7 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and
|
|||
fill_pattern = grid
|
||||
fill_density = 20%
|
||||
support_material_contact_distance = 0.08
|
||||
raft_contact_distance = 0.08
|
||||
raft_contact_distance = 0.07
|
||||
|
||||
## MK3 - 0.6mm nozzle
|
||||
|
||||
|
@ -1028,7 +1031,7 @@ perimeter_speed = 45
|
|||
solid_infill_speed = 70
|
||||
top_solid_infill_speed = 45
|
||||
support_material_contact_distance = 0.22
|
||||
raft_contact_distance = 0.22
|
||||
raft_contact_distance = 0.2
|
||||
bridge_flow_ratio = 1
|
||||
|
||||
[print:0.20mm DETAIL @0.6 nozzle MK3]
|
||||
|
@ -1042,7 +1045,7 @@ perimeter_speed = 45
|
|||
solid_infill_speed = 70
|
||||
top_solid_infill_speed = 45
|
||||
support_material_contact_distance = 0.22
|
||||
raft_contact_distance = 0.22
|
||||
raft_contact_distance = 0.2
|
||||
bridge_flow_ratio = 1
|
||||
|
||||
[print:0.30mm QUALITY @0.6 nozzle MK3]
|
||||
|
@ -1311,7 +1314,7 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and
|
|||
fill_pattern = grid
|
||||
fill_density = 20%
|
||||
support_material_contact_distance = 0.08
|
||||
raft_contact_distance = 0.08
|
||||
raft_contact_distance = 0.07
|
||||
|
||||
# MINI - 0.6mm nozzle
|
||||
|
||||
|
@ -4496,7 +4499,31 @@ initial_exposure_time = 35
|
|||
material_type = Tough
|
||||
material_vendor = Monocure
|
||||
|
||||
## Prusa
|
||||
## Prusa Polymers 0.025
|
||||
|
||||
[sla_material:Prusament Resin Tough Prusa Orange @0.025]
|
||||
inherits = *common 0.025*
|
||||
exposure_time = 5
|
||||
initial_exposure_time = 35
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
[sla_material:Prusament Resin Tough Rich Black @0.025]
|
||||
inherits = *common 0.025*
|
||||
exposure_time = 5
|
||||
initial_exposure_time = 35
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
[sla_material:Prusament Resin Tough Anthracite Grey @0.025]
|
||||
inherits = *common 0.025*
|
||||
exposure_time = 6
|
||||
initial_exposure_time = 35
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
## Prusa 0.025
|
||||
|
||||
[sla_material:Prusa Orange Tough @0.025]
|
||||
inherits = *common 0.025*
|
||||
exposure_time = 6
|
||||
|
@ -5185,7 +5212,30 @@ initial_exposure_time = 35
|
|||
material_type = Tough
|
||||
material_vendor = Zortrax
|
||||
|
||||
## Prusa
|
||||
## Prusa Polymers 0.05
|
||||
|
||||
[sla_material:Prusament Resin Tough Prusa Orange @0.05]
|
||||
inherits = *common 0.05*
|
||||
exposure_time = 6
|
||||
initial_exposure_time = 35
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
[sla_material:Prusament Resin Tough Rich Black @0.05]
|
||||
inherits = *common 0.05*
|
||||
exposure_time = 6
|
||||
initial_exposure_time = 35
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
[sla_material:Prusament Resin Tough Anthracite Grey @0.05]
|
||||
inherits = *common 0.05*
|
||||
exposure_time = 7
|
||||
initial_exposure_time = 35
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
## Prusa 0.05
|
||||
|
||||
[sla_material:Prusa Beige Tough @0.05]
|
||||
inherits = *common 0.05*
|
||||
|
@ -5447,7 +5497,30 @@ initial_exposure_time = 50
|
|||
material_type = Tough
|
||||
material_vendor = BlueCast
|
||||
|
||||
## Prusa
|
||||
## Prusa Polymers 0.1
|
||||
|
||||
[sla_material:Prusament Resin Tough Prusa Orange @0.1]
|
||||
inherits = *common 0.1*
|
||||
exposure_time = 13
|
||||
initial_exposure_time = 45
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
[sla_material:Prusament Resin Tough Rich Black @0.1]
|
||||
inherits = *common 0.1*
|
||||
exposure_time = 13
|
||||
initial_exposure_time = 45
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
[sla_material:Prusament Resin Tough Anthracite Grey @0.1]
|
||||
inherits = *common 0.1*
|
||||
exposure_time = 14
|
||||
initial_exposure_time = 45
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
## Prusa 0.1
|
||||
|
||||
[sla_material:Prusa Orange Tough @0.1]
|
||||
inherits = *common 0.1*
|
||||
|
@ -5530,6 +5603,31 @@ material_vendor = Made for Prusa
|
|||
|
||||
## 0.025 SL1S
|
||||
|
||||
## Prusa Polymers 0.025
|
||||
|
||||
[sla_material:Prusament Resin Tough Prusa Orange @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 1.8
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
[sla_material:Prusament Resin Tough Rich Black @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 1.8
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
[sla_material:Prusament Resin Tough Anthracite Grey @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 2
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
## Made for Prusa 0.025
|
||||
|
||||
[sla_material:Prusa Orange Tough @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 1.8
|
||||
|
@ -5644,6 +5742,31 @@ material_vendor = Peopoly
|
|||
|
||||
## 0.05 SL1S
|
||||
|
||||
## Prusa Polymers 0.05
|
||||
|
||||
[sla_material:Prusament Resin Tough Prusa Orange @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 2
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
[sla_material:Prusament Resin Tough Rich Black @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 2
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
[sla_material:Prusament Resin Tough Anthracite Grey @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 2.4
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
## Made for Prusa 0.05
|
||||
|
||||
[sla_material:Prusa Orange Tough @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 2
|
||||
|
@ -5758,6 +5881,31 @@ material_vendor = Peopoly
|
|||
|
||||
## 0.1 SL1S
|
||||
|
||||
## Prusa Polymers 0.1
|
||||
|
||||
[sla_material:Prusament Resin Tough Prusa Orange @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 2.6
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
[sla_material:Prusament Resin Tough Rich Black @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 2.6
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
[sla_material:Prusament Resin Tough Anthracite Grey @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 3
|
||||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
|
||||
## Made for Prusa 0.1
|
||||
|
||||
[sla_material:Prusa Orange Tough @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 2.6
|
||||
|
@ -6573,7 +6721,7 @@ nozzle_diameter = 0.6
|
|||
max_layer_height = 0.40
|
||||
min_layer_height = 0.15
|
||||
default_print_profile = 0.30mm QUALITY @0.6 nozzle MINI
|
||||
retract_length = 3.5
|
||||
retract_length = 3.2
|
||||
retract_before_travel = 1.5
|
||||
|
||||
[printer:Original Prusa MINI & MINI+ 0.8 nozzle]
|
||||
|
|
|
@ -34,9 +34,7 @@ void main()
|
|||
float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
|
||||
|
||||
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
|
||||
float width = 1.5 * i_scales.x;
|
||||
float height = 1.5 * i_scales.y;
|
||||
vec4 world_position = vec4(v_position * vec3(vec2(width), height) + i_offset - vec3(0.0, 0.0, 0.5 * i_scales.y), 1.0);
|
||||
vec4 world_position = vec4(v_position * vec3(vec2(1.5 * i_scales.x), 1.5 * i_scales.y) + i_offset - vec3(0.0, 0.0, 0.5 * i_scales.y), 1.0);
|
||||
vec3 eye_position = (gl_ModelViewMatrix * world_position).xyz;
|
||||
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
|
||||
|
||||
|
|
|
@ -212,8 +212,7 @@ int main(const int argc, const char *argv[])
|
|||
return -1;
|
||||
}
|
||||
|
||||
mesh.repair();
|
||||
if (mesh.facets_count() == 0) {
|
||||
if (mesh.empty()) {
|
||||
std::cerr << "Error loading " << argv[1] << " . It is empty." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ int main(const int argc, const char * argv[])
|
|||
TriangleMesh input;
|
||||
|
||||
input.ReadSTLFile(argv[1]);
|
||||
input.repair();
|
||||
|
||||
Benchmark bench;
|
||||
|
||||
|
|
|
@ -145,7 +145,7 @@ void IndexedVertexArray::load_mesh(const TriangleMesh &mesh)
|
|||
this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count());
|
||||
|
||||
int vertices_count = 0;
|
||||
for (size_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
|
||||
for (size_t i = 0; i < mesh.facets_count(); ++i) {
|
||||
const stl_facet &facet = mesh.stl.facet_start[i];
|
||||
for (int j = 0; j < 3; ++j)
|
||||
this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2));
|
||||
|
@ -409,7 +409,6 @@ void CSGDisplay::on_scene_updated(const Scene &scene)
|
|||
interior.transform(po->trafo().inverse());
|
||||
|
||||
mshinst.merge(interior);
|
||||
mshinst.require_shared_vertices();
|
||||
|
||||
mi->transform_mesh(&mshinst);
|
||||
|
||||
|
@ -417,14 +416,12 @@ void CSGDisplay::on_scene_updated(const Scene &scene)
|
|||
auto center = bb.center().cast<float>();
|
||||
mshinst.translate(-center);
|
||||
|
||||
mshinst.require_shared_vertices();
|
||||
m_scene_cache.add_mesh(mshinst, OpenCSG::Intersection,
|
||||
m_csgsettings.get_convexity());
|
||||
}
|
||||
|
||||
for (const sla::DrainHole &holept : holedata) {
|
||||
TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh());
|
||||
holemesh.require_shared_vertices();
|
||||
m_scene_cache.add_mesh(holemesh, OpenCSG::Subtraction, 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@ void ShaderCSGDisplay::on_scene_updated(const Scene &scene)
|
|||
interior.transform(po->trafo().inverse());
|
||||
|
||||
mshinst.merge(interior);
|
||||
mshinst.require_shared_vertices();
|
||||
|
||||
mi->transform_mesh(&mshinst);
|
||||
|
||||
|
@ -51,15 +50,11 @@ void ShaderCSGDisplay::on_scene_updated(const Scene &scene)
|
|||
auto center = bb.center().cast<float>();
|
||||
mshinst.translate(-center);
|
||||
|
||||
mshinst.require_shared_vertices();
|
||||
add_mesh(mshinst);
|
||||
}
|
||||
|
||||
for (const sla::DrainHole &holept : holedata) {
|
||||
TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh());
|
||||
holemesh.require_shared_vertices();
|
||||
add_mesh(holemesh);
|
||||
}
|
||||
for (const sla::DrainHole &holept : holedata)
|
||||
add_mesh(sla::to_triangle_mesh(holept.to_mesh()));
|
||||
}
|
||||
|
||||
repaint();
|
||||
|
|
|
@ -397,7 +397,7 @@ int CLI::run(int argc, char **argv)
|
|||
TriangleMesh mesh = model.mesh();
|
||||
mesh.repair();
|
||||
|
||||
TriangleMeshPtrs meshes = mesh.cut_by_grid(m_config.option<ConfigOptionPoint>("cut_grid")->value);
|
||||
std::vector<TriangleMesh> meshes = mesh.cut_by_grid(m_config.option<ConfigOptionPoint>("cut_grid")->value);
|
||||
size_t i = 0;
|
||||
for (TriangleMesh* m : meshes) {
|
||||
Model out;
|
||||
|
|
|
@ -239,6 +239,7 @@ private:
|
|||
return edge_a.facet_number != edge_b.facet_number && edge_a == edge_b;
|
||||
}
|
||||
|
||||
// Connect edge_a with edge_b, update edge connection statistics.
|
||||
static void record_neighbors(stl_file *stl, const HashEdge &edge_a, const HashEdge &edge_b)
|
||||
{
|
||||
// Facet a's neighbor is facet b
|
||||
|
@ -249,7 +250,7 @@ private:
|
|||
stl->neighbors_start[edge_b.facet_number].neighbor[edge_b.which_edge % 3] = edge_a.facet_number; /* sets the .neighbor part */
|
||||
stl->neighbors_start[edge_b.facet_number].which_vertex_not[edge_b.which_edge % 3] = (edge_a.which_edge + 2) % 3; /* sets the .which_vertex_not part */
|
||||
|
||||
if (((edge_a.which_edge < 3) && (edge_b.which_edge < 3)) || ((edge_a.which_edge > 2) && (edge_b.which_edge > 2))) {
|
||||
if ((edge_a.which_edge < 3 && edge_b.which_edge < 3) || (edge_a.which_edge > 2 && edge_b.which_edge > 2)) {
|
||||
// These facets are oriented in opposite directions, their normals are probably messed up.
|
||||
stl->neighbors_start[edge_a.facet_number].which_vertex_not[edge_a.which_edge % 3] += 3;
|
||||
stl->neighbors_start[edge_b.facet_number].which_vertex_not[edge_b.which_edge % 3] += 3;
|
||||
|
@ -479,12 +480,13 @@ void stl_check_facets_exact(stl_file *stl)
|
|||
|
||||
void stl_check_facets_nearby(stl_file *stl, float tolerance)
|
||||
{
|
||||
if ( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets)
|
||||
&& (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets)
|
||||
&& (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)) {
|
||||
assert(stl->stats.connected_facets_3_edge <= stl->stats.connected_facets_2_edge);
|
||||
assert(stl->stats.connected_facets_2_edge <= stl->stats.connected_facets_1_edge);
|
||||
assert(stl->stats.connected_facets_1_edge <= stl->stats.number_of_facets);
|
||||
|
||||
if (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)
|
||||
// No need to check any further. All facets are connected.
|
||||
return;
|
||||
}
|
||||
|
||||
HashTableEdges hash_table(stl->stats.number_of_facets);
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
|
@ -514,22 +516,12 @@ void stl_remove_unconnected_facets(stl_file *stl)
|
|||
/* Update list of connected edges */
|
||||
stl_neighbors &neighbors = stl->neighbors_start[facet_number];
|
||||
// Update statistics on unconnected triangle edges.
|
||||
switch ((neighbors.neighbor[0] == -1) + (neighbors.neighbor[1] == -1) + (neighbors.neighbor[2] == -1)) {
|
||||
case 0: // Facet has 3 neighbors
|
||||
-- stl->stats.connected_facets_3_edge;
|
||||
-- stl->stats.connected_facets_2_edge;
|
||||
-- stl->stats.connected_facets_1_edge;
|
||||
break;
|
||||
case 1: // Facet has 2 neighbors
|
||||
-- stl->stats.connected_facets_2_edge;
|
||||
-- stl->stats.connected_facets_1_edge;
|
||||
break;
|
||||
case 2: // Facet has 1 neighbor
|
||||
-- stl->stats.connected_facets_1_edge;
|
||||
case 3: // Facet has 0 neighbors
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
switch (neighbors.num_neighbors()) {
|
||||
case 3: -- stl->stats.connected_facets_3_edge; // fall through
|
||||
case 2: -- stl->stats.connected_facets_2_edge; // fall through
|
||||
case 1: -- stl->stats.connected_facets_1_edge; // fall through
|
||||
case 0: break;
|
||||
default: assert(false);
|
||||
}
|
||||
|
||||
if (facet_number < int(-- stl->stats.number_of_facets)) {
|
||||
|
@ -555,20 +547,14 @@ void stl_remove_unconnected_facets(stl_file *stl)
|
|||
|
||||
auto remove_degenerate = [stl, remove_facet](int facet)
|
||||
{
|
||||
// Update statistics on face connectivity.
|
||||
auto stl_update_connects_remove_1 = [stl](int facet_num) {
|
||||
//FIXME when decreasing 3_edge, should I increase 2_edge etc?
|
||||
switch ((stl->neighbors_start[facet_num].neighbor[0] == -1) + (stl->neighbors_start[facet_num].neighbor[1] == -1) + (stl->neighbors_start[facet_num].neighbor[2] == -1)) {
|
||||
case 0: // Facet has 3 neighbors
|
||||
-- stl->stats.connected_facets_3_edge; break;
|
||||
case 1: // Facet has 2 neighbors
|
||||
-- stl->stats.connected_facets_2_edge; break;
|
||||
case 2: // Facet has 1 neighbor
|
||||
-- stl->stats.connected_facets_1_edge; break;
|
||||
case 3: // Facet has 0 neighbors
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
// Update statistics on face connectivity after one edge was disconnected on the facet "facet_num".
|
||||
auto update_connects_remove_1 = [stl](int facet_num) {
|
||||
switch (stl->neighbors_start[facet_num].num_neighbors()) {
|
||||
case 0: assert(false); break;
|
||||
case 1: -- stl->stats.connected_facets_1_edge; break;
|
||||
case 2: -- stl->stats.connected_facets_2_edge; break;
|
||||
case 3: -- stl->stats.connected_facets_3_edge; break;
|
||||
default: assert(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -604,9 +590,9 @@ void stl_remove_unconnected_facets(stl_file *stl)
|
|||
|
||||
// Update statistics on edge connectivity.
|
||||
if ((neighbor[0] == -1) && (neighbor[1] != -1))
|
||||
stl_update_connects_remove_1(neighbor[1]);
|
||||
update_connects_remove_1(neighbor[1]);
|
||||
if ((neighbor[1] == -1) && (neighbor[0] != -1))
|
||||
stl_update_connects_remove_1(neighbor[0]);
|
||||
update_connects_remove_1(neighbor[0]);
|
||||
|
||||
if (neighbor[0] >= 0) {
|
||||
if (neighbor[1] >= 0) {
|
||||
|
@ -634,7 +620,7 @@ void stl_remove_unconnected_facets(stl_file *stl)
|
|||
stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = vnot[0];
|
||||
}
|
||||
if (neighbor[2] >= 0) {
|
||||
stl_update_connects_remove_1(neighbor[2]);
|
||||
update_connects_remove_1(neighbor[2]);
|
||||
stl->neighbors_start[neighbor[2]].neighbor[(vnot[2] + 1) % 3] = -1;
|
||||
}
|
||||
|
||||
|
@ -652,11 +638,9 @@ void stl_remove_unconnected_facets(stl_file *stl)
|
|||
++ i;
|
||||
|
||||
if (stl->stats.connected_facets_1_edge < (int)stl->stats.number_of_facets) {
|
||||
// remove completely unconnected facets
|
||||
// There are some faces with no connected edge at all. Remove completely unconnected facets.
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets;)
|
||||
if (stl->neighbors_start[i].neighbor[0] == -1 &&
|
||||
stl->neighbors_start[i].neighbor[1] == -1 &&
|
||||
stl->neighbors_start[i].neighbor[2] == -1) {
|
||||
if (stl->neighbors_start[i].num_neighbors() == 0) {
|
||||
// This facet is completely unconnected. Remove it.
|
||||
remove_facet(i);
|
||||
assert(stl_validate(stl));
|
||||
|
|
|
@ -79,8 +79,7 @@ struct stl_neighbors {
|
|||
which_vertex_not[1] = -1;
|
||||
which_vertex_not[2] = -1;
|
||||
}
|
||||
int num_neighbors_missing() const { return (this->neighbor[0] == -1) + (this->neighbor[1] == -1) + (this->neighbor[2] == -1); }
|
||||
int num_neighbors() const { return 3 - this->num_neighbors_missing(); }
|
||||
int num_neighbors() const { return 3 - ((this->neighbor[0] == -1) + (this->neighbor[1] == -1) + (this->neighbor[2] == -1)); }
|
||||
|
||||
// Index of a neighbor facet.
|
||||
int neighbor[3];
|
||||
|
@ -92,28 +91,44 @@ struct stl_stats {
|
|||
stl_stats() { memset(&header, 0, 81); }
|
||||
char header[81];
|
||||
stl_type type = (stl_type)0;
|
||||
// Should always match the number of facets stored inside stl_file::facet_start.
|
||||
uint32_t number_of_facets = 0;
|
||||
// Bounding box.
|
||||
stl_vertex max = stl_vertex::Zero();
|
||||
stl_vertex min = stl_vertex::Zero();
|
||||
stl_vertex size = stl_vertex::Zero();
|
||||
float bounding_diameter = 0.f;
|
||||
float shortest_edge = 0.f;
|
||||
// After repair, the volume shall always be positive.
|
||||
float volume = -1.f;
|
||||
// Number of face edges connected to another face.
|
||||
// Don't use this statistics after repair, use the connected_facets_1/2/3_edge instead!
|
||||
int connected_edges = 0;
|
||||
// Faces with >=1, >=2 and 3 edges connected to another face.
|
||||
int connected_facets_1_edge = 0;
|
||||
int connected_facets_2_edge = 0;
|
||||
int connected_facets_3_edge = 0;
|
||||
// Faces with 1, 2 and 3 open edges after exact chaining, but before repair.
|
||||
int facets_w_1_bad_edge = 0;
|
||||
int facets_w_2_bad_edge = 0;
|
||||
int facets_w_3_bad_edge = 0;
|
||||
// Number of faces read form an STL file.
|
||||
int original_num_facets = 0;
|
||||
// Number of edges connected one to another by snapping their end vertices.
|
||||
int edges_fixed = 0;
|
||||
// Number of faces removed because they were degenerated.
|
||||
int degenerate_facets = 0;
|
||||
// Total number of facets removed: Degenerate faces and unconnected faces.
|
||||
int facets_removed = 0;
|
||||
// Number of faces added by hole filling.
|
||||
int facets_added = 0;
|
||||
// Number of faces reversed because of negative volume or because one patch was connected to another patch with incompatible normals.
|
||||
int facets_reversed = 0;
|
||||
// Number of incompatible edges remaining after the patches were connected together and possibly their normals flipped.
|
||||
int backwards_edges = 0;
|
||||
// Number of triangles, which were flipped during the fixing process.
|
||||
int normals_fixed = 0;
|
||||
// Number of connected triangle patches.
|
||||
int number_of_parts = 0;
|
||||
|
||||
void clear() { *this = stl_stats(); }
|
||||
|
@ -140,8 +155,6 @@ struct stl_file {
|
|||
|
||||
struct indexed_triangle_set
|
||||
{
|
||||
indexed_triangle_set() {}
|
||||
|
||||
void clear() { indices.clear(); vertices.clear(); }
|
||||
|
||||
size_t memsize() const {
|
||||
|
@ -150,8 +163,6 @@ struct indexed_triangle_set
|
|||
|
||||
std::vector<stl_triangle_vertex_indices> indices;
|
||||
std::vector<stl_vertex> vertices;
|
||||
//FIXME add normals once we get rid of the stl_file from TriangleMesh completely.
|
||||
//std::vector<stl_normal> normals
|
||||
|
||||
bool empty() const { return indices.empty() || vertices.empty(); }
|
||||
};
|
||||
|
@ -244,9 +255,15 @@ inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::Don
|
|||
stl_get_size(stl);
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
inline void its_translate(indexed_triangle_set &its, const V v)
|
||||
{
|
||||
for (stl_vertex &v_dst : its.vertices)
|
||||
v_dst += v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
extern void its_transform(indexed_triangle_set &its, T *trafo3x4)
|
||||
inline void its_transform(indexed_triangle_set &its, T *trafo3x4)
|
||||
{
|
||||
for (stl_vertex &v_dst : its.vertices) {
|
||||
stl_vertex v_src = v_dst;
|
||||
|
|
|
@ -205,11 +205,12 @@ bool stl_write_quad_object(stl_file *stl, char *file)
|
|||
|
||||
fprintf(fp, "CQUAD\n");
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
switch (stl->neighbors_start[i].num_neighbors_missing()) {
|
||||
case 0: color = connect_color; break;
|
||||
case 1: color = uncon_1_color; break;
|
||||
case 2: color = uncon_2_color; break;
|
||||
default: color = uncon_3_color;
|
||||
switch (stl->neighbors_start[i].num_neighbors()) {
|
||||
case 0:
|
||||
default: color = uncon_3_color; break;
|
||||
case 1: color = uncon_2_color; break;
|
||||
case 2: color = uncon_1_color; break;
|
||||
case 3: color = connect_color; break;
|
||||
}
|
||||
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), stl->facet_start[i].vertex[0](2), color(0), color(1), color(2));
|
||||
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), stl->facet_start[i].vertex[1](2), color(0), color(1), color(2));
|
||||
|
|
|
@ -139,6 +139,9 @@ void AppConfig::set_defaults()
|
|||
if (get("default_action_on_select_preset").empty())
|
||||
set("default_action_on_select_preset", "none"); // , "transfer", "discard" or "save"
|
||||
|
||||
if (get("default_action_on_new_project").empty())
|
||||
set("default_action_on_new_project", "none"); // , "keep(transfer)", "discard" or "save"
|
||||
|
||||
if (get("color_mapinulation_panel").empty())
|
||||
set("color_mapinulation_panel", "0");
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace Slic3r {
|
|||
#ifdef WIN32
|
||||
|
||||
//only dll name with .dll suffix - currently case sensitive
|
||||
const std::vector<std::wstring> BlacklistedLibraryCheck::blacklist({ L"NahimicOSD.dll", L"SS2OSD.dll" });
|
||||
const std::vector<std::wstring> BlacklistedLibraryCheck::blacklist({ L"NahimicOSD.dll", L"SS2OSD.dll", L"amhook.dll", L"AMHook.dll" });
|
||||
|
||||
bool BlacklistedLibraryCheck::get_blacklisted(std::vector<std::wstring>& names)
|
||||
{
|
||||
|
|
|
@ -78,8 +78,6 @@ add_library(libslic3r STATIC
|
|||
Format/OBJ.hpp
|
||||
Format/objparser.cpp
|
||||
Format/objparser.hpp
|
||||
Format/PRUS.cpp
|
||||
Format/PRUS.hpp
|
||||
Format/STL.cpp
|
||||
Format/STL.hpp
|
||||
Format/SL1.hpp
|
||||
|
|
|
@ -126,6 +126,45 @@ ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input)
|
|||
return PolyTreeToExPolygons(std::move(polytree));
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Global test.
|
||||
bool has_duplicate_points(const ClipperLib::PolyTree &polytree)
|
||||
{
|
||||
struct Helper {
|
||||
static void collect_points_recursive(const ClipperLib::PolyNode &polynode, ClipperLib::Path &out) {
|
||||
// For each hole of the current expolygon:
|
||||
out.insert(out.end(), polynode.Contour.begin(), polynode.Contour.end());
|
||||
for (int i = 0; i < polynode.ChildCount(); ++ i)
|
||||
collect_points_recursive(*polynode.Childs[i], out);
|
||||
}
|
||||
};
|
||||
ClipperLib::Path pts;
|
||||
for (int i = 0; i < polytree.ChildCount(); ++ i)
|
||||
Helper::collect_points_recursive(*polytree.Childs[i], pts);
|
||||
return has_duplicate_points(std::move(pts));
|
||||
}
|
||||
#else
|
||||
// Local test inside each of the contours.
|
||||
bool has_duplicate_points(const ClipperLib::PolyTree &polytree)
|
||||
{
|
||||
struct Helper {
|
||||
static bool has_duplicate_points_recursive(const ClipperLib::PolyNode &polynode) {
|
||||
if (has_duplicate_points(polynode.Contour))
|
||||
return true;
|
||||
for (int i = 0; i < polynode.ChildCount(); ++ i)
|
||||
if (has_duplicate_points_recursive(*polynode.Childs[i]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
ClipperLib::Path pts;
|
||||
for (int i = 0; i < polytree.ChildCount(); ++ i)
|
||||
if (Helper::has_duplicate_points_recursive(*polytree.Childs[i]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Offset outside by 10um, one by one.
|
||||
template<typename PathsProvider>
|
||||
static ClipperLib::Paths safety_offset(PathsProvider &&paths)
|
||||
|
@ -467,6 +506,8 @@ Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons
|
|||
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
|
||||
Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); }
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); }
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset)
|
||||
|
@ -496,6 +537,8 @@ Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Surfac
|
|||
{ return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::SurfacesProvider(clip), do_safety_offset); }
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); }
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); }
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
|
@ -610,12 +653,18 @@ Polylines _clipper_pl_closed(ClipperLib::ClipType clipType, PathProvider1 &&subj
|
|||
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip)
|
||||
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(clip)); }
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip)
|
||||
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::ExPolygonProvider(clip)); }
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip)
|
||||
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonProvider(clip)); }
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip)
|
||||
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonsProvider(clip)); }
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip)
|
||||
{ return _clipper_pl_closed(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip)); }
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip)
|
||||
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::SinglePathProvider(clip.points)); }
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip)
|
||||
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::PolygonsProvider(clip)); }
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip)
|
||||
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(clip)); }
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip)
|
||||
|
@ -637,7 +686,9 @@ Lines _clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Pol
|
|||
// convert Polylines to Lines
|
||||
Lines retval;
|
||||
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline)
|
||||
retval.emplace_back(polyline->operator Line());
|
||||
if (polyline->size() >= 2)
|
||||
//FIXME It may happen, that Clipper produced a polyline with more than 2 collinear points by clipping a single line with polygons. It is a very rare issue, but it happens, see GH #6933.
|
||||
retval.push_back({ polyline->front(), polyline->back() });
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
@ -303,6 +303,7 @@ Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygo
|
|||
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::Polygon &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
|
@ -312,6 +313,7 @@ Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Surf
|
|||
Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip);
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip);
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip);
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip);
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip);
|
||||
|
@ -322,6 +324,7 @@ inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygon
|
|||
}
|
||||
|
||||
// Safety offset is applied to the clipping polygons only.
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
|
@ -337,6 +340,8 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r
|
|||
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip);
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip);
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip);
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip);
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip);
|
||||
|
|
|
@ -740,7 +740,11 @@ ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, Fo
|
|||
}
|
||||
|
||||
// Load the config keys from the given string.
|
||||
static inline size_t load_from_gcode_string_legacy(ConfigBase &config, const char *str, ConfigSubstitutionContext &substitutions)
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
size_t ConfigBase::load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions)
|
||||
#else
|
||||
static inline size_t load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions)
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
{
|
||||
if (str == nullptr)
|
||||
return 0;
|
||||
|
|
|
@ -2015,6 +2015,10 @@ public:
|
|||
// Set all the nullable values to nils.
|
||||
void null_nullables();
|
||||
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
static size_t load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions);
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
|
||||
private:
|
||||
// Set a configuration value from a string.
|
||||
bool set_deserialize_raw(const t_config_option_key& opt_key_src, const std::string& value, ConfigSubstitutionContext& substitutions, bool append);
|
||||
|
|
|
@ -92,7 +92,7 @@ bool ExPolygon::contains(const Line &line) const
|
|||
|
||||
bool ExPolygon::contains(const Polyline &polyline) const
|
||||
{
|
||||
return diff_pl((Polylines)polyline, *this).empty();
|
||||
return diff_pl(polyline, *this).empty();
|
||||
}
|
||||
|
||||
bool ExPolygon::contains(const Polylines &polylines) const
|
||||
|
@ -114,10 +114,11 @@ bool ExPolygon::contains(const Polylines &polylines) const
|
|||
|
||||
bool ExPolygon::contains(const Point &point) const
|
||||
{
|
||||
if (!this->contour.contains(point)) return false;
|
||||
for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
|
||||
if (it->contains(point)) return false;
|
||||
}
|
||||
if (! this->contour.contains(point))
|
||||
return false;
|
||||
for (const Polygon &hole : this->holes)
|
||||
if (hole.contains(point))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -367,6 +368,57 @@ extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons)
|
|||
return out;
|
||||
}
|
||||
|
||||
bool has_duplicate_points(const ExPolygon &expoly)
|
||||
{
|
||||
#if 1
|
||||
// Check globally.
|
||||
size_t cnt = expoly.contour.points.size();
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
cnt += hole.points.size();
|
||||
std::vector<Point> allpts;
|
||||
allpts.reserve(cnt);
|
||||
allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
allpts.insert(allpts.end(), hole.points.begin(), hole.points.end());
|
||||
return has_duplicate_points(std::move(allpts));
|
||||
#else
|
||||
// Check per contour.
|
||||
if (has_duplicate_points(expoly.contour))
|
||||
return true;
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
if (has_duplicate_points(hole))
|
||||
return true;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool has_duplicate_points(const ExPolygons &expolys)
|
||||
{
|
||||
#if 1
|
||||
// Check globally.
|
||||
size_t cnt = 0;
|
||||
for (const ExPolygon &expoly : expolys) {
|
||||
cnt += expoly.contour.points.size();
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
cnt += hole.points.size();
|
||||
}
|
||||
std::vector<Point> allpts;
|
||||
allpts.reserve(cnt);
|
||||
for (const ExPolygon &expoly : expolys) {
|
||||
allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
allpts.insert(allpts.end(), hole.points.begin(), hole.points.end());
|
||||
}
|
||||
return has_duplicate_points(std::move(allpts));
|
||||
#else
|
||||
// Check per contour.
|
||||
for (const ExPolygon &expoly : expolys)
|
||||
if (has_duplicate_points(expoly))
|
||||
return true;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool remove_sticks(ExPolygon &poly)
|
||||
{
|
||||
return remove_sticks(poly.contour) || remove_sticks(poly.holes);
|
||||
|
|
|
@ -351,20 +351,24 @@ inline ExPolygons expolygons_simplify(const ExPolygons &expolys, double toleranc
|
|||
return out;
|
||||
}
|
||||
|
||||
extern BoundingBox get_extents(const ExPolygon &expolygon);
|
||||
extern BoundingBox get_extents(const ExPolygons &expolygons);
|
||||
extern BoundingBox get_extents_rotated(const ExPolygon &poly, double angle);
|
||||
extern BoundingBox get_extents_rotated(const ExPolygons &polygons, double angle);
|
||||
extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons);
|
||||
BoundingBox get_extents(const ExPolygon &expolygon);
|
||||
BoundingBox get_extents(const ExPolygons &expolygons);
|
||||
BoundingBox get_extents_rotated(const ExPolygon &poly, double angle);
|
||||
BoundingBox get_extents_rotated(const ExPolygons &polygons, double angle);
|
||||
std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons);
|
||||
|
||||
extern bool remove_sticks(ExPolygon &poly);
|
||||
extern void keep_largest_contour_only(ExPolygons &polygons);
|
||||
// Test for duplicate points. The points are copied, sorted and checked for duplicates globally.
|
||||
bool has_duplicate_points(const ExPolygon &expoly);
|
||||
bool has_duplicate_points(const ExPolygons &expolys);
|
||||
|
||||
bool remove_sticks(ExPolygon &poly);
|
||||
void keep_largest_contour_only(ExPolygons &polygons);
|
||||
|
||||
inline double area(const ExPolygon &poly) { return poly.area(); }
|
||||
inline double area(const ExPolygons &polys) { double s = 0.; for (auto &p : polys) s += p.area(); return s; }
|
||||
|
||||
// Removes all expolygons smaller than min_area and also removes all holes smaller than min_area
|
||||
extern bool remove_small_and_small_holes(ExPolygons &expolygons, double min_area);
|
||||
bool remove_small_and_small_holes(ExPolygons &expolygons, double min_area);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
@ -928,7 +928,9 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
|
|||
Linef l { { bg::get<0, 0>(seg), bg::get<0, 1>(seg) }, { bg::get<1, 0>(seg), bg::get<1, 1>(seg) } };
|
||||
assert(line_alg::distance_to_squared(l, Vec2d(pt.cast<double>())) > 1000 * 1000);
|
||||
#endif // NDEBUG
|
||||
} else if (((Line)pl).distance_to_squared(pt) <= 1000 * 1000)
|
||||
} else if (pl.size() >= 2 &&
|
||||
//FIXME Hoping that pl is really a line, trimmed by a polygon using ClipperUtils. Sometimes Clipper leaves some additional collinear points on the polyline, let's hope it is all right.
|
||||
Line{ pl.front(), pl.back() }.distance_to_squared(pt) <= 1000 * 1000)
|
||||
out = closest.front().second;
|
||||
}
|
||||
return out;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/spirit/include/karma.hpp>
|
||||
#include <boost/spirit/include/qi_int.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
@ -32,6 +33,8 @@ namespace pt = boost::property_tree;
|
|||
#include <Eigen/Dense>
|
||||
#include "miniz_extension.hpp"
|
||||
|
||||
#include <fast_float/fast_float.h>
|
||||
|
||||
// Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
|
||||
// https://github.com/boostorg/spirit/pull/586
|
||||
// where the exported string is one digit shorter than it should be to guarantee lossless round trip.
|
||||
|
@ -172,14 +175,18 @@ std::string get_attribute_value_string(const char** attributes, unsigned int att
|
|||
|
||||
float get_attribute_value_float(const char** attributes, unsigned int attributes_size, const char* attribute_key)
|
||||
{
|
||||
const char* text = get_attribute_value_charptr(attributes, attributes_size, attribute_key);
|
||||
return (text != nullptr) ? (float)::atof(text) : 0.0f;
|
||||
float value = 0.0f;
|
||||
if (const char *text = get_attribute_value_charptr(attributes, attributes_size, attribute_key); text != nullptr)
|
||||
fast_float::from_chars(text, text + strlen(text), value);
|
||||
return value;
|
||||
}
|
||||
|
||||
int get_attribute_value_int(const char** attributes, unsigned int attributes_size, const char* attribute_key)
|
||||
{
|
||||
const char* text = get_attribute_value_charptr(attributes, attributes_size, attribute_key);
|
||||
return (text != nullptr) ? ::atoi(text) : 0;
|
||||
int value = 0;
|
||||
if (const char *text = get_attribute_value_charptr(attributes, attributes_size, attribute_key); text != nullptr)
|
||||
boost::spirit::qi::parse(text, text + strlen(text), boost::spirit::qi::int_, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool get_attribute_value_bool(const char** attributes, unsigned int attributes_size, const char* attribute_key)
|
||||
|
@ -298,8 +305,8 @@ namespace Slic3r {
|
|||
|
||||
struct Geometry
|
||||
{
|
||||
std::vector<float> vertices;
|
||||
std::vector<unsigned int> triangles;
|
||||
std::vector<Vec3f> vertices;
|
||||
std::vector<Vec3i> triangles;
|
||||
std::vector<std::string> custom_supports;
|
||||
std::vector<std::string> custom_seam;
|
||||
std::vector<std::string> mmu_segmentation;
|
||||
|
@ -713,7 +720,7 @@ namespace Slic3r {
|
|||
}
|
||||
|
||||
// use the geometry to create the volumes in the new model objects
|
||||
ObjectMetadata::VolumeMetadataList volumes(1, { 0, (unsigned int)geometry->triangles.size() / 3 - 1 });
|
||||
ObjectMetadata::VolumeMetadataList volumes(1, { 0, (unsigned int)geometry->triangles.size() - 1 });
|
||||
|
||||
// for each instance after the 1st, create a new model object containing only that instance
|
||||
// and copy into it the geometry
|
||||
|
@ -786,7 +793,7 @@ namespace Slic3r {
|
|||
// config data not found, this model was not saved using slic3r pe
|
||||
|
||||
// add the entire geometry as the single volume to generate
|
||||
volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() / 3 - 1);
|
||||
volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1);
|
||||
|
||||
// select as volumes
|
||||
volumes_ptr = &volumes;
|
||||
|
@ -1552,9 +1559,10 @@ namespace Slic3r {
|
|||
{
|
||||
// appends the vertex coordinates
|
||||
// missing values are set equal to ZERO
|
||||
m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, X_ATTR));
|
||||
m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, Y_ATTR));
|
||||
m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, Z_ATTR));
|
||||
m_curr_object.geometry.vertices.emplace_back(
|
||||
m_unit_factor * get_attribute_value_float(attributes, num_attributes, X_ATTR),
|
||||
m_unit_factor * get_attribute_value_float(attributes, num_attributes, Y_ATTR),
|
||||
m_unit_factor * get_attribute_value_float(attributes, num_attributes, Z_ATTR));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1588,9 +1596,10 @@ namespace Slic3r {
|
|||
|
||||
// appends the triangle's vertices indices
|
||||
// missing values are set equal to ZERO
|
||||
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V1_ATTR));
|
||||
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V2_ATTR));
|
||||
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V3_ATTR));
|
||||
m_curr_object.geometry.triangles.emplace_back(
|
||||
get_attribute_value_int(attributes, num_attributes, V1_ATTR),
|
||||
get_attribute_value_int(attributes, num_attributes, V2_ATTR),
|
||||
get_attribute_value_int(attributes, num_attributes, V3_ATTR));
|
||||
|
||||
m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR));
|
||||
m_curr_object.geometry.custom_seam.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR));
|
||||
|
@ -1879,8 +1888,9 @@ namespace Slic3r {
|
|||
return false;
|
||||
}
|
||||
|
||||
unsigned int geo_tri_count = (unsigned int)geometry.triangles.size() / 3;
|
||||
unsigned int geo_tri_count = (unsigned int)geometry.triangles.size();
|
||||
unsigned int renamed_volumes_count = 0;
|
||||
int processed_vertices_max_id = 0;
|
||||
|
||||
for (const ObjectMetadata::VolumeMetadata& volume_data : volumes) {
|
||||
if (geo_tri_count <= volume_data.first_triangle_id || geo_tri_count <= volume_data.last_triangle_id || volume_data.last_triangle_id < volume_data.first_triangle_id) {
|
||||
|
@ -1890,77 +1900,68 @@ namespace Slic3r {
|
|||
|
||||
Transform3d volume_matrix_to_object = Transform3d::Identity();
|
||||
bool has_transform = false;
|
||||
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
|
||||
bool is_left_handed = false;
|
||||
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
|
||||
// extract the volume transformation from the volume's metadata, if present
|
||||
for (const Metadata& metadata : volume_data.metadata) {
|
||||
if (metadata.key == MATRIX_KEY) {
|
||||
volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value);
|
||||
has_transform = ! volume_matrix_to_object.isApprox(Transform3d::Identity(), 1e-10);
|
||||
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
|
||||
is_left_handed = Slic3r::Geometry::Transformation(volume_matrix_to_object).is_left_handed();
|
||||
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// splits volume out of imported geometry
|
||||
TriangleMesh triangle_mesh;
|
||||
stl_file &stl = triangle_mesh.stl;
|
||||
unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1;
|
||||
stl.stats.type = inmemory;
|
||||
stl.stats.number_of_facets = (uint32_t)triangles_count;
|
||||
stl.stats.original_num_facets = (int)stl.stats.number_of_facets;
|
||||
stl_allocate(&stl);
|
||||
std::vector<Vec3i> faces(geometry.triangles.begin() + volume_data.first_triangle_id, geometry.triangles.begin() + volume_data.last_triangle_id + 1);
|
||||
const size_t triangles_count = faces.size();
|
||||
|
||||
unsigned int src_start_id = volume_data.first_triangle_id * 3;
|
||||
|
||||
for (unsigned int i = 0; i < triangles_count; ++i) {
|
||||
unsigned int ii = i * 3;
|
||||
stl_facet& facet = stl.facet_start[i];
|
||||
for (unsigned int v = 0; v < 3; ++v) {
|
||||
unsigned int tri_id = geometry.triangles[src_start_id + ii + v] * 3;
|
||||
if (tri_id + 2 >= geometry.vertices.size()) {
|
||||
add_error("Malformed triangle mesh");
|
||||
int min_id = faces.front()[0];
|
||||
int max_id = faces.front()[0];
|
||||
for (const Vec3i& face : faces) {
|
||||
for (const int tri_id : face) {
|
||||
if (tri_id < 0 || tri_id >= geometry.vertices.size()) {
|
||||
add_error("Found invalid vertex id");
|
||||
return false;
|
||||
}
|
||||
facet.vertex[v] = Vec3f(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]);
|
||||
min_id = std::min(min_id, tri_id);
|
||||
max_id = std::max(max_id, tri_id);
|
||||
}
|
||||
}
|
||||
|
||||
stl_get_size(&stl);
|
||||
triangle_mesh.repair();
|
||||
// rebase indices to the current vertices list
|
||||
for (Vec3i& face : faces) {
|
||||
for (int& tri_id : face) {
|
||||
tri_id -= min_id;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
|
||||
// PrusaSlicer older than 2.4.0 saved mirrored volumes with reversed winding of the triangles
|
||||
// This caused the call to TriangleMesh::repair() to reverse all the facets because the calculated volume was negative
|
||||
if (is_left_handed && stl.stats.facets_reversed > 0 && stl.stats.facets_reversed == stl.stats.original_num_facets)
|
||||
stl.stats.facets_reversed = 0;
|
||||
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
|
||||
processed_vertices_max_id = 1 + std::max(processed_vertices_max_id, max_id);
|
||||
|
||||
std::vector<Vec3f> vertices(geometry.vertices.begin() + min_id, geometry.vertices.begin() + max_id + 1);
|
||||
TriangleMesh triangle_mesh(std::move(vertices), std::move(faces));
|
||||
|
||||
if (m_version == 0) {
|
||||
// if the 3mf was not produced by PrusaSlicer and there is only one instance,
|
||||
// bake the transformation into the geometry to allow the reload from disk command
|
||||
// to work properly
|
||||
if (object.instances.size() == 1) {
|
||||
triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix());
|
||||
triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false);
|
||||
object.instances.front()->set_transformation(Slic3r::Geometry::Transformation());
|
||||
//FIXME do the mesh fixing?
|
||||
}
|
||||
}
|
||||
if (triangle_mesh.volume() < 0)
|
||||
triangle_mesh.flip_triangles();
|
||||
|
||||
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
|
||||
// stores the volume matrix taken from the metadata, if present
|
||||
if (has_transform)
|
||||
volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
|
||||
volume->calculate_convex_hull();
|
||||
|
||||
// recreate custom supports, seam and mmu segmentation from previously loaded attribute
|
||||
volume->supported_facets.reserve(triangles_count);
|
||||
volume->seam_facets.reserve(triangles_count);
|
||||
volume->mmu_segmentation_facets.reserve(triangles_count);
|
||||
for (unsigned i=0; i<triangles_count; ++i) {
|
||||
size_t index = src_start_id/3 + i;
|
||||
for (size_t i=0; i<triangles_count; ++i) {
|
||||
size_t index = volume_data.first_triangle_id + i;
|
||||
assert(index < geometry.custom_supports.size());
|
||||
assert(index < geometry.custom_seam.size());
|
||||
assert(index < geometry.mmu_segmentation.size());
|
||||
|
@ -2536,11 +2537,6 @@ namespace Slic3r {
|
|||
if (volume == nullptr)
|
||||
continue;
|
||||
|
||||
if (!volume->mesh().repaired)
|
||||
throw Slic3r::FileIOError("store_3mf() requires repair()");
|
||||
if (!volume->mesh().has_shared_vertices())
|
||||
throw Slic3r::FileIOError("store_3mf() requires shared vertices");
|
||||
|
||||
volumes_offsets.insert({ volume, Offsets(vertices_count) });
|
||||
|
||||
const indexed_triangle_set &its = volume->mesh().its;
|
||||
|
@ -2581,10 +2577,7 @@ namespace Slic3r {
|
|||
if (volume == nullptr)
|
||||
continue;
|
||||
|
||||
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
|
||||
bool is_left_handed = volume->is_left_handed();
|
||||
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
|
||||
|
||||
VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume);
|
||||
assert(volume_it != volumes_offsets.end());
|
||||
|
||||
|
@ -2599,7 +2592,6 @@ namespace Slic3r {
|
|||
{
|
||||
const Vec3i &idx = its.indices[i];
|
||||
char *ptr = buf;
|
||||
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
|
||||
boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
|
||||
" v1=\"" << boost::spirit::int_ <<
|
||||
"\" v2=\"" << boost::spirit::int_ <<
|
||||
|
@ -2607,15 +2599,6 @@ namespace Slic3r {
|
|||
idx[is_left_handed ? 2 : 0] + volume_it->second.first_vertex_id,
|
||||
idx[1] + volume_it->second.first_vertex_id,
|
||||
idx[is_left_handed ? 0 : 2] + volume_it->second.first_vertex_id);
|
||||
#else
|
||||
boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
|
||||
" v1=\"" << boost::spirit::int_ <<
|
||||
"\" v2=\"" << boost::spirit::int_ <<
|
||||
"\" v3=\"" << boost::spirit::int_ << "\"",
|
||||
idx[0] + volume_it->second.first_vertex_id,
|
||||
idx[1] + volume_it->second.first_vertex_id,
|
||||
idx[2] + volume_it->second.first_vertex_id);
|
||||
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
|
||||
*ptr = '\0';
|
||||
output_buffer += buf;
|
||||
}
|
||||
|
|
|
@ -244,11 +244,11 @@ struct AMFParserContext
|
|||
// Map from obect name to object idx & instances.
|
||||
std::map<std::string, Object> m_object_instances_map;
|
||||
// Vertices parsed for the current m_object.
|
||||
std::vector<float> m_object_vertices;
|
||||
std::vector<Vec3f> m_object_vertices;
|
||||
// Current volume allocated for an amf/object/mesh/volume subtree.
|
||||
ModelVolume *m_volume { nullptr };
|
||||
// Faces collected for the current m_volume.
|
||||
std::vector<int> m_volume_facets;
|
||||
std::vector<Vec3i> m_volume_facets;
|
||||
// Transformation matrix of a volume mesh from its coordinate system to Object's coordinate system.
|
||||
Transform3d m_volume_transform;
|
||||
// Current material allocated for an amf/metadata subtree.
|
||||
|
@ -598,9 +598,7 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
case NODE_TYPE_VERTEX:
|
||||
assert(m_object);
|
||||
// Parse the vertex data
|
||||
m_object_vertices.emplace_back((float)atof(m_value[0].c_str()));
|
||||
m_object_vertices.emplace_back((float)atof(m_value[1].c_str()));
|
||||
m_object_vertices.emplace_back((float)atof(m_value[2].c_str()));
|
||||
m_object_vertices.emplace_back(float(atof(m_value[0].c_str())), float(atof(m_value[1].c_str())), float(atof(m_value[2].c_str())));
|
||||
m_value[0].clear();
|
||||
m_value[1].clear();
|
||||
m_value[2].clear();
|
||||
|
@ -609,9 +607,7 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
// Faces of the current volume:
|
||||
case NODE_TYPE_TRIANGLE:
|
||||
assert(m_object && m_volume);
|
||||
m_volume_facets.emplace_back(atoi(m_value[0].c_str()));
|
||||
m_volume_facets.emplace_back(atoi(m_value[1].c_str()));
|
||||
m_volume_facets.emplace_back(atoi(m_value[2].c_str()));
|
||||
m_volume_facets.emplace_back(atoi(m_value[0].c_str()), atoi(m_value[1].c_str()), atoi(m_value[2].c_str()));
|
||||
m_value[0].clear();
|
||||
m_value[1].clear();
|
||||
m_value[2].clear();
|
||||
|
@ -621,44 +617,36 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
case NODE_TYPE_VOLUME:
|
||||
{
|
||||
assert(m_object && m_volume);
|
||||
TriangleMesh mesh;
|
||||
stl_file &stl = mesh.stl;
|
||||
stl.stats.type = inmemory;
|
||||
stl.stats.number_of_facets = int(m_volume_facets.size() / 3);
|
||||
stl.stats.original_num_facets = stl.stats.number_of_facets;
|
||||
stl_allocate(&stl);
|
||||
|
||||
bool has_transform = ! m_volume_transform.isApprox(Transform3d::Identity(), 1e-10);
|
||||
for (size_t i = 0; i < m_volume_facets.size();) {
|
||||
stl_facet &facet = stl.facet_start[i/3];
|
||||
for (unsigned int v = 0; v < 3; ++v)
|
||||
{
|
||||
unsigned int tri_id = m_volume_facets[i++] * 3;
|
||||
if (tri_id < 0 || tri_id + 2 >= m_object_vertices.size()) {
|
||||
// Verify validity of face indices.
|
||||
for (Vec3i face : m_volume_facets)
|
||||
for (unsigned int tri_id : face)
|
||||
if (tri_id < 0 || tri_id >= m_object_vertices.size()) {
|
||||
this->stop("Malformed triangle mesh");
|
||||
return;
|
||||
}
|
||||
facet.vertex[v] = Vec3f(m_object_vertices[tri_id + 0], m_object_vertices[tri_id + 1], m_object_vertices[tri_id + 2]);
|
||||
}
|
||||
}
|
||||
stl_get_size(&stl);
|
||||
mesh.repair();
|
||||
m_volume->set_mesh(std::move(mesh));
|
||||
// stores the volume matrix taken from the metadata, if present
|
||||
if (has_transform)
|
||||
m_volume->source.transform = Slic3r::Geometry::Transformation(m_volume_transform);
|
||||
if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART))
|
||||
|
||||
{
|
||||
TriangleMesh triangle_mesh { std::move(m_object_vertices), std::move(m_volume_facets) };
|
||||
if (triangle_mesh.volume() < 0)
|
||||
triangle_mesh.flip_triangles();
|
||||
m_volume->set_mesh(std::move(triangle_mesh));
|
||||
}
|
||||
|
||||
// stores the volume matrix taken from the metadata, if present
|
||||
if (bool has_transform = !m_volume_transform.isApprox(Transform3d::Identity(), 1e-10); has_transform)
|
||||
m_volume->source.transform = Slic3r::Geometry::Transformation(m_volume_transform);
|
||||
|
||||
if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART)) {
|
||||
m_volume->source.object_idx = (int)m_model.objects.size() - 1;
|
||||
m_volume->source.volume_idx = (int)m_model.objects.back()->volumes.size() - 1;
|
||||
m_volume->center_geometry_after_creation();
|
||||
}
|
||||
else
|
||||
} else
|
||||
// pass false if the mesh offset has been already taken from the data
|
||||
m_volume->center_geometry_after_creation(m_volume->source.input_file.empty());
|
||||
|
||||
m_volume->calculate_convex_hull();
|
||||
m_volume_facets.clear();
|
||||
m_object_vertices.clear();
|
||||
m_volume = nullptr;
|
||||
break;
|
||||
}
|
||||
|
@ -1187,10 +1175,6 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
|
|||
int num_vertices = 0;
|
||||
for (ModelVolume *volume : object->volumes) {
|
||||
vertices_offsets.push_back(num_vertices);
|
||||
if (! volume->mesh().repaired)
|
||||
throw Slic3r::FileIOError("store_amf() requires repair()");
|
||||
if (! volume->mesh().has_shared_vertices())
|
||||
throw Slic3r::FileIOError("store_amf() requires shared vertices");
|
||||
const indexed_triangle_set &its = volume->mesh().its;
|
||||
const Transform3d& matrix = volume->get_matrix();
|
||||
for (size_t i = 0; i < its.vertices.size(); ++i) {
|
||||
|
|
|
@ -19,7 +19,8 @@ namespace Slic3r {
|
|||
|
||||
bool load_obj(const char *path, TriangleMesh *meshptr)
|
||||
{
|
||||
if(meshptr == nullptr) return false;
|
||||
if (meshptr == nullptr)
|
||||
return false;
|
||||
|
||||
// Parse the OBJ file.
|
||||
ObjParser::ObjData data;
|
||||
|
@ -31,84 +32,69 @@ bool load_obj(const char *path, TriangleMesh *meshptr)
|
|||
// Count the faces and verify, that all faces are triangular.
|
||||
size_t num_faces = 0;
|
||||
size_t num_quads = 0;
|
||||
for (size_t i = 0; i < data.vertices.size(); ) {
|
||||
for (size_t i = 0; i < data.vertices.size(); ++ i) {
|
||||
// Find the end of face.
|
||||
size_t j = i;
|
||||
for (; j < data.vertices.size() && data.vertices[j].coordIdx != -1; ++ j) ;
|
||||
if (i == j)
|
||||
continue;
|
||||
size_t face_vertices = j - i;
|
||||
if (face_vertices != 3 && face_vertices != 4) {
|
||||
if (size_t num_face_vertices = j - i; num_face_vertices > 0) {
|
||||
if (num_face_vertices > 4) {
|
||||
// Non-triangular and non-quad faces are not supported as of now.
|
||||
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains polygons with more than 4 vertices.";
|
||||
return false;
|
||||
} else if (num_face_vertices < 3) {
|
||||
// Non-triangular and non-quad faces are not supported as of now.
|
||||
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains polygons with less than 2 vertices.";
|
||||
return false;
|
||||
}
|
||||
if (face_vertices == 4)
|
||||
if (num_face_vertices == 4)
|
||||
++ num_quads;
|
||||
++ num_faces;
|
||||
i = j + 1;
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert ObjData into STL.
|
||||
TriangleMesh &mesh = *meshptr;
|
||||
stl_file &stl = mesh.stl;
|
||||
stl.stats.type = inmemory;
|
||||
stl.stats.number_of_facets = uint32_t(num_faces + num_quads);
|
||||
stl.stats.original_num_facets = int(num_faces + num_quads);
|
||||
// stl_allocate clears all the allocated data to zero, all normals are set to zeros as well.
|
||||
stl_allocate(&stl);
|
||||
size_t i_face = 0;
|
||||
for (size_t i = 0; i < data.vertices.size(); ++ i) {
|
||||
// Convert ObjData into indexed triangle set.
|
||||
indexed_triangle_set its;
|
||||
size_t num_vertices = data.coordinates.size() / 4;
|
||||
its.vertices.reserve(num_vertices);
|
||||
its.indices.reserve(num_faces + num_quads);
|
||||
for (size_t i = 0; i < num_vertices; ++ i) {
|
||||
size_t j = i << 2;
|
||||
its.vertices.emplace_back(data.coordinates[j], data.coordinates[j + 1], data.coordinates[j + 2]);
|
||||
}
|
||||
int indices[4];
|
||||
for (size_t i = 0; i < data.vertices.size();)
|
||||
if (data.vertices[i].coordIdx == -1)
|
||||
continue;
|
||||
stl_facet &facet = stl.facet_start[i_face ++];
|
||||
size_t num_normals = 0;
|
||||
stl_normal normal(stl_normal::Zero());
|
||||
for (unsigned int v = 0; v < 3; ++ v) {
|
||||
const ObjParser::ObjVertex &vertex = data.vertices[i++];
|
||||
memcpy(facet.vertex[v].data(), &data.coordinates[vertex.coordIdx*4], 3 * sizeof(float));
|
||||
if (vertex.normalIdx != -1) {
|
||||
normal(0) += data.normals[vertex.normalIdx*3];
|
||||
normal(1) += data.normals[vertex.normalIdx*3+1];
|
||||
normal(2) += data.normals[vertex.normalIdx*3+2];
|
||||
++ num_normals;
|
||||
++ i;
|
||||
else {
|
||||
int cnt = 0;
|
||||
while (i < data.vertices.size())
|
||||
if (const ObjParser::ObjVertex &vertex = data.vertices[i ++]; vertex.coordIdx == -1) {
|
||||
break;
|
||||
} else {
|
||||
assert(cnt < 4);
|
||||
if (vertex.coordIdx < 0 || vertex.coordIdx >= its.vertices.size()) {
|
||||
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains invalid vertex index.";
|
||||
return false;
|
||||
}
|
||||
indices[cnt ++] = vertex.coordIdx;
|
||||
}
|
||||
if (cnt) {
|
||||
assert(cnt == 3 || cnt == 4);
|
||||
// Insert one or two faces (triangulate a quad).
|
||||
its.indices.emplace_back(indices[0], indices[1], indices[2]);
|
||||
if (cnt == 4)
|
||||
its.indices.emplace_back(indices[0], indices[2], indices[3]);
|
||||
}
|
||||
}
|
||||
// Result of obj_parseline() call is not checked, thus not all vertices are necessarily finalized with coord_Idx == -1.
|
||||
if (i < data.vertices.size() && data.vertices[i].coordIdx != -1) {
|
||||
// This is a quad. Produce the other triangle.
|
||||
stl_facet &facet2 = stl.facet_start[i_face++];
|
||||
facet2.vertex[0] = facet.vertex[0];
|
||||
facet2.vertex[1] = facet.vertex[2];
|
||||
const ObjParser::ObjVertex &vertex = data.vertices[i++];
|
||||
memcpy(facet2.vertex[2].data(), &data.coordinates[vertex.coordIdx * 4], 3 * sizeof(float));
|
||||
if (vertex.normalIdx != -1) {
|
||||
normal(0) += data.normals[vertex.normalIdx*3];
|
||||
normal(1) += data.normals[vertex.normalIdx*3+1];
|
||||
normal(2) += data.normals[vertex.normalIdx*3+2];
|
||||
++ num_normals;
|
||||
}
|
||||
if (num_normals == 4) {
|
||||
// Normalize an average normal of a quad.
|
||||
float len = facet.normal.norm();
|
||||
if (len > EPSILON) {
|
||||
normal /= len;
|
||||
facet.normal = normal;
|
||||
facet2.normal = normal;
|
||||
}
|
||||
}
|
||||
} else if (num_normals == 3) {
|
||||
// Normalize an average normal of a triangle.
|
||||
float len = facet.normal.norm();
|
||||
if (len > EPSILON)
|
||||
facet.normal = normal / len;
|
||||
}
|
||||
}
|
||||
stl_get_size(&stl);
|
||||
mesh.repair();
|
||||
if (mesh.facets_count() == 0) {
|
||||
|
||||
*meshptr = TriangleMesh(std::move(its));
|
||||
if (meshptr->empty()) {
|
||||
BOOST_LOG_TRIVIAL(error) << "load_obj: This OBJ file couldn't be read because it's empty. " << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (meshptr->volume() < 0)
|
||||
meshptr->flip_triangles();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,333 +0,0 @@
|
|||
#include <string.h>
|
||||
#include <exception>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "miniz_extension.hpp"
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../Model.hpp"
|
||||
|
||||
#include "PRUS.hpp"
|
||||
|
||||
#if 0
|
||||
// Enable debugging and assert in this file.
|
||||
#define DEBUG
|
||||
#define _DEBUG
|
||||
#undef NDEBUG
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
struct StlHeader
|
||||
{
|
||||
char comment[80];
|
||||
uint32_t nTriangles;
|
||||
};
|
||||
|
||||
static_assert(sizeof(StlHeader) == 84, "StlHeader size not correct");
|
||||
|
||||
// Buffered line reader to a string buffer.
|
||||
class LineReader
|
||||
{
|
||||
public:
|
||||
LineReader(std::vector<char> &data) : m_buffer(data), m_pos(0), m_len((int)data.size()) {}
|
||||
|
||||
const char* next_line() {
|
||||
// Skip empty lines.
|
||||
while (m_pos < m_len && (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n'))
|
||||
++ m_pos;
|
||||
if (m_pos == m_len) {
|
||||
// End of file.
|
||||
return nullptr;
|
||||
}
|
||||
// The buffer is nonempty and it does not start with end of lines. Find the first end of line.
|
||||
int end = m_pos + 1;
|
||||
while (end < m_len && m_buffer[end] != '\r' && m_buffer[end] != '\n')
|
||||
++ end;
|
||||
char *ptr_out = m_buffer.data() + m_pos;
|
||||
m_pos = end + 1;
|
||||
m_buffer[end] = 0;
|
||||
return ptr_out;
|
||||
}
|
||||
|
||||
int next_line_scanf(const char *format, ...)
|
||||
{
|
||||
const char *line = next_line();
|
||||
if (line == nullptr)
|
||||
return -1;
|
||||
int result;
|
||||
va_list arglist;
|
||||
va_start(arglist, format);
|
||||
result = vsscanf(line, format, arglist);
|
||||
va_end(arglist);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<char> &m_buffer;
|
||||
int m_pos;
|
||||
int m_len;
|
||||
};
|
||||
|
||||
static void extract_model_from_archive(
|
||||
// name of the model file
|
||||
const char *name,
|
||||
// path to the archive
|
||||
const char *path,
|
||||
// content of scene.xml
|
||||
const std::vector<char> &scene_xml_data,
|
||||
// loaded data of this STL
|
||||
std::vector<char> &data,
|
||||
// Model, to which the newly loaded objects will be added
|
||||
Model *model,
|
||||
// To map multiple STLs into a single model object for multi-material prints.
|
||||
std::map<int, ModelObject*> &group_to_model_object)
|
||||
{
|
||||
// Find the model entry in the XML data.
|
||||
char model_name_tag[1024];
|
||||
sprintf(model_name_tag, "<model name=\"%s\">", name);
|
||||
const char *model_xml = strstr(scene_xml_data.data(), model_name_tag);
|
||||
const char *zero_tag = "<zero>";
|
||||
const char *zero_xml = strstr(scene_xml_data.data(), zero_tag);
|
||||
Vec3d instance_rotation = Vec3d::Zero();
|
||||
Vec3d instance_scaling_factor = Vec3d::Ones();
|
||||
Vec3d instance_offset = Vec3d::Zero();
|
||||
bool trafo_set = false;
|
||||
unsigned int group_id = (unsigned int)-1;
|
||||
unsigned int extruder_id = (unsigned int)-1;
|
||||
ModelObject *model_object = nullptr;
|
||||
if (model_xml != nullptr) {
|
||||
model_xml += strlen(model_name_tag);
|
||||
const char *position_tag = "<position>";
|
||||
const char *position_xml = strstr(model_xml, position_tag);
|
||||
const char *rotation_tag = "<rotation>";
|
||||
const char *rotation_xml = strstr(model_xml, rotation_tag);
|
||||
const char *scale_tag = "<scale>";
|
||||
const char *scale_xml = strstr(model_xml, scale_tag);
|
||||
float position[3], rotation[3], scale[3], zero[3];
|
||||
if (position_xml != nullptr && rotation_xml != nullptr && scale_xml != nullptr && zero_xml != nullptr &&
|
||||
sscanf(position_xml+strlen(position_tag),
|
||||
"[%f, %f, %f]", position, position+1, position+2) == 3 &&
|
||||
sscanf(rotation_xml+strlen(rotation_tag),
|
||||
"[%f, %f, %f]", rotation, rotation+1, rotation+2) == 3 &&
|
||||
sscanf(scale_xml+strlen(scale_tag),
|
||||
"[%f, %f, %f]", scale, scale+1, scale+2) == 3 &&
|
||||
sscanf(zero_xml+strlen(zero_tag),
|
||||
"[%f, %f, %f]", zero, zero+1, zero+2) == 3) {
|
||||
instance_scaling_factor = Vec3d((double)scale[0], (double)scale[1], (double)scale[2]);
|
||||
instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]);
|
||||
instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2]));
|
||||
trafo_set = true;
|
||||
}
|
||||
const char *group_tag = "<group>";
|
||||
const char *group_xml = strstr(model_xml, group_tag);
|
||||
const char *extruder_tag = "<extruder>";
|
||||
const char *extruder_xml = strstr(model_xml, extruder_tag);
|
||||
if (group_xml != nullptr) {
|
||||
int group = atoi(group_xml + strlen(group_tag));
|
||||
if (group > 0) {
|
||||
group_id = group;
|
||||
auto it = group_to_model_object.find(group_id);
|
||||
if (it != group_to_model_object.end())
|
||||
model_object = it->second;
|
||||
}
|
||||
}
|
||||
if (extruder_xml != nullptr) {
|
||||
int e = atoi(extruder_xml + strlen(extruder_tag));
|
||||
if (e > 0)
|
||||
extruder_id = e;
|
||||
}
|
||||
}
|
||||
if (! trafo_set)
|
||||
throw Slic3r::FileIOError(std::string("Archive ") + path + " does not contain a valid entry in scene.xml for " + name);
|
||||
|
||||
// Extract the STL.
|
||||
StlHeader header;
|
||||
TriangleMesh mesh;
|
||||
bool mesh_valid = false;
|
||||
bool stl_ascii = false;
|
||||
if (data.size() > sizeof(StlHeader)) {
|
||||
memcpy((char*)&header, data.data(), sizeof(StlHeader));
|
||||
if (strncmp(header.comment, "solid ", 6) == 0)
|
||||
stl_ascii = true;
|
||||
else {
|
||||
// Header has been extracted. Now read the faces.
|
||||
stl_file &stl = mesh.stl;
|
||||
stl.stats.type = inmemory;
|
||||
stl.stats.number_of_facets = header.nTriangles;
|
||||
stl.stats.original_num_facets = header.nTriangles;
|
||||
stl_allocate(&stl);
|
||||
if (header.nTriangles > 0 && data.size() == 50 * header.nTriangles + sizeof(StlHeader)) {
|
||||
memcpy((char*)stl.facet_start.data(), data.data() + sizeof(StlHeader), 50 * header.nTriangles);
|
||||
if (sizeof(stl_facet) > SIZEOF_STL_FACET) {
|
||||
// The stl.facet_start is not packed tightly. Unpack the array of stl_facets.
|
||||
unsigned char *data = (unsigned char*)stl.facet_start.data();
|
||||
for (size_t i = header.nTriangles - 1; i > 0; -- i)
|
||||
memmove(data + i * sizeof(stl_facet), data + i * SIZEOF_STL_FACET, SIZEOF_STL_FACET);
|
||||
}
|
||||
// All the faces have been read.
|
||||
stl_get_size(&stl);
|
||||
mesh.repair();
|
||||
if (std::abs(stl.stats.min(2)) < EPSILON)
|
||||
stl.stats.min(2) = 0.;
|
||||
// Add a mesh to a model.
|
||||
if (mesh.facets_count() > 0)
|
||||
mesh_valid = true;
|
||||
}
|
||||
}
|
||||
} else
|
||||
stl_ascii = true;
|
||||
|
||||
if (stl_ascii) {
|
||||
// Try to parse ASCII STL.
|
||||
char normal_buf[3][32];
|
||||
stl_facet facet;
|
||||
std::vector<stl_facet> facets;
|
||||
LineReader line_reader(data);
|
||||
std::string solid_name;
|
||||
facet.extra[0] = facet.extra[1] = 0;
|
||||
for (;;) {
|
||||
const char *line = line_reader.next_line();
|
||||
if (line == nullptr)
|
||||
// End of file.
|
||||
break;
|
||||
if (strncmp(line, "solid", 5) == 0) {
|
||||
// Opening the "solid" block.
|
||||
if (! solid_name.empty()) {
|
||||
// Error, solid block is already open.
|
||||
facets.clear();
|
||||
break;
|
||||
}
|
||||
solid_name = line + 5;
|
||||
if (solid_name.empty())
|
||||
solid_name = "unknown";
|
||||
continue;
|
||||
}
|
||||
if (strncmp(line, "endsolid", 8) == 0) {
|
||||
// Closing the "solid" block.
|
||||
if (solid_name.empty()) {
|
||||
// Error, no solid block is open.
|
||||
facets.clear();
|
||||
break;
|
||||
}
|
||||
solid_name.clear();
|
||||
continue;
|
||||
}
|
||||
// Line has to start with the word solid.
|
||||
int res_normal = sscanf(line, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);
|
||||
assert(res_normal == 3);
|
||||
int res_outer_loop = line_reader.next_line_scanf(" outer loop");
|
||||
assert(res_outer_loop == 0);
|
||||
int res_vertex1 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2));
|
||||
assert(res_vertex1 == 3);
|
||||
int res_vertex2 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2));
|
||||
assert(res_vertex2 == 3);
|
||||
int res_vertex3 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2));
|
||||
assert(res_vertex3 == 3);
|
||||
int res_endloop = line_reader.next_line_scanf(" endloop");
|
||||
assert(res_endloop == 0);
|
||||
int res_endfacet = line_reader.next_line_scanf(" endfacet");
|
||||
if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) {
|
||||
// perror("Something is syntactically very wrong with this ASCII STL!");
|
||||
facets.clear();
|
||||
break;
|
||||
}
|
||||
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
|
||||
if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 ||
|
||||
sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 ||
|
||||
sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
|
||||
// Normal was mangled. Maybe denormals or "not a number" were stored?
|
||||
// Just reset the normal and silently ignore it.
|
||||
facet.normal = stl_normal::Zero();
|
||||
}
|
||||
facets.emplace_back(facet);
|
||||
}
|
||||
if (! facets.empty() && solid_name.empty()) {
|
||||
stl_file &stl = mesh.stl;
|
||||
stl.stats.type = inmemory;
|
||||
stl.stats.number_of_facets = (uint32_t)facets.size();
|
||||
stl.stats.original_num_facets = (int)facets.size();
|
||||
stl_allocate(&stl);
|
||||
memcpy((void*)stl.facet_start.data(), facets.data(), facets.size() * 50);
|
||||
stl_get_size(&stl);
|
||||
mesh.repair();
|
||||
// Add a mesh to a model.
|
||||
if (mesh.facets_count() > 0)
|
||||
mesh_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! mesh_valid)
|
||||
throw Slic3r::FileIOError(std::string("Archive ") + path + " does not contain a valid mesh for " + name);
|
||||
|
||||
// Add this mesh to the model.
|
||||
ModelVolume *volume = nullptr;
|
||||
if (model_object == nullptr) {
|
||||
// This is a first mesh of a group. Create a new object & volume.
|
||||
model_object = model->add_object(name, path, std::move(mesh));
|
||||
volume = model_object->volumes.front();
|
||||
ModelInstance *instance = model_object->add_instance();
|
||||
instance->set_rotation(instance_rotation);
|
||||
instance->set_scaling_factor(instance_scaling_factor);
|
||||
instance->set_offset(instance_offset);
|
||||
if (group_id != (unsigned int)(-1))
|
||||
group_to_model_object[group_id] = model_object;
|
||||
} else {
|
||||
// This is not the 1st mesh of a group. Add it to the ModelObject.
|
||||
volume = model_object->add_volume(std::move(mesh));
|
||||
volume->name = name;
|
||||
}
|
||||
// Set the extruder to the volume.
|
||||
if (extruder_id != (unsigned int)-1)
|
||||
volume->config.set("extruder", int(extruder_id));
|
||||
}
|
||||
|
||||
// Load a PrusaControl project file into a provided model.
|
||||
bool load_prus(const char *path, Model *model)
|
||||
{
|
||||
mz_zip_archive archive;
|
||||
mz_zip_zero_struct(&archive);
|
||||
|
||||
size_t n_models_initial = model->objects.size();
|
||||
mz_bool res = MZ_FALSE;
|
||||
try {
|
||||
if (!open_zip_reader(&archive, path))
|
||||
throw Slic3r::FileIOError(std::string("Unable to init zip reader for ") + path);
|
||||
std::vector<char> scene_xml_data;
|
||||
// For grouping multiple STLs into a single ModelObject for multi-material prints.
|
||||
std::map<int, ModelObject*> group_to_model_object;
|
||||
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
|
||||
for (mz_uint i = 0; i < num_entries; ++ i) {
|
||||
mz_zip_archive_file_stat stat;
|
||||
if (! mz_zip_reader_file_stat(&archive, i, &stat))
|
||||
continue;
|
||||
std::vector<char> buffer;
|
||||
buffer.assign((size_t)stat.m_uncomp_size, 0);
|
||||
res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (char*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
||||
if (res == MZ_FALSE)
|
||||
throw Slic3r::FileIOError(std::string("Error while extracting a file from ") + path);
|
||||
if (strcmp(stat.m_filename, "scene.xml") == 0) {
|
||||
if (! scene_xml_data.empty())
|
||||
throw Slic3r::FileIOError(std::string("Multiple scene.xml were found in the archive.") + path);
|
||||
scene_xml_data = std::move(buffer);
|
||||
} else if (boost::iends_with(stat.m_filename, ".stl")) {
|
||||
// May throw std::exception
|
||||
extract_model_from_archive(stat.m_filename, path, scene_xml_data, buffer, model, group_to_model_object);
|
||||
}
|
||||
}
|
||||
} catch (std::exception &ex) {
|
||||
close_zip_reader(&archive);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
close_zip_reader(&archive);
|
||||
return model->objects.size() > n_models_initial;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
|
@ -1,11 +0,0 @@
|
|||
#define slic3r_Format_PRUS_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class TriangleMesh;
|
||||
class Model;
|
||||
|
||||
// Load a PrusaControl project file into a provided model.
|
||||
extern bool load_prus(const char *path, Model *model);
|
||||
|
||||
}; // namespace Slic3r
|
|
@ -203,7 +203,7 @@ RasterParams get_raster_params(const DynamicPrintConfig &cfg)
|
|||
|
||||
if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h ||
|
||||
!opt_mirror_x || !opt_mirror_y || !opt_orient)
|
||||
throw Slic3r::FileIOError("Invalid SL1 / SL1S file");
|
||||
throw MissingProfileError("Invalid SL1 / SL1S file");
|
||||
|
||||
RasterParams rstp;
|
||||
|
||||
|
@ -229,7 +229,7 @@ SliceParams get_slice_params(const DynamicPrintConfig &cfg)
|
|||
auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height");
|
||||
|
||||
if (!opt_layerh || !opt_init_layerh)
|
||||
throw Slic3r::FileIOError("Invalid SL1 / SL1S file");
|
||||
throw MissingProfileError("Invalid SL1 / SL1S file");
|
||||
|
||||
return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()};
|
||||
}
|
||||
|
@ -293,24 +293,34 @@ ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrint
|
|||
return out.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
|
||||
}
|
||||
|
||||
// If the profile is missing from the archive (older PS versions did not have
|
||||
// it), profile_out's initial value will be used as fallback. profile_out will be empty on
|
||||
// function return if the archive did not contain any profile.
|
||||
ConfigSubstitutions import_sla_archive(
|
||||
const std::string & zipfname,
|
||||
Vec2i windowsize,
|
||||
indexed_triangle_set & out,
|
||||
DynamicPrintConfig & profile,
|
||||
DynamicPrintConfig & profile_out,
|
||||
std::function<bool(int)> progr)
|
||||
{
|
||||
// Ensure minimum window size for marching squares
|
||||
windowsize.x() = std::max(2, windowsize.x());
|
||||
windowsize.y() = std::max(2, windowsize.y());
|
||||
|
||||
ArchiveData arch = extract_sla_archive(zipfname, "thumbnail");
|
||||
ConfigSubstitutions config_substitutions = profile.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
|
||||
std::string exclude_entries{"thumbnail"};
|
||||
ArchiveData arch = extract_sla_archive(zipfname, exclude_entries);
|
||||
DynamicPrintConfig profile_in, profile_use;
|
||||
ConfigSubstitutions config_substitutions = profile_in.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
|
||||
|
||||
RasterParams rstp = get_raster_params(profile);
|
||||
// If the archive contains an empty profile, use the one that was passed as output argument
|
||||
// then replace it with the readed profile to report that it was empty.
|
||||
profile_use = profile_in.empty() ? profile_out : profile_in;
|
||||
profile_out = profile_in;
|
||||
|
||||
RasterParams rstp = get_raster_params(profile_use);
|
||||
rstp.win = {windowsize.y(), windowsize.x()};
|
||||
|
||||
SliceParams slicp = get_slice_params(profile);
|
||||
SliceParams slicp = get_slice_params(profile_use);
|
||||
|
||||
std::vector<ExPolygons> slices =
|
||||
extract_slices_from_sla_archive(arch, rstp, progr);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
class SL1Archive: public SLAPrinter {
|
||||
class SL1Archive: public SLAArchive {
|
||||
SLAPrinterConfig m_cfg;
|
||||
|
||||
protected:
|
||||
|
@ -57,6 +57,8 @@ inline ConfigSubstitutions import_sla_archive(
|
|||
return import_sla_archive(zipfname, windowsize, out, profile, progr);
|
||||
}
|
||||
|
||||
class MissingProfileError : public RuntimeError { using RuntimeError::RuntimeError; };
|
||||
|
||||
} // namespace Slic3r::sla
|
||||
|
||||
#endif // ARCHIVETRAITS_HPP
|
||||
|
|
|
@ -21,8 +21,7 @@ bool load_stl(const char *path, Model *model, const char *object_name_in)
|
|||
// die "Failed to open $file\n" if !-e $path;
|
||||
return false;
|
||||
}
|
||||
mesh.repair();
|
||||
if (mesh.facets_count() == 0) {
|
||||
if (mesh.empty()) {
|
||||
// die "This STL file couldn't be read because it's empty.\n"
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -784,7 +784,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
|
|||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info();
|
||||
m_processor.finalize();
|
||||
// Post-process the G-code to update time stamps.
|
||||
m_processor.finalize(true);
|
||||
// DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics);
|
||||
DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics);
|
||||
if (result != nullptr) {
|
||||
|
@ -1842,11 +1843,7 @@ namespace ProcessLayer
|
|||
assert(m600_extruder_before_layer >= 0);
|
||||
// Color Change or Tool Change as Color Change.
|
||||
// add tag for processor
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(m600_extruder_before_layer) + "," + custom_gcode->color + "\n";
|
||||
#else
|
||||
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(m600_extruder_before_layer) + "\n";
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
if (!single_extruder_printer && m600_extruder_before_layer >= 0 && first_extruder_id != (unsigned)m600_extruder_before_layer
|
||||
// && !MMU1
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "GCodeProcessor.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
@ -27,9 +28,7 @@ static const float DEFAULT_TOOLPATH_HEIGHT = 0.2f;
|
|||
static const float INCHES_TO_MM = 25.4f;
|
||||
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
|
||||
static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
|
||||
#if ENABLE_RETRACT_ACCELERATION
|
||||
static const float DEFAULT_RETRACT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
|
||||
#endif // ENABLE_RETRACT_ACCELERATION
|
||||
static const float DEFAULT_TRAVEL_ACCELERATION = 1250.0f;
|
||||
|
||||
static const size_t MIN_EXTRUDERS_COUNT = 5;
|
||||
|
@ -184,10 +183,8 @@ void GCodeProcessor::TimeMachine::reset()
|
|||
enabled = false;
|
||||
acceleration = 0.0f;
|
||||
max_acceleration = 0.0f;
|
||||
#if ENABLE_RETRACT_ACCELERATION
|
||||
retract_acceleration = 0.0f;
|
||||
max_retract_acceleration = 0.0f;
|
||||
#endif // ENABLE_RETRACT_ACCELERATION
|
||||
travel_acceleration = 0.0f;
|
||||
max_travel_acceleration = 0.0f;
|
||||
extrude_factor_override_percentage = 1.0f;
|
||||
|
@ -347,18 +344,6 @@ void GCodeProcessor::TimeProcessor::reset()
|
|||
machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
|
||||
}
|
||||
|
||||
struct FilePtr {
|
||||
FilePtr(FILE *f) : f(f) {}
|
||||
~FilePtr() { this->close(); }
|
||||
void close() {
|
||||
if (this->f) {
|
||||
::fclose(this->f);
|
||||
this->f = nullptr;
|
||||
}
|
||||
}
|
||||
FILE* f = nullptr;
|
||||
};
|
||||
|
||||
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<MoveVertex>& moves, std::vector<size_t>& lines_ends)
|
||||
{
|
||||
FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
|
||||
|
@ -740,9 +725,7 @@ void GCodeProcessor::Result::reset() {
|
|||
extruder_colors = std::vector<std::string>();
|
||||
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
|
||||
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
time = 0;
|
||||
}
|
||||
#else
|
||||
|
@ -756,18 +739,19 @@ void GCodeProcessor::Result::reset() {
|
|||
extruder_colors = std::vector<std::string>();
|
||||
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
|
||||
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
||||
const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> GCodeProcessor::Producers = {
|
||||
{ EProducer::PrusaSlicer, "PrusaSlicer" },
|
||||
{ EProducer::Slic3rPE, "Slic3r Prusa Edition" },
|
||||
{ EProducer::Slic3r, "Slic3r" },
|
||||
{ EProducer::PrusaSlicer, "generated by PrusaSlicer" },
|
||||
{ EProducer::Slic3rPE, "generated by Slic3r Prusa Edition" },
|
||||
{ EProducer::Slic3r, "generated by Slic3r" },
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
{ EProducer::SuperSlicer, "generated by SuperSlicer" },
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
{ EProducer::Cura, "Cura_SteamEngine" },
|
||||
{ EProducer::Simplify3D, "Simplify3D" },
|
||||
{ EProducer::Simplify3D, "G-Code generated by Simplify3D(R)" },
|
||||
{ EProducer::CraftWare, "CraftWare" },
|
||||
{ EProducer::ideaMaker, "ideaMaker" },
|
||||
{ EProducer::KissSlicer, "KISSlicer" }
|
||||
|
@ -849,26 +833,16 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
|||
m_result.extruders_count = extruders_count;
|
||||
|
||||
m_extruder_offsets.resize(extruders_count);
|
||||
for (size_t i = 0; i < extruders_count; ++i) {
|
||||
Vec2f offset = config.extruder_offset.get_at(i).cast<float>();
|
||||
m_extruder_offsets[i] = { offset(0), offset(1), 0.0f };
|
||||
}
|
||||
|
||||
m_extruder_colors.resize(extruders_count);
|
||||
for (size_t i = 0; i < extruders_count; ++i) {
|
||||
m_extruder_colors[i] = static_cast<unsigned char>(i);
|
||||
}
|
||||
|
||||
m_result.filament_diameters.resize(extruders_count);
|
||||
m_result.filament_densities.resize(extruders_count);
|
||||
m_extruder_temps.resize(extruders_count);
|
||||
|
||||
m_result.filament_diameters.resize(config.filament_diameter.values.size());
|
||||
for (size_t i = 0; i < config.filament_diameter.values.size(); ++i) {
|
||||
m_result.filament_diameters[i] = static_cast<float>(config.filament_diameter.values[i]);
|
||||
}
|
||||
|
||||
m_result.filament_densities.resize(config.filament_density.values.size());
|
||||
for (size_t i = 0; i < config.filament_density.values.size(); ++i) {
|
||||
m_result.filament_densities[i] = static_cast<float>(config.filament_density.values[i]);
|
||||
for (size_t i = 0; i < extruders_count; ++ i) {
|
||||
m_extruder_offsets[i] = to_3d(config.extruder_offset.get_at(i).cast<float>().eval(), 0.f);
|
||||
m_extruder_colors[i] = static_cast<unsigned char>(i);
|
||||
m_result.filament_diameters[i] = static_cast<float>(config.filament_diameter.get_at(i));
|
||||
m_result.filament_densities[i] = static_cast<float>(config.filament_density.get_at(i));
|
||||
}
|
||||
|
||||
if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) {
|
||||
|
@ -895,11 +869,9 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
|||
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
|
||||
m_time_processor.machines[i].max_acceleration = max_acceleration;
|
||||
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
|
||||
#if ENABLE_RETRACT_ACCELERATION
|
||||
float max_retract_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i);
|
||||
m_time_processor.machines[i].max_retract_acceleration = max_retract_acceleration;
|
||||
m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
|
||||
#endif // ENABLE_RETRACT_ACCELERATION
|
||||
float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i);
|
||||
m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration;
|
||||
m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
|
||||
|
@ -1117,11 +1089,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
|
||||
m_time_processor.machines[i].max_acceleration = max_acceleration;
|
||||
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
|
||||
#if ENABLE_RETRACT_ACCELERATION
|
||||
float max_retract_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i);
|
||||
m_time_processor.machines[i].max_retract_acceleration = max_retract_acceleration;
|
||||
m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
|
||||
#endif // ENABLE_RETRACT_ACCELERATION
|
||||
float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i);
|
||||
m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration;
|
||||
m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
|
||||
|
@ -1200,9 +1170,7 @@ void GCodeProcessor::reset()
|
|||
m_result.id = ++s_result_id;
|
||||
|
||||
m_use_volumetric_e = false;
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
m_last_default_color_id = 0;
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_options_z_corrector.reset();
|
||||
|
@ -1215,6 +1183,18 @@ void GCodeProcessor::reset()
|
|||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
}
|
||||
|
||||
static inline const char* skip_whitespaces(const char *begin, const char *end) {
|
||||
for (; begin != end && (*begin == ' ' || *begin == '\t'); ++ begin);
|
||||
return begin;
|
||||
}
|
||||
|
||||
static inline const char* remove_eols(const char *begin, const char *end) {
|
||||
for (; begin != end && (*(end - 1) == '\r' || *(end - 1) == '\n'); -- end);
|
||||
return end;
|
||||
}
|
||||
|
||||
// Load a G-code into a stand-alone G-code viewer.
|
||||
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
|
||||
void GCodeProcessor::process_file(const std::string& filename, std::function<void()> cancel_callback)
|
||||
{
|
||||
CNumericLocalesSetter locales_setter;
|
||||
|
@ -1226,11 +1206,13 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
|
|||
// pre-processing
|
||||
// parse the gcode file to detect its producer
|
||||
{
|
||||
m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
|
||||
const std::string_view cmd = line.cmd();
|
||||
if (cmd.empty()) {
|
||||
const std::string_view comment = line.comment();
|
||||
if (comment.length() > 1 && detect_producer(comment))
|
||||
m_parser.parse_file_raw(filename, [this](GCodeReader& reader, const char *begin, const char *end) {
|
||||
begin = skip_whitespaces(begin, end);
|
||||
if (begin != end && *begin == ';') {
|
||||
// Comment.
|
||||
begin = skip_whitespaces(++ begin, end);
|
||||
end = remove_eols(begin, end);
|
||||
if (begin != end && detect_producer(std::string_view(begin, end - begin)))
|
||||
m_parser.quit_parsing();
|
||||
}
|
||||
});
|
||||
|
@ -1249,6 +1231,10 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
|
|||
}
|
||||
else if (m_producer == EProducer::Simplify3D)
|
||||
apply_config_simplify3d(filename);
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
else if (m_producer == EProducer::SuperSlicer)
|
||||
apply_config_superslicer(filename);
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
}
|
||||
|
||||
// process gcode
|
||||
|
@ -1267,7 +1253,8 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
|
|||
this->process_gcode_line(line, true);
|
||||
});
|
||||
|
||||
this->finalize();
|
||||
// Don't post-process the G-code to update time stamps.
|
||||
this->finalize(false);
|
||||
}
|
||||
|
||||
void GCodeProcessor::initialize(const std::string& filename)
|
||||
|
@ -1293,7 +1280,7 @@ void GCodeProcessor::process_buffer(const std::string &buffer)
|
|||
});
|
||||
}
|
||||
|
||||
void GCodeProcessor::finalize()
|
||||
void GCodeProcessor::finalize(bool post_process)
|
||||
{
|
||||
// update width/height of wipe moves
|
||||
for (MoveVertex& move : m_result.moves) {
|
||||
|
@ -1323,6 +1310,7 @@ void GCodeProcessor::finalize()
|
|||
m_width_compare.output();
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
if (post_process)
|
||||
m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends);
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
m_result.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_start_time).count();
|
||||
|
@ -1380,6 +1368,41 @@ std::vector<std::pair<ExtrusionRole, float>> GCodeProcessor::get_roles_time(Prin
|
|||
return ret;
|
||||
}
|
||||
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
ConfigSubstitutions load_from_superslicer_gcode_file(const std::string& filename, DynamicPrintConfig& config, ForwardCompatibilitySubstitutionRule compatibility_rule)
|
||||
{
|
||||
// for reference, see: ConfigBase::load_from_gcode_file()
|
||||
|
||||
boost::nowide::ifstream ifs(filename);
|
||||
|
||||
auto header_end_pos = ifs.tellg();
|
||||
ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
|
||||
size_t key_value_pairs = 0;
|
||||
|
||||
ifs.seekg(0, ifs.end);
|
||||
auto file_length = ifs.tellg();
|
||||
auto data_length = std::min<std::fstream::pos_type>(65535, file_length - header_end_pos);
|
||||
ifs.seekg(file_length - data_length, ifs.beg);
|
||||
std::vector<char> data(size_t(data_length) + 1, 0);
|
||||
ifs.read(data.data(), data_length);
|
||||
ifs.close();
|
||||
key_value_pairs = ConfigBase::load_from_gcode_string_legacy(config, data.data(), substitutions_ctxt);
|
||||
|
||||
if (key_value_pairs < 80)
|
||||
throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", filename, key_value_pairs));
|
||||
|
||||
return std::move(substitutions_ctxt.substitutions);
|
||||
}
|
||||
|
||||
void GCodeProcessor::apply_config_superslicer(const std::string& filename)
|
||||
{
|
||||
DynamicPrintConfig config;
|
||||
config.apply(FullPrintConfig::defaults());
|
||||
load_from_superslicer_gcode_file(filename, config, ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||
apply_config(config);
|
||||
}
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
|
||||
std::vector<float> GCodeProcessor::get_layers_time(PrintEstimatedStatistics::ETimeMode mode) const
|
||||
{
|
||||
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ?
|
||||
|
@ -1398,9 +1421,11 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
|
|||
};
|
||||
|
||||
BedSize bed_size;
|
||||
bool producer_detected = false;
|
||||
|
||||
m_parser.parse_file(filename, [this, &bed_size](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
|
||||
auto extract_double = [](const std::string& cmt, const std::string& key, double& out) {
|
||||
m_parser.parse_file_raw(filename, [this, &bed_size, &producer_detected](GCodeReader& reader, const char* begin, const char* end) {
|
||||
|
||||
auto extract_double = [](const std::string_view cmt, const std::string& key, double& out) {
|
||||
size_t pos = cmt.find(key);
|
||||
if (pos != cmt.npos) {
|
||||
pos = cmt.find(',', pos);
|
||||
|
@ -1412,12 +1437,12 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
|
|||
return false;
|
||||
};
|
||||
|
||||
auto extract_floats = [](const std::string& cmt, const std::string& key, std::vector<float>& out) {
|
||||
auto extract_floats = [](const std::string_view cmt, const std::string& key, std::vector<float>& out) {
|
||||
size_t pos = cmt.find(key);
|
||||
if (pos != cmt.npos) {
|
||||
pos = cmt.find(',', pos);
|
||||
if (pos != cmt.npos) {
|
||||
std::string data_str = cmt.substr(pos + 1);
|
||||
const std::string_view data_str = cmt.substr(pos + 1);
|
||||
std::vector<std::string> values_str;
|
||||
boost::split(values_str, data_str, boost::is_any_of("|,"), boost::token_compress_on);
|
||||
for (const std::string& s : values_str) {
|
||||
|
@ -1429,8 +1454,15 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
|
|||
return false;
|
||||
};
|
||||
|
||||
const std::string& comment = line.raw();
|
||||
if (comment.length() > 2 && comment.front() == ';') {
|
||||
begin = skip_whitespaces(begin, end);
|
||||
end = remove_eols(begin, end);
|
||||
if (begin != end)
|
||||
if (*begin == ';') {
|
||||
// Comment.
|
||||
begin = skip_whitespaces(++ begin, end);
|
||||
if (begin != end) {
|
||||
std::string_view comment(begin, end - begin);
|
||||
if (producer_detected) {
|
||||
if (bed_size.x == 0.0 && comment.find("strokeXoverride") != comment.npos)
|
||||
extract_double(comment, "strokeXoverride", bed_size.x);
|
||||
else if (bed_size.y == 0.0 && comment.find("strokeYoverride") != comment.npos)
|
||||
|
@ -1438,16 +1470,20 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
|
|||
else if (comment.find("filamentDiameters") != comment.npos) {
|
||||
m_result.filament_diameters.clear();
|
||||
extract_floats(comment, "filamentDiameters", m_result.filament_diameters);
|
||||
}
|
||||
else if (comment.find("filamentDensities") != comment.npos) {
|
||||
} else if (comment.find("filamentDensities") != comment.npos) {
|
||||
m_result.filament_densities.clear();
|
||||
extract_floats(comment, "filamentDensities", m_result.filament_densities);
|
||||
}
|
||||
else if (comment.find("extruderDiameter") != comment.npos) {
|
||||
} else if (comment.find("extruderDiameter") != comment.npos) {
|
||||
std::vector<float> extruder_diameters;
|
||||
extract_floats(comment, "extruderDiameter", extruder_diameters);
|
||||
m_result.extruders_count = extruder_diameters.size();
|
||||
}
|
||||
} else if (boost::starts_with(comment, "G-Code generated by Simplify3D(R)"))
|
||||
producer_detected = true;
|
||||
}
|
||||
} else {
|
||||
// Some non-empty G-code line detected, stop parsing config comments.
|
||||
reader.quit_parsing();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1741,7 +1777,6 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
|
|||
// color change tag
|
||||
if (boost::starts_with(comment, reserved_tag(ETags::Color_Change))) {
|
||||
unsigned char extruder_id = 0;
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
static std::vector<std::string> Default_Colors = {
|
||||
"#0B2C7A", // { 0.043f, 0.173f, 0.478f }, // bluish
|
||||
"#1C8891", // { 0.110f, 0.533f, 0.569f },
|
||||
|
@ -1790,16 +1825,6 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
|
|||
if (m_last_default_color_id == Default_Colors.size())
|
||||
m_last_default_color_id = 0;
|
||||
}
|
||||
#else
|
||||
if (boost::starts_with(comment.substr(reserved_tag(ETags::Color_Change).size()), ",T")) {
|
||||
int eid;
|
||||
if (!parse_number(comment.substr(reserved_tag(ETags::Color_Change).size() + 2), eid) || eid < 0 || eid > 255) {
|
||||
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Color_Change (" << comment << ").";
|
||||
return;
|
||||
}
|
||||
extruder_id = static_cast<unsigned char>(eid);
|
||||
}
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
if (extruder_id < m_extruder_colors.size())
|
||||
m_extruder_colors[extruder_id] = static_cast<unsigned char>(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview
|
||||
|
@ -1810,7 +1835,6 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
|
|||
if (m_extruder_id == extruder_id) {
|
||||
m_cp_color.current = m_extruder_colors[extruder_id];
|
||||
store_move_vertex(EMoveType::Color_change);
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::ColorChange, extruder_id + 1, color, "" };
|
||||
m_result.custom_gcode_per_print_z.emplace_back(item);
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
|
@ -1818,27 +1842,19 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
|
|||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
process_custom_gcode_time(CustomGCode::ColorChange);
|
||||
process_filaments(CustomGCode::ColorChange);
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
}
|
||||
|
||||
#if !ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
process_custom_gcode_time(CustomGCode::ColorChange);
|
||||
process_filaments(CustomGCode::ColorChange);
|
||||
#endif // !ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// pause print tag
|
||||
if (comment == reserved_tag(ETags::Pause_Print)) {
|
||||
store_move_vertex(EMoveType::Pause_Print);
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::PausePrint, m_extruder_id + 1, "", "" };
|
||||
m_result.custom_gcode_per_print_z.emplace_back(item);
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_options_z_corrector.set();
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
process_custom_gcode_time(CustomGCode::PausePrint);
|
||||
return;
|
||||
}
|
||||
|
@ -1846,13 +1862,11 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
|
|||
// custom code tag
|
||||
if (comment == reserved_tag(ETags::Custom_Code)) {
|
||||
store_move_vertex(EMoveType::Custom_GCode);
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::Custom, m_extruder_id + 1, "", "" };
|
||||
m_result.custom_gcode_per_print_z.emplace_back(item);
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_options_z_corrector.set();
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1878,6 +1892,9 @@ bool GCodeProcessor::process_producers_tags(const std::string_view comment)
|
|||
{
|
||||
case EProducer::Slic3rPE:
|
||||
case EProducer::Slic3r:
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
case EProducer::SuperSlicer:
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
case EProducer::PrusaSlicer: { return process_prusaslicer_tags(comment); }
|
||||
case EProducer::Cura: { return process_cura_tags(comment); }
|
||||
case EProducer::Simplify3D: { return process_simplify3d_tags(comment); }
|
||||
|
@ -2707,15 +2724,15 @@ void GCodeProcessor::process_G28(const GCodeReader::GCodeLine& line)
|
|||
std::string_view cmd = line.cmd();
|
||||
std::string new_line_raw = { cmd.data(), cmd.size() };
|
||||
bool found = false;
|
||||
if (line.has_x()) {
|
||||
if (line.has('X')) {
|
||||
new_line_raw += " X0";
|
||||
found = true;
|
||||
}
|
||||
if (line.has_y()) {
|
||||
if (line.has('Y')) {
|
||||
new_line_raw += " Y0";
|
||||
found = true;
|
||||
}
|
||||
if (line.has_z()) {
|
||||
if (line.has('Z')) {
|
||||
new_line_raw += " Z0";
|
||||
found = true;
|
||||
}
|
||||
|
@ -2851,16 +2868,16 @@ void GCodeProcessor::process_M132(const GCodeReader::GCodeLine& line)
|
|||
// see: https://github.com/makerbot/s3g/blob/master/doc/GCodeProtocol.md
|
||||
// Using this command to reset the axis origin to zero helps in fixing: https://github.com/prusa3d/PrusaSlicer/issues/3082
|
||||
|
||||
if (line.has_x())
|
||||
if (line.has('X'))
|
||||
m_origin[X] = 0.0f;
|
||||
|
||||
if (line.has_y())
|
||||
if (line.has('Y'))
|
||||
m_origin[Y] = 0.0f;
|
||||
|
||||
if (line.has_z())
|
||||
if (line.has('Z'))
|
||||
m_origin[Z] = 0.0f;
|
||||
|
||||
if (line.has_e())
|
||||
if (line.has('E'))
|
||||
m_origin[E] = 0.0f;
|
||||
}
|
||||
|
||||
|
@ -2943,22 +2960,14 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
|
|||
set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
|
||||
set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
|
||||
if (line.has_value('T', value))
|
||||
#if ENABLE_RETRACT_ACCELERATION
|
||||
set_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
|
||||
#else
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
|
||||
#endif // ENABLE_RETRACT_ACCELERATION
|
||||
}
|
||||
else {
|
||||
// New acceleration format, compatible with the upstream Marlin.
|
||||
if (line.has_value('P', value))
|
||||
set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
|
||||
if (line.has_value('R', value))
|
||||
#if ENABLE_RETRACT_ACCELERATION
|
||||
set_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
|
||||
#else
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
|
||||
#endif // ENABLE_RETRACT_ACCELERATION
|
||||
if (line.has_value('T', value))
|
||||
// Interpret the T value as the travel acceleration in the new Marlin format.
|
||||
set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
|
||||
|
@ -3029,7 +3038,7 @@ void GCodeProcessor::process_M402(const GCodeReader::GCodeLine& line)
|
|||
// https://github.com/repetier/Repetier-Firmware/blob/master/src/ArduinoAVR/Repetier/Printer.cpp
|
||||
// void Printer::GoToMemoryPosition(bool x, bool y, bool z, bool e, float feed)
|
||||
|
||||
bool has_xyz = !(line.has_x() || line.has_y() || line.has_z());
|
||||
bool has_xyz = !(line.has('X') || line.has('Y') || line.has('Z'));
|
||||
|
||||
float p = FLT_MAX;
|
||||
for (unsigned char a = X; a <= Z; ++a) {
|
||||
|
@ -3216,20 +3225,12 @@ float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode
|
|||
}
|
||||
}
|
||||
|
||||
#if ENABLE_RETRACT_ACCELERATION
|
||||
float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
|
||||
{
|
||||
size_t id = static_cast<size_t>(mode);
|
||||
return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
|
||||
}
|
||||
#else
|
||||
float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
|
||||
{
|
||||
return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, static_cast<size_t>(mode));
|
||||
}
|
||||
#endif // ENABLE_RETRACT_ACCELERATION
|
||||
|
||||
#if ENABLE_RETRACT_ACCELERATION
|
||||
void GCodeProcessor::set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value)
|
||||
{
|
||||
size_t id = static_cast<size_t>(mode);
|
||||
|
@ -3239,7 +3240,6 @@ void GCodeProcessor::set_retract_acceleration(PrintEstimatedStatistics::ETimeMod
|
|||
std::min(value, m_time_processor.machines[id].max_retract_acceleration);
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_RETRACT_ACCELERATION
|
||||
|
||||
float GCodeProcessor::get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
|
||||
{
|
||||
|
|
|
@ -242,11 +242,9 @@ namespace Slic3r {
|
|||
float acceleration; // mm/s^2
|
||||
// hard limit for the acceleration, to which the firmware will clamp.
|
||||
float max_acceleration; // mm/s^2
|
||||
#if ENABLE_RETRACT_ACCELERATION
|
||||
float retract_acceleration; // mm/s^2
|
||||
// hard limit for the acceleration, to which the firmware will clamp.
|
||||
float max_retract_acceleration; // mm/s^2
|
||||
#endif // ENABLE_RETRACT_ACCELERATION
|
||||
float travel_acceleration; // mm/s^2
|
||||
// hard limit for the travel acceleration, to which the firmware will clamp.
|
||||
float max_travel_acceleration; // mm/s^2
|
||||
|
@ -359,9 +357,7 @@ namespace Slic3r {
|
|||
std::vector<float> filament_diameters;
|
||||
std::vector<float> filament_densities;
|
||||
PrintEstimatedStatistics print_statistics;
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
std::vector<CustomGCode::Item> custom_gcode_per_print_z;
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
int64_t time{ 0 };
|
||||
|
@ -536,9 +532,7 @@ namespace Slic3r {
|
|||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
OptionsZCorrector m_options_z_corrector;
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
size_t m_last_default_color_id;
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
@ -549,6 +543,9 @@ namespace Slic3r {
|
|||
PrusaSlicer,
|
||||
Slic3rPE,
|
||||
Slic3r,
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
SuperSlicer,
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
Cura,
|
||||
Simplify3D,
|
||||
CraftWare,
|
||||
|
@ -585,14 +582,14 @@ namespace Slic3r {
|
|||
const Result& get_result() const { return m_result; }
|
||||
Result&& extract_result() { return std::move(m_result); }
|
||||
|
||||
// Process the gcode contained in the file with the given filename
|
||||
// Load a G-code into a stand-alone G-code viewer.
|
||||
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
|
||||
void process_file(const std::string& filename, std::function<void()> cancel_callback = nullptr);
|
||||
|
||||
// Streaming interface, for processing G-codes just generated by PrusaSlicer in a pipelined fashion.
|
||||
void initialize(const std::string& filename);
|
||||
void process_buffer(const std::string& buffer);
|
||||
void finalize();
|
||||
void finalize(bool post_process);
|
||||
|
||||
float get_time(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
|
@ -605,6 +602,9 @@ namespace Slic3r {
|
|||
private:
|
||||
void apply_config(const DynamicPrintConfig& config);
|
||||
void apply_config_simplify3d(const std::string& filename);
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
void apply_config_superslicer(const std::string& filename);
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
void process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled);
|
||||
|
||||
// Process tags embedded into comments
|
||||
|
@ -724,9 +724,7 @@ namespace Slic3r {
|
|||
float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
|
||||
float get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
|
||||
float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
#if ENABLE_RETRACT_ACCELERATION
|
||||
void set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
|
||||
#endif // ENABLE_RETRACT_ACCELERATION
|
||||
float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
void set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
|
||||
float get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include "LocalesUtils.hpp"
|
||||
|
||||
|
@ -126,44 +128,92 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pair<const char*, co
|
|||
}
|
||||
}
|
||||
|
||||
bool GCodeReader::parse_file(const std::string &file, callback_t callback)
|
||||
template<typename ParseLineCallback, typename LineEndCallback>
|
||||
bool GCodeReader::parse_file_raw_internal(const std::string &filename, ParseLineCallback parse_line_callback, LineEndCallback line_end_callback)
|
||||
{
|
||||
boost::nowide::ifstream f(file);
|
||||
f.sync_with_stdio(false);
|
||||
FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
|
||||
|
||||
// Read the input stream 64kB at a time, extract lines and process them.
|
||||
std::vector<char> buffer(65536 * 10, 0);
|
||||
std::string line;
|
||||
// Line buffer.
|
||||
std::string gcode_line;
|
||||
size_t file_pos = 0;
|
||||
m_parsing = true;
|
||||
GCodeLine gline;
|
||||
while (m_parsing && ! f.eof()) {
|
||||
f.read(buffer.data(), buffer.size());
|
||||
if (! f.eof() && ! f.good())
|
||||
// Reading the input file failed.
|
||||
for (;;) {
|
||||
size_t cnt_read = ::fread(buffer.data(), 1, buffer.size(), in.f);
|
||||
if (::ferror(in.f))
|
||||
return false;
|
||||
bool eof = cnt_read == 0;
|
||||
auto it = buffer.begin();
|
||||
auto it_bufend = buffer.begin() + f.gcount();
|
||||
while (it != it_bufend) {
|
||||
auto it_bufend = buffer.begin() + cnt_read;
|
||||
while (it != it_bufend || (eof && ! gcode_line.empty())) {
|
||||
// Find end of line.
|
||||
bool eol = false;
|
||||
auto it_end = it;
|
||||
for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ;
|
||||
eol |= f.eof() && it_end == it_bufend;
|
||||
for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end)
|
||||
if (*it_end == '\n')
|
||||
line_end_callback((it_end - buffer.begin()) + 1);
|
||||
// End of line is indicated also if end of file was reached.
|
||||
eol |= eof && it_end == it_bufend;
|
||||
if (eol) {
|
||||
gline.reset();
|
||||
if (line.empty())
|
||||
this->parse_line(&(*it), &(*it_end), gline, callback);
|
||||
if (gcode_line.empty())
|
||||
parse_line_callback(&(*it), &(*it_end));
|
||||
else {
|
||||
line.insert(line.end(), it, it_end);
|
||||
this->parse_line(line.c_str(), line.c_str() + line.size(), gline, callback);
|
||||
line.clear();
|
||||
gcode_line.insert(gcode_line.end(), it, it_end);
|
||||
parse_line_callback(gcode_line.c_str(), gcode_line.c_str() + gcode_line.size());
|
||||
gcode_line.clear();
|
||||
}
|
||||
if (! m_parsing)
|
||||
// The callback wishes to exit.
|
||||
return true;
|
||||
} else
|
||||
line.insert(line.end(), it, it_end);
|
||||
// Skip all the empty lines.
|
||||
for (it = it_end; it != it_bufend && (*it == '\r' || *it == '\n'); ++ it) ;
|
||||
gcode_line.insert(gcode_line.end(), it, it_end);
|
||||
// Skip EOL.
|
||||
it = it_end;
|
||||
if (it != it_bufend && *it == '\r')
|
||||
++ it;
|
||||
if (it != it_bufend && *it == '\n') {
|
||||
line_end_callback((it - buffer.begin()) + 1);
|
||||
++ it;
|
||||
}
|
||||
}
|
||||
if (eof)
|
||||
break;
|
||||
file_pos += cnt_read;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename ParseLineCallback, typename LineEndCallback>
|
||||
bool GCodeReader::parse_file_internal(const std::string &filename, ParseLineCallback parse_line_callback, LineEndCallback line_end_callback)
|
||||
{
|
||||
GCodeLine gline;
|
||||
return this->parse_file_raw_internal(filename,
|
||||
[this, &gline, parse_line_callback](const char *begin, const char *end) {
|
||||
gline.reset();
|
||||
this->parse_line(begin, end, gline, parse_line_callback);
|
||||
},
|
||||
line_end_callback);
|
||||
}
|
||||
|
||||
bool GCodeReader::parse_file(const std::string &file, callback_t callback)
|
||||
{
|
||||
return this->parse_file_internal(file, callback, [](size_t){});
|
||||
}
|
||||
|
||||
bool GCodeReader::parse_file(const std::string &file, callback_t callback, std::vector<size_t> &lines_ends)
|
||||
{
|
||||
lines_ends.clear();
|
||||
return this->parse_file_internal(file, callback, [&lines_ends](size_t file_pos){ lines_ends.emplace_back(file_pos); });
|
||||
}
|
||||
|
||||
bool GCodeReader::parse_file_raw(const std::string &filename, raw_line_callback_t line_callback)
|
||||
{
|
||||
return this->parse_file_raw_internal(filename,
|
||||
[this, line_callback](const char *begin, const char *end) { line_callback(*this, begin, end); },
|
||||
[](size_t){});
|
||||
}
|
||||
|
||||
bool GCodeReader::GCodeLine::has(char axis) const
|
||||
{
|
||||
const char *c = m_raw.c_str();
|
||||
|
|
|
@ -76,6 +76,7 @@ public:
|
|||
};
|
||||
|
||||
typedef std::function<void(GCodeReader&, const GCodeLine&)> callback_t;
|
||||
typedef std::function<void(GCodeReader&, const char*, const char*)> raw_line_callback_t;
|
||||
|
||||
GCodeReader() : m_verbose(false), m_extrusion_axis('E') { this->reset(); }
|
||||
void reset() { memset(m_position, 0, sizeof(m_position)); }
|
||||
|
@ -114,6 +115,13 @@ public:
|
|||
|
||||
// Returns false if reading the file failed.
|
||||
bool parse_file(const std::string &file, callback_t callback);
|
||||
// Collect positions of line ends in the binary G-code to be used by the G-code viewer when memory mapping and displaying section of G-code
|
||||
// as an overlay in the 3D scene.
|
||||
bool parse_file(const std::string &file, callback_t callback, std::vector<size_t> &lines_ends);
|
||||
// Just read the G-code file line by line, calls callback (const char *begin, const char *end). Returns false if reading the file failed.
|
||||
bool parse_file_raw(const std::string &file, raw_line_callback_t callback);
|
||||
|
||||
// To be called by the callback to stop parsing.
|
||||
void quit_parsing() { m_parsing = false; }
|
||||
|
||||
float& x() { return m_position[X]; }
|
||||
|
@ -132,6 +140,11 @@ public:
|
|||
// void set_extrusion_axis(char axis) { m_extrusion_axis = axis; }
|
||||
|
||||
private:
|
||||
template<typename ParseLineCallback, typename LineEndCallback>
|
||||
bool parse_file_raw_internal(const std::string &filename, ParseLineCallback parse_line_callback, LineEndCallback line_end_callback);
|
||||
template<typename ParseLineCallback, typename LineEndCallback>
|
||||
bool parse_file_internal(const std::string &filename, ParseLineCallback parse_line_callback, LineEndCallback line_end_callback);
|
||||
|
||||
const char* parse_line_internal(const char *ptr, const char *end, GCodeLine &gline, std::pair<const char*, const char*> &command);
|
||||
void update_coordinates(GCodeLine &gline, std::pair<const char*, const char*> &command);
|
||||
|
||||
|
@ -154,6 +167,7 @@ private:
|
|||
char m_extrusion_axis;
|
||||
float m_position[NUM_AXES];
|
||||
bool m_verbose;
|
||||
// To be set by the callback to stop parsing.
|
||||
bool m_parsing{ false };
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "GCodeWriter.hpp"
|
||||
#include "CustomGCode.hpp"
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
@ -11,15 +10,8 @@
|
|||
#include <boost/spirit/include/karma.hpp>
|
||||
#endif
|
||||
|
||||
#define XYZF_EXPORT_DIGITS 3
|
||||
#define E_EXPORT_DIGITS 5
|
||||
|
||||
#define FLAVOR_IS(val) this->config.gcode_flavor == val
|
||||
#define FLAVOR_IS_NOT(val) this->config.gcode_flavor != val
|
||||
#define COMMENT(comment) if (this->config.gcode_comments && !comment.empty()) gcode << " ; " << comment;
|
||||
#define PRECISION(val, precision) std::fixed << std::setprecision(precision) << (val)
|
||||
#define XYZF_NUM(val) PRECISION(val, XYZF_EXPORT_DIGITS)
|
||||
#define E_NUM(val) PRECISION(val, E_EXPORT_DIGITS)
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -261,115 +253,12 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id)
|
|||
return gcode.str();
|
||||
}
|
||||
|
||||
class G1Writer {
|
||||
private:
|
||||
static constexpr const size_t buflen = 256;
|
||||
char buf[buflen];
|
||||
char *buf_end;
|
||||
std::to_chars_result ptr_err;
|
||||
|
||||
public:
|
||||
G1Writer() {
|
||||
this->buf[0] = 'G';
|
||||
this->buf[1] = '1';
|
||||
this->buf_end = this->buf + buflen;
|
||||
this->ptr_err.ptr = this->buf + 2;
|
||||
}
|
||||
|
||||
void emit_axis(const char axis, const double v, size_t digits) {
|
||||
assert(digits <= 6);
|
||||
static constexpr const std::array<int, 7> pow_10{1, 10, 100, 1000, 10000, 100000, 1000000};
|
||||
*ptr_err.ptr++ = ' '; *ptr_err.ptr++ = axis;
|
||||
|
||||
char *base_ptr = this->ptr_err.ptr;
|
||||
auto v_int = int64_t(std::round(v * pow_10[digits]));
|
||||
// Older stdlib on macOS doesn't support std::from_chars at all, so it is used boost::spirit::karma::generate instead of it.
|
||||
// That is a little bit slower than std::to_chars but not much.
|
||||
#ifdef __APPLE__
|
||||
boost::spirit::karma::generate(this->ptr_err.ptr, boost::spirit::karma::int_generator<int64_t>(), v_int);
|
||||
#else
|
||||
// this->buf_end minus 1 because we need space for adding the extra decimal point.
|
||||
this->ptr_err = std::to_chars(this->ptr_err.ptr, this->buf_end - 1, v_int);
|
||||
#endif
|
||||
size_t writen_digits = (this->ptr_err.ptr - base_ptr) - (v_int < 0 ? 1 : 0);
|
||||
if (writen_digits < digits) {
|
||||
// Number is smaller than 10^digits, so that we will pad it with zeros.
|
||||
size_t remaining_digits = digits - writen_digits;
|
||||
// Move all newly inserted chars by remaining_digits to allocate space for padding with zeros.
|
||||
for (char *from_ptr = this->ptr_err.ptr - 1, *to_ptr = from_ptr + remaining_digits; from_ptr >= this->ptr_err.ptr - writen_digits; --to_ptr, --from_ptr)
|
||||
*to_ptr = *from_ptr;
|
||||
|
||||
memset(this->ptr_err.ptr - writen_digits, '0', remaining_digits);
|
||||
this->ptr_err.ptr += remaining_digits;
|
||||
}
|
||||
|
||||
// Move all newly inserted chars by one to allocate space for a decimal point.
|
||||
for (char *to_ptr = this->ptr_err.ptr, *from_ptr = to_ptr - 1; from_ptr >= this->ptr_err.ptr - digits; --to_ptr, --from_ptr)
|
||||
*to_ptr = *from_ptr;
|
||||
|
||||
*(this->ptr_err.ptr - digits) = '.';
|
||||
for (size_t i = 0; i < digits; ++i) {
|
||||
if (*this->ptr_err.ptr != '0')
|
||||
break;
|
||||
this->ptr_err.ptr--;
|
||||
}
|
||||
if (*this->ptr_err.ptr == '.')
|
||||
this->ptr_err.ptr--;
|
||||
if ((this->ptr_err.ptr + 1) == base_ptr || *this->ptr_err.ptr == '-')
|
||||
*(++this->ptr_err.ptr) = '0';
|
||||
this->ptr_err.ptr++;
|
||||
}
|
||||
|
||||
void emit_xy(const Vec2d &point) {
|
||||
this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
|
||||
this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
|
||||
}
|
||||
|
||||
void emit_xyz(const Vec3d &point) {
|
||||
this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
|
||||
this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
|
||||
this->emit_z(point.z());
|
||||
}
|
||||
|
||||
void emit_z(const double z) {
|
||||
this->emit_axis('Z', z, XYZF_EXPORT_DIGITS);
|
||||
}
|
||||
|
||||
void emit_e(const std::string &axis, double v) {
|
||||
if (! axis.empty()) {
|
||||
// not gcfNoExtrusion
|
||||
this->emit_axis(axis[0], v, E_EXPORT_DIGITS);
|
||||
}
|
||||
}
|
||||
|
||||
void emit_f(double speed) {
|
||||
this->emit_axis('F', speed, XYZF_EXPORT_DIGITS);
|
||||
}
|
||||
|
||||
void emit_string(const std::string &s) {
|
||||
strncpy(ptr_err.ptr, s.c_str(), s.size());
|
||||
ptr_err.ptr += s.size();
|
||||
}
|
||||
|
||||
void emit_comment(bool allow_comments, const std::string &comment) {
|
||||
if (allow_comments && ! comment.empty()) {
|
||||
*ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = ';'; *ptr_err.ptr ++ = ' ';
|
||||
this->emit_string(comment);
|
||||
}
|
||||
}
|
||||
|
||||
std::string string() {
|
||||
*ptr_err.ptr ++ = '\n';
|
||||
return std::string(this->buf, ptr_err.ptr - buf);
|
||||
}
|
||||
};
|
||||
|
||||
std::string GCodeWriter::set_speed(double F, const std::string &comment, const std::string &cooling_marker) const
|
||||
{
|
||||
assert(F > 0.);
|
||||
assert(F < 100000.);
|
||||
|
||||
G1Writer w;
|
||||
GCodeG1Formatter w;
|
||||
w.emit_f(F);
|
||||
w.emit_comment(this->config.gcode_comments, comment);
|
||||
w.emit_string(cooling_marker);
|
||||
|
@ -381,7 +270,7 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com
|
|||
m_pos(0) = point(0);
|
||||
m_pos(1) = point(1);
|
||||
|
||||
G1Writer w;
|
||||
GCodeG1Formatter w;
|
||||
w.emit_xy(point);
|
||||
w.emit_f(this->config.travel_speed.value * 60.0);
|
||||
w.emit_comment(this->config.gcode_comments, comment);
|
||||
|
@ -414,7 +303,7 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
|
|||
m_lifted = 0;
|
||||
m_pos = point;
|
||||
|
||||
G1Writer w;
|
||||
GCodeG1Formatter w;
|
||||
w.emit_xyz(point);
|
||||
w.emit_f(this->config.travel_speed.value * 60.0);
|
||||
w.emit_comment(this->config.gcode_comments, comment);
|
||||
|
@ -448,7 +337,7 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
|
|||
if (speed == 0.)
|
||||
speed = this->config.travel_speed.value;
|
||||
|
||||
G1Writer w;
|
||||
GCodeG1Formatter w;
|
||||
w.emit_z(z);
|
||||
w.emit_f(speed * 60.0);
|
||||
w.emit_comment(this->config.gcode_comments, comment);
|
||||
|
@ -473,7 +362,7 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std:
|
|||
m_pos(1) = point(1);
|
||||
m_extruder->extrude(dE);
|
||||
|
||||
G1Writer w;
|
||||
GCodeG1Formatter w;
|
||||
w.emit_xy(point);
|
||||
w.emit_e(m_extrusion_axis, m_extruder->E());
|
||||
w.emit_comment(this->config.gcode_comments, comment);
|
||||
|
@ -486,7 +375,7 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std
|
|||
m_lifted = 0;
|
||||
m_extruder->extrude(dE);
|
||||
|
||||
G1Writer w;
|
||||
GCodeG1Formatter w;
|
||||
w.emit_xyz(point);
|
||||
w.emit_e(m_extrusion_axis, m_extruder->E());
|
||||
w.emit_comment(this->config.gcode_comments, comment);
|
||||
|
@ -517,12 +406,11 @@ std::string GCodeWriter::retract_for_toolchange(bool before_wipe)
|
|||
|
||||
std::string GCodeWriter::_retract(double length, double restart_extra, const std::string &comment)
|
||||
{
|
||||
std::ostringstream gcode;
|
||||
|
||||
/* If firmware retraction is enabled, we use a fake value of 1
|
||||
since we ignore the actual configured retract_length which
|
||||
might be 0, in which case the retraction logic gets skipped. */
|
||||
if (this->config.use_firmware_retraction) length = 1;
|
||||
if (this->config.use_firmware_retraction)
|
||||
length = 1;
|
||||
|
||||
// If we use volumetric E values we turn lengths into volumes */
|
||||
if (this->config.use_volumetric_e) {
|
||||
|
@ -532,52 +420,48 @@ std::string GCodeWriter::_retract(double length, double restart_extra, const std
|
|||
restart_extra = restart_extra * area;
|
||||
}
|
||||
|
||||
double dE = m_extruder->retract(length, restart_extra);
|
||||
if (dE != 0) {
|
||||
|
||||
std::string gcode;
|
||||
if (double dE = m_extruder->retract(length, restart_extra); dE != 0) {
|
||||
if (this->config.use_firmware_retraction) {
|
||||
if (FLAVOR_IS(gcfMachinekit))
|
||||
gcode << "G22 ; retract\n";
|
||||
else
|
||||
gcode << "G10 ; retract\n";
|
||||
gcode = FLAVOR_IS(gcfMachinekit) ? "G22 ; retract\n" : "G10 ; retract\n";
|
||||
} else if (! m_extrusion_axis.empty()) {
|
||||
gcode << "G1 " << m_extrusion_axis << E_NUM(m_extruder->E())
|
||||
<< " F" << XYZF_NUM(m_extruder->retract_speed() * 60.);
|
||||
COMMENT(comment);
|
||||
gcode << "\n";
|
||||
GCodeG1Formatter w;
|
||||
w.emit_e(m_extrusion_axis, m_extruder->E());
|
||||
w.emit_f(m_extruder->retract_speed() * 60.);
|
||||
w.emit_comment(this->config.gcode_comments, comment);
|
||||
gcode = w.string();
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAVOR_IS(gcfMakerWare))
|
||||
gcode << "M103 ; extruder off\n";
|
||||
gcode += "M103 ; extruder off\n";
|
||||
|
||||
return gcode.str();
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string GCodeWriter::unretract()
|
||||
{
|
||||
std::ostringstream gcode;
|
||||
std::string gcode;
|
||||
|
||||
if (FLAVOR_IS(gcfMakerWare))
|
||||
gcode << "M101 ; extruder on\n";
|
||||
gcode = "M101 ; extruder on\n";
|
||||
|
||||
double dE = m_extruder->unretract();
|
||||
if (dE != 0) {
|
||||
if (double dE = m_extruder->unretract(); dE != 0) {
|
||||
if (this->config.use_firmware_retraction) {
|
||||
if (FLAVOR_IS(gcfMachinekit))
|
||||
gcode << "G23 ; unretract\n";
|
||||
else
|
||||
gcode << "G11 ; unretract\n";
|
||||
gcode << this->reset_e();
|
||||
gcode += FLAVOR_IS(gcfMachinekit) ? "G23 ; unretract\n" : "G11 ; unretract\n";
|
||||
gcode += this->reset_e();
|
||||
} else if (! m_extrusion_axis.empty()) {
|
||||
// use G1 instead of G0 because G0 will blend the restart with the previous travel move
|
||||
gcode << "G1 " << m_extrusion_axis << E_NUM(m_extruder->E())
|
||||
<< " F" << XYZF_NUM(m_extruder->deretract_speed() * 60.);
|
||||
if (this->config.gcode_comments) gcode << " ; unretract";
|
||||
gcode << "\n";
|
||||
GCodeG1Formatter w;
|
||||
w.emit_e(m_extrusion_axis, m_extruder->E());
|
||||
w.emit_f(m_extruder->deretract_speed() * 60.);
|
||||
w.emit_comment(this->config.gcode_comments, " ; unretract");
|
||||
gcode += w.string();
|
||||
}
|
||||
}
|
||||
|
||||
return gcode.str();
|
||||
return gcode;
|
||||
}
|
||||
|
||||
/* If this method is called more than once before calling unlift(),
|
||||
|
@ -649,4 +533,49 @@ std::string GCodeWriter::set_fan(unsigned int speed) const
|
|||
return GCodeWriter::set_fan(this->config.gcode_flavor, this->config.gcode_comments, speed);
|
||||
}
|
||||
|
||||
|
||||
void GCodeFormatter::emit_axis(const char axis, const double v, size_t digits) {
|
||||
assert(digits <= 6);
|
||||
static constexpr const std::array<int, 7> pow_10{1, 10, 100, 1000, 10000, 100000, 1000000};
|
||||
*ptr_err.ptr++ = ' '; *ptr_err.ptr++ = axis;
|
||||
|
||||
char *base_ptr = this->ptr_err.ptr;
|
||||
auto v_int = int64_t(std::round(v * pow_10[digits]));
|
||||
// Older stdlib on macOS doesn't support std::from_chars at all, so it is used boost::spirit::karma::generate instead of it.
|
||||
// That is a little bit slower than std::to_chars but not much.
|
||||
#ifdef __APPLE__
|
||||
boost::spirit::karma::generate(this->ptr_err.ptr, boost::spirit::karma::int_generator<int64_t>(), v_int);
|
||||
#else
|
||||
// this->buf_end minus 1 because we need space for adding the extra decimal point.
|
||||
this->ptr_err = std::to_chars(this->ptr_err.ptr, this->buf_end - 1, v_int);
|
||||
#endif
|
||||
size_t writen_digits = (this->ptr_err.ptr - base_ptr) - (v_int < 0 ? 1 : 0);
|
||||
if (writen_digits < digits) {
|
||||
// Number is smaller than 10^digits, so that we will pad it with zeros.
|
||||
size_t remaining_digits = digits - writen_digits;
|
||||
// Move all newly inserted chars by remaining_digits to allocate space for padding with zeros.
|
||||
for (char *from_ptr = this->ptr_err.ptr - 1, *to_ptr = from_ptr + remaining_digits; from_ptr >= this->ptr_err.ptr - writen_digits; --to_ptr, --from_ptr)
|
||||
*to_ptr = *from_ptr;
|
||||
|
||||
memset(this->ptr_err.ptr - writen_digits, '0', remaining_digits);
|
||||
this->ptr_err.ptr += remaining_digits;
|
||||
}
|
||||
|
||||
// Move all newly inserted chars by one to allocate space for a decimal point.
|
||||
for (char *to_ptr = this->ptr_err.ptr, *from_ptr = to_ptr - 1; from_ptr >= this->ptr_err.ptr - digits; --to_ptr, --from_ptr)
|
||||
*to_ptr = *from_ptr;
|
||||
|
||||
*(this->ptr_err.ptr - digits) = '.';
|
||||
for (size_t i = 0; i < digits; ++i) {
|
||||
if (*this->ptr_err.ptr != '0')
|
||||
break;
|
||||
this->ptr_err.ptr--;
|
||||
}
|
||||
if (*this->ptr_err.ptr == '.')
|
||||
this->ptr_err.ptr--;
|
||||
if ((this->ptr_err.ptr + 1) == base_ptr || *this->ptr_err.ptr == '-')
|
||||
*(++this->ptr_err.ptr) = '0';
|
||||
this->ptr_err.ptr++;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "libslic3r.h"
|
||||
#include <string>
|
||||
#include <charconv>
|
||||
#include "Extruder.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
|
@ -93,6 +94,84 @@ private:
|
|||
std::string _retract(double length, double restart_extra, const std::string &comment);
|
||||
};
|
||||
|
||||
class GCodeFormatter {
|
||||
public:
|
||||
GCodeFormatter() {
|
||||
this->buf_end = buf + buflen;
|
||||
this->ptr_err.ptr = this->buf;
|
||||
}
|
||||
|
||||
GCodeFormatter(const GCodeFormatter&) = delete;
|
||||
GCodeFormatter& operator=(const GCodeFormatter&) = delete;
|
||||
|
||||
static constexpr const int XYZF_EXPORT_DIGITS = 3;
|
||||
static constexpr const int E_EXPORT_DIGITS = 5;
|
||||
|
||||
void emit_axis(const char axis, const double v, size_t digits);
|
||||
|
||||
void emit_xy(const Vec2d &point) {
|
||||
this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
|
||||
this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
|
||||
}
|
||||
|
||||
void emit_xyz(const Vec3d &point) {
|
||||
this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
|
||||
this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
|
||||
this->emit_z(point.z());
|
||||
}
|
||||
|
||||
void emit_z(const double z) {
|
||||
this->emit_axis('Z', z, XYZF_EXPORT_DIGITS);
|
||||
}
|
||||
|
||||
void emit_e(const std::string &axis, double v) {
|
||||
if (! axis.empty()) {
|
||||
// not gcfNoExtrusion
|
||||
this->emit_axis(axis[0], v, E_EXPORT_DIGITS);
|
||||
}
|
||||
}
|
||||
|
||||
void emit_f(double speed) {
|
||||
this->emit_axis('F', speed, XYZF_EXPORT_DIGITS);
|
||||
}
|
||||
|
||||
void emit_string(const std::string &s) {
|
||||
strncpy(ptr_err.ptr, s.c_str(), s.size());
|
||||
ptr_err.ptr += s.size();
|
||||
}
|
||||
|
||||
void emit_comment(bool allow_comments, const std::string &comment) {
|
||||
if (allow_comments && ! comment.empty()) {
|
||||
*ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = ';'; *ptr_err.ptr ++ = ' ';
|
||||
this->emit_string(comment);
|
||||
}
|
||||
}
|
||||
|
||||
std::string string() {
|
||||
*ptr_err.ptr ++ = '\n';
|
||||
return std::string(this->buf, ptr_err.ptr - buf);
|
||||
}
|
||||
|
||||
protected:
|
||||
static constexpr const size_t buflen = 256;
|
||||
char buf[buflen];
|
||||
char* buf_end;
|
||||
std::to_chars_result ptr_err;
|
||||
};
|
||||
|
||||
class GCodeG1Formatter : public GCodeFormatter {
|
||||
public:
|
||||
GCodeG1Formatter() {
|
||||
this->buf[0] = 'G';
|
||||
this->buf[1] = '1';
|
||||
this->buf_end = buf + buflen;
|
||||
this->ptr_err.ptr = this->buf + 2;
|
||||
}
|
||||
|
||||
GCodeG1Formatter(const GCodeG1Formatter&) = delete;
|
||||
GCodeG1Formatter& operator=(const GCodeG1Formatter&) = delete;
|
||||
};
|
||||
|
||||
} /* namespace Slic3r */
|
||||
|
||||
#endif /* slic3r_GCodeWriter_hpp_ */
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
#include "LocalesUtils.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <charconv>
|
||||
#endif
|
||||
#include <stdexcept>
|
||||
|
||||
#include <fast_float/fast_float.h>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
|
@ -11,15 +17,15 @@ CNumericLocalesSetter::CNumericLocalesSetter()
|
|||
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
||||
m_orig_numeric_locale = std::setlocale(LC_NUMERIC, nullptr);
|
||||
std::setlocale(LC_NUMERIC, "C");
|
||||
#elif __linux__
|
||||
#elif __APPLE__
|
||||
m_original_locale = uselocale((locale_t)0);
|
||||
m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_original_locale);
|
||||
uselocale(m_new_locale);
|
||||
#else // linux / BSD
|
||||
m_original_locale = uselocale((locale_t)0);
|
||||
m_new_locale = duplocale(m_original_locale);
|
||||
m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_new_locale);
|
||||
uselocale(m_new_locale);
|
||||
#else // APPLE
|
||||
m_original_locale = uselocale((locale_t)0);
|
||||
m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_original_locale);
|
||||
uselocale(m_new_locale);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -45,28 +51,37 @@ bool is_decimal_separator_point()
|
|||
}
|
||||
|
||||
|
||||
double string_to_double_decimal_point(const std::string& str, size_t* pos /* = nullptr*/)
|
||||
double string_to_double_decimal_point(const std::string_view str, size_t* pos /* = nullptr*/)
|
||||
{
|
||||
double out;
|
||||
std::istringstream stream(str);
|
||||
if (! (stream >> out))
|
||||
throw std::invalid_argument("string_to_double_decimal_point conversion failed.");
|
||||
if (pos) {
|
||||
if (stream.eof())
|
||||
*pos = str.size();
|
||||
else
|
||||
*pos = stream.tellg();
|
||||
}
|
||||
size_t p = fast_float::from_chars(str.data(), str.data() + str.size(), out).ptr - str.data();
|
||||
if (pos)
|
||||
*pos = p;
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string float_to_string_decimal_point(double value, int precision/* = -1*/)
|
||||
{
|
||||
// Our Windows build server fully supports C++17 std::to_chars. Let's use it.
|
||||
// Other platforms are behind, fall back to slow stringstreams for now.
|
||||
#ifdef _WIN32
|
||||
constexpr size_t SIZE = 20;
|
||||
char out[SIZE] = "";
|
||||
std::to_chars_result res;
|
||||
if (precision >=0)
|
||||
res = std::to_chars(out, out+SIZE, value, std::chars_format::fixed, precision);
|
||||
else
|
||||
res = std::to_chars(out, out+SIZE, value, std::chars_format::general, 6);
|
||||
if (res.ec == std::errc::value_too_large)
|
||||
throw std::invalid_argument("float_to_string_decimal_point conversion failed.");
|
||||
return std::string(out, res.ptr - out);
|
||||
#else
|
||||
std::stringstream buf;
|
||||
if (precision >= 0)
|
||||
buf << std::fixed << std::setprecision(precision);
|
||||
buf << value;
|
||||
return buf.str();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <clocale>
|
||||
#include <iomanip>
|
||||
#include <cassert>
|
||||
#include <string_view>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <xlocale.h>
|
||||
|
@ -40,7 +41,7 @@ bool is_decimal_separator_point();
|
|||
// (We use user C locales and "C" C++ locales in most of the code.)
|
||||
std::string float_to_string_decimal_point(double value, int precision = -1);
|
||||
//std::string float_to_string_decimal_point(float value, int precision = -1);
|
||||
double string_to_double_decimal_point(const std::string& str, size_t* pos = nullptr);
|
||||
double string_to_double_decimal_point(const std::string_view str, size_t* pos = nullptr);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
@ -29,18 +29,17 @@ TriangleMesh eigen_to_triangle_mesh(const EigenMesh &emesh)
|
|||
{
|
||||
auto &VC = emesh.first; auto &FC = emesh.second;
|
||||
|
||||
Pointf3s points(size_t(VC.rows()));
|
||||
std::vector<Vec3i> facets(size_t(FC.rows()));
|
||||
indexed_triangle_set its;
|
||||
its.vertices.reserve(size_t(VC.rows()));
|
||||
its.indices.reserve(size_t(FC.rows()));
|
||||
|
||||
for (Eigen::Index i = 0; i < VC.rows(); ++i)
|
||||
points[size_t(i)] = VC.row(i);
|
||||
its.vertices.emplace_back(VC.row(i).cast<float>());
|
||||
|
||||
for (Eigen::Index i = 0; i < FC.rows(); ++i)
|
||||
facets[size_t(i)] = FC.row(i);
|
||||
its.indices.emplace_back(FC.row(i));
|
||||
|
||||
TriangleMesh out{points, facets};
|
||||
out.require_shared_vertices();
|
||||
return out;
|
||||
return TriangleMesh { std::move(its) };
|
||||
}
|
||||
|
||||
EigenMesh triangle_mesh_to_eigen(const TriangleMesh &mesh)
|
||||
|
@ -131,28 +130,27 @@ void triangle_mesh_to_cgal(const std::vector<stl_vertex> & V,
|
|||
out.add_face(VI(f(0)), VI(f(1)), VI(f(2)));
|
||||
}
|
||||
|
||||
inline Vec3d to_vec3d(const _EpicMesh::Point &v)
|
||||
inline Vec3f to_vec3f(const _EpicMesh::Point& v)
|
||||
{
|
||||
return {v.x(), v.y(), v.z()};
|
||||
return { float(v.x()), float(v.y()), float(v.z()) };
|
||||
}
|
||||
|
||||
inline Vec3d to_vec3d(const _EpecMesh::Point &v)
|
||||
inline Vec3f to_vec3f(const _EpecMesh::Point& v)
|
||||
{
|
||||
CGAL::Cartesian_converter<EpecKernel, EpicKernel> cvt;
|
||||
auto iv = cvt(v);
|
||||
return {iv.x(), iv.y(), iv.z()};
|
||||
return { float(iv.x()), float(iv.y()), float(iv.z()) };
|
||||
}
|
||||
|
||||
template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
|
||||
{
|
||||
Pointf3s points;
|
||||
std::vector<Vec3i> facets;
|
||||
points.reserve(cgalmesh.num_vertices());
|
||||
facets.reserve(cgalmesh.num_faces());
|
||||
indexed_triangle_set its;
|
||||
its.vertices.reserve(cgalmesh.num_vertices());
|
||||
its.indices.reserve(cgalmesh.num_faces());
|
||||
|
||||
for (auto &vi : cgalmesh.vertices()) {
|
||||
auto &v = cgalmesh.point(vi); // Don't ask...
|
||||
points.emplace_back(to_vec3d(v));
|
||||
its.vertices.emplace_back(to_vec3f(v));
|
||||
}
|
||||
|
||||
for (auto &face : cgalmesh.faces()) {
|
||||
|
@ -166,12 +164,10 @@ template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
|
|||
}
|
||||
|
||||
if (i == 3)
|
||||
facets.emplace_back(facet);
|
||||
its.indices.emplace_back(facet);
|
||||
}
|
||||
|
||||
TriangleMesh out{points, facets};
|
||||
out.repair();
|
||||
return out;
|
||||
return TriangleMesh(std::move(its));
|
||||
}
|
||||
|
||||
std::unique_ptr<CGALMesh, CGALMeshDeleter>
|
||||
|
|
|
@ -28,65 +28,84 @@ template<> struct ItsWithNeighborsIndex_<indexed_triangle_set> {
|
|||
}
|
||||
};
|
||||
|
||||
// Visit all unvisited neighboring facets that are reachable from the first unvisited facet,
|
||||
// and return them.
|
||||
// Discover connected patches of facets one by one.
|
||||
template<class NeighborIndex>
|
||||
std::vector<size_t> its_find_unvisited_neighbors(
|
||||
const indexed_triangle_set &its,
|
||||
const NeighborIndex & neighbor_index,
|
||||
std::vector<char> & visited)
|
||||
{
|
||||
using stack_el = size_t;
|
||||
|
||||
auto facestack = reserve_vector<stack_el>(its.indices.size());
|
||||
auto push = [&facestack] (const stack_el &s) { facestack.emplace_back(s); };
|
||||
auto pop = [&facestack] () -> stack_el {
|
||||
stack_el ret = facestack.back();
|
||||
facestack.pop_back();
|
||||
return ret;
|
||||
};
|
||||
struct NeighborVisitor {
|
||||
NeighborVisitor(const indexed_triangle_set &its, const NeighborIndex &neighbor_index) :
|
||||
its(its), neighbor_index(neighbor_index) {
|
||||
m_visited.assign(its.indices.size(), false);
|
||||
m_facestack.reserve(its.indices.size());
|
||||
}
|
||||
NeighborVisitor(const indexed_triangle_set &its, NeighborIndex &&aneighbor_index) :
|
||||
its(its), neighbor_index(m_neighbor_index_data), m_neighbor_index_data(std::move(aneighbor_index)) {
|
||||
m_visited.assign(its.indices.size(), false);
|
||||
m_facestack.reserve(its.indices.size());
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void visit(Visitor visitor)
|
||||
{
|
||||
// find the next unvisited facet and push the index
|
||||
auto facet = std::find(visited.begin(), visited.end(), false);
|
||||
std::vector<size_t> ret;
|
||||
auto facet = std::find(m_visited.begin() + m_seed, m_visited.end(), false);
|
||||
m_seed = facet - m_visited.begin();
|
||||
|
||||
if (facet != visited.end()) {
|
||||
ret.reserve(its.indices.size());
|
||||
auto idx = size_t(facet - visited.begin());
|
||||
push(idx);
|
||||
ret.emplace_back(idx);
|
||||
visited[idx] = true;
|
||||
if (facet != m_visited.end()) {
|
||||
// Skip this element in the next round.
|
||||
auto idx = m_seed ++;
|
||||
if (! visitor(idx))
|
||||
return;
|
||||
this->push(idx);
|
||||
m_visited[idx] = true;
|
||||
while (! m_facestack.empty()) {
|
||||
size_t facet_idx = this->pop();
|
||||
for (auto neighbor_idx : neighbor_index[facet_idx]) {
|
||||
assert(neighbor_idx < int(m_visited.size()));
|
||||
if (neighbor_idx >= 0 && !m_visited[neighbor_idx]) {
|
||||
if (! visitor(size_t(neighbor_idx)))
|
||||
return;
|
||||
m_visited[neighbor_idx] = true;
|
||||
this->push(stack_el(neighbor_idx));
|
||||
}
|
||||
}
|
||||
|
||||
while (!facestack.empty()) {
|
||||
size_t facet_idx = pop();
|
||||
const auto &neighbors = neighbor_index[facet_idx];
|
||||
for (auto neighbor_idx : neighbors) {
|
||||
if (size_t(neighbor_idx) < visited.size() && !visited[size_t(neighbor_idx)]) {
|
||||
visited[size_t(neighbor_idx)] = true;
|
||||
push(stack_el(neighbor_idx));
|
||||
ret.emplace_back(size_t(neighbor_idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
const indexed_triangle_set &its;
|
||||
const NeighborIndex &neighbor_index;
|
||||
|
||||
private:
|
||||
// If initialized with &&neighbor_index, take the ownership of the data.
|
||||
const NeighborIndex m_neighbor_index_data;
|
||||
|
||||
std::vector<char> m_visited;
|
||||
|
||||
using stack_el = size_t;
|
||||
std::vector<stack_el> m_facestack;
|
||||
void push(const stack_el &s) { m_facestack.emplace_back(s); }
|
||||
stack_el pop() { stack_el ret = m_facestack.back(); m_facestack.pop_back(); return ret; }
|
||||
|
||||
// Last face visited.
|
||||
size_t m_seed { 0 };
|
||||
};
|
||||
|
||||
} // namespace meshsplit_detail
|
||||
|
||||
// Funky wrapper for timinig of its_split() using various neighbor index creating methods, see sandboxes/its_neighbor_index/main.cpp
|
||||
template<class IndexT> struct ItsNeighborsWrapper
|
||||
{
|
||||
using Index = IndexT;
|
||||
const indexed_triangle_set *its;
|
||||
IndexT index;
|
||||
const indexed_triangle_set &its;
|
||||
const IndexT &index_ref;
|
||||
const IndexT index;
|
||||
|
||||
ItsNeighborsWrapper(const indexed_triangle_set &m, IndexT &&idx)
|
||||
: its{&m}, index{std::move(idx)}
|
||||
{}
|
||||
// Keeping a reference to index, the caller is responsible for keeping the index alive.
|
||||
ItsNeighborsWrapper(const indexed_triangle_set &its, const IndexT &index) : its{its}, index_ref{index} {}
|
||||
// Taking ownership of the index.
|
||||
ItsNeighborsWrapper(const indexed_triangle_set &its, IndexT &&aindex) : its{its}, index_ref{index}, index(std::move(aindex)) {}
|
||||
|
||||
const auto& get_its() const noexcept { return *its; }
|
||||
const auto& get_index() const noexcept { return index; }
|
||||
const auto& get_its() const noexcept { return its; }
|
||||
const auto& get_index() const noexcept { return index_ref; }
|
||||
};
|
||||
|
||||
// Splits a mesh into multiple meshes when possible.
|
||||
|
@ -97,20 +116,19 @@ void its_split(const Its &m, OutputIt out_it)
|
|||
|
||||
const indexed_triangle_set &its = ItsWithNeighborsIndex_<Its>::get_its(m);
|
||||
|
||||
std::vector<char> visited(its.indices.size(), false);
|
||||
|
||||
struct VertexConv {
|
||||
size_t part_id = std::numeric_limits<size_t>::max();
|
||||
size_t vertex_image;
|
||||
};
|
||||
std::vector<VertexConv> vidx_conv(its.vertices.size());
|
||||
|
||||
const auto& neighbor_index = ItsWithNeighborsIndex_<Its>::get_index(m);
|
||||
meshsplit_detail::NeighborVisitor visitor(its, meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_index(m));
|
||||
|
||||
std::vector<size_t> facets;
|
||||
for (size_t part_id = 0;; ++part_id) {
|
||||
std::vector<size_t> facets =
|
||||
its_find_unvisited_neighbors(its, neighbor_index, visited);
|
||||
|
||||
// Collect all faces of the next patch.
|
||||
facets.clear();
|
||||
visitor.visit([&facets](size_t idx) { facets.emplace_back(idx); return true; });
|
||||
if (facets.empty())
|
||||
break;
|
||||
|
||||
|
@ -150,17 +168,34 @@ std::vector<indexed_triangle_set> its_split(const Its &its)
|
|||
return ret;
|
||||
}
|
||||
|
||||
template<class Its> bool its_is_splittable(const Its &m)
|
||||
template<class Its>
|
||||
bool its_is_splittable(const Its &m)
|
||||
{
|
||||
using namespace meshsplit_detail;
|
||||
const indexed_triangle_set &its = ItsWithNeighborsIndex_<Its>::get_its(m);
|
||||
const auto& neighbor_index = ItsWithNeighborsIndex_<Its>::get_index(m);
|
||||
meshsplit_detail::NeighborVisitor visitor(meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_its(m), meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_index(m));
|
||||
bool has_some = false;
|
||||
bool has_some2 = false;
|
||||
// Traverse the 1st patch fully.
|
||||
visitor.visit([&has_some](size_t idx) { has_some = true; return true; });
|
||||
if (has_some)
|
||||
// Just check whether there is any face of the 2nd patch.
|
||||
visitor.visit([&has_some2](size_t idx) { has_some2 = true; return false; });
|
||||
return has_some && has_some2;
|
||||
}
|
||||
|
||||
std::vector<char> visited(its.indices.size(), false);
|
||||
its_find_unvisited_neighbors(its, neighbor_index, visited);
|
||||
auto faces = its_find_unvisited_neighbors(its, neighbor_index, visited);
|
||||
|
||||
return !faces.empty();
|
||||
template<class Its>
|
||||
size_t its_number_of_patches(const Its &m)
|
||||
{
|
||||
meshsplit_detail::NeighborVisitor visitor(meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_its(m), meshsplit_detail::ItsWithNeighborsIndex_<Its>::get_index(m));
|
||||
size_t num_patches = 0;
|
||||
for (;;) {
|
||||
bool has_some = false;
|
||||
// Traverse the 1st patch fully.
|
||||
visitor.visit([&has_some](size_t idx) { has_some = true; return true; });
|
||||
if (! has_some)
|
||||
break;
|
||||
++ num_patches;
|
||||
}
|
||||
return num_patches;
|
||||
}
|
||||
|
||||
template<class ExPolicy>
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "Format/AMF.hpp"
|
||||
#include "Format/OBJ.hpp"
|
||||
#include "Format/PRUS.hpp"
|
||||
#include "Format/STL.hpp"
|
||||
#include "Format/3mf.hpp"
|
||||
|
||||
|
@ -118,8 +117,6 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
|
|||
else if (boost::algorithm::iends_with(input_file, ".3mf"))
|
||||
//FIXME options & LoadAttribute::CheckVersion ?
|
||||
result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, false);
|
||||
else if (boost::algorithm::iends_with(input_file, ".prusa"))
|
||||
result = load_prus(input_file.c_str(), &model);
|
||||
else
|
||||
throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension.");
|
||||
|
||||
|
@ -478,10 +475,10 @@ bool Model::looks_like_imperial_units() const
|
|||
|
||||
void Model::convert_from_imperial_units(bool only_small_volumes)
|
||||
{
|
||||
static constexpr const double in_to_mm = 25.4;
|
||||
static constexpr const float in_to_mm = 25.4f;
|
||||
for (ModelObject* obj : this->objects)
|
||||
if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_inches) {
|
||||
obj->scale_mesh_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
|
||||
obj->scale_mesh_after_creation(in_to_mm);
|
||||
for (ModelVolume* v : obj->volumes) {
|
||||
assert(! v->source.is_converted_from_meters);
|
||||
v->source.is_converted_from_inches = true;
|
||||
|
@ -508,7 +505,7 @@ void Model::convert_from_meters(bool only_small_volumes)
|
|||
static constexpr const double m_to_mm = 1000;
|
||||
for (ModelObject* obj : this->objects)
|
||||
if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_meters) {
|
||||
obj->scale_mesh_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm));
|
||||
obj->scale_mesh_after_creation(m_to_mm);
|
||||
for (ModelVolume* v : obj->volumes) {
|
||||
assert(! v->source.is_converted_from_inches);
|
||||
v->source.is_converted_from_meters = true;
|
||||
|
@ -1065,11 +1062,11 @@ void ModelObject::mirror(Axis axis)
|
|||
}
|
||||
|
||||
// This method could only be called before the meshes of this ModelVolumes are not shared!
|
||||
void ModelObject::scale_mesh_after_creation(const Vec3d &versor)
|
||||
void ModelObject::scale_mesh_after_creation(const float scale)
|
||||
{
|
||||
for (ModelVolume *v : this->volumes) {
|
||||
v->scale_geometry_after_creation(versor);
|
||||
v->set_offset(versor.cwiseProduct(v->get_offset()));
|
||||
v->scale_geometry_after_creation(scale);
|
||||
v->set_offset(Vec3d(scale, scale, scale).cwiseProduct(v->get_offset()));
|
||||
}
|
||||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
@ -1080,9 +1077,8 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
|
|||
|
||||
ModelObject* new_object = new_clone(*this);
|
||||
|
||||
double koef = conv_type == ConversionType::CONV_FROM_INCH ? 25.4 : conv_type == ConversionType::CONV_TO_INCH ? 0.0393700787 :
|
||||
conv_type == ConversionType::CONV_FROM_METER ? 1000 : conv_type == ConversionType::CONV_TO_METER ? 0.001 : 1;
|
||||
const Vec3d versor = Vec3d(koef, koef, koef);
|
||||
float koef = conv_type == ConversionType::CONV_FROM_INCH ? 25.4f : conv_type == ConversionType::CONV_TO_INCH ? 0.0393700787f :
|
||||
conv_type == ConversionType::CONV_FROM_METER ? 1000.f : conv_type == ConversionType::CONV_TO_METER ? 0.001f : 1.f;
|
||||
|
||||
new_object->set_model(nullptr);
|
||||
new_object->sla_support_points.clear();
|
||||
|
@ -1095,7 +1091,6 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
|
|||
for (ModelVolume* volume : volumes) {
|
||||
if (!volume->mesh().empty()) {
|
||||
TriangleMesh mesh(volume->mesh());
|
||||
mesh.require_shared_vertices();
|
||||
|
||||
ModelVolume* vol = new_object->add_volume(mesh);
|
||||
vol->name = volume->name;
|
||||
|
@ -1121,8 +1116,8 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
|
|||
if (//vol->source.is_converted_from_inches != from_imperial &&
|
||||
(volume_idxs.empty() ||
|
||||
std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end())) {
|
||||
vol->scale_geometry_after_creation(versor);
|
||||
vol->set_offset(versor.cwiseProduct(volume->get_offset()));
|
||||
vol->scale_geometry_after_creation(koef);
|
||||
vol->set_offset(Vec3d(koef, koef, koef).cwiseProduct(volume->get_offset()));
|
||||
if (conv_type == ConversionType::CONV_FROM_INCH || conv_type == ConversionType::CONV_TO_INCH)
|
||||
vol->source.is_converted_from_inches = conv_type == ConversionType::CONV_FROM_INCH;
|
||||
if (conv_type == ConversionType::CONV_FROM_METER || conv_type == ConversionType::CONV_TO_METER)
|
||||
|
@ -1154,7 +1149,7 @@ size_t ModelObject::facets_count() const
|
|||
size_t num = 0;
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part())
|
||||
num += v->mesh().stl.stats.number_of_facets;
|
||||
num += v->mesh().facets_count();
|
||||
return num;
|
||||
}
|
||||
|
||||
|
@ -1167,14 +1162,6 @@ size_t ModelObject::parts_count() const
|
|||
return num;
|
||||
}
|
||||
|
||||
bool ModelObject::needed_repair() const
|
||||
{
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part() && v->mesh().needed_repair())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes)
|
||||
{
|
||||
if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||
|
@ -1256,21 +1243,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
|||
TriangleMesh upper_mesh, lower_mesh;
|
||||
{
|
||||
indexed_triangle_set upper_its, lower_its;
|
||||
mesh.require_shared_vertices();
|
||||
cut_mesh(mesh.its, float(z), &upper_its, &lower_its);
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
|
||||
upper_mesh = TriangleMesh(upper_its);
|
||||
upper_mesh.repair();
|
||||
upper_mesh.reset_repair_stats();
|
||||
}
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||
lower_mesh = TriangleMesh(lower_its);
|
||||
lower_mesh.repair();
|
||||
lower_mesh.reset_repair_stats();
|
||||
}
|
||||
}
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper_mesh.facets_count() > 0) {
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && ! upper_mesh.empty()) {
|
||||
ModelVolume* vol = upper->add_volume(upper_mesh);
|
||||
vol->name = volume->name;
|
||||
// Don't copy the config's ID.
|
||||
|
@ -1279,7 +1259,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
|||
assert(vol->config.id() != volume->config.id());
|
||||
vol->set_material(volume->material_id(), *volume->material());
|
||||
}
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower_mesh.facets_count() > 0) {
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && ! lower_mesh.empty()) {
|
||||
ModelVolume* vol = lower->add_volume(lower_mesh);
|
||||
vol->name = volume->name;
|
||||
// Don't copy the config's ID.
|
||||
|
@ -1349,24 +1329,22 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
|||
if (volume->type() != ModelVolumeType::MODEL_PART)
|
||||
continue;
|
||||
|
||||
TriangleMeshPtrs meshptrs = volume->mesh().split();
|
||||
std::vector<TriangleMesh> meshes = volume->mesh().split();
|
||||
size_t counter = 1;
|
||||
for (TriangleMesh* mesh : meshptrs) {
|
||||
|
||||
for (TriangleMesh &mesh : meshes) {
|
||||
// FIXME: crashes if not satisfied
|
||||
if (mesh->facets_count() < 3) continue;
|
||||
|
||||
mesh->repair();
|
||||
if (mesh.facets_count() < 3)
|
||||
continue;
|
||||
|
||||
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
|
||||
ModelObject* new_object = m_model->add_object();
|
||||
if (meshptrs.size() == 1) {
|
||||
if (meshes.size() == 1) {
|
||||
new_object->name = volume->name;
|
||||
// Don't copy the config's ID.
|
||||
new_object->config.assign_config(this->config.size() > 0 ? this->config : volume->config);
|
||||
}
|
||||
else {
|
||||
new_object->name = this->name + (meshptrs.size() > 1 ? "_" + std::to_string(counter++) : "");
|
||||
new_object->name = this->name + (meshes.size() > 1 ? "_" + std::to_string(counter++) : "");
|
||||
// Don't copy the config's ID.
|
||||
new_object->config.assign_config(this->config);
|
||||
}
|
||||
|
@ -1375,7 +1353,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
|||
new_object->instances.reserve(this->instances.size());
|
||||
for (const ModelInstance* model_instance : this->instances)
|
||||
new_object->add_instance(*model_instance);
|
||||
ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh));
|
||||
ModelVolume* new_vol = new_object->add_volume(*volume, std::move(mesh));
|
||||
|
||||
for (ModelInstance* model_instance : new_object->instances)
|
||||
{
|
||||
|
@ -1387,7 +1365,6 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
|||
// reset the source to disable reload from disk
|
||||
new_vol->source = ModelVolume::Source();
|
||||
new_objects->emplace_back(new_object);
|
||||
delete mesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1405,7 +1382,6 @@ void ModelObject::merge()
|
|||
for (ModelVolume* volume : volumes)
|
||||
if (!volume->mesh().empty())
|
||||
mesh.merge(volume->mesh());
|
||||
mesh.repair();
|
||||
|
||||
this->clear_volumes();
|
||||
ModelVolume* vol = this->add_volume(mesh);
|
||||
|
@ -1508,9 +1484,9 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const
|
|||
|
||||
const Transform3d mv = mi * v->get_matrix();
|
||||
const TriangleMesh& hull = v->get_convex_hull();
|
||||
for (const stl_facet &facet : hull.stl.facet_start)
|
||||
for (const stl_triangle_vertex_indices& facet : hull.its.indices)
|
||||
for (int i = 0; i < 3; ++ i)
|
||||
min_z = std::min(min_z, (mv * facet.vertex[i].cast<double>()).z());
|
||||
min_z = std::min(min_z, (mv * hull.its.vertices[facet[i]].cast<double>()).z());
|
||||
}
|
||||
|
||||
return min_z + inst->get_offset(Z);
|
||||
|
@ -1529,9 +1505,9 @@ double ModelObject::get_instance_max_z(size_t instance_idx) const
|
|||
|
||||
const Transform3d mv = mi * v->get_matrix();
|
||||
const TriangleMesh& hull = v->get_convex_hull();
|
||||
for (const stl_facet& facet : hull.stl.facet_start)
|
||||
for (const stl_triangle_vertex_indices& facet : hull.its.indices)
|
||||
for (int i = 0; i < 3; ++i)
|
||||
max_z = std::max(max_z, (mv * facet.vertex[i].cast<double>()).z());
|
||||
max_z = std::max(max_z, (mv * hull.its.vertices[facet[i]].cast<double>()).z());
|
||||
}
|
||||
|
||||
return max_z + inst->get_offset(Z);
|
||||
|
@ -1572,7 +1548,6 @@ void ModelObject::print_info() const
|
|||
boost::nowide::cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl;
|
||||
|
||||
TriangleMesh mesh = this->raw_mesh();
|
||||
mesh.check_topology();
|
||||
BoundingBoxf3 bb = mesh.bounding_box();
|
||||
Vec3d size = bb.size();
|
||||
cout << "size_x = " << size(0) << endl;
|
||||
|
@ -1584,26 +1559,25 @@ void ModelObject::print_info() const
|
|||
cout << "max_x = " << bb.max(0) << endl;
|
||||
cout << "max_y = " << bb.max(1) << endl;
|
||||
cout << "max_z = " << bb.max(2) << endl;
|
||||
cout << "number_of_facets = " << mesh.stl.stats.number_of_facets << endl;
|
||||
cout << "manifold = " << (mesh.is_manifold() ? "yes" : "no") << endl;
|
||||
cout << "number_of_facets = " << mesh.facets_count() << endl;
|
||||
|
||||
mesh.repair(); // this calculates number_of_parts
|
||||
if (mesh.needed_repair()) {
|
||||
mesh.repair();
|
||||
if (mesh.stl.stats.degenerate_facets > 0)
|
||||
cout << "degenerate_facets = " << mesh.stl.stats.degenerate_facets << endl;
|
||||
if (mesh.stl.stats.edges_fixed > 0)
|
||||
cout << "edges_fixed = " << mesh.stl.stats.edges_fixed << endl;
|
||||
if (mesh.stl.stats.facets_removed > 0)
|
||||
cout << "facets_removed = " << mesh.stl.stats.facets_removed << endl;
|
||||
if (mesh.stl.stats.facets_added > 0)
|
||||
cout << "facets_added = " << mesh.stl.stats.facets_added << endl;
|
||||
if (mesh.stl.stats.facets_reversed > 0)
|
||||
cout << "facets_reversed = " << mesh.stl.stats.facets_reversed << endl;
|
||||
if (mesh.stl.stats.backwards_edges > 0)
|
||||
cout << "backwards_edges = " << mesh.stl.stats.backwards_edges << endl;
|
||||
cout << "manifold = " << (mesh.stats().manifold() ? "yes" : "no") << endl;
|
||||
if (! mesh.stats().manifold())
|
||||
cout << "open_edges = " << mesh.stats().open_edges << endl;
|
||||
|
||||
if (mesh.stats().repaired()) {
|
||||
if (mesh.stats().degenerate_facets > 0)
|
||||
cout << "degenerate_facets = " << mesh.stats().degenerate_facets << endl;
|
||||
if (mesh.stats().edges_fixed > 0)
|
||||
cout << "edges_fixed = " << mesh.stats().edges_fixed << endl;
|
||||
if (mesh.stats().facets_removed > 0)
|
||||
cout << "facets_removed = " << mesh.stats().facets_removed << endl;
|
||||
if (mesh.stats().facets_reversed > 0)
|
||||
cout << "facets_reversed = " << mesh.stats().facets_reversed << endl;
|
||||
if (mesh.stats().backwards_edges > 0)
|
||||
cout << "backwards_edges = " << mesh.stats().backwards_edges << endl;
|
||||
}
|
||||
cout << "number_of_parts = " << mesh.stl.stats.number_of_parts << endl;
|
||||
cout << "number_of_parts = " << mesh.stats().number_of_parts << endl;
|
||||
cout << "volume = " << mesh.volume() << endl;
|
||||
}
|
||||
|
||||
|
@ -1627,24 +1601,24 @@ std::string ModelObject::get_export_filename() const
|
|||
return ret;
|
||||
}
|
||||
|
||||
stl_stats ModelObject::get_object_stl_stats() const
|
||||
TriangleMeshStats ModelObject::get_object_stl_stats() const
|
||||
{
|
||||
if (this->volumes.size() == 1)
|
||||
return this->volumes[0]->mesh().stl.stats;
|
||||
return this->volumes[0]->mesh().stats();
|
||||
|
||||
stl_stats full_stats;
|
||||
TriangleMeshStats full_stats;
|
||||
full_stats.volume = 0.f;
|
||||
|
||||
// fill full_stats from all objet's meshes
|
||||
for (ModelVolume* volume : this->volumes)
|
||||
{
|
||||
const stl_stats& stats = volume->mesh().stl.stats;
|
||||
const TriangleMeshStats& stats = volume->mesh().stats();
|
||||
|
||||
// initialize full_stats (for repaired errors)
|
||||
full_stats.open_edges += stats.open_edges;
|
||||
full_stats.degenerate_facets += stats.degenerate_facets;
|
||||
full_stats.edges_fixed += stats.edges_fixed;
|
||||
full_stats.facets_removed += stats.facets_removed;
|
||||
full_stats.facets_added += stats.facets_added;
|
||||
full_stats.facets_reversed += stats.facets_reversed;
|
||||
full_stats.backwards_edges += stats.backwards_edges;
|
||||
|
||||
|
@ -1663,10 +1637,10 @@ int ModelObject::get_mesh_errors_count(const int vol_idx /*= -1*/) const
|
|||
if (vol_idx >= 0)
|
||||
return this->volumes[vol_idx]->get_mesh_errors_count();
|
||||
|
||||
const stl_stats& stats = get_object_stl_stats();
|
||||
const TriangleMeshStats& stats = get_object_stl_stats();
|
||||
|
||||
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
|
||||
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
|
||||
stats.facets_reversed + stats.backwards_edges;
|
||||
}
|
||||
|
||||
void ModelVolume::set_material_id(t_model_material_id material_id)
|
||||
|
@ -1730,14 +1704,15 @@ void ModelVolume::center_geometry_after_creation(bool update_source_offset)
|
|||
void ModelVolume::calculate_convex_hull()
|
||||
{
|
||||
m_convex_hull = std::make_shared<TriangleMesh>(this->mesh().convex_hull_3d());
|
||||
assert(m_convex_hull.get());
|
||||
}
|
||||
|
||||
int ModelVolume::get_mesh_errors_count() const
|
||||
{
|
||||
const stl_stats& stats = this->mesh().stl.stats;
|
||||
const TriangleMeshStats &stats = this->mesh().stats();
|
||||
|
||||
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
|
||||
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
|
||||
stats.facets_reversed + stats.backwards_edges;
|
||||
}
|
||||
|
||||
const TriangleMesh& ModelVolume::get_convex_hull() const
|
||||
|
@ -1785,11 +1760,9 @@ std::string ModelVolume::type_to_string(const ModelVolumeType t)
|
|||
// This is useful to assign different materials to different volumes of an object.
|
||||
size_t ModelVolume::split(unsigned int max_extruders)
|
||||
{
|
||||
TriangleMeshPtrs meshptrs = this->mesh().split();
|
||||
if (meshptrs.size() <= 1) {
|
||||
delete meshptrs.front();
|
||||
std::vector<TriangleMesh> meshes = this->mesh().split();
|
||||
if (meshes.size() <= 1)
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t idx = 0;
|
||||
size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin();
|
||||
|
@ -1798,15 +1771,14 @@ size_t ModelVolume::split(unsigned int max_extruders)
|
|||
unsigned int extruder_counter = 0;
|
||||
Vec3d offset = this->get_offset();
|
||||
|
||||
for (TriangleMesh *mesh : meshptrs) {
|
||||
mesh->repair();
|
||||
if (mesh->empty())
|
||||
for (TriangleMesh &mesh : meshes) {
|
||||
if (mesh.empty())
|
||||
// Repair may have removed unconnected triangles, thus emptying the mesh.
|
||||
continue;
|
||||
|
||||
if (idx == 0)
|
||||
{
|
||||
this->set_mesh(std::move(*mesh));
|
||||
this->set_mesh(std::move(mesh));
|
||||
this->calculate_convex_hull();
|
||||
// Assign a new unique ID, so that a new GLVolume will be generated.
|
||||
this->set_new_unique_id();
|
||||
|
@ -1814,7 +1786,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
|
|||
this->source = ModelVolume::Source();
|
||||
}
|
||||
else
|
||||
this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(*mesh)));
|
||||
this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(mesh)));
|
||||
|
||||
this->object->volumes[ivolume]->set_offset(Vec3d::Zero());
|
||||
this->object->volumes[ivolume]->center_geometry_after_creation();
|
||||
|
@ -1822,7 +1794,6 @@ size_t ModelVolume::split(unsigned int max_extruders)
|
|||
this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1);
|
||||
this->object->volumes[ivolume]->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
|
||||
this->object->volumes[ivolume]->m_is_splittable = 0;
|
||||
delete mesh;
|
||||
++ idx;
|
||||
}
|
||||
|
||||
|
@ -1891,7 +1862,7 @@ void ModelVolume::mirror(Axis axis)
|
|||
}
|
||||
|
||||
// This method could only be called before the meshes of this ModelVolumes are not shared!
|
||||
void ModelVolume::scale_geometry_after_creation(const Vec3d& versor)
|
||||
void ModelVolume::scale_geometry_after_creation(const Vec3f& versor)
|
||||
{
|
||||
const_cast<TriangleMesh*>(m_mesh.get())->scale(versor);
|
||||
const_cast<TriangleMesh*>(m_convex_hull.get())->scale(versor);
|
||||
|
@ -1924,8 +1895,7 @@ void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_hand
|
|||
void ModelVolume::convert_from_imperial_units()
|
||||
{
|
||||
assert(! this->source.is_converted_from_meters);
|
||||
double in_to_mm = 25.4;
|
||||
this->scale_geometry_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
|
||||
this->scale_geometry_after_creation(25.4f);
|
||||
this->set_offset(Vec3d(0, 0, 0));
|
||||
this->source.is_converted_from_inches = true;
|
||||
}
|
||||
|
@ -1933,8 +1903,7 @@ void ModelVolume::convert_from_imperial_units()
|
|||
void ModelVolume::convert_from_meters()
|
||||
{
|
||||
assert(! this->source.is_converted_from_inches);
|
||||
double m_to_mm = 1000;
|
||||
this->scale_geometry_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm));
|
||||
this->scale_geometry_after_creation(1000.f);
|
||||
this->set_offset(Vec3d(0, 0, 0));
|
||||
this->source.is_converted_from_meters = true;
|
||||
}
|
||||
|
|
|
@ -346,13 +346,12 @@ public:
|
|||
void mirror(Axis axis);
|
||||
|
||||
// This method could only be called before the meshes of this ModelVolumes are not shared!
|
||||
void scale_mesh_after_creation(const Vec3d& versor);
|
||||
void scale_mesh_after_creation(const float scale);
|
||||
void convert_units(ModelObjectPtrs&new_objects, ConversionType conv_type, std::vector<int> volume_idxs);
|
||||
|
||||
size_t materials_count() const;
|
||||
size_t facets_count() const;
|
||||
size_t parts_count() const;
|
||||
bool needed_repair() const;
|
||||
ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes);
|
||||
void split(ModelObjectPtrs* new_objects);
|
||||
void merge();
|
||||
|
@ -376,7 +375,7 @@ public:
|
|||
std::string get_export_filename() const;
|
||||
|
||||
// Get full stl statistics for all object's meshes
|
||||
stl_stats get_object_stl_stats() const;
|
||||
TriangleMeshStats get_object_stl_stats() const;
|
||||
// Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
|
||||
int get_mesh_errors_count(const int vol_idx = -1) const;
|
||||
|
||||
|
@ -620,6 +619,8 @@ public:
|
|||
const TriangleMesh& mesh() const { return *m_mesh.get(); }
|
||||
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
|
||||
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<const TriangleMesh>(std::move(mesh)); }
|
||||
void set_mesh(const indexed_triangle_set &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
|
||||
void set_mesh(indexed_triangle_set &&mesh) { m_mesh = std::make_shared<const TriangleMesh>(std::move(mesh)); }
|
||||
void set_mesh(std::shared_ptr<const TriangleMesh> &mesh) { m_mesh = mesh; }
|
||||
void set_mesh(std::unique_ptr<const TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
|
||||
void reset_mesh() { m_mesh = std::make_shared<const TriangleMesh>(); }
|
||||
|
@ -670,7 +671,8 @@ public:
|
|||
void mirror(Axis axis);
|
||||
|
||||
// This method could only be called before the meshes of this ModelVolumes are not shared!
|
||||
void scale_geometry_after_creation(const Vec3d& versor);
|
||||
void scale_geometry_after_creation(const Vec3f &versor);
|
||||
void scale_geometry_after_creation(const float scale) { this->scale_geometry_after_creation(Vec3f(scale, scale, scale)); }
|
||||
|
||||
// Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box.
|
||||
// Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared!
|
||||
|
@ -774,7 +776,7 @@ private:
|
|||
assert(this->id() != this->supported_facets.id());
|
||||
assert(this->id() != this->seam_facets.id());
|
||||
assert(this->id() != this->mmu_segmentation_facets.id());
|
||||
if (mesh.stl.stats.number_of_facets > 1)
|
||||
if (mesh.facets_count() > 1)
|
||||
calculate_convex_hull();
|
||||
}
|
||||
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull, ModelVolumeType type = ModelVolumeType::MODEL_PART) :
|
||||
|
@ -830,7 +832,7 @@ private:
|
|||
assert(this->config.id() == other.config.id());
|
||||
this->set_material_id(other.material_id());
|
||||
this->config.set_new_unique_id();
|
||||
if (mesh.stl.stats.number_of_facets > 1)
|
||||
if (mesh.facets_count() > 1)
|
||||
calculate_convex_hull();
|
||||
assert(this->config.id().valid());
|
||||
assert(this->config.id() != other.config.id());
|
||||
|
|
|
@ -33,7 +33,9 @@ public:
|
|||
void rotate(double angle, const Point ¢er);
|
||||
void reverse() { std::reverse(this->points.begin(), this->points.end()); }
|
||||
|
||||
const Point& first_point() const { return this->points.front(); }
|
||||
const Point& front() const { return this->points.front(); }
|
||||
const Point& back() const { return this->points.back(); }
|
||||
const Point& first_point() const { return this->front(); }
|
||||
virtual const Point& last_point() const = 0;
|
||||
virtual Lines lines() const = 0;
|
||||
size_t size() const { return points.size(); }
|
||||
|
|
|
@ -179,6 +179,15 @@ Point Point::projection_onto(const Line &line) const
|
|||
return ((line.a - *this).cast<double>().squaredNorm() < (line.b - *this).cast<double>().squaredNorm()) ? line.a : line.b;
|
||||
}
|
||||
|
||||
bool has_duplicate_points(std::vector<Point> &&pts)
|
||||
{
|
||||
std::sort(pts.begin(), pts.end());
|
||||
for (size_t i = 1; i < pts.size(); ++ i)
|
||||
if (pts[i - 1] == pts[i])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const Points &pts)
|
||||
{
|
||||
return BoundingBox(pts);
|
||||
|
|
|
@ -211,8 +211,34 @@ inline Point lerp(const Point &a, const Point &b, double t)
|
|||
return ((1. - t) * a.cast<double>() + t * b.cast<double>()).cast<coord_t>();
|
||||
}
|
||||
|
||||
extern BoundingBox get_extents(const Points &pts);
|
||||
extern BoundingBox get_extents(const std::vector<Points> &pts);
|
||||
BoundingBox get_extents(const Points &pts);
|
||||
BoundingBox get_extents(const std::vector<Points> &pts);
|
||||
|
||||
// Test for duplicate points in a vector of points.
|
||||
// The points are copied, sorted and checked for duplicates globally.
|
||||
bool has_duplicate_points(std::vector<Point> &&pts);
|
||||
inline bool has_duplicate_points(const std::vector<Point> &pts)
|
||||
{
|
||||
std::vector<Point> cpy = pts;
|
||||
return has_duplicate_points(std::move(cpy));
|
||||
}
|
||||
|
||||
// Test for duplicate points in a vector of points.
|
||||
// Only successive points are checked for equality.
|
||||
inline bool has_duplicate_successive_points(const std::vector<Point> &pts)
|
||||
{
|
||||
for (size_t i = 1; i < pts.size(); ++ i)
|
||||
if (pts[i - 1] == pts[i])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test for duplicate points in a vector of points.
|
||||
// Only successive points are checked for equality. Additionally, first and last points are compared for equality.
|
||||
inline bool has_duplicate_successive_points_closed(const std::vector<Point> &pts)
|
||||
{
|
||||
return has_duplicate_successive_points(pts) || (pts.size() >= 2 && pts.front() == pts.back());
|
||||
}
|
||||
|
||||
namespace int128 {
|
||||
// Exact orientation predicate,
|
||||
|
@ -418,7 +444,7 @@ template<class Tout = double,
|
|||
class = FloatingOnly<Tout>>
|
||||
inline constexpr Tout unscaled(const Tin &v) noexcept
|
||||
{
|
||||
return Tout(v * Tout(SCALING_FACTOR));
|
||||
return Tout(v) * Tout(SCALING_FACTOR);
|
||||
}
|
||||
|
||||
// Unscaling for Eigen vectors. Input base type can be arithmetic, output base
|
||||
|
@ -432,7 +458,7 @@ template<class Tout = double,
|
|||
inline constexpr Eigen::Matrix<Tout, N, EigenArgs...>
|
||||
unscaled(const Eigen::Matrix<Tin, N, EigenArgs...> &v) noexcept
|
||||
{
|
||||
return v.template cast<Tout>() * SCALING_FACTOR;
|
||||
return v.template cast<Tout>() * Tout(SCALING_FACTOR);
|
||||
}
|
||||
|
||||
// Align a coordinate to a grid. The coordinate may be negative,
|
||||
|
|
|
@ -334,6 +334,27 @@ extern std::vector<BoundingBox> get_extents_vector(const Polygons &polygons)
|
|||
return out;
|
||||
}
|
||||
|
||||
bool has_duplicate_points(const Polygons &polys)
|
||||
{
|
||||
#if 1
|
||||
// Check globally.
|
||||
size_t cnt = 0;
|
||||
for (const Polygon &poly : polys)
|
||||
cnt += poly.points.size();
|
||||
std::vector<Point> allpts;
|
||||
allpts.reserve(cnt);
|
||||
for (const Polygon &poly : polys)
|
||||
allpts.insert(allpts.end(), poly.points.begin(), poly.points.end());
|
||||
return has_duplicate_points(std::move(allpts));
|
||||
#else
|
||||
// Check per contour.
|
||||
for (const Polygon &poly : polys)
|
||||
if (has_duplicate_points(poly))
|
||||
return true;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool is_stick(const Point &p1, const Point &p2, const Point &p3)
|
||||
{
|
||||
Point v1 = p2 - p1;
|
||||
|
|
|
@ -18,11 +18,6 @@ using ConstPolygonPtrs = std::vector<const Polygon*>;
|
|||
class Polygon : public MultiPoint
|
||||
{
|
||||
public:
|
||||
explicit operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; }
|
||||
explicit operator Polyline() const { return this->split_at_first_point(); }
|
||||
Point& operator[](Points::size_type idx) { return this->points[idx]; }
|
||||
const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
|
||||
|
||||
Polygon() = default;
|
||||
virtual ~Polygon() = default;
|
||||
explicit Polygon(const Points &points) : MultiPoint(points) {}
|
||||
|
@ -39,6 +34,9 @@ public:
|
|||
Polygon& operator=(const Polygon &other) { points = other.points; return *this; }
|
||||
Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; }
|
||||
|
||||
Point& operator[](Points::size_type idx) { return this->points[idx]; }
|
||||
const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
|
||||
|
||||
// last point == first point for polygons
|
||||
const Point& last_point() const override { return this->points.front(); }
|
||||
|
||||
|
@ -80,11 +78,16 @@ public:
|
|||
inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; }
|
||||
inline bool operator!=(const Polygon &lhs, const Polygon &rhs) { return lhs.points != rhs.points; }
|
||||
|
||||
extern BoundingBox get_extents(const Polygon &poly);
|
||||
extern BoundingBox get_extents(const Polygons &polygons);
|
||||
extern BoundingBox get_extents_rotated(const Polygon &poly, double angle);
|
||||
extern BoundingBox get_extents_rotated(const Polygons &polygons, double angle);
|
||||
extern std::vector<BoundingBox> get_extents_vector(const Polygons &polygons);
|
||||
BoundingBox get_extents(const Polygon &poly);
|
||||
BoundingBox get_extents(const Polygons &polygons);
|
||||
BoundingBox get_extents_rotated(const Polygon &poly, double angle);
|
||||
BoundingBox get_extents_rotated(const Polygons &polygons, double angle);
|
||||
std::vector<BoundingBox> get_extents_vector(const Polygons &polygons);
|
||||
|
||||
// Test for duplicate points. The points are copied, sorted and checked for duplicates globally.
|
||||
inline bool has_duplicate_points(Polygon &&poly) { return has_duplicate_points(std::move(poly.points)); }
|
||||
inline bool has_duplicate_points(const Polygon &poly) { return has_duplicate_points(poly.points); }
|
||||
bool has_duplicate_points(const Polygons &polys);
|
||||
|
||||
inline double total_length(const Polygons &polylines) {
|
||||
double total = 0;
|
||||
|
@ -104,14 +107,14 @@ inline double area(const Polygons &polys)
|
|||
}
|
||||
|
||||
// Remove sticks (tentacles with zero area) from the polygon.
|
||||
extern bool remove_sticks(Polygon &poly);
|
||||
extern bool remove_sticks(Polygons &polys);
|
||||
bool remove_sticks(Polygon &poly);
|
||||
bool remove_sticks(Polygons &polys);
|
||||
|
||||
// Remove polygons with less than 3 edges.
|
||||
extern bool remove_degenerate(Polygons &polys);
|
||||
extern bool remove_small(Polygons &polys, double min_area);
|
||||
extern void remove_collinear(Polygon &poly);
|
||||
extern void remove_collinear(Polygons &polys);
|
||||
bool remove_degenerate(Polygons &polys);
|
||||
bool remove_small(Polygons &polys, double min_area);
|
||||
void remove_collinear(Polygon &poly);
|
||||
void remove_collinear(Polygons &polys);
|
||||
|
||||
// Append a vector of polygons at the end of another vector of polygons.
|
||||
inline void polygons_append(Polygons &dst, const Polygons &src) { dst.insert(dst.end(), src.begin(), src.end()); }
|
||||
|
|
|
@ -10,20 +10,6 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
Polyline::operator Polylines() const
|
||||
{
|
||||
Polylines polylines;
|
||||
polylines.push_back(*this);
|
||||
return polylines;
|
||||
}
|
||||
|
||||
Polyline::operator Line() const
|
||||
{
|
||||
if (this->points.size() > 2)
|
||||
throw Slic3r::InvalidArgument("Can't convert polyline with more than two points to a line");
|
||||
return Line(this->points.front(), this->points.back());
|
||||
}
|
||||
|
||||
const Point& Polyline::leftmost_point() const
|
||||
{
|
||||
const Point *p = &this->points.front();
|
||||
|
|
|
@ -60,12 +60,10 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
explicit operator Polylines() const;
|
||||
explicit operator Line() const;
|
||||
const Point& last_point() const override { return this->points.back(); }
|
||||
|
||||
const Point& leftmost_point() const;
|
||||
Lines lines() const override;
|
||||
|
||||
void clip_end(double distance);
|
||||
void clip_start(double distance);
|
||||
void extend_end(double distance);
|
||||
|
|
|
@ -1097,14 +1097,6 @@ size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfil
|
|||
return m_idx_selected;
|
||||
}
|
||||
|
||||
// Save the preset under a new name. If the name is different from the old one,
|
||||
// a new preset is stored into the list of presets.
|
||||
// All presets are marked as not modified and the new preset is activated.
|
||||
//void PresetCollection::save_current_preset(const std::string &new_name);
|
||||
|
||||
// Delete the current preset, activate the first visible preset.
|
||||
//void PresetCollection::delete_current_preset();
|
||||
|
||||
// Update a dirty flag of the current preset
|
||||
// Return true if the dirty flag changed.
|
||||
bool PresetCollection::update_dirty()
|
||||
|
|
|
@ -143,7 +143,7 @@ public:
|
|||
|
||||
const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias) const;
|
||||
|
||||
// Save current preset of a required type under a new name. If the name is different from the old one,
|
||||
// Save current preset of a provided type under a new name. If the name is different from the old one,
|
||||
// Unselected option would be reverted to the beginning values
|
||||
void save_changes_for_preset(const std::string& new_name, Preset::Type type, const std::vector<std::string>& unselected_options);
|
||||
|
||||
|
|
|
@ -397,7 +397,7 @@ bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Poly
|
|||
convex_hull.translate(instance.shift - print_object->center_offset());
|
||||
// if output needed, collect indices (inside convex_hulls_other) of intersecting hulls
|
||||
for (size_t i = 0; i < convex_hulls_other.size(); ++i) {
|
||||
if (!intersection((Polygons)convex_hulls_other[i], (Polygons)convex_hull).empty()) {
|
||||
if (! intersection(convex_hulls_other[i], convex_hull).empty()) {
|
||||
if (polygons == nullptr)
|
||||
return false;
|
||||
else {
|
||||
|
@ -434,6 +434,8 @@ static inline bool sequential_print_vertical_clearance_valid(const Print &print)
|
|||
return it == print_instances_ordered.end() || (*it)->print_object->height() <= scale_(print.config().extruder_clearance_height.value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
|
||||
std::string Print::validate(std::string* warning) const
|
||||
{
|
||||
|
@ -526,42 +528,11 @@ std::string Print::validate(std::string* warning) const
|
|||
}
|
||||
|
||||
if (has_custom_layering) {
|
||||
const std::vector<coordf_t> &layer_height_profile_tallest = layer_height_profiles[tallest_object_idx];
|
||||
for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) {
|
||||
if (idx_object == tallest_object_idx)
|
||||
continue;
|
||||
const std::vector<coordf_t> &layer_height_profile = layer_height_profiles[idx_object];
|
||||
|
||||
// The comparison of the profiles is not just about element-wise equality, some layers may not be
|
||||
// explicitely included. Always remember z and height of last reference layer that in the vector
|
||||
// and compare to that. In case some layers are in the vectors multiple times, only the last entry is
|
||||
// taken into account and compared.
|
||||
size_t i = 0; // index into tested profile
|
||||
size_t j = 0; // index into reference profile
|
||||
coordf_t ref_z = -1.;
|
||||
coordf_t next_ref_z = layer_height_profile_tallest[0];
|
||||
coordf_t ref_height = -1.;
|
||||
while (i < layer_height_profile.size()) {
|
||||
coordf_t this_z = layer_height_profile[i];
|
||||
// find the last entry with this z
|
||||
while (i+2 < layer_height_profile.size() && layer_height_profile[i+2] == this_z)
|
||||
i += 2;
|
||||
|
||||
coordf_t this_height = layer_height_profile[i+1];
|
||||
if (ref_height < -1. || next_ref_z < this_z + EPSILON) {
|
||||
ref_z = next_ref_z;
|
||||
do { // one layer can be in the vector several times
|
||||
ref_height = layer_height_profile_tallest[j+1];
|
||||
if (j+2 >= layer_height_profile_tallest.size())
|
||||
break;
|
||||
j += 2;
|
||||
next_ref_z = layer_height_profile_tallest[j];
|
||||
} while (ref_z == next_ref_z);
|
||||
}
|
||||
if (std::abs(this_height - ref_height) > EPSILON)
|
||||
if (layer_height_profiles[idx_object] != layer_height_profiles[tallest_object_idx])
|
||||
return L("The Wipe tower is only supported if all objects have the same variable layer height");
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -812,7 +783,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
|
|||
// Slicing process, running at a background thread.
|
||||
void Print::process()
|
||||
{
|
||||
name_tbb_thread_pool_threads();
|
||||
name_tbb_thread_pool_threads_set_locale();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
|
||||
for (PrintObject *obj : m_objects)
|
||||
|
|
|
@ -1300,8 +1300,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
num_extruders,
|
||||
painting_extruders,
|
||||
*print_object_regions,
|
||||
[&print_object, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) {
|
||||
update_apply_status(print_object.invalidate_state_by_config_options(old_config, new_config, diff_keys));
|
||||
[&print_object, it_print_object, it_print_object_end, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) {
|
||||
for (auto it = it_print_object; it != it_print_object_end; ++it)
|
||||
if ((*it)->m_shared_regions != nullptr)
|
||||
update_apply_status((*it)->invalidate_state_by_config_options(old_config, new_config, diff_keys));
|
||||
})) {
|
||||
// Regions are valid, just keep them.
|
||||
} else {
|
||||
|
|
|
@ -60,6 +60,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str
|
|||
DynamicConfig cfg;
|
||||
if (config_override != nullptr)
|
||||
cfg = *config_override;
|
||||
cfg.set_key_value("version", new ConfigOptionString(std::string(SLIC3R_VERSION)));
|
||||
PlaceholderParser::update_timestamp(cfg);
|
||||
this->update_object_placeholders(cfg, default_ext);
|
||||
if (! filename_base.empty()) {
|
||||
|
|
|
@ -1447,7 +1447,7 @@ void PrintObject::bridge_over_infill()
|
|||
Polygons to_bridge_pp = internal_solid;
|
||||
|
||||
// iterate through lower layers spanned by bridge_flow
|
||||
double bottom_z = layer->print_z - bridge_flow.height();
|
||||
double bottom_z = layer->print_z - bridge_flow.height() - EPSILON;
|
||||
for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) {
|
||||
const Layer* lower_layer = m_layers[i];
|
||||
|
||||
|
|
|
@ -39,23 +39,6 @@ LayerPtrs new_layers(
|
|||
return out;
|
||||
}
|
||||
|
||||
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
|
||||
// This function will go away once we get rid of admesh from ModelVolume.
|
||||
static indexed_triangle_set get_mesh_its_fix_mesh_connectivity(TriangleMesh mesh)
|
||||
{
|
||||
assert(mesh.repaired && mesh.has_shared_vertices());
|
||||
if (mesh.stl.stats.number_of_facets > 0) {
|
||||
assert(mesh.repaired && mesh.has_shared_vertices());
|
||||
auto nr_degenerated = mesh.stl.stats.degenerate_facets;
|
||||
stl_check_facets_exact(&mesh.stl);
|
||||
if (nr_degenerated != mesh.stl.stats.degenerate_facets)
|
||||
// stl_check_facets_exact() removed some newly degenerated faces. Some faces could become degenerate after some mesh transformation.
|
||||
stl_generate_shared_vertices(&mesh.stl, mesh.its);
|
||||
} else
|
||||
mesh.its.clear();
|
||||
return std::move(mesh.its);
|
||||
}
|
||||
|
||||
// Slice single triangle mesh.
|
||||
static std::vector<ExPolygons> slice_volume(
|
||||
const ModelVolume &volume,
|
||||
|
@ -65,7 +48,7 @@ static std::vector<ExPolygons> slice_volume(
|
|||
{
|
||||
std::vector<ExPolygons> layers;
|
||||
if (! zs.empty()) {
|
||||
indexed_triangle_set its = get_mesh_its_fix_mesh_connectivity(volume.mesh());
|
||||
indexed_triangle_set its = volume.mesh().its;
|
||||
if (its.indices.size() > 0) {
|
||||
MeshSlicingParamsEx params2 { params };
|
||||
params2.trafo = params2.trafo * volume.get_matrix();
|
||||
|
|
|
@ -286,8 +286,6 @@ void cut_drainholes(std::vector<ExPolygons> & obj_slices,
|
|||
|
||||
if (mesh.empty()) return;
|
||||
|
||||
mesh.require_shared_vertices();
|
||||
|
||||
std::vector<ExPolygons> hole_slices = slice_mesh_ex(mesh.its, slicegrid, closing_radius, thr);
|
||||
|
||||
if (obj_slices.size() != hole_slices.size())
|
||||
|
@ -316,7 +314,6 @@ void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags)
|
|||
remove_inside_triangles(mesh, interior);
|
||||
|
||||
mesh.merge(TriangleMesh{interior.mesh});
|
||||
mesh.require_shared_vertices();
|
||||
}
|
||||
|
||||
// Get the distance of p to the interior's zero iso_surface. Interior should
|
||||
|
@ -557,8 +554,7 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior,
|
|||
new_faces = {};
|
||||
|
||||
mesh = TriangleMesh{mesh.its};
|
||||
mesh.repaired = true;
|
||||
mesh.require_shared_vertices();
|
||||
//FIXME do we want to repair the mesh? Are there duplicate vertices or flipped triangles?
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::sla
|
||||
|
|
|
@ -370,7 +370,7 @@ bool add_cavity(indexed_triangle_set &pad,
|
|||
|
||||
if (inner_base.empty() || middle_base.empty()) { logerr(); return false; }
|
||||
|
||||
ExPolygons pdiff = diff_ex((Polygons)top_poly, (Polygons)middle_base.contour);
|
||||
ExPolygons pdiff = diff_ex(top_poly, middle_base.contour);
|
||||
|
||||
if (pdiff.size() != 1) { logerr(); return false; }
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@ inline void reproject_points_and_holes(ModelObject *object)
|
|||
if (!object || (!has_holes && !has_sppoints)) return;
|
||||
|
||||
TriangleMesh rmsh = object->raw_mesh();
|
||||
rmsh.require_shared_vertices();
|
||||
IndexedMesh emesh{rmsh};
|
||||
|
||||
if (has_sppoints)
|
||||
|
|
|
@ -205,7 +205,6 @@ inline bool is_on_floor(const SLAPrintObjectConfig &cfg)
|
|||
std::vector<XYRotation> get_chull_rotations(const TriangleMesh &mesh, size_t max_count)
|
||||
{
|
||||
TriangleMesh chull = mesh.convex_hull_3d();
|
||||
chull.require_shared_vertices();
|
||||
double chull2d_area = chull.convex_hull().area();
|
||||
double area_threshold = chull2d_area / (scaled<double>(1e3) * scaled(1.));
|
||||
|
||||
|
@ -299,7 +298,6 @@ struct RotfinderBoilerplate {
|
|||
static TriangleMesh get_mesh_to_rotate(const ModelObject &mo)
|
||||
{
|
||||
TriangleMesh mesh = mo.raw_mesh();
|
||||
mesh.require_shared_vertices();
|
||||
|
||||
ModelInstance *mi = mo.instances[0];
|
||||
auto rotation = Vec3d::Zero();
|
||||
|
@ -437,7 +435,6 @@ Vec2d find_min_z_height_rotation(const ModelObject &mo,
|
|||
RotfinderBoilerplate<1000> bp{mo, params};
|
||||
|
||||
TriangleMesh chull = bp.mesh.convex_hull_3d();
|
||||
chull.require_shared_vertices();
|
||||
auto inputs = reserve_vector<XYRotation>(chull.its.indices.size());
|
||||
auto rotcmp = [](const XYRotation &r1, const XYRotation &r2) {
|
||||
double xdiff = r1[X] - r2[X], ydiff = r1[Y] - r2[Y];
|
||||
|
|
|
@ -670,7 +670,7 @@ std::string SLAPrint::validate(std::string*) const
|
|||
return "";
|
||||
}
|
||||
|
||||
void SLAPrint::set_printer(SLAPrinter *arch)
|
||||
void SLAPrint::set_printer(SLAArchive *arch)
|
||||
{
|
||||
invalidate_step(slapsRasterize);
|
||||
m_printer = arch;
|
||||
|
@ -693,7 +693,7 @@ void SLAPrint::process()
|
|||
if (m_objects.empty())
|
||||
return;
|
||||
|
||||
name_tbb_thread_pool_threads();
|
||||
name_tbb_thread_pool_threads_set_locale();
|
||||
|
||||
// Assumption: at this point the print objects should be populated only with
|
||||
// the model objects we have to process and the instances are also filtered
|
||||
|
@ -896,7 +896,6 @@ SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object)
|
|||
obj = m_model_object->raw_mesh();
|
||||
if (!obj.empty()) {
|
||||
obj.transform(m_trafo);
|
||||
obj.require_shared_vertices();
|
||||
}
|
||||
})
|
||||
{}
|
||||
|
@ -1048,15 +1047,15 @@ Vec3d SLAPrint::relative_correction() const
|
|||
Vec3d corr(1., 1., 1.);
|
||||
|
||||
if(printer_config().relative_correction.values.size() >= 2) {
|
||||
corr(X) = printer_config().relative_correction.values[0];
|
||||
corr(Y) = printer_config().relative_correction.values[0];
|
||||
corr(Z) = printer_config().relative_correction.values.back();
|
||||
corr.x() = printer_config().relative_correction.values[0];
|
||||
corr.y() = corr.x();
|
||||
corr.z() = printer_config().relative_correction.values[1];
|
||||
}
|
||||
|
||||
if(material_config().material_correction.values.size() >= 2) {
|
||||
corr(X) *= material_config().material_correction.values[0];
|
||||
corr(Y) *= material_config().material_correction.values[0];
|
||||
corr(Z) *= material_config().material_correction.values.back();
|
||||
corr.x() *= material_config().material_correction.values[0];
|
||||
corr.y() = corr.x();
|
||||
corr.z() *= material_config().material_correction.values[1];
|
||||
}
|
||||
|
||||
return corr;
|
||||
|
|
|
@ -323,7 +323,6 @@ private:
|
|||
{
|
||||
support_tree_ptr = sla::SupportTree::create(*this, ctl);
|
||||
tree_mesh = TriangleMesh{support_tree_ptr->retrieve_mesh(sla::MeshType::Support)};
|
||||
tree_mesh.require_shared_vertices();
|
||||
return support_tree_ptr;
|
||||
}
|
||||
|
||||
|
@ -388,7 +387,7 @@ struct SLAPrintStatistics
|
|||
}
|
||||
};
|
||||
|
||||
class SLAPrinter {
|
||||
class SLAArchive {
|
||||
protected:
|
||||
std::vector<sla::EncodedRaster> m_layers;
|
||||
|
||||
|
@ -396,7 +395,7 @@ protected:
|
|||
virtual sla::RasterEncoder get_encoder() const = 0;
|
||||
|
||||
public:
|
||||
virtual ~SLAPrinter() = default;
|
||||
virtual ~SLAArchive() = default;
|
||||
|
||||
virtual void apply(const SLAPrinterConfig &cfg) = 0;
|
||||
|
||||
|
@ -527,7 +526,7 @@ public:
|
|||
// TODO: use this structure for the preview in the future.
|
||||
const std::vector<PrintLayer>& print_layers() const { return m_printer_input; }
|
||||
|
||||
void set_printer(SLAPrinter *archiver);
|
||||
void set_printer(SLAArchive *archiver);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -549,7 +548,7 @@ private:
|
|||
std::vector<PrintLayer> m_printer_input;
|
||||
|
||||
// The archive object which collects the raster images after slicing
|
||||
SLAPrinter *m_printer = nullptr;
|
||||
SLAArchive *m_printer = nullptr;
|
||||
|
||||
// Estimated print time, material consumed.
|
||||
SLAPrintStatistics m_print_statistics;
|
||||
|
|
|
@ -526,7 +526,6 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
|
|||
}
|
||||
auto thr = [this]() { m_print->throw_if_canceled(); };
|
||||
auto &slice_grid = po.m_model_height_levels;
|
||||
assert(mesh.has_shared_vertices());
|
||||
po.m_model_slices = slice_mesh_ex(mesh.its, slice_grid, params, thr);
|
||||
|
||||
sla::Interior *interior = po.m_hollowing_data ?
|
||||
|
|
|
@ -14,10 +14,8 @@ void simplify_mesh(indexed_triangle_set &);
|
|||
|
||||
template<class...Args> void simplify_mesh(TriangleMesh &m, Args &&...a)
|
||||
{
|
||||
m.require_shared_vertices();
|
||||
simplify_mesh(m.its, std::forward<Args>(a)...);
|
||||
m = TriangleMesh{m.its};
|
||||
m.require_shared_vertices();
|
||||
m = TriangleMesh{ std::move(m.its) };
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -35,13 +35,6 @@ legend("tan(a) as cura - topographic lines distance limit", "sqrt(tan(a)) as Pru
|
|||
namespace Slic3r
|
||||
{
|
||||
|
||||
static inline std::pair<float, float> face_z_span(const stl_facet &f)
|
||||
{
|
||||
return std::pair<float, float>(
|
||||
std::min(std::min(f.vertex[0](2), f.vertex[1](2)), f.vertex[2](2)),
|
||||
std::max(std::max(f.vertex[0](2), f.vertex[1](2)), f.vertex[2](2)));
|
||||
}
|
||||
|
||||
// By Florens Waserfall aka @platch:
|
||||
// This constant essentially describes the volumetric error at the surface which is induced
|
||||
// by stacking "elliptic" extrusion threads. It is empirically determined by
|
||||
|
@ -88,10 +81,15 @@ void SlicingAdaptive::prepare(const ModelObject &object)
|
|||
mesh.transform(first_instance.get_matrix(), first_instance.is_left_handed());
|
||||
|
||||
// 1) Collect faces from mesh.
|
||||
m_faces.reserve(mesh.stl.stats.number_of_facets);
|
||||
for (const stl_facet &face : mesh.stl.facet_start) {
|
||||
Vec3f n = face.normal.normalized();
|
||||
m_faces.emplace_back(FaceZ({ face_z_span(face), std::abs(n.z()), std::sqrt(n.x() * n.x() + n.y() * n.y()) }));
|
||||
m_faces.reserve(mesh.facets_count());
|
||||
for (stl_triangle_vertex_indices face : mesh.its.indices) {
|
||||
stl_vertex vertex[3] = { mesh.its.vertices[face[0]], mesh.its.vertices[face[1]], mesh.its.vertices[face[2]] };
|
||||
stl_vertex n = face_normal_normalized(vertex);
|
||||
std::pair<float, float> face_z_span {
|
||||
std::min(std::min(vertex[0].z(), vertex[1].z()), vertex[2].z()),
|
||||
std::max(std::max(vertex[0].z(), vertex[1].z()), vertex[2].z())
|
||||
};
|
||||
m_faces.emplace_back(FaceZ({ face_z_span, std::abs(n.z()), std::sqrt(n.x() * n.x() + n.y() * n.y()) }));
|
||||
}
|
||||
|
||||
// 2) Sort faces lexicographically by their Z span.
|
||||
|
|
|
@ -721,9 +721,9 @@ public:
|
|||
#ifdef SUPPORT_USE_AGG_RASTERIZER
|
||||
m_bbox = bbox;
|
||||
// Oversample the grid to avoid leaking of supports through or around the object walls.
|
||||
int oversampling = std::min(8, int(scale_(m_support_spacing) / (scale_(params.extrusion_width) + 100)));
|
||||
m_pixel_size = scale_(m_support_spacing / oversampling);
|
||||
assert(scale_(params.extrusion_width) + 20 < m_pixel_size);
|
||||
int extrusion_width_scaled = scale_(params.extrusion_width);
|
||||
int oversampling = std::clamp(int(scale_(m_support_spacing) / (extrusion_width_scaled + 100)), 1, 8);
|
||||
m_pixel_size = std::max<double>(extrusion_width_scaled + 21, scale_(m_support_spacing / oversampling));
|
||||
// Add one empty column / row boundaries.
|
||||
m_bbox.offset(m_pixel_size);
|
||||
// Grid size fitting the support polygons plus one pixel boundary around the polygons.
|
||||
|
@ -1600,6 +1600,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
|
|||
const PrintConfig &print_config,
|
||||
const PrintObjectConfig &object_config,
|
||||
const SlicingParameters &slicing_params,
|
||||
const coordf_t support_layer_height_min,
|
||||
const Layer &layer,
|
||||
std::deque<PrintObjectSupportMaterial::MyLayer> &layer_storage,
|
||||
tbb::spin_mutex &layer_storage_mutex)
|
||||
|
@ -1629,7 +1630,8 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
|
|||
// Don't want to print a layer below the first layer height as it may not stick well.
|
||||
//FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
|
||||
// and it may actually make sense to do it with a thinner layer than the first layer height.
|
||||
if (print_z < slicing_params.first_print_layer_height - EPSILON) {
|
||||
const coordf_t min_print_z = slicing_params.raft_layers() > 1 ? slicing_params.raft_interface_top_z + support_layer_height_min + EPSILON : slicing_params.first_print_layer_height - EPSILON;
|
||||
if (print_z < min_print_z) {
|
||||
// This contact layer is below the first layer height, therefore not printable. Don't support this surface.
|
||||
return std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupportMaterial::MyLayer*>(nullptr, nullptr);
|
||||
} else if (print_z < slicing_params.first_print_layer_height + EPSILON) {
|
||||
|
@ -1650,7 +1652,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
|
|||
bridging_height += region->region().bridging_height_avg(print_config);
|
||||
bridging_height /= coordf_t(layer.regions().size());
|
||||
coordf_t bridging_print_z = layer.print_z - bridging_height - slicing_params.gap_support_object;
|
||||
if (bridging_print_z >= slicing_params.first_print_layer_height - EPSILON) {
|
||||
if (bridging_print_z >= min_print_z) {
|
||||
// Not below the first layer height means this layer is printable.
|
||||
if (print_z < slicing_params.first_print_layer_height + EPSILON) {
|
||||
// Align the layer with the 1st layer height.
|
||||
|
@ -1664,8 +1666,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
|
|||
if (bridging_print_z == slicing_params.first_print_layer_height) {
|
||||
bridging_layer->bottom_z = 0;
|
||||
bridging_layer->height = slicing_params.first_print_layer_height;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Don't know the height yet.
|
||||
bridging_layer->bottom_z = bridging_print_z;
|
||||
bridging_layer->height = 0;
|
||||
|
@ -1917,7 +1918,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
|
||||
// Now apply the contact areas to the layer where they need to be made.
|
||||
if (! contact_polygons.empty()) {
|
||||
auto [new_layer, bridging_layer] = new_contact_layer(*m_print_config, *m_object_config, m_slicing_params, layer, layer_storage, layer_storage_mutex);
|
||||
auto [new_layer, bridging_layer] = new_contact_layer(*m_print_config, *m_object_config, m_slicing_params, m_support_params.support_layer_height_min, layer, layer_storage, layer_storage_mutex);
|
||||
if (new_layer) {
|
||||
fill_contact_layer(*new_layer, layer_id, m_slicing_params,
|
||||
*m_object_config, slices_margin, overhang_polygons, contact_polygons, enforcer_polygons, lower_layer_polygons,
|
||||
|
@ -2600,8 +2601,6 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
|||
// No top contacts -> no intermediate layers will be produced.
|
||||
return;
|
||||
|
||||
// coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_base_layers() in parallel - start";
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, intermediate_layers.size()),
|
||||
|
@ -2696,6 +2695,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
|||
layer_intermediate.layer_type = sltBase;
|
||||
|
||||
#if 0
|
||||
// coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
|
||||
// Fillet the base polygons and trim them again with the top, interface and contact layers.
|
||||
$base->{$i} = diff(
|
||||
offset2(
|
||||
|
@ -3338,7 +3338,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
|
|||
Polygon &contour = (i_contour == 0) ? it_contact_expoly->contour : it_contact_expoly->holes[i_contour - 1];
|
||||
const Point *seg_current_pt = nullptr;
|
||||
coordf_t seg_current_t = 0.;
|
||||
if (! intersection_pl((Polylines)contour.split_at_first_point(), overhang_with_margin).empty()) {
|
||||
if (! intersection_pl(contour.split_at_first_point(), overhang_with_margin).empty()) {
|
||||
// The contour is below the overhang at least to some extent.
|
||||
//FIXME ideally one would place the circles below the overhang only.
|
||||
// Walk around the contour and place circles so their centers are not closer than circle_distance from each other.
|
||||
|
@ -3784,7 +3784,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
|||
// Prepare fillers.
|
||||
SupportMaterialPattern support_pattern = m_object_config->support_material_pattern;
|
||||
bool with_sheath = m_object_config->support_material_with_sheath;
|
||||
InfillPattern infill_pattern = (support_pattern == smpHoneycomb ? ipHoneycomb : ipSupportBase);
|
||||
InfillPattern infill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : (support_density < 1.05 ? ipRectilinear : ipSupportBase);
|
||||
std::vector<float> angles;
|
||||
angles.push_back(base_angle);
|
||||
|
||||
|
|
|
@ -41,16 +41,8 @@
|
|||
//====================
|
||||
#define ENABLE_2_4_0_ALPHA1 1
|
||||
|
||||
// Enable delayed rendering of transparent volumes
|
||||
#define ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_2_4_0_ALPHA1)
|
||||
// Enable the fix of importing color print view from gcode files into GCodeViewer
|
||||
#define ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER (1 && ENABLE_2_4_0_ALPHA1)
|
||||
// Enable drawing contours, at cut level, for sinking volumes
|
||||
#define ENABLE_SINKING_CONTOURS (1 && ENABLE_2_4_0_ALPHA1)
|
||||
// Enable implementation of retract acceleration in gcode processor
|
||||
#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA1)
|
||||
// Enable the fix for exporting and importing to/from 3mf file of mirrored volumes
|
||||
#define ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT (1 && ENABLE_2_4_0_ALPHA1)
|
||||
// Enable rendering seams (and other options) in preview using models
|
||||
#define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA1)
|
||||
// Enable save and save as commands to be enabled also when the plater is empty and allow to load empty projects
|
||||
|
@ -65,9 +57,22 @@
|
|||
// Enable rendering seams (and other options) in preview using batched models on systems not supporting OpenGL 3.3
|
||||
#define ENABLE_SEAMS_USING_BATCHED_MODELS (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_2_4_0_ALPHA2)
|
||||
// Enable fixing the z position of color change, pause print and custom gcode markers in preview
|
||||
#define ENABLE_FIX_PREVIEW_OPTIONS_Z (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER && ENABLE_2_4_0_ALPHA2)
|
||||
#define ENABLE_FIX_PREVIEW_OPTIONS_Z (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_2_4_0_ALPHA2)
|
||||
// Enable replacing a missing file during reload from disk command
|
||||
#define ENABLE_RELOAD_FROM_DISK_REPLACE_FILE (1 && ENABLE_2_4_0_ALPHA2)
|
||||
// Enable fixing the synchronization of seams with the horizontal slider in preview
|
||||
#define ENABLE_FIX_SEAMS_SYNCH (1 && ENABLE_2_4_0_ALPHA2)
|
||||
|
||||
|
||||
//====================
|
||||
// 2.4.0.alpha3 techs
|
||||
//====================
|
||||
#define ENABLE_2_4_0_ALPHA3 1
|
||||
|
||||
// Enable fixing loading of gcode files generated with SuperSlicer in GCodeViewer
|
||||
#define ENABLE_FIX_SUPERSLICER_GCODE_IMPORT (1 && ENABLE_2_4_0_ALPHA3)
|
||||
// Enable coloring of toolpaths in preview by layer time
|
||||
#define ENABLE_PREVIEW_LAYER_TIME (1 && ENABLE_2_4_0_ALPHA2)
|
||||
#define ENABLE_PREVIEW_LAYER_TIME (1 && ENABLE_2_4_0_ALPHA3)
|
||||
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
|
|
@ -188,7 +188,8 @@ std::optional<std::string> get_current_thread_name()
|
|||
#endif // _WIN32
|
||||
|
||||
// Spawn (n - 1) worker threads on Intel TBB thread pool and name them by an index and a system thread ID.
|
||||
void name_tbb_thread_pool_threads()
|
||||
// Also it sets locale of the worker threads to "C" for the G-code generator to produce "." as a decimal separator.
|
||||
void name_tbb_thread_pool_threads_set_locale()
|
||||
{
|
||||
static bool initialized = false;
|
||||
if (initialized)
|
||||
|
@ -233,6 +234,21 @@ void name_tbb_thread_pool_threads()
|
|||
std::ostringstream name;
|
||||
name << "slic3r_tbb_" << range.begin();
|
||||
set_current_thread_name(name.str().c_str());
|
||||
// Set locales of the worker thread to "C".
|
||||
#ifdef _WIN32
|
||||
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
||||
std::setlocale(LC_ALL, "C");
|
||||
#else
|
||||
// We are leaking some memory here, because the newlocale() produced memory will never be released.
|
||||
// This is not a problem though, as there will be a maximum one worker thread created per physical thread.
|
||||
uselocale(newlocale(
|
||||
#ifdef __APPLE__
|
||||
LC_ALL_MASK
|
||||
#else // some Unix / Linux / BSD
|
||||
LC_ALL
|
||||
#endif
|
||||
, "C", nullptr));
|
||||
#endif
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -33,7 +33,8 @@ std::optional<std::string> get_current_thread_name();
|
|||
|
||||
// To be called somewhere before the TBB threads are spinned for the first time, to
|
||||
// give them names recognizible in the debugger.
|
||||
void name_tbb_thread_pool_threads();
|
||||
// Also it sets locale of the worker threads to "C" for the G-code generator to produce "." as a decimal separator.
|
||||
void name_tbb_thread_pool_threads_set_locale();
|
||||
|
||||
template<class Fn>
|
||||
inline boost::thread create_thread(boost::thread::attributes &attrs, Fn &&fn)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,25 +15,79 @@ namespace Slic3r {
|
|||
|
||||
class TriangleMesh;
|
||||
class TriangleMeshSlicer;
|
||||
typedef std::vector<TriangleMesh*> TriangleMeshPtrs;
|
||||
|
||||
struct TriangleMeshStats {
|
||||
// Mesh metrics.
|
||||
uint32_t number_of_facets = 0;
|
||||
stl_vertex max = stl_vertex::Zero();
|
||||
stl_vertex min = stl_vertex::Zero();
|
||||
stl_vertex size = stl_vertex::Zero();
|
||||
float volume = -1.f;
|
||||
int number_of_parts = 0;
|
||||
|
||||
// Mesh errors, remaining.
|
||||
int open_edges = 0;
|
||||
|
||||
// Mesh errors, fixed.
|
||||
// How many edges were united by merging their end points with some other end points in epsilon neighborhood?
|
||||
int edges_fixed = 0;
|
||||
// How many degenerate faces were removed?
|
||||
int degenerate_facets = 0;
|
||||
// How many faces were removed during fixing? Includes degenerate_faces and disconnected faces.
|
||||
int facets_removed = 0;
|
||||
// New faces could only be created with stl_fill_holes() and we ditched stl_fill_holes(), because mostly it does more harm than good.
|
||||
//int facets_added = 0;
|
||||
// How many facets were revesed? Faces are reversed by admesh while it connects patches of triangles togeter and a flipped triangle is encountered.
|
||||
// Also the facets are reversed when a negative volume is corrected by flipping all facets.
|
||||
int facets_reversed = 0;
|
||||
// Edges shared by two triangles, oriented incorrectly.
|
||||
int backwards_edges = 0;
|
||||
|
||||
void clear() { *this = TriangleMeshStats(); }
|
||||
|
||||
TriangleMeshStats merge(const TriangleMeshStats &rhs) const {
|
||||
if (this->number_of_facets == 0)
|
||||
return rhs;
|
||||
else if (rhs.number_of_facets == 0)
|
||||
return *this;
|
||||
else {
|
||||
TriangleMeshStats out;
|
||||
out.number_of_facets = this->number_of_facets + rhs.number_of_facets;
|
||||
out.min = this->min.cwiseMin(rhs.min);
|
||||
out.max = this->max.cwiseMax(rhs.max);
|
||||
out.size = out.max - out.min;
|
||||
out.number_of_parts = this->number_of_parts + rhs.number_of_parts;
|
||||
out.open_edges = this->open_edges + rhs.open_edges;
|
||||
out.volume = this->volume + rhs.volume;
|
||||
out.edges_fixed = this->edges_fixed + rhs.edges_fixed;
|
||||
out.degenerate_facets = this->degenerate_facets + rhs.degenerate_facets;
|
||||
out.facets_removed = this->facets_removed + rhs.facets_removed;
|
||||
out.facets_reversed = this->facets_reversed + rhs.facets_reversed;
|
||||
out.backwards_edges = this->backwards_edges + rhs.backwards_edges;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
bool manifold() const { return open_edges == 0; }
|
||||
bool repaired() const { return degenerate_facets > 0 || edges_fixed > 0 || facets_removed > 0 || facets_reversed > 0 || backwards_edges > 0; }
|
||||
};
|
||||
|
||||
class TriangleMesh
|
||||
{
|
||||
public:
|
||||
TriangleMesh() : repaired(false) {}
|
||||
TriangleMesh(const Pointf3s &points, const std::vector<Vec3i> &facets);
|
||||
TriangleMesh() = default;
|
||||
TriangleMesh(const std::vector<Vec3f> &vertices, const std::vector<Vec3i> &faces);
|
||||
TriangleMesh(std::vector<Vec3f> &&vertices, const std::vector<Vec3i> &&faces);
|
||||
explicit TriangleMesh(const indexed_triangle_set &M);
|
||||
void clear() { this->stl.clear(); this->its.clear(); this->repaired = false; }
|
||||
bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); }
|
||||
bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); }
|
||||
bool write_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); }
|
||||
void repair(bool update_shared_vertices = true);
|
||||
explicit TriangleMesh(indexed_triangle_set &&M);
|
||||
void clear() { this->its.clear(); this->m_stats.clear(); }
|
||||
bool ReadSTLFile(const char* input_file, bool repair = true);
|
||||
bool write_ascii(const char* output_file);
|
||||
bool write_binary(const char* output_file);
|
||||
float volume();
|
||||
void check_topology();
|
||||
bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; }
|
||||
void WriteOBJFile(const char* output_file) const;
|
||||
void scale(float factor);
|
||||
void scale(const Vec3d &versor);
|
||||
void scale(const Vec3f &versor);
|
||||
void translate(float x, float y, float z);
|
||||
void translate(const Vec3f &displacement);
|
||||
void rotate(float angle, const Axis &axis);
|
||||
|
@ -41,51 +95,52 @@ public:
|
|||
void rotate_x(float angle) { this->rotate(angle, X); }
|
||||
void rotate_y(float angle) { this->rotate(angle, Y); }
|
||||
void rotate_z(float angle) { this->rotate(angle, Z); }
|
||||
void mirror(const Axis &axis);
|
||||
void mirror(const Axis axis);
|
||||
void mirror_x() { this->mirror(X); }
|
||||
void mirror_y() { this->mirror(Y); }
|
||||
void mirror_z() { this->mirror(Z); }
|
||||
void transform(const Transform3d& t, bool fix_left_handed = false);
|
||||
void transform(const Matrix3d& t, bool fix_left_handed = false);
|
||||
// Flip triangles, negate volume.
|
||||
void flip_triangles();
|
||||
void align_to_origin();
|
||||
void rotate(double angle, Point* center);
|
||||
TriangleMeshPtrs split() const;
|
||||
std::vector<TriangleMesh> split() const;
|
||||
void merge(const TriangleMesh &mesh);
|
||||
ExPolygons horizontal_projection() const;
|
||||
const float* first_vertex() const { return this->stl.facet_start.empty() ? nullptr : &this->stl.facet_start.front().vertex[0](0); }
|
||||
// 2D convex hull of a 3D mesh projected into the Z=0 plane.
|
||||
Polygon convex_hull();
|
||||
BoundingBoxf3 bounding_box() const;
|
||||
// Returns the bbox of this TriangleMesh transformed by the given transformation
|
||||
BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const;
|
||||
// Return the size of the mesh in coordinates.
|
||||
Vec3d size() const { return stl.stats.size.cast<double>(); }
|
||||
Vec3d size() const { return m_stats.size.cast<double>(); }
|
||||
/// Return the center of the related bounding box.
|
||||
Vec3d center() const { return this->bounding_box().center(); }
|
||||
// Returns the convex hull of this TriangleMesh
|
||||
TriangleMesh convex_hull_3d() const;
|
||||
// Slice this mesh at the provided Z levels and return the vector
|
||||
std::vector<ExPolygons> slice(const std::vector<double>& z) const;
|
||||
void reset_repair_stats();
|
||||
bool needed_repair() const;
|
||||
void require_shared_vertices();
|
||||
bool has_shared_vertices() const { return ! this->its.vertices.empty(); }
|
||||
size_t facets_count() const { return this->stl.stats.number_of_facets; }
|
||||
size_t facets_count() const { assert(m_stats.number_of_facets == this->its.indices.size()); return m_stats.number_of_facets; }
|
||||
bool empty() const { return this->facets_count() == 0; }
|
||||
bool repaired() const;
|
||||
bool is_splittable() const;
|
||||
// Estimate of the memory occupied by this structure, important for keeping an eye on the Undo / Redo stack allocation.
|
||||
size_t memsize() const;
|
||||
// Release optional data from the mesh if the object is on the Undo / Redo stack only. Returns the amount of memory released.
|
||||
size_t release_optional();
|
||||
// Restore optional data possibly released by release_optional().
|
||||
void restore_optional();
|
||||
|
||||
stl_file stl;
|
||||
// Used by the Undo / Redo stack, legacy interface. As of now there is nothing cached at TriangleMesh,
|
||||
// but we may decide to cache some data in the future (for example normals), thus we keep the interface in place.
|
||||
// Release optional data from the mesh if the object is on the Undo / Redo stack only. Returns the amount of memory released.
|
||||
size_t release_optional() { return 0; }
|
||||
// Restore optional data possibly released by release_optional().
|
||||
void restore_optional() {}
|
||||
|
||||
const TriangleMeshStats& stats() const { return m_stats; }
|
||||
|
||||
indexed_triangle_set its;
|
||||
bool repaired;
|
||||
|
||||
private:
|
||||
std::deque<uint32_t> find_unvisited_neighbors(std::vector<unsigned char> &facet_visited) const;
|
||||
TriangleMeshStats m_stats;
|
||||
};
|
||||
|
||||
// Index of face indices incident with a vertex index.
|
||||
|
@ -145,8 +200,18 @@ bool its_store_triangle(const indexed_triangle_set &its, const char *obj_filenam
|
|||
bool its_store_triangles(const indexed_triangle_set &its, const char *obj_filename, const std::vector<size_t>& triangles);
|
||||
|
||||
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its);
|
||||
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors);
|
||||
|
||||
// Number of disconnected patches (faces are connected if they share an edge, shared edge defined with 2 shared vertex indices).
|
||||
bool its_number_of_patches(const indexed_triangle_set &its);
|
||||
bool its_number_of_patches(const indexed_triangle_set &its, const std::vector<Vec3i> &face_neighbors);
|
||||
// Same as its_number_of_patches(its) > 1, but faster.
|
||||
bool its_is_splittable(const indexed_triangle_set &its);
|
||||
bool its_is_splittable(const indexed_triangle_set &its, const std::vector<Vec3i> &face_neighbors);
|
||||
|
||||
// Calculate number of unconnected face edges. There should be no unconnected edge in a manifold mesh.
|
||||
size_t its_num_open_edges(const indexed_triangle_set &its);
|
||||
size_t its_num_open_edges(const std::vector<Vec3i> &face_neighbors);
|
||||
|
||||
// Shrink the vectors of its.vertices and its.faces to a minimum size by reallocating the two vectors.
|
||||
void its_shrink_to_fit(indexed_triangle_set &its);
|
||||
|
@ -205,16 +270,32 @@ void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B);
|
|||
void its_merge(indexed_triangle_set &A, const std::vector<Vec3f> &triangles);
|
||||
void its_merge(indexed_triangle_set &A, const Pointf3s &triangles);
|
||||
|
||||
std::vector<Vec3f> its_face_normals(const indexed_triangle_set &its);
|
||||
inline Vec3f face_normal(const stl_vertex vertex[3]) { return (vertex[1] - vertex[0]).cross(vertex[2] - vertex[1]).normalized(); }
|
||||
inline Vec3f face_normal_normalized(const stl_vertex vertex[3]) { return face_normal(vertex).normalized(); }
|
||||
inline Vec3f its_face_normal(const indexed_triangle_set &its, const stl_triangle_vertex_indices face)
|
||||
{ const stl_vertex vertices[3] { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; return face_normal_normalized(vertices); }
|
||||
inline Vec3f its_face_normal(const indexed_triangle_set &its, const int face_idx)
|
||||
{ return its_face_normal(its, its.indices[face_idx]); }
|
||||
|
||||
indexed_triangle_set its_make_cube(double x, double y, double z);
|
||||
TriangleMesh make_cube(double x, double y, double z);
|
||||
|
||||
// Generate a TriangleMesh of a cylinder
|
||||
indexed_triangle_set its_make_prism(float width, float length, float height);
|
||||
indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360));
|
||||
TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360));
|
||||
indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360));
|
||||
indexed_triangle_set its_make_pyramid(float base, float height);
|
||||
indexed_triangle_set its_make_sphere(double radius, double fa);
|
||||
|
||||
indexed_triangle_set its_make_sphere(double rho, double fa=(2*PI/360));
|
||||
TriangleMesh make_cone(double r, double h, double fa=(2*PI/360));
|
||||
TriangleMesh make_sphere(double rho, double fa=(2*PI/360));
|
||||
inline TriangleMesh make_cube(double x, double y, double z) { return TriangleMesh(its_make_cube(x, y, z)); }
|
||||
inline TriangleMesh make_prism(float width, float length, float height) { return TriangleMesh(its_make_prism(width, length, height)); }
|
||||
inline TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360)) { return TriangleMesh{its_make_cylinder(r, h, fa)}; }
|
||||
inline TriangleMesh make_cone(double r, double h, double fa=(2*PI/360)) { return TriangleMesh(its_make_cone(r, h, fa)); }
|
||||
inline TriangleMesh make_pyramid(float base, float height) { return TriangleMesh(its_make_pyramid(base, height)); }
|
||||
inline TriangleMesh make_sphere(double rho, double fa=(2*PI/360)) { return TriangleMesh(its_make_sphere(rho, fa)); }
|
||||
|
||||
bool its_write_stl_ascii(const char *file, const char *label, const std::vector<stl_triangle_vertex_indices> &indices, const std::vector<stl_vertex> &vertices);
|
||||
inline bool its_write_stl_ascii(const char *file, const char *label, const indexed_triangle_set &its) { return its_write_stl_ascii(file, label, its.indices, its.vertices); }
|
||||
bool its_write_stl_binary(const char *file, const char *label, const std::vector<stl_triangle_vertex_indices> &indices, const std::vector<stl_vertex> &vertices);
|
||||
inline bool its_write_stl_binary(const char *file, const char *label, const indexed_triangle_set &its) { return its_write_stl_binary(file, label, its.indices, its.vertices); }
|
||||
|
||||
inline BoundingBoxf3 bounding_box(const TriangleMesh &m) { return m.bounding_box(); }
|
||||
inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its)
|
||||
|
@ -239,18 +320,12 @@ inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its)
|
|||
namespace cereal {
|
||||
template <class Archive> struct specialize<Archive, Slic3r::TriangleMesh, cereal::specialization::non_member_load_save> {};
|
||||
template<class Archive> void load(Archive &archive, Slic3r::TriangleMesh &mesh) {
|
||||
stl_file &stl = mesh.stl;
|
||||
stl.stats.type = inmemory;
|
||||
archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
|
||||
stl_allocate(&stl);
|
||||
archive.loadBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
|
||||
stl_get_size(&stl);
|
||||
mesh.repair();
|
||||
archive.loadBinary(reinterpret_cast<char*>(const_cast<Slic3r::TriangleMeshStats*>(&mesh.stats())), sizeof(Slic3r::TriangleMeshStats));
|
||||
archive(mesh.its.indices, mesh.its.vertices);
|
||||
}
|
||||
template<class Archive> void save(Archive &archive, const Slic3r::TriangleMesh &mesh) {
|
||||
const stl_file& stl = mesh.stl;
|
||||
archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
|
||||
archive.saveBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
|
||||
archive.saveBinary(reinterpret_cast<const char*>(&mesh.stats()), sizeof(Slic3r::TriangleMeshStats));
|
||||
archive(mesh.its.indices, mesh.its.vertices);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1967,7 +1967,8 @@ static void triangulate_slice(
|
|||
int num_original_vertices,
|
||||
// Z height of the slice.
|
||||
float z,
|
||||
bool triangulate)
|
||||
bool triangulate,
|
||||
bool normals_down)
|
||||
{
|
||||
sort_remove_duplicates(slice_vertices);
|
||||
|
||||
|
@ -1988,7 +1989,7 @@ static void triangulate_slice(
|
|||
std::vector<int> map_duplicate_vertex(int(its.vertices.size()) - num_original_vertices, -1);
|
||||
int i = 0;
|
||||
int k = 0;
|
||||
for (; i < int(map_vertex_to_index.size()); ++ i) {
|
||||
for (; i < int(map_vertex_to_index.size());) {
|
||||
map_vertex_to_index[k ++] = map_vertex_to_index[i];
|
||||
const Vec2f &ipos = map_vertex_to_index[i].first;
|
||||
const int iidx = map_vertex_to_index[i].second;
|
||||
|
@ -2003,6 +2004,7 @@ static void triangulate_slice(
|
|||
// map to the first vertex
|
||||
map_duplicate_vertex[jidx - num_original_vertices] = iidx;
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
map_vertex_to_index.erase(map_vertex_to_index.begin() + k, map_vertex_to_index.end());
|
||||
for (stl_triangle_vertex_indices &f : its.indices)
|
||||
|
@ -2013,7 +2015,7 @@ static void triangulate_slice(
|
|||
|
||||
if (triangulate) {
|
||||
size_t idx_vertex_new_first = its.vertices.size();
|
||||
Pointf3s triangles = triangulate_expolygons_3d(make_expolygons_simple(lines), z, true);
|
||||
Pointf3s triangles = triangulate_expolygons_3d(make_expolygons_simple(lines), z, normals_down);
|
||||
for (size_t i = 0; i < triangles.size(); ) {
|
||||
stl_triangle_vertex_indices facet;
|
||||
for (size_t j = 0; j < 3; ++ j) {
|
||||
|
@ -2049,6 +2051,33 @@ static void triangulate_slice(
|
|||
// its_remove_degenerate_faces(its);
|
||||
}
|
||||
|
||||
void project_mesh(
|
||||
const indexed_triangle_set &mesh,
|
||||
const Transform3d &trafo,
|
||||
Polygons *out_top,
|
||||
Polygons *out_bottom,
|
||||
std::function<void()> throw_on_cancel)
|
||||
{
|
||||
std::vector<Polygons> top, bottom;
|
||||
std::vector<float> zs { -1e10, 1e10 };
|
||||
slice_mesh_slabs(mesh, zs, trafo, out_top ? &top : nullptr, out_bottom ? &bottom : nullptr, throw_on_cancel);
|
||||
if (out_top)
|
||||
*out_top = std::move(top.front());
|
||||
if (out_bottom)
|
||||
*out_bottom = std::move(bottom.back());
|
||||
}
|
||||
|
||||
Polygons project_mesh(
|
||||
const indexed_triangle_set &mesh,
|
||||
const Transform3d &trafo,
|
||||
std::function<void()> throw_on_cancel)
|
||||
{
|
||||
std::vector<Polygons> top, bottom;
|
||||
std::vector<float> zs { -1e10, 1e10 };
|
||||
slice_mesh_slabs(mesh, zs, trafo, &top, &bottom, throw_on_cancel);
|
||||
return union_(top.front(), bottom.back());
|
||||
}
|
||||
|
||||
void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *upper, indexed_triangle_set *lower, bool triangulate_caps)
|
||||
{
|
||||
assert(upper || lower);
|
||||
|
@ -2069,6 +2098,10 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
|
|||
lower->indices.reserve(mesh.indices.size());
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t num_open_edges_old = triangulate_caps ? its_num_open_edges(mesh) : 0;
|
||||
#endif // NDEBUG
|
||||
|
||||
// To triangulate the caps after slicing.
|
||||
IntersectionLines upper_lines, lower_lines;
|
||||
std::vector<int> upper_slice_vertices, lower_slice_vertices;
|
||||
|
@ -2135,13 +2168,14 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
|
|||
// get vertices starting from the isolated one
|
||||
int iv = isolated_vertex;
|
||||
stl_vertex v0v1, v2v0;
|
||||
assert(facets_edge_ids[facet_idx](iv) == line.edge_a_id ||facets_edge_ids[facet_idx](iv) == line.edge_b_id);
|
||||
assert(facets_edge_ids[facet_idx](iv) == line.edge_a_id || facets_edge_ids[facet_idx](iv) == line.edge_b_id);
|
||||
if (facets_edge_ids[facet_idx](iv) == line.edge_a_id) {
|
||||
v0v1 = to_3d(unscaled<float>(line.a), z);
|
||||
v2v0 = to_3d(unscaled<float>(line.b), z);
|
||||
// Unscale to doubles first, then to floats to reach the same accuracy as triangulate_expolygons_2d().
|
||||
v0v1 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
|
||||
v2v0 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
|
||||
} else {
|
||||
v0v1 = to_3d(unscaled<float>(line.b), z);
|
||||
v2v0 = to_3d(unscaled<float>(line.a), z);
|
||||
v0v1 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
|
||||
v2v0 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
|
||||
}
|
||||
const stl_vertex &v0 = vertices[iv];
|
||||
const int iv0 = facet[iv];
|
||||
|
@ -2195,11 +2229,25 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
|
|||
}
|
||||
}
|
||||
|
||||
if (upper != nullptr)
|
||||
triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps);
|
||||
if (upper != nullptr) {
|
||||
triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_DOWN);
|
||||
#ifndef NDEBUG
|
||||
if (triangulate_caps) {
|
||||
size_t num_open_edges_new = its_num_open_edges(*upper);
|
||||
assert(num_open_edges_new <= num_open_edges_old);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
}
|
||||
|
||||
if (lower != nullptr)
|
||||
triangulate_slice(*lower, lower_lines, lower_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps);
|
||||
if (lower != nullptr) {
|
||||
triangulate_slice(*lower, lower_lines, lower_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_UP);
|
||||
#ifndef NDEBUG
|
||||
if (triangulate_caps) {
|
||||
size_t num_open_edges_new = its_num_open_edges(*lower);
|
||||
assert(num_open_edges_new <= num_open_edges_old);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -98,6 +98,20 @@ void slice_mesh_slabs(
|
|||
std::vector<Polygons> *out_bottom,
|
||||
std::function<void()> throw_on_cancel);
|
||||
|
||||
// Project mesh upwards pointing surfaces / downwards pointing surfaces into 2D polygons.
|
||||
void project_mesh(
|
||||
const indexed_triangle_set &mesh,
|
||||
const Transform3d &trafo,
|
||||
Polygons *out_top,
|
||||
Polygons *out_bottom,
|
||||
std::function<void()> throw_on_cancel);
|
||||
|
||||
// Project mesh into 2D polygons.
|
||||
Polygons project_mesh(
|
||||
const indexed_triangle_set &mesh,
|
||||
const Transform3d &trafo,
|
||||
std::function<void()> throw_on_cancel);
|
||||
|
||||
void cut_mesh(
|
||||
const indexed_triangle_set &mesh,
|
||||
float z,
|
||||
|
|
|
@ -9,16 +9,6 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
static inline Vec3i root_neighbors(const TriangleMesh &mesh, int triangle_id)
|
||||
{
|
||||
Vec3i neighbors;
|
||||
const stl_neighbors& neighbors_src = mesh.stl.neighbors_start[triangle_id];
|
||||
for (int i = 0; i < 3; ++i)
|
||||
// Refuse a neighbor with a flipped normal.
|
||||
neighbors(i) = neighbors_src.neighbor[i];
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool TriangleSelector::verify_triangle_midpoints(const Triangle &tr) const
|
||||
{
|
||||
|
@ -129,7 +119,7 @@ int TriangleSelector::select_unsplit_triangle(const Vec3f &hit, int facet_idx) c
|
|||
if (!m_triangles[facet_idx].valid())
|
||||
return -1;
|
||||
|
||||
Vec3i neighbors = root_neighbors(*m_mesh, facet_idx);
|
||||
Vec3i neighbors = m_neighbors[facet_idx];
|
||||
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
|
||||
return this->select_unsplit_triangle(hit, facet_idx, neighbors);
|
||||
}
|
||||
|
@ -167,7 +157,7 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
|
|||
if (! visited[facet]) {
|
||||
if (select_triangle(facet, new_state, triangle_splitting)) {
|
||||
// add neighboring facets to list to be proccessed later
|
||||
for (int neighbor_idx : m_mesh->stl.neighbors_start[facet].neighbor) {
|
||||
for (int neighbor_idx : m_neighbors[facet]) {
|
||||
if (neighbor_idx >=0 && (m_cursor.type == SPHERE || faces_camera(neighbor_idx)))
|
||||
facets_to_check.push_back(neighbor_idx);
|
||||
}
|
||||
|
@ -213,12 +203,12 @@ void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_st
|
|||
|
||||
if (current_facet < m_orig_size_indices)
|
||||
// Propagate over the original triangles.
|
||||
for (int neighbor_idx : m_mesh->stl.neighbors_start[current_facet].neighbor) {
|
||||
for (int neighbor_idx : m_neighbors[current_facet]) {
|
||||
assert(neighbor_idx >= -1);
|
||||
if (neighbor_idx >= 0 && !visited[neighbor_idx]) {
|
||||
// Check if neighbour_facet_idx is satisfies angle in seed_fill_angle and append it to facet_queue if it do.
|
||||
const Vec3f &n1 = m_mesh->stl.facet_start[m_triangles[neighbor_idx].source_triangle].normal;
|
||||
const Vec3f &n2 = m_mesh->stl.facet_start[m_triangles[current_facet].source_triangle].normal;
|
||||
const Vec3f &n1 = m_face_normals[m_triangles[neighbor_idx].source_triangle];
|
||||
const Vec3f &n2 = m_face_normals[m_triangles[current_facet].source_triangle];
|
||||
if (std::clamp(n1.dot(n2), 0.f, 1.f) >= facet_angle_limit)
|
||||
facet_queue.push(neighbor_idx);
|
||||
}
|
||||
|
@ -261,7 +251,7 @@ std::pair<std::vector<Vec3i>, std::vector<Vec3i>> TriangleSelector::precompute_a
|
|||
std::vector<Vec3i> neighbors(m_triangles.size(), Vec3i(-1, -1, -1));
|
||||
std::vector<Vec3i> neighbors_propagated(m_triangles.size(), Vec3i(-1, -1, -1));
|
||||
for (int facet_idx = 0; facet_idx < this->m_orig_size_indices; ++facet_idx) {
|
||||
neighbors[facet_idx] = root_neighbors(*m_mesh, facet_idx);
|
||||
neighbors[facet_idx] = m_neighbors[facet_idx];
|
||||
neighbors_propagated[facet_idx] = neighbors[facet_idx];
|
||||
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors[facet_idx]));
|
||||
if (m_triangles[facet_idx].is_split())
|
||||
|
@ -403,7 +393,7 @@ bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type,
|
|||
if (! m_triangles[facet_idx].valid())
|
||||
return false;
|
||||
|
||||
Vec3i neighbors = root_neighbors(*m_mesh, facet_idx);
|
||||
Vec3i neighbors = m_neighbors[facet_idx];
|
||||
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
|
||||
|
||||
if (! select_triangle_recursive(facet_idx, neighbors, type, triangle_splitting))
|
||||
|
@ -906,14 +896,10 @@ bool TriangleSelector::is_pointer_in_triangle(int facet_idx) const
|
|||
bool TriangleSelector::faces_camera(int facet) const
|
||||
{
|
||||
assert(facet < m_orig_size_indices);
|
||||
// The normal is cached in mesh->stl, use it.
|
||||
Vec3f normal = m_mesh->stl.facet_start[facet].normal;
|
||||
|
||||
if (! m_cursor.uniform_scaling) {
|
||||
// Transform the normal into world coords.
|
||||
normal = m_cursor.trafo_normal * normal;
|
||||
}
|
||||
return (normal.dot(m_cursor.dir) < 0.);
|
||||
Vec3f n = m_face_normals[facet];
|
||||
if (! m_cursor.uniform_scaling)
|
||||
n = m_cursor.trafo_normal * n;
|
||||
return n.dot(m_cursor.dir) < 0.;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1094,7 +1080,7 @@ void TriangleSelector::garbage_collect()
|
|||
}
|
||||
|
||||
TriangleSelector::TriangleSelector(const TriangleMesh& mesh)
|
||||
: m_mesh{&mesh}
|
||||
: m_mesh{mesh}, m_neighbors(its_face_neighbors(mesh.its)), m_face_normals(its_face_normals(mesh.its))
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
@ -1107,16 +1093,17 @@ void TriangleSelector::reset()
|
|||
m_invalid_triangles = 0;
|
||||
m_free_triangles_head = -1;
|
||||
m_free_vertices_head = -1;
|
||||
m_vertices.reserve(m_mesh->its.vertices.size());
|
||||
for (const stl_vertex& vert : m_mesh->its.vertices)
|
||||
m_vertices.reserve(m_mesh.its.vertices.size());
|
||||
for (const stl_vertex& vert : m_mesh.its.vertices)
|
||||
m_vertices.emplace_back(vert);
|
||||
m_triangles.reserve(m_mesh->its.indices.size());
|
||||
for (size_t i = 0; i < m_mesh->its.indices.size(); ++i) {
|
||||
const stl_triangle_vertex_indices &ind = m_mesh->its.indices[i];
|
||||
m_triangles.reserve(m_mesh.its.indices.size());
|
||||
for (size_t i = 0; i < m_mesh.its.indices.size(); ++i) {
|
||||
const stl_triangle_vertex_indices &ind = m_mesh.its.indices[i];
|
||||
push_triangle(ind[0], ind[1], ind[2], int(i));
|
||||
}
|
||||
m_orig_size_vertices = int(m_vertices.size());
|
||||
m_orig_size_indices = int(m_triangles.size());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1286,7 +1273,7 @@ indexed_triangle_set TriangleSelector::get_facets_strict(EnforcerBlockerType sta
|
|||
}
|
||||
|
||||
for (int itriangle = 0; itriangle < m_orig_size_indices; ++ itriangle)
|
||||
this->get_facets_strict_recursive(m_triangles[itriangle], root_neighbors(*m_mesh, itriangle), state, out.indices);
|
||||
this->get_facets_strict_recursive(m_triangles[itriangle], m_neighbors[itriangle], state, out.indices);
|
||||
|
||||
for (auto &triangle : out.indices)
|
||||
for (int i = 0; i < 3; ++ i)
|
||||
|
@ -1398,7 +1385,7 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i &vertices, const
|
|||
std::vector<Vec2i> TriangleSelector::get_seed_fill_contour() const {
|
||||
std::vector<Vec2i> edges_out;
|
||||
for (int facet_idx = 0; facet_idx < this->m_orig_size_indices; ++facet_idx) {
|
||||
const Vec3i neighbors = root_neighbors(*m_mesh, facet_idx);
|
||||
const Vec3i neighbors = m_neighbors[facet_idx];
|
||||
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
|
||||
this->get_seed_fill_contour_recursive(facet_idx, neighbors, neighbors, edges_out);
|
||||
}
|
||||
|
@ -1522,10 +1509,10 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
|
|||
|
||||
// Reserve number of triangles as if each triangle was saved with 4 bits.
|
||||
// With MMU painting this estimate may be somehow low, but better than nothing.
|
||||
m_triangles.reserve(std::max(m_mesh->its.indices.size(), data.second.size() / 4));
|
||||
m_triangles.reserve(std::max(m_mesh.its.indices.size(), data.second.size() / 4));
|
||||
// Number of triangles is twice the number of vertices on a large manifold mesh of genus zero.
|
||||
// Here the triangles count account for both the nodes and leaves, thus the following line may overestimate.
|
||||
m_vertices.reserve(std::max(m_mesh->its.vertices.size(), m_triangles.size() / 2));
|
||||
m_vertices.reserve(std::max(m_mesh.its.vertices.size(), m_triangles.size() / 2));
|
||||
|
||||
// Vector to store all parents that have offsprings.
|
||||
struct ProcessingInfo {
|
||||
|
@ -1565,7 +1552,7 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
|
|||
if (is_split) {
|
||||
// root is split, add it into list of parents and split it.
|
||||
// then go to the next.
|
||||
Vec3i neighbors = root_neighbors(*m_mesh, triangle_id);
|
||||
Vec3i neighbors = m_neighbors[triangle_id];
|
||||
parents.push_back({triangle_id, neighbors, 0, num_of_children});
|
||||
m_triangles[triangle_id].set_division(num_of_split_sides, special_side);
|
||||
perform_split(triangle_id, neighbors, EnforcerBlockerType::NONE);
|
||||
|
|
|
@ -161,7 +161,9 @@ protected:
|
|||
// Lists of vertices and triangles, both original and new
|
||||
std::vector<Vertex> m_vertices;
|
||||
std::vector<Triangle> m_triangles;
|
||||
const TriangleMesh* m_mesh;
|
||||
const TriangleMesh &m_mesh;
|
||||
const std::vector<Vec3i> m_neighbors;
|
||||
const std::vector<Vec3f> m_face_normals;
|
||||
|
||||
// Number of invalid triangles (to trigger garbage collection).
|
||||
int m_invalid_triangles;
|
||||
|
|
|
@ -256,6 +256,19 @@ template<typename T> struct IsTriviallyCopyable { static constexpr bool value =
|
|||
template<typename T> struct IsTriviallyCopyable : public std::is_trivially_copyable<T> {};
|
||||
#endif
|
||||
|
||||
// A very lightweight ROII wrapper around C FILE.
|
||||
// The old C file API is much faster than C++ streams, thus they are recommended for processing large / huge files.
|
||||
struct FilePtr {
|
||||
FilePtr(FILE *f) : f(f) {}
|
||||
~FilePtr() { this->close(); }
|
||||
void close() {
|
||||
if (this->f) {
|
||||
::fclose(this->f);
|
||||
this->f = nullptr;
|
||||
}
|
||||
}
|
||||
FILE* f = nullptr;
|
||||
};
|
||||
|
||||
class ScopeGuard
|
||||
{
|
||||
|
|
|
@ -889,7 +889,7 @@ std::string string_printf(const char *format, ...)
|
|||
|
||||
std::string header_slic3r_generated()
|
||||
{
|
||||
return std::string("generated by " SLIC3R_APP_NAME " " SLIC3R_VERSION " on " ) + Utils::utc_timestamp();
|
||||
return std::string("generated by PrusaSlicer " SLIC3R_VERSION " on " ) + Utils::utc_timestamp();
|
||||
}
|
||||
|
||||
std::string header_gcodeviewer_generated()
|
||||
|
@ -906,6 +906,7 @@ unsigned get_current_pid()
|
|||
#endif
|
||||
}
|
||||
|
||||
//FIXME this has potentially O(n^2) time complexity!
|
||||
std::string xml_escape(std::string text, bool is_marked/* = false*/)
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
|
|
|
@ -12,7 +12,7 @@ PRODUCTVERSION @SLIC3R_RC_VERSION@
|
|||
VALUE "ProductName", "@SLIC3R_APP_NAME@ G-code Viewer"
|
||||
VALUE "ProductVersion", "@SLIC3R_BUILD_ID@"
|
||||
VALUE "InternalName", "@SLIC3R_APP_NAME@ G-code Viewer"
|
||||
VALUE "LegalCopyright", "Copyright \251 2016-2020 Prusa Research, \251 2011-2018 Alessandro Ranellucci"
|
||||
VALUE "LegalCopyright", "Copyright \251 2016-2021 Prusa Research, \251 2011-2018 Alessandro Ranellucci"
|
||||
VALUE "OriginalFilename", "prusa-gcodeviewer.exe"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ PRODUCTVERSION @SLIC3R_RC_VERSION@
|
|||
VALUE "ProductName", "@SLIC3R_APP_NAME@"
|
||||
VALUE "ProductVersion", "@SLIC3R_BUILD_ID@"
|
||||
VALUE "InternalName", "@SLIC3R_APP_NAME@"
|
||||
VALUE "LegalCopyright", "Copyright \251 2016-2020 Prusa Research, \251 2011-2018 Alessandro Ranellucci"
|
||||
VALUE "LegalCopyright", "Copyright \251 2016-2021 Prusa Research, \251 2011-2018 Alessandro Ranellucci"
|
||||
VALUE "OriginalFilename", "prusa-slicer.exe"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<key>CFBundleExecutable</key>
|
||||
<string>@SLIC3R_APP_KEY@</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>@SLIC3R_APP_NAME@ Copyright (C) 2011-2019 Alessandro Ranellucci, (C) 2016-2020 Prusa Reseach</string>
|
||||
<string>@SLIC3R_APP_NAME@ Copyright (C) 2011-2019 Alessandro Ranellucci, (C) 2016-2021 Prusa Reseach</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>PrusaSlicer.icns</string>
|
||||
<key>CFBundleName</key>
|
||||
|
|
|
@ -179,6 +179,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/Jobs/SLAImportJob.hpp
|
||||
GUI/Jobs/SLAImportJob.cpp
|
||||
GUI/Jobs/ProgressIndicator.hpp
|
||||
GUI/Jobs/NotificationProgressIndicator.hpp
|
||||
GUI/Jobs/NotificationProgressIndicator.cpp
|
||||
GUI/ProgressStatusBar.hpp
|
||||
GUI/ProgressStatusBar.cpp
|
||||
GUI/Mouse3DController.cpp
|
||||
|
|
|
@ -92,7 +92,7 @@ void Bed_2D::repaint(const std::vector<Vec2d>& shape)
|
|||
for (auto y = bb.min(1) - fmod(bb.min(1), step) + step; y < bb.max(1); y += step) {
|
||||
polylines.push_back(Polyline::new_scale({ Vec2d(bb.min(0), y), Vec2d(bb.max(0), y) }));
|
||||
}
|
||||
polylines = intersection_pl(polylines, (Polygons)bed_polygon);
|
||||
polylines = intersection_pl(polylines, bed_polygon);
|
||||
|
||||
dc.SetPen(wxPen(wxColour(230, 230, 230), 1, wxPENSTYLE_SOLID));
|
||||
for (auto pl : polylines)
|
||||
|
|
|
@ -9,9 +9,7 @@
|
|||
#include "3DScene.hpp"
|
||||
#include "GLShader.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#if ENABLE_ENVIRONMENT_MAP || ENABLE_SINKING_CONTOURS
|
||||
#include "Plater.hpp"
|
||||
#endif // ENABLE_ENVIRONMENT_MAP || ENABLE_SINKING_CONTOURS
|
||||
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
#include "libslic3r/ExtrusionEntityCollection.hpp"
|
||||
|
@ -25,9 +23,7 @@
|
|||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
#include "libslic3r/Tesselate.hpp"
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -158,22 +154,27 @@ void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh& mesh)
|
|||
}
|
||||
else {
|
||||
#endif // ENABLE_SMOOTH_NORMALS
|
||||
this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count());
|
||||
|
||||
unsigned int vertices_count = 0;
|
||||
for (int i = 0; i < (int)mesh.stl.stats.number_of_facets; ++i) {
|
||||
const stl_facet& facet = mesh.stl.facet_start[i];
|
||||
for (int j = 0; j < 3; ++j)
|
||||
this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2));
|
||||
|
||||
this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2);
|
||||
vertices_count += 3;
|
||||
}
|
||||
this->load_its_flat_shading(mesh.its);
|
||||
#if ENABLE_SMOOTH_NORMALS
|
||||
}
|
||||
#endif // ENABLE_SMOOTH_NORMALS
|
||||
}
|
||||
|
||||
void GLIndexedVertexArray::load_its_flat_shading(const indexed_triangle_set &its)
|
||||
{
|
||||
this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * its.indices.size());
|
||||
unsigned int vertices_count = 0;
|
||||
for (int i = 0; i < int(its.indices.size()); ++ i) {
|
||||
stl_triangle_vertex_indices face = its.indices[i];
|
||||
stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] };
|
||||
stl_vertex n = face_normal_normalized(vertex);
|
||||
for (int j = 0; j < 3; ++j)
|
||||
this->push_geometry(vertex[j](0), vertex[j](1), vertex[j](2), n(0), n(1), n(2));
|
||||
this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2);
|
||||
vertices_count += 3;
|
||||
}
|
||||
}
|
||||
|
||||
void GLIndexedVertexArray::finalize_geometry(bool opengl_initialized)
|
||||
{
|
||||
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
|
||||
|
@ -288,7 +289,6 @@ void GLIndexedVertexArray::render(
|
|||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
}
|
||||
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
const float GLVolume::SinkingContours::HalfWidth = 0.25f;
|
||||
|
||||
void GLVolume::SinkingContours::render()
|
||||
|
@ -313,7 +313,6 @@ void GLVolume::SinkingContours::update()
|
|||
m_shift = Vec3d::Zero();
|
||||
|
||||
const TriangleMesh& mesh = model.objects[object_idx]->volumes[m_parent.volume_idx()]->mesh();
|
||||
assert(mesh.has_shared_vertices());
|
||||
|
||||
m_model.reset();
|
||||
GUI::GLModel::InitializationData init_data;
|
||||
|
@ -357,7 +356,6 @@ void GLVolume::SinkingContours::update()
|
|||
else
|
||||
m_model.reset();
|
||||
}
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
const std::array<float, 4> GLVolume::SELECTED_COLOR = { 0.0f, 1.0f, 0.0f, 1.0f };
|
||||
const std::array<float, 4> GLVolume::HOVER_SELECT_COLOR = { 0.4f, 0.9f, 0.1f, 1.0f };
|
||||
|
@ -376,12 +374,8 @@ const std::array<std::array<float, 4>, 4> GLVolume::MODEL_COLOR = { {
|
|||
} };
|
||||
|
||||
GLVolume::GLVolume(float r, float g, float b, float a)
|
||||
: m_transformed_bounding_box_dirty(true)
|
||||
, m_sla_shift_z(0.0)
|
||||
, m_transformed_convex_hull_bounding_box_dirty(true)
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
: m_sla_shift_z(0.0)
|
||||
, m_sinking_contours(*this)
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
// geometry_id == 0 -> invalid
|
||||
, geometry_id(std::pair<size_t, size_t>(0, 0))
|
||||
, extruder_id(0)
|
||||
|
@ -399,9 +393,7 @@ GLVolume::GLVolume(float r, float g, float b, float a)
|
|||
, force_transparent(false)
|
||||
, force_native_color(false)
|
||||
, force_neutral_color(false)
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
, force_sinking_contours(false)
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
, tverts_range(0, size_t(-1))
|
||||
, qverts_range(0, size_t(-1))
|
||||
{
|
||||
|
@ -506,32 +498,27 @@ bool GLVolume::is_left_handed() const
|
|||
|
||||
const BoundingBoxf3& GLVolume::transformed_bounding_box() const
|
||||
{
|
||||
if (!m_transformed_bounding_box.has_value()) {
|
||||
const BoundingBoxf3& box = bounding_box();
|
||||
assert(box.defined || box.min(0) >= box.max(0) || box.min(1) >= box.max(1) || box.min(2) >= box.max(2));
|
||||
|
||||
BoundingBoxf3* transformed_bounding_box = const_cast<BoundingBoxf3*>(&m_transformed_bounding_box);
|
||||
bool* transformed_bounding_box_dirty = const_cast<bool*>(&m_transformed_bounding_box_dirty);
|
||||
if (*transformed_bounding_box_dirty) {
|
||||
*transformed_bounding_box = box.transformed(world_matrix());
|
||||
*transformed_bounding_box_dirty = false;
|
||||
assert(box.defined || box.min.x() >= box.max.x() || box.min.y() >= box.max.y() || box.min.z() >= box.max.z());
|
||||
std::optional<BoundingBoxf3>* trans_box = const_cast<std::optional<BoundingBoxf3>*>(&m_transformed_bounding_box);
|
||||
*trans_box = box.transformed(world_matrix());
|
||||
}
|
||||
return *transformed_bounding_box;
|
||||
return *m_transformed_bounding_box;
|
||||
}
|
||||
|
||||
const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const
|
||||
{
|
||||
BoundingBoxf3* transformed_convex_hull_bounding_box = const_cast<BoundingBoxf3*>(&m_transformed_convex_hull_bounding_box);
|
||||
bool* transformed_convex_hull_bounding_box_dirty = const_cast<bool*>(&m_transformed_convex_hull_bounding_box_dirty);
|
||||
if (*transformed_convex_hull_bounding_box_dirty) {
|
||||
*transformed_convex_hull_bounding_box = this->transformed_convex_hull_bounding_box(world_matrix());
|
||||
*transformed_convex_hull_bounding_box_dirty = false;
|
||||
if (!m_transformed_convex_hull_bounding_box.has_value()) {
|
||||
std::optional<BoundingBoxf3>* trans_box = const_cast<std::optional<BoundingBoxf3>*>(&m_transformed_convex_hull_bounding_box);
|
||||
*trans_box = transformed_convex_hull_bounding_box(world_matrix());
|
||||
}
|
||||
return *transformed_convex_hull_bounding_box;
|
||||
return *m_transformed_convex_hull_bounding_box;
|
||||
}
|
||||
|
||||
BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const
|
||||
{
|
||||
return (m_convex_hull && m_convex_hull->stl.stats.number_of_facets > 0) ?
|
||||
return (m_convex_hull && ! m_convex_hull->empty()) ?
|
||||
m_convex_hull->transformed_bounding_box(trafo) :
|
||||
bounding_box().transformed(trafo);
|
||||
}
|
||||
|
@ -605,12 +592,10 @@ bool GLVolume::is_below_printbed() const
|
|||
return transformed_convex_hull_bounding_box().max.z() < 0.0;
|
||||
}
|
||||
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
void GLVolume::render_sinking_contours()
|
||||
{
|
||||
m_sinking_contours.render();
|
||||
}
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
std::vector<int> GLVolumeCollection::load_object(
|
||||
const ModelObject *model_object,
|
||||
|
@ -733,21 +718,20 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
|||
float min_width = 30.f;
|
||||
// We'll now create the box with jagged edge. y-coordinates of the pre-generated model
|
||||
// are shifted so that the front edge has y=0 and centerline of the back edge has y=depth:
|
||||
Pointf3s points;
|
||||
std::vector<Vec3i> facets;
|
||||
float out_points_idx[][3] = { { 0, -depth, 0 }, { 0, 0, 0 }, { 38.453f, 0, 0 }, { 61.547f, 0, 0 }, { 100.0f, 0, 0 }, { 100.0f, -depth, 0 }, { 55.7735f, -10.0f, 0 }, { 44.2265f, 10.0f, 0 },
|
||||
{ 38.453f, 0, 1 }, { 0, 0, 1 }, { 0, -depth, 1 }, { 100.0f, -depth, 1 }, { 100.0f, 0, 1 }, { 61.547f, 0, 1 }, { 55.7735f, -10.0f, 1 }, { 44.2265f, 10.0f, 1 } };
|
||||
int out_facets_idx[][3] = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 5, 0 }, { 3, 5, 6 }, { 6, 2, 7 }, { 6, 0, 2 }, { 8, 9, 10 }, { 11, 12, 13 }, { 10, 11, 14 }, { 14, 11, 13 }, { 15, 8, 14 },
|
||||
{8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8},
|
||||
{0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11} };
|
||||
static constexpr const int out_facets_idx[][3] = {
|
||||
{ 0, 1, 2 }, { 3, 4, 5 }, { 6, 5, 0 }, { 3, 5, 6 }, { 6, 2, 7 }, { 6, 0, 2 }, { 8, 9, 10 }, { 11, 12, 13 }, { 10, 11, 14 }, { 14, 11, 13 }, { 15, 8, 14 },
|
||||
{ 8, 10, 14 }, { 3, 12, 4 }, { 3, 13, 12 }, { 6, 13, 3 }, { 6, 14, 13 }, { 7, 14, 6 }, { 7, 15, 14 }, { 2, 15, 7 }, { 2, 8, 15 }, { 1, 8, 2 }, { 1, 9, 8 },
|
||||
{ 0, 9, 1 }, { 0, 10, 9 }, { 5, 10, 0 }, { 5, 11, 10 }, { 4, 11, 5 }, { 4, 12, 11 } };
|
||||
indexed_triangle_set its;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
points.emplace_back(out_points_idx[i][0] / (100.f / min_width),
|
||||
its.vertices.emplace_back(out_points_idx[i][0] / (100.f / min_width),
|
||||
out_points_idx[i][1] + depth, out_points_idx[i][2]);
|
||||
for (int i = 0; i < 28; ++i)
|
||||
facets.emplace_back(out_facets_idx[i][0],
|
||||
out_facets_idx[i][1],
|
||||
out_facets_idx[i][2]);
|
||||
TriangleMesh tooth_mesh(points, facets);
|
||||
its.indices.reserve(28);
|
||||
for (const int *face : out_facets_idx)
|
||||
its.indices.emplace_back(face);
|
||||
TriangleMesh tooth_mesh(std::move(its));
|
||||
|
||||
// We have the mesh ready. It has one tooth and width of min_width. We will now
|
||||
// append several of these together until we are close to the required width
|
||||
|
@ -758,7 +742,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
|||
tooth_mesh.translate(min_width, 0.f, 0.f);
|
||||
}
|
||||
|
||||
mesh.scale(Vec3d(width / (n * min_width), 1.f, height)); // Scaling to proper width
|
||||
mesh.scale(Vec3f(width / (n * min_width), 1.f, height)); // Scaling to proper width
|
||||
}
|
||||
else
|
||||
mesh = make_cube(width, depth, height);
|
||||
|
@ -834,11 +818,9 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo
|
|||
|
||||
void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func) const
|
||||
{
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func);
|
||||
if (to_render.empty())
|
||||
return;
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
GLShaderProgram* shader = GUI::wxGetApp().get_current_shader();
|
||||
if (shader == nullptr)
|
||||
|
@ -853,7 +835,6 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
|||
if (disable_cullface)
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
for (GLVolumeWithIdAndZ& volume : to_render) {
|
||||
volume.first->set_render_color();
|
||||
|
||||
|
@ -903,7 +884,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
|||
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
|
||||
}
|
||||
|
||||
if (m_show_sinking_contours)
|
||||
if (m_show_sinking_contours) {
|
||||
for (GLVolumeWithIdAndZ& volume : to_render) {
|
||||
// render sinking contours of hovered/displaced volumes
|
||||
if (volume.first->is_sinking() && !volume.first->is_below_printbed() &&
|
||||
|
@ -915,48 +896,8 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
|||
shader->start_using();
|
||||
}
|
||||
}
|
||||
#else
|
||||
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
|
||||
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
|
||||
|
||||
shader->set_uniform("print_box.min", m_print_box_min, 3);
|
||||
shader->set_uniform("print_box.max", m_print_box_max, 3);
|
||||
shader->set_uniform("z_range", m_z_range, 2);
|
||||
shader->set_uniform("clipping_plane", m_clipping_plane, 4);
|
||||
shader->set_uniform("slope.normal_z", m_slope.normal_z);
|
||||
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
unsigned int environment_texture_id = GUI::wxGetApp().plater()->get_environment_texture_id();
|
||||
bool use_environment_texture = environment_texture_id > 0 && GUI::wxGetApp().app_config->get("use_environment_map") == "1";
|
||||
shader->set_uniform("use_environment_tex", use_environment_texture);
|
||||
if (use_environment_texture)
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, environment_texture_id));
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
glcheck();
|
||||
|
||||
GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
|
||||
for (GLVolumeWithIdAndZ& volume : to_render) {
|
||||
volume.first->set_render_color();
|
||||
shader->set_uniform("uniform_color", volume.first->render_color);
|
||||
shader->set_uniform("print_box.actived", volume.first->shader_outside_printer_detection_enabled);
|
||||
shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix());
|
||||
shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower);
|
||||
shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
|
||||
volume.first->render();
|
||||
}
|
||||
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
if (use_environment_texture)
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
||||
|
||||
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
|
||||
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
if (disable_cullface)
|
||||
glsafe(::glEnable(GL_CULL_FACE));
|
||||
|
||||
|
|
|
@ -8,11 +8,10 @@
|
|||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
#include "GLModel.hpp"
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
#define HAS_GLSAFE
|
||||
#ifdef HAS_GLSAFE
|
||||
|
@ -128,6 +127,8 @@ public:
|
|||
void load_mesh(const TriangleMesh& mesh) { this->load_mesh_full_shading(mesh); }
|
||||
#endif // ENABLE_SMOOTH_NORMALS
|
||||
|
||||
void load_its_flat_shading(const indexed_triangle_set &its);
|
||||
|
||||
inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; }
|
||||
|
||||
inline void reserve(size_t sz) {
|
||||
|
@ -258,9 +259,7 @@ public:
|
|||
enum EHoverState : unsigned char
|
||||
{
|
||||
HS_None,
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
HS_Hover,
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
HS_Select,
|
||||
HS_Deselect
|
||||
};
|
||||
|
@ -275,17 +274,12 @@ private:
|
|||
// Shift in z required by sla supports+pad
|
||||
double m_sla_shift_z;
|
||||
// Bounding box of this volume, in unscaled coordinates.
|
||||
BoundingBoxf3 m_transformed_bounding_box;
|
||||
// Whether or not is needed to recalculate the transformed bounding box.
|
||||
bool m_transformed_bounding_box_dirty;
|
||||
std::optional<BoundingBoxf3> m_transformed_bounding_box;
|
||||
// Convex hull of the volume, if any.
|
||||
std::shared_ptr<const TriangleMesh> m_convex_hull;
|
||||
// Bounding box of this volume, in unscaled coordinates.
|
||||
BoundingBoxf3 m_transformed_convex_hull_bounding_box;
|
||||
// Whether or not is needed to recalculate the transformed convex hull bounding box.
|
||||
bool m_transformed_convex_hull_bounding_box_dirty;
|
||||
std::optional<BoundingBoxf3> m_transformed_convex_hull_bounding_box;
|
||||
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
class SinkingContours
|
||||
{
|
||||
static const float HalfWidth;
|
||||
|
@ -303,7 +297,6 @@ private:
|
|||
};
|
||||
|
||||
SinkingContours m_sinking_contours;
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
public:
|
||||
// Color of the triangles / quads held by this volume.
|
||||
|
@ -365,10 +358,8 @@ public:
|
|||
bool force_native_color : 1;
|
||||
// Whether or not render this volume in neutral
|
||||
bool force_neutral_color : 1;
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
// Whether or not to force rendering of sinking contours
|
||||
bool force_sinking_contours : 1;
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
};
|
||||
|
||||
// Is mouse or rectangle selection over this object to select/deselect it ?
|
||||
|
@ -490,16 +481,14 @@ public:
|
|||
void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); }
|
||||
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
|
||||
|
||||
void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; }
|
||||
void set_bounding_boxes_as_dirty() { m_transformed_bounding_box.reset(); m_transformed_convex_hull_bounding_box.reset(); }
|
||||
|
||||
bool is_sla_support() const;
|
||||
bool is_sla_pad() const;
|
||||
|
||||
bool is_sinking() const;
|
||||
bool is_below_printbed() const;
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
void render_sinking_contours();
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
// Return an estimate of the memory consumed by this class.
|
||||
size_t cpu_memory_used() const {
|
||||
|
|
|
@ -275,7 +275,7 @@ AboutDialog::AboutDialog()
|
|||
"<html>"
|
||||
"<body bgcolor= %1% link= %2%>"
|
||||
"<font color=%3%>"
|
||||
"%4% © 2016-2020 Prusa Research. <br />"
|
||||
"%4% © 2016-2021 Prusa Research. <br />"
|
||||
"%5% © 2011-2018 Alessandro Ranellucci. <br />"
|
||||
"<a href=\"http://slic3r.org/\">Slic3r</a> %6% "
|
||||
"<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">%7%</a>."
|
||||
|
|
|
@ -210,7 +210,7 @@ void BackgroundSlicingProcess::process_sla()
|
|||
void BackgroundSlicingProcess::thread_proc()
|
||||
{
|
||||
set_current_thread_name("slic3r_BgSlcPcs");
|
||||
name_tbb_thread_pool_threads();
|
||||
name_tbb_thread_pool_threads_set_locale();
|
||||
|
||||
assert(m_print != nullptr);
|
||||
assert(m_print == m_fff_print || m_print == m_sla_print);
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "format.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "UnsavedChangesDialog.hpp"
|
||||
|
||||
#if defined(__linux__) && defined(__WXGTK3__)
|
||||
#define wxLinux_gtk3 true
|
||||
|
@ -741,10 +742,10 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector<std::s
|
|||
const auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
|
||||
const auto text_clr = wxGetApp().get_label_clr_default();//wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
const auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
|
||||
wxString first_line = _L("Filaments marked with <b>*</b> are <b>not</b> compatible with some installed printers.");
|
||||
wxString first_line = format_wxstr(_L("%1% marked with <b>*</b> are <b>not</b> compatible with some installed printers."), materials->technology == T_FFF ? _L("Filaments") : _L("SLA materials"));
|
||||
wxString text;
|
||||
if (all_printers) {
|
||||
wxString second_line = _L("All installed printers are compatible with the selected filament.");
|
||||
wxString second_line = format_wxstr(_L("All installed printers are compatible with the selected %1%."), materials->technology == T_FFF ? _L("filament") : _L("SLA material"));
|
||||
text = wxString::Format(
|
||||
"<html>"
|
||||
"<style>"
|
||||
|
@ -764,7 +765,7 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector<std::s
|
|||
, second_line
|
||||
);
|
||||
} else {
|
||||
wxString second_line = _L("Only the following installed printers are compatible with the selected filament:");
|
||||
wxString second_line = printer_names.empty() ? "" : format_wxstr(_L("Only the following installed printers are compatible with the selected %1%:"), materials->technology == T_FFF ? _L("filament") : _L("SLA material"));
|
||||
text = wxString::Format(
|
||||
"<html>"
|
||||
"<style>"
|
||||
|
@ -2473,8 +2474,16 @@ static std::string get_first_added_preset(const std::map<std::string, std::strin
|
|||
return *diff.begin();
|
||||
}
|
||||
|
||||
bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater)
|
||||
bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes)
|
||||
{
|
||||
wxString header, caption = _L("Configuration is editing from ConfigWizard");
|
||||
bool check_unsaved_preset_changes = page_welcome->reset_user_profile();
|
||||
if (check_unsaved_preset_changes)
|
||||
header = _L("All user presets will be deleted.");
|
||||
int act_btns = UnsavedChangesDialog::ActionButtons::KEEP;
|
||||
if (!check_unsaved_preset_changes)
|
||||
act_btns |= UnsavedChangesDialog::ActionButtons::SAVE;
|
||||
|
||||
const auto enabled_vendors = appconfig_new.vendors();
|
||||
|
||||
// Install bundles from resources if needed:
|
||||
|
@ -2500,6 +2509,9 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||
install_bundles.emplace_back(pair.first);
|
||||
}
|
||||
}
|
||||
if (!check_unsaved_preset_changes)
|
||||
if ((check_unsaved_preset_changes = install_bundles.size() > 0))
|
||||
header = _L_PLURAL("New vendor was installed and one of its printer will be activated", "New vendors were installed and one of theirs printer will be activated", install_bundles.size());
|
||||
|
||||
#ifdef __linux__
|
||||
// Desktop integration on Linux
|
||||
|
@ -2531,6 +2543,10 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||
if (snapshot && ! take_config_snapshot_cancel_on_error(*app_config, snapshot_reason, "", _u8L("Continue with applying configuration changes?")))
|
||||
return false;
|
||||
|
||||
if (check_unsaved_preset_changes &&
|
||||
!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
|
||||
return false;
|
||||
|
||||
if (install_bundles.size() > 0) {
|
||||
// Install bundles from resources.
|
||||
// Don't create snapshot - we've already done that above if applicable.
|
||||
|
@ -2586,17 +2602,52 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||
}
|
||||
}
|
||||
|
||||
// if unsaved changes was not cheched till this moment
|
||||
if (!check_unsaved_preset_changes) {
|
||||
if ((check_unsaved_preset_changes = !preferred_model.empty())) {
|
||||
header = _L("A new Printer was installed and it will be activated.");
|
||||
if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
|
||||
return false;
|
||||
}
|
||||
else if ((check_unsaved_preset_changes = enabled_vendors_old != enabled_vendors)) {
|
||||
header = _L("Some Printers were uninstalled.");
|
||||
if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string first_added_filament, first_added_sla_material;
|
||||
auto apply_section = [this, app_config](const std::string& section_name, std::string& first_added_preset) {
|
||||
auto get_first_added_material_preset = [this, app_config](const std::string& section_name, std::string& first_added_preset) {
|
||||
if (appconfig_new.has_section(section_name)) {
|
||||
// get first of new added preset names
|
||||
const std::map<std::string, std::string>& old_presets = app_config->has_section(section_name) ? app_config->get_section(section_name) : std::map<std::string, std::string>();
|
||||
first_added_preset = get_first_added_preset(old_presets, appconfig_new.get_section(section_name));
|
||||
app_config->set_section(section_name, appconfig_new.get_section(section_name));
|
||||
}
|
||||
};
|
||||
apply_section(AppConfig::SECTION_FILAMENTS, first_added_filament);
|
||||
apply_section(AppConfig::SECTION_MATERIALS, first_added_sla_material);
|
||||
get_first_added_material_preset(AppConfig::SECTION_FILAMENTS, first_added_filament);
|
||||
get_first_added_material_preset(AppConfig::SECTION_MATERIALS, first_added_sla_material);
|
||||
|
||||
// if unsaved changes was not cheched till this moment
|
||||
if (!check_unsaved_preset_changes) {
|
||||
if ((check_unsaved_preset_changes = !first_added_filament.empty() || !first_added_sla_material.empty())) {
|
||||
header = format_wxstr(_L("A new %1% was installed and it will be activated."), !first_added_filament.empty() ? _L("Filament") : _L("SLA material"));
|
||||
if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
bool is_filaments_changed = app_config->get_section(AppConfig::SECTION_FILAMENTS) != appconfig_new.get_section(AppConfig::SECTION_FILAMENTS);
|
||||
bool is_sla_materials_changed = app_config->get_section(AppConfig::SECTION_MATERIALS) != appconfig_new.get_section(AppConfig::SECTION_MATERIALS);
|
||||
if ((check_unsaved_preset_changes = is_filaments_changed || is_sla_materials_changed)) {
|
||||
header = format_wxstr(_L("Some %1% were uninstalled."), is_filaments_changed ? _L("Filaments") : _L("SLA materials"));
|
||||
if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply materials in app_config
|
||||
for (const std::string& section_name : {AppConfig::SECTION_FILAMENTS, AppConfig::SECTION_MATERIALS})
|
||||
app_config->set_section(section_name, appconfig_new.get_section(section_name));
|
||||
|
||||
app_config->set_vendors(appconfig_new);
|
||||
|
||||
|
@ -2624,10 +2675,16 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||
|
||||
page_mode->serialize_mode(app_config);
|
||||
|
||||
if (check_unsaved_preset_changes)
|
||||
preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem,
|
||||
{preferred_model, preferred_variant, first_added_filament, first_added_sla_material});
|
||||
|
||||
if (page_custom->custom_wanted()) {
|
||||
// if unsaved changes was not cheched till this moment
|
||||
if (!check_unsaved_preset_changes &&
|
||||
!wxGetApp().check_and_keep_current_preset_changes(caption, _L("Custom printer was installed and it will be activated."), act_btns, &apply_keeped_changes))
|
||||
return false;
|
||||
|
||||
page_firmware->apply_custom_config(*custom_config);
|
||||
page_bed->apply_custom_config(*custom_config);
|
||||
page_diams->apply_custom_config(*custom_config);
|
||||
|
@ -2851,8 +2908,13 @@ bool ConfigWizard::run(RunReason reason, StartPage start_page)
|
|||
p->set_start_page(start_page);
|
||||
|
||||
if (ShowModal() == wxID_OK) {
|
||||
if (! p->apply_config(app.app_config, app.preset_bundle, app.preset_updater))
|
||||
bool apply_keeped_changes = false;
|
||||
if (! p->apply_config(app.app_config, app.preset_bundle, app.preset_updater, apply_keeped_changes))
|
||||
return false;
|
||||
|
||||
if (apply_keeped_changes)
|
||||
app.apply_keeped_preset_modifications();
|
||||
|
||||
app.app_config->set_legacy_datadir(false);
|
||||
app.update_mode();
|
||||
app.obj_manipul()->update_ui_from_settings();
|
||||
|
|
|
@ -611,7 +611,7 @@ struct ConfigWizard::priv
|
|||
|
||||
bool on_bnt_finish();
|
||||
bool check_and_install_missing_materials(Technology technology, const std::string &only_for_model_id = std::string());
|
||||
bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
|
||||
bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes);
|
||||
// #ys_FIXME_alise
|
||||
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);
|
||||
#ifdef __linux__
|
||||
|
|
|
@ -1556,9 +1556,7 @@ void Control::OnMotion(wxMouseEvent& event)
|
|||
event.Skip();
|
||||
|
||||
// Set tooltips with information for each icon
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
if (GUI::wxGetApp().is_editor())
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
this->SetToolTip(get_tooltip(tick));
|
||||
|
||||
if (action) {
|
||||
|
@ -1821,7 +1819,8 @@ void Control::OnChar(wxKeyEvent& event)
|
|||
|
||||
void Control::OnRightDown(wxMouseEvent& event)
|
||||
{
|
||||
if (HasCapture()) return;
|
||||
if (HasCapture() || m_is_left_down)
|
||||
return;
|
||||
this->CaptureMouse();
|
||||
|
||||
const wxPoint pos = event.GetLogicalPosition(wxClientDC(this));
|
||||
|
@ -2099,7 +2098,7 @@ void Control::auto_color_change()
|
|||
|
||||
void Control::OnRightUp(wxMouseEvent& event)
|
||||
{
|
||||
if (!HasCapture())
|
||||
if (!HasCapture() || m_is_left_down)
|
||||
return;
|
||||
this->ReleaseMouse();
|
||||
m_is_right_down = m_is_one_layer = false;
|
||||
|
|
|
@ -323,15 +323,19 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR
|
|||
|
||||
c_editor->SetSelection(atoi(data.GetText().c_str()));
|
||||
|
||||
// to avoid event propagation to other sidebar items
|
||||
c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) {
|
||||
evt.StopPropagation();
|
||||
|
||||
#ifdef __linux__
|
||||
c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) {
|
||||
// to avoid event propagation to other sidebar items
|
||||
evt.StopPropagation();
|
||||
// FinishEditing grabs new selection and triggers config update. We better call
|
||||
// it explicitly, automatic update on KILL_FOCUS didn't work on Linux.
|
||||
this->FinishEditing();
|
||||
#endif
|
||||
});
|
||||
#else
|
||||
// to avoid event propagation to other sidebar items
|
||||
c_editor->Bind(wxEVT_COMBOBOX, [](wxCommandEvent& evt) { evt.StopPropagation(); });
|
||||
#endif
|
||||
|
||||
return c_editor;
|
||||
}
|
||||
|
|
|
@ -977,7 +977,7 @@ void Choice::BUILD() {
|
|||
propagate_value();
|
||||
} );
|
||||
|
||||
temp->Bind(wxEVT_TEXT_ENTER, [this, temp](wxEvent& e) {
|
||||
temp->Bind(wxEVT_TEXT_ENTER, [this](wxEvent& e) {
|
||||
EnterPressed enter(this);
|
||||
propagate_value();
|
||||
} );
|
||||
|
|
|
@ -294,7 +294,12 @@ void GCodeViewer::SequentialView::Marker::render() const
|
|||
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Tool position") + ":");
|
||||
ImGui::SameLine();
|
||||
char buf[1024];
|
||||
sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", m_world_position(0), m_world_position(1), m_world_position(2));
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
const Vec3f position = m_world_position + m_world_offset;
|
||||
sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z());
|
||||
#else
|
||||
sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", m_world_position.x(), m_world_position.y(), m_world_position.z());
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
imgui.text(std::string(buf));
|
||||
|
||||
// force extra frame to automatically update window size
|
||||
|
@ -311,7 +316,7 @@ void GCodeViewer::SequentialView::Marker::render() const
|
|||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector<size_t> &lines_ends)
|
||||
void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, std::vector<size_t> &&lines_ends)
|
||||
{
|
||||
assert(! m_file.is_open());
|
||||
if (m_file.is_open())
|
||||
|
@ -590,9 +595,94 @@ GCodeViewer::GCodeViewer()
|
|||
set_toolpath_move_type_visible(EMoveType::Extrude, true);
|
||||
#endif // !ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
m_extrusions.reset_role_visibility_flags();
|
||||
|
||||
// m_sequential_view.skip_invisible_moves = true;
|
||||
}
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
void GCodeViewer::init()
|
||||
{
|
||||
if (m_gl_data_initialized)
|
||||
return;
|
||||
|
||||
// initializes opengl data of TBuffers
|
||||
for (size_t i = 0; i < m_buffers.size(); ++i) {
|
||||
TBuffer& buffer = m_buffers[i];
|
||||
EMoveType type = buffer_type(i);
|
||||
switch (type)
|
||||
{
|
||||
default: { break; }
|
||||
case EMoveType::Tool_change:
|
||||
case EMoveType::Color_change:
|
||||
case EMoveType::Pause_Print:
|
||||
case EMoveType::Custom_GCode:
|
||||
case EMoveType::Retract:
|
||||
case EMoveType::Unretract:
|
||||
case EMoveType::Seam: {
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::InstancedModel;
|
||||
buffer.shader = "gouraud_light_instanced";
|
||||
buffer.model.model.init_from(diamond(16));
|
||||
buffer.model.color = option_color(type);
|
||||
buffer.model.instances.format = InstanceVBuffer::EFormat::InstancedModel;
|
||||
}
|
||||
else {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::BatchedModel;
|
||||
buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
|
||||
buffer.shader = "gouraud_light";
|
||||
|
||||
buffer.model.data = diamond(16);
|
||||
buffer.model.color = option_color(type);
|
||||
buffer.model.instances.format = InstanceVBuffer::EFormat::BatchedModel;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Model;
|
||||
buffer.shader = "gouraud_light_instanced";
|
||||
buffer.model.model.init_from(diamond(16));
|
||||
buffer.model.color = option_color(type);
|
||||
}
|
||||
else {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point;
|
||||
buffer.vertices.format = VBuffer::EFormat::Position;
|
||||
buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110";
|
||||
}
|
||||
break;
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
}
|
||||
case EMoveType::Wipe:
|
||||
case EMoveType::Extrude: {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Triangle;
|
||||
buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
|
||||
buffer.shader = "gouraud_light";
|
||||
break;
|
||||
}
|
||||
case EMoveType::Travel: {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line;
|
||||
buffer.vertices.format = VBuffer::EFormat::PositionNormal1;
|
||||
buffer.shader = "toolpaths_lines";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
set_toolpath_move_type_visible(EMoveType::Extrude, true);
|
||||
}
|
||||
|
||||
// initializes tool marker
|
||||
m_sequential_view.marker.init();
|
||||
|
||||
// initializes point sizes
|
||||
std::array<int, 2> point_sizes;
|
||||
::glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, point_sizes.data());
|
||||
m_detected_point_sizes = { static_cast<float>(point_sizes[0]), static_cast<float>(point_sizes[1]) };
|
||||
|
||||
m_gl_data_initialized = true;
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized)
|
||||
{
|
||||
// avoid processing if called with the same gcode_result
|
||||
|
@ -604,12 +694,12 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print&
|
|||
// release gpu memory, if used
|
||||
reset();
|
||||
|
||||
m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends);
|
||||
m_sequential_view.gcode_window.load_gcode(gcode_result.filename,
|
||||
// Stealing out lines_ends should be safe because this gcode_result is processed only once (see the 1st if in this function).
|
||||
std::move(const_cast<std::vector<size_t>&>(gcode_result.lines_ends)));
|
||||
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
if (wxGetApp().is_gcode_viewer())
|
||||
m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z;
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
load_toolpaths(gcode_result);
|
||||
|
||||
|
@ -773,7 +863,6 @@ void GCodeViewer::reset()
|
|||
m_extruder_ids = std::vector<unsigned char>();
|
||||
m_filament_diameters = std::vector<float>();
|
||||
m_filament_densities = std::vector<float>();
|
||||
m_extrusions.reset_role_visibility_flags();
|
||||
m_extrusions.reset_ranges();
|
||||
m_shells.volumes.clear();
|
||||
m_layers.reset();
|
||||
|
@ -785,9 +874,7 @@ void GCodeViewer::reset()
|
|||
m_layers_times[i] = std::vector<float>();
|
||||
}
|
||||
#endif // ENABLE_PREVIEW_LAYER_TIME
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
m_custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
m_sequential_view.gcode_window.reset();
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
m_statistics.reset_all();
|
||||
|
@ -796,6 +883,7 @@ void GCodeViewer::reset()
|
|||
|
||||
void GCodeViewer::render()
|
||||
{
|
||||
#if !ENABLE_SEAMS_USING_MODELS
|
||||
auto init_gl_data = [this]() {
|
||||
// initializes opengl data of TBuffers
|
||||
for (size_t i = 0; i < m_buffers.size(); ++i) {
|
||||
|
@ -811,26 +899,6 @@ void GCodeViewer::render()
|
|||
case EMoveType::Retract:
|
||||
case EMoveType::Unretract:
|
||||
case EMoveType::Seam: {
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::InstancedModel;
|
||||
buffer.shader = "gouraud_light_instanced";
|
||||
buffer.model.model.init_from(diamond(16));
|
||||
buffer.model.color = option_color(type);
|
||||
buffer.model.instances.format = InstanceVBuffer::EFormat::InstancedModel;
|
||||
}
|
||||
else {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::BatchedModel;
|
||||
buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
|
||||
buffer.shader = "gouraud_light";
|
||||
|
||||
buffer.model.data = diamond(16);
|
||||
buffer.model.color = option_color(type);
|
||||
buffer.model.instances.format = InstanceVBuffer::EFormat::BatchedModel;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Model;
|
||||
buffer.shader = "gouraud_light_instanced";
|
||||
|
@ -843,36 +911,17 @@ void GCodeViewer::render()
|
|||
buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110";
|
||||
}
|
||||
break;
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#else
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point;
|
||||
buffer.vertices.format = VBuffer::EFormat::Position;
|
||||
buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110";
|
||||
break;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
}
|
||||
case EMoveType::Wipe:
|
||||
case EMoveType::Extrude: {
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Triangle;
|
||||
buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
buffer.shader = "gouraud_light";
|
||||
break;
|
||||
}
|
||||
case EMoveType::Travel: {
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line;
|
||||
buffer.vertices.format = VBuffer::EFormat::PositionNormal1;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
buffer.shader = "toolpaths_lines";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
set_toolpath_move_type_visible(EMoveType::Extrude, true);
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
}
|
||||
|
||||
// initializes tool marker
|
||||
|
@ -884,6 +933,7 @@ void GCodeViewer::render()
|
|||
m_detected_point_sizes = { static_cast<float>(point_sizes[0]), static_cast<float>(point_sizes[1]) };
|
||||
m_gl_data_initialized = true;
|
||||
};
|
||||
#endif // !ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
m_statistics.reset_opengl();
|
||||
|
@ -892,10 +942,12 @@ void GCodeViewer::render()
|
|||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
||||
#if !ENABLE_SEAMS_USING_MODELS
|
||||
// OpenGL data must be initialized after the glContext has been created.
|
||||
// This is ensured when this method is called by GLCanvas3D::_render_gcode().
|
||||
if (!m_gl_data_initialized)
|
||||
init_gl_data();
|
||||
#endif // !ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
if (m_roles.empty())
|
||||
return;
|
||||
|
@ -907,6 +959,9 @@ void GCodeViewer::render()
|
|||
render_legend(legend_height);
|
||||
if (m_sequential_view.current.last != m_sequential_view.endpoints.last) {
|
||||
m_sequential_view.marker.set_world_position(m_sequential_view.current_position);
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
m_sequential_view.marker.set_world_offset(m_sequential_view.current_offset);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
m_sequential_view.render(legend_height);
|
||||
}
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
@ -1545,10 +1600,18 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
m_max_bounding_box = m_paths_bounding_box;
|
||||
m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size()[2] * Vec3d::UnitZ());
|
||||
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
m_sequential_view.gcode_ids.clear();
|
||||
for (size_t i = 0; i < gcode_result.moves.size(); ++i) {
|
||||
const GCodeProcessor::MoveVertex& move = gcode_result.moves[i];
|
||||
if (move.type != EMoveType::Seam)
|
||||
m_sequential_view.gcode_ids.push_back(move.gcode_id);
|
||||
}
|
||||
#else
|
||||
for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) {
|
||||
m_sequential_view.gcode_ids.push_back(move.gcode_id);
|
||||
}
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
std::vector<MultiVertexBuffer> vertices(m_buffers.size());
|
||||
std::vector<MultiIndexBuffer> indices(m_buffers.size());
|
||||
|
@ -1556,11 +1619,27 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
std::vector<InstanceBuffer> instances(m_buffers.size());
|
||||
std::vector<InstanceIdBuffer> instances_ids(m_buffers.size());
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
std::vector<InstancesOffsets> instances_offsets(m_buffers.size());
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
std::vector<float> options_zs;
|
||||
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
size_t seams_count = 0;
|
||||
std::vector<size_t> seams_ids;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
// toolpaths data -> extract vertices from result
|
||||
for (size_t i = 0; i < m_moves_count; ++i) {
|
||||
const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
if (curr.type == EMoveType::Seam) {
|
||||
++seams_count;
|
||||
seams_ids.push_back(i);
|
||||
}
|
||||
|
||||
size_t move_id = i - seams_count;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
// skip first vertex
|
||||
if (i == 0)
|
||||
|
@ -1583,6 +1662,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
#if ENABLE_SEAMS_USING_MODELS
|
||||
InstanceBuffer& inst_buffer = instances[id];
|
||||
InstanceIdBuffer& inst_id_buffer = instances_ids[id];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
InstancesOffsets& inst_offsets = instances_offsets[id];
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
// ensure there is at least one vertex buffer
|
||||
|
@ -1601,7 +1683,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) {
|
||||
Path& last_path = t_buffer.paths.back();
|
||||
if (prev.type == curr.type && last_path.matches(curr))
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
last_path.add_sub_path(prev, static_cast<unsigned int>(v_multibuffer.size()) - 1, 0, move_id - 1);
|
||||
#else
|
||||
last_path.add_sub_path(prev, static_cast<unsigned int>(v_multibuffer.size()) - 1, 0, i - 1);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1611,12 +1697,21 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
{
|
||||
case TBuffer::ERenderPrimitiveType::Point: { add_vertices_as_point(curr, v_buffer); break; }
|
||||
case TBuffer::ERenderPrimitiveType::Line: { add_vertices_as_line(prev, curr, v_buffer); break; }
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, move_id); break; }
|
||||
#else
|
||||
case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, i); break; }
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
case TBuffer::ERenderPrimitiveType::InstancedModel:
|
||||
{
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
add_model_instance(curr, inst_buffer, inst_id_buffer, move_id);
|
||||
inst_offsets.push_back(prev.position - curr.position);
|
||||
#else
|
||||
add_model_instance(curr, inst_buffer, inst_id_buffer, i);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
++m_statistics.instances_count;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
@ -1624,7 +1719,12 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
}
|
||||
case TBuffer::ERenderPrimitiveType::BatchedModel:
|
||||
{
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
add_vertices_as_model_batch(curr, t_buffer.model.data, v_buffer, inst_buffer, inst_id_buffer, move_id);
|
||||
inst_offsets.push_back(prev.position - curr.position);
|
||||
#else
|
||||
add_vertices_as_model_batch(curr, t_buffer.model.data, v_buffer, inst_buffer, inst_id_buffer, i);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
++m_statistics.batched_count;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
@ -1633,7 +1733,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
#else
|
||||
case TBuffer::ERenderPrimitiveType::Model:
|
||||
{
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
add_model_instance(curr, inst_buffer, inst_id_buffer, move_id);
|
||||
#else
|
||||
add_model_instance(curr, inst_buffer, inst_id_buffer, i);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
++m_statistics.instances_count;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
@ -1652,7 +1756,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
}
|
||||
|
||||
// smooth toolpaths corners for the given TBuffer using triangles
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
auto smooth_triangle_toolpaths_corners = [&gcode_result, &seams_ids](const TBuffer& t_buffer, MultiVertexBuffer& v_multibuffer) {
|
||||
#else
|
||||
auto smooth_triangle_toolpaths_corners = [&gcode_result](const TBuffer& t_buffer, MultiVertexBuffer& v_multibuffer) {
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
auto extract_position_at = [](const VertexBuffer& vertices, size_t offset) {
|
||||
return Vec3f(vertices[offset + 0], vertices[offset + 1], vertices[offset + 2]);
|
||||
};
|
||||
|
@ -1726,6 +1834,16 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
}
|
||||
};
|
||||
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
auto extract_move_id = [&seams_ids](size_t id) {
|
||||
for (int i = seams_ids.size() - 1; i >= 0; --i) {
|
||||
if (seams_ids[i] < id + i + 1)
|
||||
return id + (size_t)i + 1;
|
||||
}
|
||||
return id;
|
||||
};
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
size_t vertex_size_floats = t_buffer.vertices.vertex_size_floats();
|
||||
for (const Path& path : t_buffer.paths) {
|
||||
// the two segments of the path sharing the current vertex may belong
|
||||
|
@ -1736,9 +1854,16 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
const float half_width = 0.5f * path.width;
|
||||
for (size_t j = 1; j < path_vertices_count - 1; ++j) {
|
||||
size_t curr_s_id = path.sub_paths.front().first.s_id + j;
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
size_t move_id = extract_move_id(curr_s_id);
|
||||
const Vec3f& prev = gcode_result.moves[move_id - 1].position;
|
||||
const Vec3f& curr = gcode_result.moves[move_id].position;
|
||||
const Vec3f& next = gcode_result.moves[move_id + 1].position;
|
||||
#else
|
||||
const Vec3f& prev = gcode_result.moves[curr_s_id - 1].position;
|
||||
const Vec3f& curr = gcode_result.moves[curr_s_id].position;
|
||||
const Vec3f& next = gcode_result.moves[curr_s_id + 1].position;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
// select the subpaths which contains the previous/next segments
|
||||
if (!path.sub_paths[prev_sub_path_id].contains(curr_s_id))
|
||||
|
@ -1805,6 +1930,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
}
|
||||
}
|
||||
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
// dismiss, no more needed
|
||||
std::vector<size_t>().swap(seams_ids);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
for (MultiVertexBuffer& v_multibuffer : vertices) {
|
||||
for (VertexBuffer& v_buffer : v_multibuffer) {
|
||||
v_buffer.shrink_to_fit();
|
||||
|
@ -1829,6 +1959,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
if (!inst_buffer.empty()) {
|
||||
t_buffer.model.instances.buffer = inst_buffer;
|
||||
t_buffer.model.instances.s_ids = instances_ids[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
t_buffer.model.instances.offsets = instances_offsets[i];
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -1837,6 +1970,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
if (!inst_buffer.empty()) {
|
||||
t_buffer.model.instances.buffer = inst_buffer;
|
||||
t_buffer.model.instances.s_ids = instances_ids[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
t_buffer.model.instances.offsets = instances_offsets[i];
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
@ -1845,6 +1981,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
if (!inst_buffer.empty()) {
|
||||
t_buffer.model.instances.buffer = inst_buffer;
|
||||
t_buffer.model.instances.s_ids = instances_ids[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
t_buffer.model.instances.offsets = instances_offsets[i];
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -1905,8 +2044,18 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
using VboIndexList = std::vector<unsigned int>;
|
||||
std::vector<VboIndexList> vbo_indices(m_buffers.size());
|
||||
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
seams_count = 0;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
for (size_t i = 0; i < m_moves_count; ++i) {
|
||||
const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
if (curr.type == EMoveType::Seam)
|
||||
++seams_count;
|
||||
|
||||
size_t move_id = i - seams_count;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
// skip first vertex
|
||||
if (i == 0)
|
||||
|
@ -1957,7 +2106,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
Path& last_path = t_buffer.paths.back();
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, move_id - 1);
|
||||
#else
|
||||
last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i - 1);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1982,7 +2135,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
Path& last_path = t_buffer.paths.back();
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, move_id - 1);
|
||||
#else
|
||||
last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i - 1);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1991,17 +2148,29 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
switch (t_buffer.render_primitive_type)
|
||||
{
|
||||
case TBuffer::ERenderPrimitiveType::Point: {
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
add_indices_as_point(curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id);
|
||||
#else
|
||||
add_indices_as_point(curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, i);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
curr_vertex_buffer.second += t_buffer.max_vertices_per_segment();
|
||||
break;
|
||||
}
|
||||
case TBuffer::ERenderPrimitiveType::Line: {
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id);
|
||||
#else
|
||||
add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, i);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
curr_vertex_buffer.second += t_buffer.max_vertices_per_segment();
|
||||
break;
|
||||
}
|
||||
case TBuffer::ERenderPrimitiveType::Triangle: {
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id);
|
||||
#else
|
||||
add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, i);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
break;
|
||||
}
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
|
@ -2096,16 +2265,34 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
|
||||
// layers zs / roles / extruder ids -> extract from result
|
||||
size_t last_travel_s_id = 0;
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
seams_count = 0;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
for (size_t i = 0; i < m_moves_count; ++i) {
|
||||
const GCodeProcessor::MoveVertex& move = gcode_result.moves[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
if (move.type == EMoveType::Seam)
|
||||
++seams_count;
|
||||
|
||||
size_t move_id = i - seams_count;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
if (move.type == EMoveType::Extrude) {
|
||||
// layers zs
|
||||
const double* const last_z = m_layers.empty() ? nullptr : &m_layers.get_zs().back();
|
||||
const double z = static_cast<double>(move.position.z());
|
||||
if (last_z == nullptr || z < *last_z - EPSILON || *last_z + EPSILON < z)
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
m_layers.append(z, { last_travel_s_id, move_id });
|
||||
#else
|
||||
m_layers.append(z, { last_travel_s_id, i });
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
else
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
m_layers.get_ranges().back().last = move_id;
|
||||
#else
|
||||
m_layers.get_ranges().back().last = i;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
// extruder ids
|
||||
m_extruder_ids.emplace_back(move.extruder_id);
|
||||
// roles
|
||||
|
@ -2113,10 +2300,17 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
m_roles.emplace_back(move.extrusion_role);
|
||||
}
|
||||
else if (move.type == EMoveType::Travel) {
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
if (move_id - last_travel_s_id > 1 && !m_layers.empty())
|
||||
m_layers.get_ranges().back().last = move_id;
|
||||
|
||||
last_travel_s_id = move_id;
|
||||
#else
|
||||
if (i - last_travel_s_id > 1 && !m_layers.empty())
|
||||
m_layers.get_ranges().back().last = i;
|
||||
|
||||
last_travel_s_id = i;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2407,6 +2601,9 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
|
|||
sequential_view->current_position.x() = buffer.model.instances.buffer[offset + 0];
|
||||
sequential_view->current_position.y() = buffer.model.instances.buffer[offset + 1];
|
||||
sequential_view->current_position.z() = buffer.model.instances.buffer[offset + 2];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
sequential_view->current_offset = buffer.model.instances.offsets[i];
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
found = true;
|
||||
break;
|
||||
|
@ -2446,6 +2643,10 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
|
|||
glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast<GLintptr>(index * buffer.vertices.vertex_size_bytes()), static_cast<GLsizeiptr>(3 * sizeof(float)), static_cast<void*>(sequential_view->current_position.data())));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
sequential_view->current_offset = Vec3f::Zero();
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
@ -3115,7 +3316,11 @@ void GCodeViewer::render_legend(float& legend_height)
|
|||
|
||||
bool imperial_units = wxGetApp().app_config->get("use_inches") == "1";
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
auto append_item = [icon_size, percent_bar_size, &imgui, imperial_units](EItemType type, const Color& color, const std::string& label,
|
||||
#else
|
||||
auto append_item = [this, icon_size, percent_bar_size, &imgui, imperial_units](EItemType type, const Color& color, const std::string& label,
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
bool visible = true, const std::string& time = "", float percent = 0.0f, float max_percent = 0.0f, const std::array<float, 4>& offsets = { 0.0f, 0.0f, 0.0f, 0.0f },
|
||||
double used_filament_m = 0.0, double used_filament_g = 0.0,
|
||||
std::function<void()> callback = nullptr) {
|
||||
|
@ -3514,12 +3719,9 @@ void GCodeViewer::render_legend(float& legend_height)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case EViewType::ColorPrint: {
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
case EViewType::ColorPrint:
|
||||
{
|
||||
const std::vector<CustomGCode::Item>& custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes : m_custom_gcode_per_print_z;
|
||||
#else
|
||||
const std::vector<CustomGCode::Item>& custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
size_t total_items = 1;
|
||||
for (unsigned char i : m_extruder_ids) {
|
||||
total_items += color_print_ranges(i, custom_gcode_per_print_z).size();
|
||||
|
@ -3614,11 +3816,7 @@ void GCodeViewer::render_legend(float& legend_height)
|
|||
auto generate_partial_times = [this, get_used_filament_from_volume](const TimesList& times, const std::vector<double>& used_filaments) {
|
||||
PartialTimes items;
|
||||
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes : m_custom_gcode_per_print_z;
|
||||
#else
|
||||
std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
int extruders_count = wxGetApp().extruders_edited_cnt();
|
||||
std::vector<Color> last_color(extruders_count);
|
||||
for (int i = 0; i < extruders_count; ++i) {
|
||||
|
|
|
@ -31,6 +31,9 @@ class GCodeViewer
|
|||
using InstanceBuffer = std::vector<float>;
|
||||
using InstanceIdBuffer = std::vector<size_t>;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
using InstancesOffsets = std::vector<Vec3f>;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
static const std::vector<Color> Extrusion_Role_Colors;
|
||||
static const std::vector<Color> Options_Colors;
|
||||
|
@ -151,6 +154,10 @@ class GCodeViewer
|
|||
InstanceBuffer buffer;
|
||||
// indices of the moves for all instances
|
||||
std::vector<size_t> s_ids;
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
// position offsets, used to show the correct value of the tool position
|
||||
InstancesOffsets offsets;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
Ranges render_ranges;
|
||||
|
||||
size_t data_size_bytes() const { return s_ids.size() * instance_size_bytes(); }
|
||||
|
@ -686,6 +693,12 @@ public:
|
|||
GLModel m_model;
|
||||
Vec3f m_world_position;
|
||||
Transform3f m_world_transform;
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
// for seams, the position of the marker is on the last endpoint of the toolpath containing it
|
||||
// the offset is used to show the correct value of tool position in the "ToolPosition" window
|
||||
// see implementation of render() method
|
||||
Vec3f m_world_offset;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
float m_z_offset{ 0.5f };
|
||||
bool m_visible{ true };
|
||||
|
||||
|
@ -695,6 +708,9 @@ public:
|
|||
const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); }
|
||||
|
||||
void set_world_position(const Vec3f& position);
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
void set_world_offset(const Vec3f& offset) { m_world_offset = offset; }
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
bool is_visible() const { return m_visible; }
|
||||
void set_visible(bool visible) { m_visible = visible; }
|
||||
|
@ -723,7 +739,7 @@ public:
|
|||
public:
|
||||
GCodeWindow() = default;
|
||||
~GCodeWindow() { stop_mapping_file(); }
|
||||
void load_gcode(const std::string& filename, const std::vector<size_t> &lines_ends);
|
||||
void load_gcode(const std::string& filename, std::vector<size_t> &&lines_ends);
|
||||
void reset() {
|
||||
stop_mapping_file();
|
||||
m_lines_ends.clear();
|
||||
|
@ -752,6 +768,9 @@ public:
|
|||
Endpoints global;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
Vec3f current_position{ Vec3f::Zero() };
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
Vec3f current_offset{ Vec3f::Zero() };
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
Marker marker;
|
||||
GCodeWindow gcode_window;
|
||||
std::vector<unsigned int> gcode_ids;
|
||||
|
@ -811,14 +830,16 @@ private:
|
|||
std::array<std::vector<float>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> m_layers_times;
|
||||
#endif // ENABLE_PREVIEW_LAYER_TIME
|
||||
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
std::vector<CustomGCode::Item> m_custom_gcode_per_print_z;
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
public:
|
||||
GCodeViewer();
|
||||
~GCodeViewer() { reset(); }
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
void init();
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
// extract rendering data from the given parameters
|
||||
void load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized);
|
||||
// recalculate ranges in dependence of what is visible and sets tool/print colors
|
||||
|
@ -862,10 +883,8 @@ public:
|
|||
|
||||
void toggle_gcode_window_visibility() { m_sequential_view.gcode_window.toggle_visibility(); }
|
||||
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
std::vector<CustomGCode::Item>& get_custom_gcode_per_print_z() { return m_custom_gcode_per_print_z; }
|
||||
size_t get_extruders_count() { return m_extruders_count; }
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
private:
|
||||
void load_toolpaths(const GCodeProcessor::Result& gcode_result);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "slic3r/GUI/3DBed.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
|
@ -1399,6 +1400,11 @@ void GLCanvas3D::render()
|
|||
if (!is_initialized() && !init())
|
||||
return;
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
if (!m_main_toolbar.is_enabled())
|
||||
m_gcode_viewer.init();
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
if (wxGetApp().plater()->get_bed().get_shape().empty()) {
|
||||
// this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_UPDATE_BED_SHAPE));
|
||||
|
@ -1450,19 +1456,13 @@ void GLCanvas3D::render()
|
|||
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
||||
_render_background();
|
||||
|
||||
#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque);
|
||||
#else
|
||||
_render_objects();
|
||||
#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
if (!m_main_toolbar.is_enabled())
|
||||
_render_gcode();
|
||||
_render_sla_slices();
|
||||
_render_selection();
|
||||
_render_bed(!camera.is_looking_downward(), true);
|
||||
#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
_render_objects(GLVolumeCollection::ERenderType::Transparent);
|
||||
#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
|
||||
_render_sequential_clearance();
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
|
@ -2092,7 +2092,7 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume&
|
|||
vol_old.finalize_geometry(gl_initialized);
|
||||
}
|
||||
|
||||
void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result)
|
||||
void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors)
|
||||
{
|
||||
m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized);
|
||||
|
||||
|
@ -2100,10 +2100,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result)
|
|||
m_gcode_viewer.update_shells_color_by_extruder(m_config);
|
||||
_set_warning_notification_if_needed(EWarning::ToolpathOutside);
|
||||
}
|
||||
}
|
||||
|
||||
void GLCanvas3D::refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors)
|
||||
{
|
||||
m_gcode_viewer.refresh(gcode_result, str_tool_colors);
|
||||
set_as_dirty();
|
||||
request_extra_frame();
|
||||
|
@ -2935,7 +2932,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
return;
|
||||
}
|
||||
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
volume->force_sinking_contours = false;
|
||||
}
|
||||
|
@ -2947,7 +2943,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
}
|
||||
m_dirty = true;
|
||||
};
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
if (m_gizmos.on_mouse(evt)) {
|
||||
if (wxWindow::FindFocus() != m_canvas)
|
||||
|
@ -2973,7 +2968,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
default: { break; }
|
||||
}
|
||||
}
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
else if (evt.Dragging()) {
|
||||
switch (m_gizmos.get_current_type())
|
||||
{
|
||||
|
@ -2987,7 +2981,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
default: { break; }
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -3297,10 +3290,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
else
|
||||
evt.Skip();
|
||||
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
if (m_moving)
|
||||
show_sinking_contours();
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
#ifdef __WXMSW__
|
||||
if (on_enter_workaround)
|
||||
|
@ -4442,12 +4433,11 @@ bool GLCanvas3D::_init_main_toolbar()
|
|||
arrow_data.top = 0;
|
||||
arrow_data.right = 0;
|
||||
arrow_data.bottom = 0;
|
||||
|
||||
if (!m_main_toolbar.init_arrow(arrow_data))
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Main toolbar failed to load arrow texture.";
|
||||
}
|
||||
|
||||
// m_gizmos is created at constructor, thus we can init arrow here.
|
||||
if (!m_gizmos.init_arrow(arrow_data))
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Gizmos manager failed to load arrow texture.";
|
||||
|
@ -4658,6 +4648,18 @@ bool GLCanvas3D::_init_undoredo_toolbar()
|
|||
return true;
|
||||
}
|
||||
|
||||
// init arrow
|
||||
BackgroundTexture::Metadata arrow_data;
|
||||
arrow_data.filename = "toolbar_arrow.svg";
|
||||
arrow_data.left = 0;
|
||||
arrow_data.top = 0;
|
||||
arrow_data.right = 0;
|
||||
arrow_data.bottom = 0;
|
||||
if (!m_undoredo_toolbar.init_arrow(arrow_data))
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Undo/Redo toolbar failed to load arrow texture.";
|
||||
}
|
||||
|
||||
// m_undoredo_toolbar.set_layout_type(GLToolbar::Layout::Vertical);
|
||||
m_undoredo_toolbar.set_layout_type(GLToolbar::Layout::Horizontal);
|
||||
m_undoredo_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Left);
|
||||
|
@ -5074,12 +5076,7 @@ void GLCanvas3D::_render_bed_for_picking(bool bottom)
|
|||
wxGetApp().plater()->get_bed().render_for_picking(*this, bottom, scale_factor);
|
||||
}
|
||||
|
||||
|
||||
#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
|
||||
#else
|
||||
void GLCanvas3D::_render_objects()
|
||||
#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
{
|
||||
if (m_volumes.empty())
|
||||
return;
|
||||
|
@ -5111,20 +5108,14 @@ void GLCanvas3D::_render_objects()
|
|||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
|
||||
#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
case GLVolumeCollection::ERenderType::Opaque:
|
||||
{
|
||||
#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) {
|
||||
int object_id = m_layers_editing.last_object_id;
|
||||
#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
m_volumes.render(type, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) {
|
||||
#else
|
||||
m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) {
|
||||
#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
// Which volume to paint without the layer height profile shader?
|
||||
return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
|
||||
});
|
||||
|
@ -5133,11 +5124,7 @@ void GLCanvas3D::_render_objects()
|
|||
}
|
||||
else {
|
||||
// do not cull backfaces to show broken geometry, if any
|
||||
#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
m_volumes.render(type, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this](const GLVolume& volume) {
|
||||
#else
|
||||
m_volumes.render(GLVolumeCollection::ERenderType::Opaque, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this](const GLVolume& volume) {
|
||||
#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
|
||||
});
|
||||
}
|
||||
|
@ -5156,7 +5143,6 @@ void GLCanvas3D::_render_objects()
|
|||
shader->start_using();
|
||||
}
|
||||
}
|
||||
#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
break;
|
||||
}
|
||||
case GLVolumeCollection::ERenderType::Transparent:
|
||||
|
@ -5165,9 +5151,6 @@ void GLCanvas3D::_render_objects()
|
|||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
m_volumes.render(GLVolumeCollection::ERenderType::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix());
|
||||
#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
shader->stop_using();
|
||||
}
|
||||
|
||||
|
@ -5410,6 +5393,10 @@ void GLCanvas3D::_render_undoredo_toolbar()
|
|||
|
||||
m_undoredo_toolbar.set_position(top, left);
|
||||
m_undoredo_toolbar.render(*this);
|
||||
if (m_toolbar_highlighter.m_render_arrow)
|
||||
{
|
||||
m_undoredo_toolbar.render_arrow(*this, m_toolbar_highlighter.m_toolbar_item);
|
||||
}
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_collapse_toolbar() const
|
||||
|
@ -5676,11 +5663,8 @@ void GLCanvas3D::_update_volumes_hover_state()
|
|||
}
|
||||
}
|
||||
}
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
else if (volume.selected)
|
||||
volume.hover = GLVolume::HS_Hover;
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6449,7 +6433,7 @@ void GLCanvas3D::_update_selection_from_hover()
|
|||
|
||||
// the selection is going to be modified (Add)
|
||||
if (!contains_all) {
|
||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Add from rectangle")));
|
||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Add from rectangle")), UndoRedo::SnapshotType::Selection);
|
||||
selection_changed = true;
|
||||
}
|
||||
}
|
||||
|
@ -6464,7 +6448,7 @@ void GLCanvas3D::_update_selection_from_hover()
|
|||
|
||||
// the selection is going to be modified (Remove)
|
||||
if (contains_any) {
|
||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Remove from rectangle")));
|
||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Remove from rectangle")), UndoRedo::SnapshotType::Selection);
|
||||
selection_changed = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -617,6 +617,9 @@ public:
|
|||
void reset_volumes();
|
||||
ModelInstanceEPrintVolumeState check_volumes_outside_state() const;
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
void init_gcode_viewer() { m_gcode_viewer.init(); }
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
void reset_gcode_toolpaths() { m_gcode_viewer.reset(); }
|
||||
const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); }
|
||||
void update_gcode_sequential_view_current(unsigned int first, unsigned int last) { m_gcode_viewer.update_sequential_view_current(first, last); }
|
||||
|
@ -710,10 +713,8 @@ public:
|
|||
void set_toolpath_view_type(GCodeViewer::EViewType type);
|
||||
void set_volumes_z_range(const std::array<double, 2>& range);
|
||||
void set_toolpaths_z_range(const std::array<unsigned int, 2>& range);
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
std::vector<CustomGCode::Item>& get_custom_gcode_per_print_z() { return m_gcode_viewer.get_custom_gcode_per_print_z(); }
|
||||
size_t get_gcode_extruders_count() { return m_gcode_viewer.get_extruders_count(); }
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
std::vector<int> load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs);
|
||||
std::vector<int> load_object(const Model& model, int obj_idx);
|
||||
|
@ -722,8 +723,7 @@ public:
|
|||
|
||||
void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false);
|
||||
|
||||
void load_gcode_preview(const GCodeProcessor::Result& gcode_result);
|
||||
void refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
|
||||
void load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
|
||||
void refresh_gcode_preview_render_paths();
|
||||
void set_gcode_view_preview_type(GCodeViewer::EViewType type) { return m_gcode_viewer.set_view_type(type); }
|
||||
GCodeViewer::EViewType get_gcode_view_preview_type() const { return m_gcode_viewer.get_view_type(); }
|
||||
|
@ -896,11 +896,7 @@ private:
|
|||
void _render_background() const;
|
||||
void _render_bed(bool bottom, bool show_axes);
|
||||
void _render_bed_for_picking(bool bottom);
|
||||
#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
void _render_objects(GLVolumeCollection::ERenderType type);
|
||||
#else
|
||||
void _render_objects();
|
||||
#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
|
||||
void _render_gcode();
|
||||
void _render_selection() const;
|
||||
void _render_sequential_clearance();
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
@ -78,7 +76,7 @@ void GLModel::init_from(const InitializationData& data)
|
|||
}
|
||||
}
|
||||
|
||||
void GLModel::init_from(const TriangleMesh& mesh)
|
||||
void GLModel::init_from(const indexed_triangle_set& its, const BoundingBoxf3 &bbox)
|
||||
{
|
||||
if (!m_render_data.empty()) // call reset() if you want to reuse this model
|
||||
return;
|
||||
|
@ -86,31 +84,36 @@ void GLModel::init_from(const TriangleMesh& mesh)
|
|||
RenderData data;
|
||||
data.type = PrimitiveType::Triangles;
|
||||
|
||||
std::vector<float> vertices = std::vector<float>(18 * mesh.stl.stats.number_of_facets);
|
||||
std::vector<unsigned int> indices = std::vector<unsigned int>(3 * mesh.stl.stats.number_of_facets);
|
||||
std::vector<float> vertices = std::vector<float>(18 * its.indices.size());
|
||||
std::vector<unsigned int> indices = std::vector<unsigned int>(3 * its.indices.size());
|
||||
|
||||
unsigned int vertices_count = 0;
|
||||
for (uint32_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
|
||||
const stl_facet& facet = mesh.stl.facet_start[i];
|
||||
for (size_t j = 0; j < 3; ++j) {
|
||||
for (uint32_t i = 0; i < its.indices.size(); ++i) {
|
||||
stl_triangle_vertex_indices face = its.indices[i];
|
||||
stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] };
|
||||
stl_vertex n = face_normal_normalized(vertex);
|
||||
for (size_t j = 0; j < 3; ++ j) {
|
||||
size_t offset = i * 18 + j * 6;
|
||||
::memcpy(static_cast<void*>(&vertices[offset]), static_cast<const void*>(facet.vertex[j].data()), 3 * sizeof(float));
|
||||
::memcpy(static_cast<void*>(&vertices[3 + offset]), static_cast<const void*>(facet.normal.data()), 3 * sizeof(float));
|
||||
::memcpy(static_cast<void*>(&vertices[offset]), static_cast<const void*>(vertex[j].data()), 3 * sizeof(float));
|
||||
::memcpy(static_cast<void*>(&vertices[3 + offset]), static_cast<const void*>(n.data()), 3 * sizeof(float));
|
||||
}
|
||||
for (size_t j = 0; j < 3; ++j) {
|
||||
for (size_t j = 0; j < 3; ++j)
|
||||
indices[i * 3 + j] = vertices_count + j;
|
||||
}
|
||||
vertices_count += 3;
|
||||
}
|
||||
|
||||
data.indices_count = static_cast<unsigned int>(indices.size());
|
||||
m_bounding_box = mesh.bounding_box();
|
||||
m_bounding_box = bbox;
|
||||
|
||||
send_to_gpu(data, vertices, indices);
|
||||
m_render_data.emplace_back(data);
|
||||
}
|
||||
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
void GLModel::init_from(const indexed_triangle_set& its)
|
||||
{
|
||||
this->init_from(its, bounding_box(its));
|
||||
}
|
||||
|
||||
void GLModel::init_from(const Polygons& polygons, float z)
|
||||
{
|
||||
auto append_polygon = [](const Polygon& polygon, float z, GUI::GLModel::InitializationData& data) {
|
||||
|
@ -137,7 +140,6 @@ void GLModel::init_from(const Polygons& polygons, float z)
|
|||
}
|
||||
init_from(init_data);
|
||||
}
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
bool GLModel::init_from_file(const std::string& filename)
|
||||
{
|
||||
|
@ -157,7 +159,8 @@ bool GLModel::init_from_file(const std::string& filename)
|
|||
return false;
|
||||
}
|
||||
|
||||
init_from(model.mesh());
|
||||
TriangleMesh mesh = model.mesh();
|
||||
init_from(mesh.its, mesh.bounding_box());
|
||||
|
||||
m_filename = filename;
|
||||
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
struct indexed_triangle_set;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class TriangleMesh;
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
class Polygon;
|
||||
using Polygons = std::vector<Polygon>;
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
|
||||
namespace GUI {
|
||||
|
||||
|
@ -70,10 +70,9 @@ namespace GUI {
|
|||
virtual ~GLModel() { reset(); }
|
||||
|
||||
void init_from(const InitializationData& data);
|
||||
void init_from(const TriangleMesh& mesh);
|
||||
#if ENABLE_SINKING_CONTOURS
|
||||
void init_from(const indexed_triangle_set& its, const BoundingBoxf3& bbox);
|
||||
void init_from(const indexed_triangle_set& its);
|
||||
void init_from(const Polygons& polygons, float z);
|
||||
#endif // ENABLE_SINKING_CONTOURS
|
||||
bool init_from_file(const std::string& filename);
|
||||
|
||||
// if entity_id == -1 set the color of all entities
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue