Merge branch 'master' into fs_emboss
# Conflicts: # src/libslic3r/Technologies.hpp # src/slic3r/GUI/GLCanvas3D.cpp # src/slic3r/GUI/Gizmos/GLGizmosManager.cpp # src/slic3r/GUI/Plater.cpp # src/slic3r/GUI/Selection.hpp
This commit is contained in:
commit
c77f8373bd
52 changed files with 5938 additions and 233 deletions
deps/wxWidgets
resources
src
imgui
libslic3r
CMakeLists.txtGCode.cppMeasure.cppMeasure.hppMeasureUtils.hppModel.cppPrintApply.cppSurfaceMesh.hppTechnologies.hpp
slic3r
CMakeLists.txt
GUI
3DBed.cppGLCanvas3D.cppGLCanvas3D.hppGLModel.cppGLModel.hppGUI_ObjectList.cppGUI_Utils.hpp
Gizmos
GLGizmoCut.cppGLGizmoCut.hppGLGizmoFlatten.hppGLGizmoHollow.cppGLGizmoMeasure.cppGLGizmoMeasure.hppGLGizmoSlaSupports.cppGLGizmosCommon.cppGLGizmosCommon.hppGLGizmosManager.cppGLGizmosManager.hpp
ImGuiWrapper.cppImGuiWrapper.hppMainFrame.cppObjectDataViewModel.cppPlater.cppPlater.hppPresetComboBoxes.cppSceneRaycaster.cppSceneRaycaster.hppSearch.cppSelection.hppTab.cppTab.hppformat.hpptests/libslic3r
1
deps/wxWidgets/wxWidgets.cmake
vendored
1
deps/wxWidgets/wxWidgets.cmake
vendored
|
@ -38,6 +38,7 @@ prusaslicer_add_cmake_project(wxWidgets
|
|||
-DwxUSE_LIBSDL=OFF
|
||||
-DwxUSE_XTEST=OFF
|
||||
-DwxUSE_GLCANVAS_EGL=OFF
|
||||
-DwxUSE_WEBREQUEST=OFF
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
|
|
93
resources/icons/measure.svg
Normal file
93
resources/icons/measure.svg
Normal file
|
@ -0,0 +1,93 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.0"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 128 128"
|
||||
enable-background="new 0 0 128 128"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="measure2.svg"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs976">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</defs><sodipodi:namedview
|
||||
id="namedview974"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:pageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="6.34375"
|
||||
inkscape:cx="63.921182"
|
||||
inkscape:cy="64.078818"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="3191"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<g
|
||||
id="g1872"><path
|
||||
fill="#ed6b21"
|
||||
d="m 26.966044,54.457929 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
|
||||
id="path958-8"
|
||||
style="fill:#ed6b21;fill-opacity:1;paint-order:stroke fill markers"
|
||||
sodipodi:nodetypes="sccsccs" /><path
|
||||
fill="#ed6b21"
|
||||
d="m 36.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
|
||||
id="path958-8-1"
|
||||
style="fill:#ed6b21;fill-opacity:1;paint-order:stroke fill markers"
|
||||
sodipodi:nodetypes="sccsccs" /><path
|
||||
fill="#ed6b21"
|
||||
d="m 46.966202,60.202938 0.07558,-14.999888 c 0.0063,-1.244993 0.681318,-2.242399 1.511294,-2.232988 0.829976,0.0087 1.494893,1.021998 1.48862,2.266999 l -0.07558,14.999887 c -0.0063,1.244995 -0.681318,2.242401 -1.511294,2.232988 -0.829976,-0.0087 -1.494893,-1.021997 -1.48862,-2.266998 z"
|
||||
id="path958-8-9"
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:1.22476;paint-order:stroke fill markers"
|
||||
sodipodi:nodetypes="sccsccs" /><path
|
||||
fill="#ed6b21"
|
||||
d="m 56.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
|
||||
id="path958-8-9-3"
|
||||
style="fill:#ed6b21;fill-opacity:1;paint-order:stroke fill markers"
|
||||
sodipodi:nodetypes="sccsccs" /><path
|
||||
fill="#ed6b21"
|
||||
d="m 66.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
|
||||
id="path958-8-9-3-0"
|
||||
style="fill:#ed6b21;fill-opacity:1;paint-order:stroke fill markers"
|
||||
sodipodi:nodetypes="sccsccs" /><path
|
||||
fill="#ed6b21"
|
||||
d="m 76.966202,60.202932 0.07558,-14.999881 c 0.0063,-1.244994 0.681318,-2.2424 1.511294,-2.23299 0.829976,0.0085 1.494893,1.021998 1.48862,2.266999 l -0.07558,14.999882 c -0.0063,1.244995 -0.681318,2.2424 -1.511294,2.232987 -0.829976,-0.0085 -1.494893,-1.021996 -1.48862,-2.266997 z"
|
||||
id="path958-8-9-3-1"
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:1.22474;paint-order:stroke fill markers"
|
||||
sodipodi:nodetypes="sccsccs" /><path
|
||||
fill="#ed6b21"
|
||||
d="m 86.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494893,0.681318 1.48862,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
|
||||
id="path958-8-9-3-1-4"
|
||||
style="fill:#ed6b21;fill-opacity:1;paint-order:stroke fill markers"
|
||||
sodipodi:nodetypes="sccsccs" /><path
|
||||
fill="#ed6b21"
|
||||
d="m 96.966044,54.458378 0.07558,-9.999714 c 0.0063,-0.829976 0.681318,-1.494893 1.511294,-1.48862 0.829976,0.0063 1.494892,0.681318 1.488622,1.511294 l -0.07558,9.999714 c -0.0063,0.829977 -0.681318,1.494894 -1.511294,1.48862 -0.829976,-0.0063 -1.494893,-0.681317 -1.48862,-1.511294 z"
|
||||
id="path958-8-9-3-1-4-2"
|
||||
style="fill:#ed6b21;fill-opacity:1;paint-order:stroke fill markers"
|
||||
sodipodi:nodetypes="sccsccs" /></g><g
|
||||
id="g964"
|
||||
transform="translate(0,34.9)">
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="M 108.79,51.6 H 19.21 c -1.93,0 -3.5,-1.57 -3.5,-3.5 V 10.12 c 0,-1.93 1.57,-3.5 3.5,-3.5 h 89.57 c 1.93,0 3.5,1.57 3.5,3.5 V 48.1 c 0.01,1.93 -1.57,3.5 -3.49,3.5 z M 19.21,9.62 c -0.27,0 -0.5,0.23 -0.5,0.5 V 48.1 c 0,0.27 0.23,0.5 0.5,0.5 h 89.57 c 0.27,0 0.5,-0.23 0.5,-0.5 V 10.12 c 0,-0.27 -0.23,-0.5 -0.5,-0.5 z"
|
||||
id="path962" />
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 5.1 KiB |
2
resources/profiles/Anker.idx
Normal file
2
resources/profiles/Anker.idx
Normal file
|
@ -0,0 +1,2 @@
|
|||
min_slic3r_version = 2.6.0-alpha1
|
||||
1.0.0 Initial Version
|
393
resources/profiles/Anker.ini
Normal file
393
resources/profiles/Anker.ini
Normal file
|
@ -0,0 +1,393 @@
|
|||
# Print profiles for the AnkerMake printers.
|
||||
# https://github.com/prusa3d/PrusaSlicer/pull/9075 by @just-trey
|
||||
|
||||
[vendor]
|
||||
# Vendor name will be shown by the Config Wizard.
|
||||
name = Anker
|
||||
# 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.0.0
|
||||
# Where to get the updates from?
|
||||
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Anker/
|
||||
|
||||
# The printer models will be shown by the Configuration Wizard in this order,
|
||||
# also the first model installed & the first nozzle installed will be activated after install.
|
||||
# Printer model name will be shown by the installation wizard.
|
||||
|
||||
[printer_model:M5]
|
||||
name = AnkerMake M5
|
||||
variants = 0.4
|
||||
technology = FFF
|
||||
family = AnkerMake
|
||||
bed_model = M5-bed.stl
|
||||
bed_texture = M5-texture.svg
|
||||
default_materials = Generic PLA+ @ANKER; Generic PLA @ANKER; Generic PET @ANKER; Generic ABS @ANKER
|
||||
|
||||
# All presets starting with asterisk, for example *common*, are intermediate and they will
|
||||
# not make it into the user interface.
|
||||
|
||||
# Common print preset
|
||||
[print:*common*]
|
||||
avoid_crossing_perimeters = 0
|
||||
bridge_acceleration = 2500
|
||||
bridge_angle = 0
|
||||
bridge_flow_ratio = 0.95
|
||||
bridge_speed = 150
|
||||
brim_separation = 0.1
|
||||
brim_type = outer_only
|
||||
brim_width = 0
|
||||
clip_multipart_objects = 1
|
||||
complete_objects = 0
|
||||
default_acceleration = 2500
|
||||
dont_support_bridges = 1
|
||||
elefant_foot_compensation = 0.1
|
||||
ensure_vertical_shell_thickness = 1
|
||||
external_perimeter_speed = 150
|
||||
external_perimeters_first = 0
|
||||
extra_perimeters = 0
|
||||
extruder_clearance_height = 30
|
||||
extruder_clearance_radius = 45
|
||||
fill_angle = 45
|
||||
fill_density = 15%
|
||||
fill_pattern = cubic
|
||||
first_layer_acceleration = 2500
|
||||
first_layer_acceleration_over_raft = 0
|
||||
first_layer_extrusion_width = 200%
|
||||
first_layer_speed = 50%
|
||||
first_layer_speed_over_raft = 30
|
||||
gap_fill_enabled = 1
|
||||
gap_fill_speed = 150
|
||||
gcode_comments = 0
|
||||
infill_acceleration = 2500
|
||||
infill_anchor = 600%
|
||||
infill_anchor_max = 50
|
||||
infill_every_layers = 1
|
||||
infill_extruder = 1
|
||||
infill_first = 0
|
||||
infill_only_where_needed = 0
|
||||
infill_overlap = 23%
|
||||
infill_speed = 250
|
||||
interface_shells = 0
|
||||
max_print_speed = 250
|
||||
max_volumetric_extrusion_rate_slope_negative = 0
|
||||
max_volumetric_extrusion_rate_slope_positive = 0
|
||||
max_volumetric_speed = 0
|
||||
min_skirt_length = 4
|
||||
notes =
|
||||
only_retract_when_crossing_perimeters = 0
|
||||
ooze_prevention = 0
|
||||
output_filename_format = {input_filename_base}_{digits(layer_height,1,2)}mm_{filament_type[0]}_{printer_model}.gcode
|
||||
overhangs = 1
|
||||
perimeter_acceleration = 2500
|
||||
perimeter_extruder = 1
|
||||
perimeter_extrusion_width = 0
|
||||
perimeter_generator = arachne
|
||||
perimeter_speed = 250
|
||||
perimeters = 3
|
||||
post_process =
|
||||
print_settings_id =
|
||||
raft_layers = 0
|
||||
resolution = 0
|
||||
seam_position = aligned
|
||||
single_extruder_multi_material_priming = 0
|
||||
skirt_distance = 3
|
||||
skirt_height = 1
|
||||
skirts = 3
|
||||
small_perimeter_speed = 150
|
||||
solid_infill_below_area = 0
|
||||
solid_infill_every_layers = 0
|
||||
solid_infill_extruder = 1
|
||||
solid_infill_speed = 175
|
||||
spiral_vase = 0
|
||||
standby_temperature_delta = -5
|
||||
support_material = 0
|
||||
support_material_angle = 0
|
||||
support_material_buildplate_only = 0
|
||||
support_material_contact_distance = 0.15
|
||||
support_material_enforce_layers = 0
|
||||
support_material_extruder = 0
|
||||
support_material_interface_contact_loops = 0
|
||||
support_material_interface_extruder = 0
|
||||
support_material_interface_layers = 2
|
||||
support_material_interface_spacing = 0.2
|
||||
support_material_interface_speed = 100%
|
||||
support_material_pattern = rectilinear
|
||||
support_material_spacing = 2
|
||||
support_material_speed = 125
|
||||
support_material_synchronize_layers = 0
|
||||
support_material_threshold = 40
|
||||
support_material_with_sheath = 0
|
||||
support_material_xy_spacing = 60%
|
||||
thick_bridges = 1
|
||||
thin_walls = 0
|
||||
top_solid_infill_speed = 150
|
||||
travel_speed = 300
|
||||
travel_speed_z = 10
|
||||
wipe_tower = 0
|
||||
wipe_tower_bridging = 10
|
||||
wipe_tower_rotation_angle = 0
|
||||
wipe_tower_width = 60
|
||||
wipe_tower_x = 170
|
||||
wipe_tower_y = 140
|
||||
xy_size_compensation = 0
|
||||
|
||||
[print:*0.08mm*]
|
||||
inherits = *common*
|
||||
layer_height = 0.08
|
||||
first_layer_height = 0.12
|
||||
bottom_solid_layers = 9
|
||||
top_solid_layers = 11
|
||||
bridge_flow_ratio = 0.70
|
||||
|
||||
[print:*0.10mm*]
|
||||
inherits = *common*
|
||||
layer_height = 0.10
|
||||
first_layer_height = 0.14
|
||||
bottom_solid_layers = 7
|
||||
top_solid_layers = 9
|
||||
bridge_flow_ratio = 0.70
|
||||
|
||||
[print:*0.12mm*]
|
||||
inherits = *common*
|
||||
layer_height = 0.12
|
||||
first_layer_height = 0.16
|
||||
bottom_solid_layers = 6
|
||||
top_solid_layers = 7
|
||||
bridge_flow_ratio = 0.70
|
||||
|
||||
[print:*0.16mm*]
|
||||
inherits = *common*
|
||||
layer_height = 0.16
|
||||
first_layer_height = 0.20
|
||||
bottom_solid_layers = 5
|
||||
top_solid_layers = 7
|
||||
bridge_flow_ratio = 0.85
|
||||
|
||||
[print:*0.20mm*]
|
||||
inherits = *common*
|
||||
layer_height = 0.20
|
||||
first_layer_height = 0.24
|
||||
bottom_solid_layers = 4
|
||||
top_solid_layers = 5
|
||||
|
||||
[print:*0.24mm*]
|
||||
inherits = *common*
|
||||
layer_height = 0.24
|
||||
first_layer_height = 0.28
|
||||
bottom_solid_layers = 3
|
||||
top_solid_layers = 4
|
||||
|
||||
[print:*0.28mm*]
|
||||
inherits = *common*
|
||||
layer_height = 0.28
|
||||
first_layer_height = 0.28
|
||||
bottom_solid_layers = 3
|
||||
top_solid_layers = 4
|
||||
|
||||
[print:0.08 mm SUPERDETAIL (0.4 mm nozzle) @ANKER]
|
||||
inherits = *0.08mm*
|
||||
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.10 mm HIGHDETAIL (0.4 mm nozzle) @ANKER]
|
||||
inherits = *0.10mm*
|
||||
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.12 mm DETAIL (0.4 mm nozzle) @ANKER]
|
||||
inherits = *0.12mm*
|
||||
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.16 mm OPTIMAL (0.4 mm nozzle) @ANKER]
|
||||
inherits = *0.16mm*
|
||||
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.20 mm NORMAL (0.4 mm nozzle) @ANKER]
|
||||
inherits = *0.20mm*
|
||||
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.24 mm DRAFT (0.4 mm nozzle) @ANKER]
|
||||
inherits = *0.24mm*
|
||||
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.28 mm SUPERDRAFT (0.4 mm nozzle) @ANKER]
|
||||
inherits = *0.28mm*
|
||||
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
|
||||
|
||||
[filament:*common*]
|
||||
cooling = 0
|
||||
compatible_printers =
|
||||
extrusion_multiplier = 1
|
||||
filament_cost = 0
|
||||
filament_density = 0
|
||||
filament_diameter = 1.75
|
||||
filament_notes = ""
|
||||
filament_settings_id = ""
|
||||
filament_soluble = 0
|
||||
min_print_speed = 30
|
||||
slowdown_below_layer_time = 8
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_ANKERMAKE.*/
|
||||
|
||||
[filament:*PLA*]
|
||||
inherits = *common*
|
||||
bed_temperature = 60
|
||||
fan_below_layer_time = 100
|
||||
filament_colour = #DDDDDD
|
||||
filament_type = PLA
|
||||
filament_density = 1.24
|
||||
filament_cost = 20
|
||||
first_layer_bed_temperature = 60
|
||||
first_layer_temperature = 210
|
||||
fan_always_on = 1
|
||||
max_fan_speed = 100
|
||||
min_fan_speed = 100
|
||||
bridge_fan_speed = 100
|
||||
disable_fan_first_layers = 2
|
||||
temperature = 205
|
||||
|
||||
[filament:*PLA+*]
|
||||
inherits = *common*
|
||||
bed_temperature = 60
|
||||
fan_below_layer_time = 100
|
||||
filament_colour = #DDDDDD
|
||||
filament_type = PLA
|
||||
filament_density = 1.24
|
||||
filament_cost = 20
|
||||
first_layer_bed_temperature = 60
|
||||
first_layer_temperature = 220
|
||||
fan_always_on = 1
|
||||
max_fan_speed = 100
|
||||
min_fan_speed = 100
|
||||
bridge_fan_speed = 100
|
||||
disable_fan_first_layers = 2
|
||||
temperature = 210
|
||||
|
||||
[filament:*PET*]
|
||||
inherits = *common*
|
||||
bed_temperature = 70
|
||||
disable_fan_first_layers = 2
|
||||
fan_below_layer_time = 20
|
||||
filament_colour = #DDDDDD
|
||||
filament_type = PETG
|
||||
filament_density = 1.27
|
||||
filament_cost = 30
|
||||
first_layer_bed_temperature = 70
|
||||
first_layer_temperature = 240
|
||||
fan_always_on = 1
|
||||
max_fan_speed = 50
|
||||
min_fan_speed = 50
|
||||
bridge_fan_speed = 100
|
||||
temperature = 240
|
||||
|
||||
[filament:*ABS*]
|
||||
inherits = *common*
|
||||
bed_temperature = 90
|
||||
disable_fan_first_layers = 2
|
||||
fan_below_layer_time = 20
|
||||
filament_colour = #DDDDDD
|
||||
filament_type = ABS
|
||||
filament_density = 1.04
|
||||
filament_cost = 20
|
||||
first_layer_bed_temperature = 100
|
||||
first_layer_temperature = 245
|
||||
fan_always_on = 0
|
||||
max_fan_speed = 0
|
||||
min_fan_speed = 0
|
||||
bridge_fan_speed = 30
|
||||
top_fan_speed = 0
|
||||
temperature = 245
|
||||
|
||||
[filament:Generic PLA @ANKER]
|
||||
inherits = *PLA*
|
||||
filament_vendor = Generic
|
||||
|
||||
[filament:Generic PLA+ @ANKER]
|
||||
inherits = *PLA+*
|
||||
filament_vendor = Generic
|
||||
|
||||
[filament:Generic PETG @ANKER]
|
||||
inherits = *PET*
|
||||
filament_vendor = Generic
|
||||
|
||||
[filament:Generic ABS @ANKER]
|
||||
inherits = *ABS*
|
||||
first_layer_bed_temperature = 90
|
||||
bed_temperature = 90
|
||||
filament_vendor = Generic
|
||||
|
||||
|
||||
# Common printer preset
|
||||
[printer:*common*]
|
||||
printer_technology = FFF
|
||||
before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;{layer_z}
|
||||
between_objects_gcode =
|
||||
pause_print_gcode =
|
||||
deretract_speed = 60
|
||||
extruder_colour = #FCE94F
|
||||
extruder_offset = 0x0
|
||||
gcode_flavor = marlin
|
||||
silent_mode = 0
|
||||
remaining_times = 0
|
||||
machine_max_acceleration_e = 2500
|
||||
machine_max_acceleration_extruding = 2500
|
||||
machine_max_acceleration_retracting = 2500
|
||||
machine_max_acceleration_travel = 1500,1250
|
||||
machine_max_acceleration_x = 2500
|
||||
machine_max_acceleration_y = 2500
|
||||
machine_max_acceleration_z = 2500
|
||||
machine_max_feedrate_e = 100
|
||||
machine_max_feedrate_x = 300
|
||||
machine_max_feedrate_y = 300
|
||||
machine_max_feedrate_z = 20
|
||||
machine_max_jerk_e = 3
|
||||
machine_max_jerk_x = 30
|
||||
machine_max_jerk_y = 30
|
||||
machine_max_jerk_z = 0.3
|
||||
machine_min_extruding_rate = 0
|
||||
machine_min_travel_rate = 0
|
||||
layer_gcode = ;AFTER_LAYER_CHANGE\n;{layer_z}
|
||||
max_print_height = 250
|
||||
printer_notes =
|
||||
printer_settings_id =
|
||||
retract_before_travel = 2
|
||||
retract_before_wipe = 70%
|
||||
retract_layer_change = 1
|
||||
retract_length_toolchange = 1
|
||||
retract_lift = 0
|
||||
retract_lift_above = 0
|
||||
retract_lift_below = 0
|
||||
retract_restart_extra = 0
|
||||
retract_restart_extra_toolchange = 0
|
||||
retract_speed = 60
|
||||
single_extruder_multi_material = 0
|
||||
thumbnails = 64x64,256x256
|
||||
thumbnails_format = JPG
|
||||
toolchange_gcode =
|
||||
use_firmware_retraction = 0
|
||||
use_relative_e_distances = 0
|
||||
use_volumetric_e = 0
|
||||
variable_layer_height = 1
|
||||
wipe = 1
|
||||
z_offset = 0
|
||||
default_filament_profile = "Generic PLA+ @ANKER"
|
||||
start_gcode = M104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; set and wait for nozzle temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG28 ;Home\nG1 E10 F3600; push out retracted filament(fix for over retraction after prime)
|
||||
end_gcode = M104 S0\nM140 S0\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 X0 Y0\nM84
|
||||
|
||||
[printer:*M5*]
|
||||
inherits = *common*
|
||||
bed_shape = 0x0,235-0,235x235,0x235
|
||||
max_print_height = 250
|
||||
printer_model = M5
|
||||
retract_length = 1.25
|
||||
retract_speed = 60
|
||||
deretract_speed = 60
|
||||
retract_before_travel = 1
|
||||
retract_before_wipe = 0%
|
||||
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ANKERMAKE\nPRINTER_MODEL_M5
|
||||
|
||||
[printer:AnkerMake M5 (0.4 mm nozzle)]
|
||||
inherits = *M5*
|
||||
nozzle_diameter = 0.4
|
||||
printer_variant = 0.4
|
||||
min_layer_height = 0.08
|
||||
max_layer_height = 0.32
|
||||
retract_lift_above = 0.2
|
||||
default_print_profile = "0.16 mm OPTIMAL (0.4 mm nozzle) @ANKER"
|
BIN
resources/profiles/Anker/M5-bed.stl
Normal file
BIN
resources/profiles/Anker/M5-bed.stl
Normal file
Binary file not shown.
1003
resources/profiles/Anker/M5-texture.svg
Normal file
1003
resources/profiles/Anker/M5-texture.svg
Normal file
File diff suppressed because one or more lines are too long
After (image error) Size: 50 KiB |
BIN
resources/profiles/Anker/M5_thumbnail.png
Normal file
BIN
resources/profiles/Anker/M5_thumbnail.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 36 KiB |
|
@ -157,23 +157,25 @@ namespace ImGui
|
|||
const wchar_t InfoMarker = 0x2603;
|
||||
const wchar_t SliderFloatEditBtnIcon = 0x2604;
|
||||
const wchar_t SliderFloatEditBtnPressedIcon = 0x2605;
|
||||
const wchar_t LegendTravel = 0x2606;
|
||||
const wchar_t LegendWipe = 0x2607;
|
||||
const wchar_t LegendRetract = 0x2608;
|
||||
const wchar_t LegendDeretract = 0x2609;
|
||||
const wchar_t LegendSeams = 0x2610;
|
||||
const wchar_t LegendToolChanges = 0x2611;
|
||||
const wchar_t LegendColorChanges = 0x2612;
|
||||
const wchar_t LegendPausePrints = 0x2613;
|
||||
const wchar_t LegendCustomGCodes = 0x2614;
|
||||
const wchar_t LegendCOG = 0x2615;
|
||||
const wchar_t LegendShells = 0x2616;
|
||||
const wchar_t LegendToolMarker = 0x2617;
|
||||
const wchar_t WarningMarkerSmall = 0x2618;
|
||||
const wchar_t ExpandBtn = 0x2619;
|
||||
const wchar_t CollapseBtn = 0x2620;
|
||||
const wchar_t InfoMarkerSmall = 0x2621;
|
||||
const wchar_t ClipboardBtnIcon = 0x2606;
|
||||
|
||||
// void MyFunction(const char* name, const MyMatrix44& v);
|
||||
const wchar_t LegendTravel = 0x2701;
|
||||
const wchar_t LegendWipe = 0x2702;
|
||||
const wchar_t LegendRetract = 0x2703;
|
||||
const wchar_t LegendDeretract = 0x2704;
|
||||
const wchar_t LegendSeams = 0x2705;
|
||||
const wchar_t LegendToolChanges = 0x2706;
|
||||
const wchar_t LegendColorChanges = 0x2707;
|
||||
const wchar_t LegendPausePrints = 0x2708;
|
||||
const wchar_t LegendCustomGCodes = 0x2709;
|
||||
const wchar_t LegendCOG = 0x2710;
|
||||
const wchar_t LegendShells = 0x2711;
|
||||
const wchar_t LegendToolMarker = 0x2712;
|
||||
const wchar_t WarningMarkerSmall = 0x2713;
|
||||
const wchar_t ExpandBtn = 0x2714;
|
||||
const wchar_t CollapseBtn = 0x2715;
|
||||
const wchar_t InfoMarkerSmall = 0x2716;
|
||||
|
||||
// void MyFunction(const char* name, const MyMatrix44& v);
|
||||
}
|
||||
|
||||
|
|
|
@ -186,6 +186,9 @@ set(SLIC3R_SOURCES
|
|||
MultiMaterialSegmentation.hpp
|
||||
MeshNormals.hpp
|
||||
MeshNormals.cpp
|
||||
Measure.hpp
|
||||
Measure.cpp
|
||||
MeasureUtils.hpp
|
||||
CustomGCode.cpp
|
||||
CustomGCode.hpp
|
||||
Arrange.hpp
|
||||
|
@ -261,6 +264,7 @@ set(SLIC3R_SOURCES
|
|||
Surface.hpp
|
||||
SurfaceCollection.cpp
|
||||
SurfaceCollection.hpp
|
||||
SurfaceMesh.hpp
|
||||
SVG.cpp
|
||||
SVG.hpp
|
||||
Technologies.hpp
|
||||
|
|
|
@ -1502,27 +1502,27 @@ void GCode::process_layers(
|
|||
}
|
||||
});
|
||||
const auto spiral_vase = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&spiral_vase = *this->m_spiral_vase](LayerResult in) -> LayerResult {
|
||||
[spiral_vase = this->m_spiral_vase.get()](LayerResult in) -> LayerResult {
|
||||
if (in.nop_layer_result)
|
||||
return in;
|
||||
|
||||
spiral_vase.enable(in.spiral_vase_enable);
|
||||
return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush};
|
||||
spiral_vase->enable(in.spiral_vase_enable);
|
||||
return { spiral_vase->process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush};
|
||||
});
|
||||
const auto pressure_equalizer = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&pressure_equalizer = *this->m_pressure_equalizer](LayerResult in) -> LayerResult {
|
||||
return pressure_equalizer.process_layer(std::move(in));
|
||||
[pressure_equalizer = this->m_pressure_equalizer.get()](LayerResult in) -> LayerResult {
|
||||
return pressure_equalizer->process_layer(std::move(in));
|
||||
});
|
||||
const auto cooling = tbb::make_filter<LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&cooling_buffer = *this->m_cooling_buffer](LayerResult in) -> std::string {
|
||||
[cooling_buffer = this->m_cooling_buffer.get()](LayerResult in) -> std::string {
|
||||
if (in.nop_layer_result)
|
||||
return in.gcode;
|
||||
|
||||
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||
return cooling_buffer->process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||
});
|
||||
const auto find_replace = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&self = *this->m_find_replace](std::string s) -> std::string {
|
||||
return self.process_layer(std::move(s));
|
||||
[find_replace = this->m_find_replace.get()](std::string s) -> std::string {
|
||||
return find_replace->process_layer(std::move(s));
|
||||
});
|
||||
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&output_stream](std::string s) { output_stream.write(s); }
|
||||
|
@ -1584,25 +1584,25 @@ void GCode::process_layers(
|
|||
}
|
||||
});
|
||||
const auto spiral_vase = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&spiral_vase = *this->m_spiral_vase](LayerResult in)->LayerResult {
|
||||
[spiral_vase = this->m_spiral_vase.get()](LayerResult in)->LayerResult {
|
||||
if (in.nop_layer_result)
|
||||
return in;
|
||||
spiral_vase.enable(in.spiral_vase_enable);
|
||||
return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush };
|
||||
spiral_vase->enable(in.spiral_vase_enable);
|
||||
return { spiral_vase->process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush };
|
||||
});
|
||||
const auto pressure_equalizer = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&pressure_equalizer = *this->m_pressure_equalizer](LayerResult in) -> LayerResult {
|
||||
return pressure_equalizer.process_layer(std::move(in));
|
||||
[pressure_equalizer = this->m_pressure_equalizer.get()](LayerResult in) -> LayerResult {
|
||||
return pressure_equalizer->process_layer(std::move(in));
|
||||
});
|
||||
const auto cooling = tbb::make_filter<LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&cooling_buffer = *this->m_cooling_buffer](LayerResult in)->std::string {
|
||||
[cooling_buffer = this->m_cooling_buffer.get()](LayerResult in)->std::string {
|
||||
if (in.nop_layer_result)
|
||||
return in.gcode;
|
||||
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||
return cooling_buffer->process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||
});
|
||||
const auto find_replace = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&self = *this->m_find_replace](std::string s) -> std::string {
|
||||
return self.process_layer(std::move(s));
|
||||
[find_replace = this->m_find_replace.get()](std::string s) -> std::string {
|
||||
return find_replace->process_layer(std::move(s));
|
||||
});
|
||||
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&output_stream](std::string s) { output_stream.write(s); }
|
||||
|
|
1190
src/libslic3r/Measure.cpp
Normal file
1190
src/libslic3r/Measure.cpp
Normal file
File diff suppressed because it is too large
Load diff
208
src/libslic3r/Measure.hpp
Normal file
208
src/libslic3r/Measure.hpp
Normal file
|
@ -0,0 +1,208 @@
|
|||
#ifndef Slic3r_Measure_hpp_
|
||||
#define Slic3r_Measure_hpp_
|
||||
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
#include "Point.hpp"
|
||||
|
||||
|
||||
struct indexed_triangle_set;
|
||||
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
namespace Measure {
|
||||
|
||||
|
||||
enum class SurfaceFeatureType : int {
|
||||
Undef = 0,
|
||||
Point = 1 << 0,
|
||||
Edge = 1 << 1,
|
||||
Circle = 1 << 2,
|
||||
Plane = 1 << 3
|
||||
};
|
||||
|
||||
class SurfaceFeature {
|
||||
public:
|
||||
SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional<Vec3d> pt3 = std::nullopt, double value = 0.0)
|
||||
: m_type(type), m_pt1(pt1), m_pt2(pt2), m_pt3(pt3), m_value(value) {}
|
||||
|
||||
explicit SurfaceFeature(const Vec3d& pt)
|
||||
: m_type{SurfaceFeatureType::Point}, m_pt1{pt} {}
|
||||
|
||||
// Get type of this feature.
|
||||
SurfaceFeatureType get_type() const { return m_type; }
|
||||
|
||||
// For points, return the point.
|
||||
Vec3d get_point() const { assert(m_type == SurfaceFeatureType::Point); return m_pt1; }
|
||||
|
||||
// For edges, return start and end.
|
||||
std::pair<Vec3d, Vec3d> get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); }
|
||||
|
||||
// For circles, return center, radius and normal.
|
||||
std::tuple<Vec3d, double, Vec3d> get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); }
|
||||
|
||||
// For planes, return index into vector provided by Measuring::get_plane_triangle_indices, normal and point.
|
||||
std::tuple<int, Vec3d, Vec3d> get_plane() const { assert(m_type == SurfaceFeatureType::Plane); return std::make_tuple(int(m_value), m_pt1, m_pt2); }
|
||||
|
||||
// For anything, return an extra point that should also be considered a part of this.
|
||||
std::optional<Vec3d> get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; }
|
||||
|
||||
bool operator == (const SurfaceFeature& other) const {
|
||||
if (this->m_type != other.m_type) return false;
|
||||
switch (this->m_type)
|
||||
{
|
||||
case SurfaceFeatureType::Undef: { break; }
|
||||
case SurfaceFeatureType::Point: { return (this->m_pt1.isApprox(other.m_pt1)); }
|
||||
case SurfaceFeatureType::Edge: {
|
||||
return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2)) ||
|
||||
(this->m_pt1.isApprox(other.m_pt2) && this->m_pt2.isApprox(other.m_pt1));
|
||||
}
|
||||
case SurfaceFeatureType::Plane:
|
||||
case SurfaceFeatureType::Circle: {
|
||||
return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2) && std::abs(this->m_value - other.m_value) < EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator != (const SurfaceFeature& other) const {
|
||||
return !operator == (other);
|
||||
}
|
||||
|
||||
private:
|
||||
SurfaceFeatureType m_type{ SurfaceFeatureType::Undef };
|
||||
Vec3d m_pt1{ Vec3d::Zero() };
|
||||
Vec3d m_pt2{ Vec3d::Zero() };
|
||||
std::optional<Vec3d> m_pt3;
|
||||
double m_value{ 0.0 };
|
||||
};
|
||||
|
||||
|
||||
|
||||
class MeasuringImpl;
|
||||
|
||||
|
||||
class Measuring {
|
||||
public:
|
||||
// Construct the measurement object on a given its. The its must remain
|
||||
// valid and unchanged during the whole lifetime of the object.
|
||||
explicit Measuring(const indexed_triangle_set& its);
|
||||
~Measuring();
|
||||
|
||||
// Return a reference to a list of all features identified on the its.
|
||||
// Use only for debugging. Expensive, do not call often.
|
||||
std::vector<SurfaceFeature> get_all_features() const;
|
||||
|
||||
// Given a face_idx where the mouse cursor points, return a feature that
|
||||
// should be highlighted (if any).
|
||||
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point) const;
|
||||
|
||||
// Returns a list of triangle indices for each identified plane. Each
|
||||
// Plane object contains an index into this vector. Expensive, do not
|
||||
// call too often.
|
||||
std::vector<std::vector<int>> get_planes_triangle_indices() const;
|
||||
|
||||
// Returns the surface features of the plane with the given index
|
||||
const std::vector<SurfaceFeature>& get_plane_features(unsigned int plane_id) const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<MeasuringImpl> priv;
|
||||
};
|
||||
|
||||
|
||||
struct DistAndPoints {
|
||||
DistAndPoints(double dist_, Vec3d from_, Vec3d to_) : dist(dist_), from(from_), to(to_) {}
|
||||
double dist;
|
||||
Vec3d from;
|
||||
Vec3d to;
|
||||
|
||||
void transform(const Transform3d& trafo);
|
||||
};
|
||||
|
||||
struct AngleAndEdges {
|
||||
AngleAndEdges(double angle_, const Vec3d& center_, const std::pair<Vec3d, Vec3d>& e1_, const std::pair<Vec3d, Vec3d>& e2_, double radius_, bool coplanar_)
|
||||
: angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {}
|
||||
double angle;
|
||||
Vec3d center;
|
||||
std::pair<Vec3d, Vec3d> e1;
|
||||
std::pair<Vec3d, Vec3d> e2;
|
||||
double radius;
|
||||
bool coplanar;
|
||||
|
||||
void transform(const Transform3d& trafo);
|
||||
|
||||
static const AngleAndEdges Dummy;
|
||||
};
|
||||
|
||||
struct MeasurementResult {
|
||||
std::optional<AngleAndEdges> angle;
|
||||
std::optional<DistAndPoints> distance_infinite;
|
||||
std::optional<DistAndPoints> distance_strict;
|
||||
std::optional<Vec3d> distance_xyz;
|
||||
|
||||
bool has_distance_data() const {
|
||||
return distance_infinite.has_value() || distance_strict.has_value();
|
||||
}
|
||||
|
||||
bool has_any_data() const {
|
||||
return angle.has_value() || distance_infinite.has_value() || distance_strict.has_value() || distance_xyz.has_value();
|
||||
}
|
||||
|
||||
void transform(const Transform3d& trafo) {
|
||||
if (angle.has_value())
|
||||
angle->transform(trafo);
|
||||
if (distance_infinite.has_value())
|
||||
distance_infinite->transform(trafo);
|
||||
if (distance_strict.has_value()) {
|
||||
distance_strict->transform(trafo);
|
||||
distance_xyz = (distance_strict->to - distance_strict->from).cwiseAbs();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Returns distance/angle between two SurfaceFeatures.
|
||||
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring = nullptr);
|
||||
|
||||
inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); }
|
||||
inline Vec3d edge_direction(const std::pair<Vec3d, Vec3d>& e) { return edge_direction(e.first, e.second); }
|
||||
inline Vec3d edge_direction(const SurfaceFeature& edge) {
|
||||
assert(edge.get_type() == SurfaceFeatureType::Edge);
|
||||
return edge_direction(edge.get_edge());
|
||||
}
|
||||
|
||||
inline Vec3d plane_normal(const SurfaceFeature& plane) {
|
||||
assert(plane.get_type() == SurfaceFeatureType::Plane);
|
||||
return std::get<1>(plane.get_plane());
|
||||
}
|
||||
|
||||
inline bool are_parallel(const Vec3d& v1, const Vec3d& v2) { return std::abs(std::abs(v1.dot(v2)) - 1.0) < EPSILON; }
|
||||
inline bool are_perpendicular(const Vec3d& v1, const Vec3d& v2) { return std::abs(v1.dot(v2)) < EPSILON; }
|
||||
|
||||
inline bool are_parallel(const std::pair<Vec3d, Vec3d>& e1, const std::pair<Vec3d, Vec3d>& e2) {
|
||||
return are_parallel(e1.second - e1.first, e2.second - e2.first);
|
||||
}
|
||||
inline bool are_parallel(const SurfaceFeature& f1, const SurfaceFeature& f2) {
|
||||
if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge)
|
||||
return are_parallel(edge_direction(f1), edge_direction(f2));
|
||||
else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane)
|
||||
return are_perpendicular(edge_direction(f1), plane_normal(f2));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool are_perpendicular(const SurfaceFeature& f1, const SurfaceFeature& f2) {
|
||||
if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge)
|
||||
return are_perpendicular(edge_direction(f1), edge_direction(f2));
|
||||
else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane)
|
||||
return are_parallel(edge_direction(f1), plane_normal(f2));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Measure
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // Slic3r_Measure_hpp_
|
386
src/libslic3r/MeasureUtils.hpp
Normal file
386
src/libslic3r/MeasureUtils.hpp
Normal file
|
@ -0,0 +1,386 @@
|
|||
#ifndef Slic3r_MeasureUtils_hpp_
|
||||
#define Slic3r_MeasureUtils_hpp_
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace Measure {
|
||||
|
||||
// Utility class used to calculate distance circle-circle
|
||||
// Adaptation of code found in:
|
||||
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Polynomial1.h
|
||||
|
||||
class Polynomial1
|
||||
{
|
||||
public:
|
||||
Polynomial1(std::initializer_list<double> values)
|
||||
{
|
||||
// C++ 11 will call the default constructor for
|
||||
// Polynomial1<Real> p{}, so it is guaranteed that
|
||||
// values.size() > 0.
|
||||
m_coefficient.resize(values.size());
|
||||
std::copy(values.begin(), values.end(), m_coefficient.begin());
|
||||
EliminateLeadingZeros();
|
||||
}
|
||||
|
||||
// Construction and destruction. The first constructor creates a
|
||||
// polynomial of the specified degree but sets all coefficients to
|
||||
// zero (to ensure initialization). You are responsible for setting
|
||||
// the coefficients, presumably with the degree-term set to a nonzero
|
||||
// number. In the second constructor, the degree is the number of
|
||||
// initializers plus 1, but then adjusted so that coefficient[degree]
|
||||
// is not zero (unless all initializer values are zero).
|
||||
explicit Polynomial1(uint32_t degree)
|
||||
: m_coefficient(static_cast<size_t>(degree) + 1, 0.0)
|
||||
{}
|
||||
|
||||
// Eliminate any leading zeros in the polynomial, except in the case
|
||||
// the degree is 0 and the coefficient is 0. The elimination is
|
||||
// necessary when arithmetic operations cause a decrease in the degree
|
||||
// of the result. For example, (1 + x + x^2) + (1 + 2*x - x^2) =
|
||||
// (2 + 3*x). The inputs both have degree 2, so the result is created
|
||||
// with degree 2. After the addition we find that the degree is in
|
||||
// fact 1 and resize the array of coefficients. This function is
|
||||
// called internally by the arithmetic operators, but it is exposed in
|
||||
// the public interface in case you need it for your own purposes.
|
||||
void EliminateLeadingZeros()
|
||||
{
|
||||
const size_t size = m_coefficient.size();
|
||||
if (size > 1) {
|
||||
const double zero = 0.0;
|
||||
int32_t leading;
|
||||
for (leading = static_cast<int32_t>(size) - 1; leading > 0; --leading) {
|
||||
if (m_coefficient[leading] != zero)
|
||||
break;
|
||||
}
|
||||
|
||||
m_coefficient.resize(++leading);
|
||||
}
|
||||
}
|
||||
|
||||
// Set all coefficients to the specified value.
|
||||
void SetCoefficients(double value)
|
||||
{
|
||||
std::fill(m_coefficient.begin(), m_coefficient.end(), value);
|
||||
}
|
||||
|
||||
inline uint32_t GetDegree() const
|
||||
{
|
||||
// By design, m_coefficient.size() > 0.
|
||||
return static_cast<uint32_t>(m_coefficient.size() - 1);
|
||||
}
|
||||
|
||||
inline const double& operator[](uint32_t i) const { return m_coefficient[i]; }
|
||||
inline double& operator[](uint32_t i) { return m_coefficient[i]; }
|
||||
|
||||
// Evaluate the polynomial. If the polynomial is invalid, the
|
||||
// function returns zero.
|
||||
double operator()(double t) const
|
||||
{
|
||||
int32_t i = static_cast<int32_t>(m_coefficient.size());
|
||||
double result = m_coefficient[--i];
|
||||
for (--i; i >= 0; --i) {
|
||||
result *= t;
|
||||
result += m_coefficient[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected:
|
||||
// The class is designed so that m_coefficient.size() >= 1.
|
||||
std::vector<double> m_coefficient;
|
||||
};
|
||||
|
||||
inline Polynomial1 operator * (const Polynomial1& p0, const Polynomial1& p1)
|
||||
{
|
||||
const uint32_t p0Degree = p0.GetDegree();
|
||||
const uint32_t p1Degree = p1.GetDegree();
|
||||
Polynomial1 result(p0Degree + p1Degree);
|
||||
result.SetCoefficients(0.0);
|
||||
for (uint32_t i0 = 0; i0 <= p0Degree; ++i0) {
|
||||
for (uint32_t i1 = 0; i1 <= p1Degree; ++i1) {
|
||||
result[i0 + i1] += p0[i0] * p1[i1];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline Polynomial1 operator + (const Polynomial1& p0, const Polynomial1& p1)
|
||||
{
|
||||
const uint32_t p0Degree = p0.GetDegree();
|
||||
const uint32_t p1Degree = p1.GetDegree();
|
||||
uint32_t i;
|
||||
if (p0Degree >= p1Degree) {
|
||||
Polynomial1 result(p0Degree);
|
||||
for (i = 0; i <= p1Degree; ++i) {
|
||||
result[i] = p0[i] + p1[i];
|
||||
}
|
||||
for (/**/; i <= p0Degree; ++i) {
|
||||
result[i] = p0[i];
|
||||
}
|
||||
result.EliminateLeadingZeros();
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
Polynomial1 result(p1Degree);
|
||||
for (i = 0; i <= p0Degree; ++i) {
|
||||
result[i] = p0[i] + p1[i];
|
||||
}
|
||||
for (/**/; i <= p1Degree; ++i) {
|
||||
result[i] = p1[i];
|
||||
}
|
||||
result.EliminateLeadingZeros();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
inline Polynomial1 operator - (const Polynomial1& p0, const Polynomial1& p1)
|
||||
{
|
||||
const uint32_t p0Degree = p0.GetDegree();
|
||||
const uint32_t p1Degree = p1.GetDegree();
|
||||
uint32_t i;
|
||||
if (p0Degree >= p1Degree) {
|
||||
Polynomial1 result(p0Degree);
|
||||
for (i = 0; i <= p1Degree; ++i) {
|
||||
result[i] = p0[i] - p1[i];
|
||||
}
|
||||
for (/**/; i <= p0Degree; ++i) {
|
||||
result[i] = p0[i];
|
||||
}
|
||||
result.EliminateLeadingZeros();
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
Polynomial1 result(p1Degree);
|
||||
for (i = 0; i <= p0Degree; ++i) {
|
||||
result[i] = p0[i] - p1[i];
|
||||
}
|
||||
for (/**/; i <= p1Degree; ++i) {
|
||||
result[i] = -p1[i];
|
||||
}
|
||||
result.EliminateLeadingZeros();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
inline Polynomial1 operator * (double scalar, const Polynomial1& p)
|
||||
{
|
||||
const uint32_t degree = p.GetDegree();
|
||||
Polynomial1 result(degree);
|
||||
for (uint32_t i = 0; i <= degree; ++i) {
|
||||
result[i] = scalar * p[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Utility class used to calculate distance circle-circle
|
||||
// Adaptation of code found in:
|
||||
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/RootsPolynomial.h
|
||||
|
||||
class RootsPolynomial
|
||||
{
|
||||
public:
|
||||
// General equations: sum_{i=0}^{d} c(i)*t^i = 0. The input array 'c'
|
||||
// must have at least d+1 elements and the output array 'root' must
|
||||
// have at least d elements.
|
||||
|
||||
// Find the roots on (-infinity,+infinity).
|
||||
static int32_t Find(int32_t degree, const double* c, uint32_t maxIterations, double* roots)
|
||||
{
|
||||
if (degree >= 0 && c != nullptr) {
|
||||
const double zero = 0.0;
|
||||
while (degree >= 0 && c[degree] == zero) {
|
||||
--degree;
|
||||
}
|
||||
|
||||
if (degree > 0) {
|
||||
// Compute the Cauchy bound.
|
||||
const double one = 1.0;
|
||||
const double invLeading = one / c[degree];
|
||||
double maxValue = zero;
|
||||
for (int32_t i = 0; i < degree; ++i) {
|
||||
const double value = std::fabs(c[i] * invLeading);
|
||||
if (value > maxValue)
|
||||
maxValue = value;
|
||||
}
|
||||
const double bound = one + maxValue;
|
||||
|
||||
return FindRecursive(degree, c, -bound, bound, maxIterations, roots);
|
||||
}
|
||||
else if (degree == 0)
|
||||
// The polynomial is a nonzero constant.
|
||||
return 0;
|
||||
else {
|
||||
// The polynomial is identically zero.
|
||||
roots[0] = zero;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
// Invalid degree or c.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If you know that p(tmin) * p(tmax) <= 0, then there must be at
|
||||
// least one root in [tmin, tmax]. Compute it using bisection.
|
||||
static bool Find(int32_t degree, const double* c, double tmin, double tmax, uint32_t maxIterations, double& root)
|
||||
{
|
||||
const double zero = 0.0;
|
||||
double pmin = Evaluate(degree, c, tmin);
|
||||
if (pmin == zero) {
|
||||
root = tmin;
|
||||
return true;
|
||||
}
|
||||
double pmax = Evaluate(degree, c, tmax);
|
||||
if (pmax == zero) {
|
||||
root = tmax;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pmin * pmax > zero)
|
||||
// It is not known whether the interval bounds a root.
|
||||
return false;
|
||||
|
||||
if (tmin >= tmax)
|
||||
// Invalid ordering of interval endpoitns.
|
||||
return false;
|
||||
|
||||
for (uint32_t i = 1; i <= maxIterations; ++i) {
|
||||
root = 0.5 * (tmin + tmax);
|
||||
|
||||
// This test is designed for 'float' or 'double' when tmin
|
||||
// and tmax are consecutive floating-point numbers.
|
||||
if (root == tmin || root == tmax)
|
||||
break;
|
||||
|
||||
const double p = Evaluate(degree, c, root);
|
||||
const double product = p * pmin;
|
||||
if (product < zero) {
|
||||
tmax = root;
|
||||
pmax = p;
|
||||
}
|
||||
else if (product > zero) {
|
||||
tmin = root;
|
||||
pmin = p;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Support for the Find functions.
|
||||
static int32_t FindRecursive(int32_t degree, double const* c, double tmin, double tmax, uint32_t maxIterations, double* roots)
|
||||
{
|
||||
// The base of the recursion.
|
||||
const double zero = 0.0;
|
||||
double root = zero;
|
||||
if (degree == 1) {
|
||||
int32_t numRoots;
|
||||
if (c[1] != zero) {
|
||||
root = -c[0] / c[1];
|
||||
numRoots = 1;
|
||||
}
|
||||
else if (c[0] == zero) {
|
||||
root = zero;
|
||||
numRoots = 1;
|
||||
}
|
||||
else
|
||||
numRoots = 0;
|
||||
|
||||
if (numRoots > 0 && tmin <= root && root <= tmax) {
|
||||
roots[0] = root;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the roots of the derivative polynomial scaled by 1/degree.
|
||||
// The scaling avoids the factorial growth in the coefficients;
|
||||
// for example, without the scaling, the high-order term x^d
|
||||
// becomes (d!)*x through multiple differentiations. With the
|
||||
// scaling we instead get x. This leads to better numerical
|
||||
// behavior of the root finder.
|
||||
const int32_t derivDegree = degree - 1;
|
||||
std::vector<double> derivCoeff(static_cast<size_t>(derivDegree) + 1);
|
||||
std::vector<double> derivRoots(derivDegree);
|
||||
for (int32_t i = 0, ip1 = 1; i <= derivDegree; ++i, ++ip1) {
|
||||
derivCoeff[i] = c[ip1] * (double)(ip1) / (double)degree;
|
||||
}
|
||||
const int32_t numDerivRoots = FindRecursive(degree - 1, &derivCoeff[0], tmin, tmax, maxIterations, &derivRoots[0]);
|
||||
|
||||
int32_t numRoots = 0;
|
||||
if (numDerivRoots > 0) {
|
||||
// Find root on [tmin,derivRoots[0]].
|
||||
if (Find(degree, c, tmin, derivRoots[0], maxIterations, root))
|
||||
roots[numRoots++] = root;
|
||||
|
||||
// Find root on [derivRoots[i],derivRoots[i+1]].
|
||||
for (int32_t i = 0, ip1 = 1; i <= numDerivRoots - 2; ++i, ++ip1) {
|
||||
if (Find(degree, c, derivRoots[i], derivRoots[ip1], maxIterations, root))
|
||||
roots[numRoots++] = root;
|
||||
}
|
||||
|
||||
// Find root on [derivRoots[numDerivRoots-1],tmax].
|
||||
if (Find(degree, c, derivRoots[static_cast<size_t>(numDerivRoots) - 1], tmax, maxIterations, root))
|
||||
roots[numRoots++] = root;
|
||||
}
|
||||
else {
|
||||
// The polynomial is monotone on [tmin,tmax], so has at most one root.
|
||||
if (Find(degree, c, tmin, tmax, maxIterations, root))
|
||||
roots[numRoots++] = root;
|
||||
}
|
||||
return numRoots;
|
||||
}
|
||||
|
||||
static double Evaluate(int32_t degree, const double* c, double t)
|
||||
{
|
||||
int32_t i = degree;
|
||||
double result = c[i];
|
||||
while (--i >= 0) {
|
||||
result = t * result + c[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Adaptation of code found in:
|
||||
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Vector.h
|
||||
|
||||
// Construct a single vector orthogonal to the nonzero input vector. If
|
||||
// the maximum absolute component occurs at index i, then the orthogonal
|
||||
// vector U has u[i] = v[i+1], u[i+1] = -v[i], and all other components
|
||||
// zero. The index addition i+1 is computed modulo N.
|
||||
inline Vec3d get_orthogonal(const Vec3d& v, bool unitLength)
|
||||
{
|
||||
double cmax = std::fabs(v[0]);
|
||||
int32_t imax = 0;
|
||||
for (int32_t i = 1; i < 3; ++i) {
|
||||
double c = std::fabs(v[i]);
|
||||
if (c > cmax) {
|
||||
cmax = c;
|
||||
imax = i;
|
||||
}
|
||||
}
|
||||
|
||||
Vec3d result = Vec3d::Zero();
|
||||
int32_t inext = imax + 1;
|
||||
if (inext == 3)
|
||||
inext = 0;
|
||||
|
||||
result[imax] = v[inext];
|
||||
result[inext] = -v[imax];
|
||||
if (unitLength) {
|
||||
const double sqrDistance = result[imax] * result[imax] + result[inext] * result[inext];
|
||||
const double invLength = 1.0 / std::sqrt(sqrDistance);
|
||||
result[imax] *= invLength;
|
||||
result[inext] *= invLength;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
} // namespace Measure
|
||||
|
||||
#endif // Slic3r_MeasureUtils_hpp_
|
|
@ -1661,6 +1661,12 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
|||
new_object->add_instance(*model_instance);
|
||||
ModelVolume* new_vol = new_object->add_volume(*volume, std::move(mesh));
|
||||
|
||||
// Invalidate extruder value in volume's config,
|
||||
// otherwise there will no way to change extruder for object after splitting,
|
||||
// because volume's extruder value overrides object's extruder value.
|
||||
if (new_vol->config.has("extruder"))
|
||||
new_vol->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||
|
||||
for (ModelInstance* model_instance : new_object->instances) {
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
Vec3d shift = model_instance->get_transformation().get_matrix_no_offset() * new_vol->get_offset();
|
||||
|
|
|
@ -988,6 +988,11 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
DynamicPrintConfig filament_overrides;
|
||||
t_config_option_keys print_diff = print_config_diffs(m_config, new_full_config, filament_overrides);
|
||||
t_config_option_keys full_config_diff = full_print_config_diffs(m_full_print_config, new_full_config);
|
||||
// If just a physical printer was changed, but printer preset is the same, then there is no need to apply whole print
|
||||
// see https://github.com/prusa3d/PrusaSlicer/issues/8800
|
||||
if (full_config_diff.size() == 1 && full_config_diff[0] == "physical_printer_settings_id")
|
||||
full_config_diff.clear();
|
||||
|
||||
// Collect changes to object and region configs.
|
||||
t_config_option_keys object_diff = m_default_object_config.diff(new_full_config);
|
||||
t_config_option_keys region_diff = m_default_region_config.diff(new_full_config);
|
||||
|
|
154
src/libslic3r/SurfaceMesh.hpp
Normal file
154
src/libslic3r/SurfaceMesh.hpp
Normal file
|
@ -0,0 +1,154 @@
|
|||
#ifndef slic3r_SurfaceMesh_hpp_
|
||||
#define slic3r_SurfaceMesh_hpp_
|
||||
|
||||
#include <admesh/stl.h>
|
||||
#include <libslic3r/TriangleMesh.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class TriangleMesh;
|
||||
|
||||
|
||||
|
||||
enum Face_index : int;
|
||||
|
||||
class Halfedge_index {
|
||||
friend class SurfaceMesh;
|
||||
|
||||
public:
|
||||
Halfedge_index() : m_face(Face_index(-1)), m_side(0) {}
|
||||
Face_index face() const { return m_face; }
|
||||
unsigned char side() const { return m_side; }
|
||||
bool is_invalid() const { return int(m_face) < 0; }
|
||||
bool operator!=(const Halfedge_index& rhs) const { return ! ((*this) == rhs); }
|
||||
bool operator==(const Halfedge_index& rhs) const { return m_face == rhs.m_face && m_side == rhs.m_side; }
|
||||
|
||||
private:
|
||||
Halfedge_index(int face_idx, unsigned char side_idx) : m_face(Face_index(face_idx)), m_side(side_idx) {}
|
||||
|
||||
Face_index m_face;
|
||||
unsigned char m_side;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Vertex_index {
|
||||
friend class SurfaceMesh;
|
||||
|
||||
public:
|
||||
Vertex_index() : m_face(Face_index(-1)), m_vertex_idx(0) {}
|
||||
bool is_invalid() const { return int(m_face) < 0; }
|
||||
bool operator==(const Vertex_index& rhs) const = delete; // Use SurfaceMesh::is_same_vertex.
|
||||
|
||||
private:
|
||||
Vertex_index(int face_idx, unsigned char vertex_idx) : m_face(Face_index(face_idx)), m_vertex_idx(vertex_idx) {}
|
||||
|
||||
Face_index m_face;
|
||||
unsigned char m_vertex_idx;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class SurfaceMesh {
|
||||
public:
|
||||
explicit SurfaceMesh(const indexed_triangle_set& its)
|
||||
: m_its(its),
|
||||
m_face_neighbors(its_face_neighbors_par(its))
|
||||
{}
|
||||
SurfaceMesh(const SurfaceMesh&) = delete;
|
||||
SurfaceMesh& operator=(const SurfaceMesh&) = delete;
|
||||
|
||||
Vertex_index source(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side); }
|
||||
Vertex_index target(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side == 2 ? 0 : h.m_side + 1); }
|
||||
Face_index face(Halfedge_index h) const { assert(! h.is_invalid()); return h.m_face; }
|
||||
|
||||
Halfedge_index next(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side + 1) % 3; return h; }
|
||||
Halfedge_index prev(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side == 0 ? 2 : h.m_side - 1); return h; }
|
||||
Halfedge_index halfedge(Vertex_index v) const { return Halfedge_index(v.m_face, (v.m_vertex_idx == 0 ? 2 : v.m_vertex_idx - 1)); }
|
||||
Halfedge_index halfedge(Face_index f) const { return Halfedge_index(f, 0); }
|
||||
Halfedge_index opposite(Halfedge_index h) const {
|
||||
if (h.is_invalid())
|
||||
return h;
|
||||
|
||||
int face_idx = m_face_neighbors[h.m_face][h.m_side];
|
||||
Halfedge_index h_candidate = halfedge(Face_index(face_idx));
|
||||
|
||||
if (h_candidate.is_invalid())
|
||||
return Halfedge_index(); // invalid
|
||||
|
||||
for (int i=0; i<3; ++i) {
|
||||
if (is_same_vertex(source(h_candidate), target(h))) {
|
||||
// Meshes in PrusaSlicer should be fixed enough for the following not to happen.
|
||||
assert(is_same_vertex(target(h_candidate), source(h)));
|
||||
return h_candidate;
|
||||
}
|
||||
h_candidate = next(h_candidate);
|
||||
}
|
||||
return Halfedge_index(); // invalid
|
||||
}
|
||||
|
||||
Halfedge_index next_around_target(Halfedge_index h) const { return opposite(next(h)); }
|
||||
Halfedge_index prev_around_target(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : prev(op)); }
|
||||
Halfedge_index next_around_source(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : next(op)); }
|
||||
Halfedge_index prev_around_source(Halfedge_index h) const { return opposite(prev(h)); }
|
||||
Halfedge_index halfedge(Vertex_index source, Vertex_index target) const
|
||||
{
|
||||
Halfedge_index hi(source.m_face, source.m_vertex_idx);
|
||||
assert(! hi.is_invalid());
|
||||
|
||||
const Vertex_index orig_target = this->target(hi);
|
||||
Vertex_index current_target = orig_target;
|
||||
|
||||
while (! is_same_vertex(current_target, target)) {
|
||||
hi = next_around_source(hi);
|
||||
if (hi.is_invalid())
|
||||
break;
|
||||
current_target = this->target(hi);
|
||||
if (is_same_vertex(current_target, orig_target))
|
||||
return Halfedge_index(); // invalid
|
||||
}
|
||||
|
||||
return hi;
|
||||
}
|
||||
|
||||
const stl_vertex& point(Vertex_index v) const { return m_its.vertices[m_its.indices[v.m_face][v.m_vertex_idx]]; }
|
||||
|
||||
size_t degree(Vertex_index v) const
|
||||
{
|
||||
Halfedge_index h_first = halfedge(v);
|
||||
Halfedge_index h = next_around_target(h_first);
|
||||
size_t degree = 2;
|
||||
while (! h.is_invalid() && h != h_first) {
|
||||
h = next_around_target(h);
|
||||
++degree;
|
||||
}
|
||||
return h.is_invalid() ? 0 : degree - 1;
|
||||
}
|
||||
|
||||
size_t degree(Face_index f) const {
|
||||
size_t total = 0;
|
||||
for (unsigned char i=0; i<3; ++i) {
|
||||
size_t d = degree(Vertex_index(f, i));
|
||||
if (d == 0)
|
||||
return 0;
|
||||
total += d;
|
||||
}
|
||||
assert(total - 6 >= 0);
|
||||
return total - 6; // we counted 3 halfedges from f, and one more for each neighbor
|
||||
}
|
||||
|
||||
bool is_border(Halfedge_index h) const { return m_face_neighbors[h.m_face][h.m_side] == -1; }
|
||||
|
||||
bool is_same_vertex(const Vertex_index& a, const Vertex_index& b) const { return m_its.indices[a.m_face][a.m_vertex_idx] == m_its.indices[b.m_face][b.m_vertex_idx]; }
|
||||
Vec3i get_face_neighbors(Face_index face_id) const { assert(int(face_id) < int(m_face_neighbors.size())); return m_face_neighbors[face_id]; }
|
||||
|
||||
|
||||
|
||||
private:
|
||||
const std::vector<Vec3i> m_face_neighbors;
|
||||
const indexed_triangle_set& m_its;
|
||||
};
|
||||
|
||||
} //namespace Slic3r
|
||||
|
||||
#endif // slic3r_SurfaceMesh_hpp_
|
|
@ -26,6 +26,8 @@
|
|||
#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW 0
|
||||
// Disable using instanced models to render options in gcode preview
|
||||
#define DISABLE_GCODEVIEWER_INSTANCED_MODELS 1
|
||||
// Enable Measure Gizmo debug window
|
||||
#define ENABLE_MEASURE_GIZMO_DEBUG 0
|
||||
|
||||
|
||||
// Enable rendering of objects using environment map
|
||||
|
|
|
@ -65,6 +65,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/Gizmos/GLGizmoSimplify.hpp
|
||||
GUI/Gizmos/GLGizmoMmuSegmentation.cpp
|
||||
GUI/Gizmos/GLGizmoMmuSegmentation.hpp
|
||||
GUI/Gizmos/GLGizmoMeasure.cpp
|
||||
GUI/Gizmos/GLGizmoMeasure.hpp
|
||||
GUI/GLSelectionRectangle.cpp
|
||||
GUI/GLSelectionRectangle.hpp
|
||||
GUI/GLModel.hpp
|
||||
|
|
|
@ -223,7 +223,10 @@ bool Bed3D::set_shape(const Pointfs& bed_shape, const double max_print_height, c
|
|||
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
m_contour = ExPolygon(Polygon::new_scale(bed_shape));
|
||||
m_polygon = offset(m_contour.contour, (float)m_contour.contour.bounding_box().radius() * 1.7f, jtRound, scale_(0.5)).front();
|
||||
const BoundingBox bbox = m_contour.contour.bounding_box();
|
||||
if (!bbox.defined)
|
||||
throw RuntimeError(std::string("Invalid bed shape"));
|
||||
m_polygon = offset(m_contour.contour, (float)bbox.radius() * 1.7f, jtRound, scale_(0.5)).front();
|
||||
|
||||
m_triangles.reset();
|
||||
m_gridlines.reset();
|
||||
|
|
|
@ -1379,7 +1379,7 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject
|
|||
if ( (gizmo_type == GLGizmosManager::FdmSupports
|
||||
|| gizmo_type == GLGizmosManager::Seam
|
||||
|| gizmo_type == GLGizmosManager::Cut)
|
||||
&& ! vol->is_modifier) {
|
||||
&& !vol->is_modifier) {
|
||||
vol->force_neutral_color = true;
|
||||
if (gizmo_type == GLGizmosManager::Cut)
|
||||
vol->color.a(0.95f);
|
||||
|
@ -2438,8 +2438,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
// refresh volume raycasters for picking
|
||||
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Volume);
|
||||
for (size_t i = 0; i < m_volumes.volumes.size(); ++i) {
|
||||
assert(m_volumes.volumes[i]->mesh_raycaster != nullptr);
|
||||
add_raycaster_for_picking(SceneRaycaster::EType::Volume, i, *m_volumes.volumes[i]->mesh_raycaster, m_volumes.volumes[i]->world_matrix());
|
||||
const GLVolume* v = m_volumes.volumes[i];
|
||||
assert(v->mesh_raycaster != nullptr);
|
||||
std::shared_ptr<SceneRaycasterItem> raycaster = add_raycaster_for_picking(SceneRaycaster::EType::Volume, i, *v->mesh_raycaster, v->world_matrix());
|
||||
raycaster->set_active(v->is_active);
|
||||
}
|
||||
|
||||
// refresh gizmo elements raycasters for picking
|
||||
|
@ -3458,6 +3460,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
m_gizmos.get_current_type() != GLGizmosManager::FdmSupports &&
|
||||
m_gizmos.get_current_type() != GLGizmosManager::Seam &&
|
||||
m_gizmos.get_current_type() != GLGizmosManager::Cut &&
|
||||
m_gizmos.get_current_type() != GLGizmosManager::Measure &&
|
||||
m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation) {
|
||||
m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect);
|
||||
m_dirty = true;
|
||||
|
@ -3693,7 +3696,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
// if right clicking on volume, propagate event through callback (shows context menu)
|
||||
int volume_idx = get_first_hover_volume_idx();
|
||||
if (!m_volumes.volumes[volume_idx]->is_wipe_tower // no context menu for the wipe tower
|
||||
&& m_gizmos.get_current_type() != GLGizmosManager::SlaSupports) // disable context menu when the gizmo is open
|
||||
&& (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports && m_gizmos.get_current_type() != GLGizmosManager::Measure)) // disable context menu when the gizmo is open
|
||||
{
|
||||
// forces the selection of the volume
|
||||
/* m_selection.add(volume_idx); // #et_FIXME_if_needed
|
||||
|
@ -3717,7 +3720,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
if (!m_mouse.dragging) {
|
||||
// do not post the event if the user is panning the scene
|
||||
// or if right click was done over the wipe tower
|
||||
const bool post_right_click_event = m_hover_volume_idxs.empty() || !m_volumes.volumes[get_first_hover_volume_idx()]->is_wipe_tower;
|
||||
const bool post_right_click_event = (m_hover_volume_idxs.empty() || !m_volumes.volumes[get_first_hover_volume_idx()]->is_wipe_tower) &&
|
||||
m_gizmos.get_current_type() != GLGizmosManager::Measure;
|
||||
if (post_right_click_event)
|
||||
post_event(RBtnEvent(EVT_GLCANVAS_RIGHT_CLICK, { logical_pos, m_hover_volume_idxs.empty() }));
|
||||
}
|
||||
|
@ -5467,12 +5471,11 @@ void GLCanvas3D::_picking_pass()
|
|||
const GLVolume* volume = m_volumes.volumes[hit.raycaster_id];
|
||||
if (volume->is_active && !volume->disabled && (volume->composite_id.volume_id >= 0 || m_render_sla_auxiliaries)) {
|
||||
// do not add the volume id if any gizmo is active and CTRL is pressed
|
||||
if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL)) {
|
||||
if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL))
|
||||
m_hover_volume_idxs.emplace_back(hit.raycaster_id);
|
||||
m_gizmos.set_hover_id(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
assert(false);
|
||||
|
||||
|
@ -5481,7 +5484,7 @@ void GLCanvas3D::_picking_pass()
|
|||
case SceneRaycaster::EType::Gizmo:
|
||||
{
|
||||
const Size& cnv_size = get_canvas_size();
|
||||
bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() &&
|
||||
const bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() &&
|
||||
0 <= m_mouse.position.y() && m_mouse.position.y() < cnv_size.get_height();
|
||||
m_gizmos.set_hover_id(inside ? hit.raycaster_id : -1);
|
||||
break;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#if ENABLE_RAYCAST_PICKING
|
||||
#include "SceneRaycaster.hpp"
|
||||
#endif // ENABLE_RAYCAST_PICKING
|
||||
#include "GUI_Utils.hpp"
|
||||
|
||||
#include "libslic3r/Slicing.hpp"
|
||||
|
||||
|
@ -135,16 +136,6 @@ private:
|
|||
wxTimer* m_timer;
|
||||
};
|
||||
|
||||
class KeyAutoRepeatFilter
|
||||
{
|
||||
size_t m_count{ 0 };
|
||||
|
||||
public:
|
||||
void increase_count() { ++m_count; }
|
||||
void reset_count() { m_count = 0; }
|
||||
bool is_first() const { return m_count == 0; }
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
|
||||
|
||||
using Vec2dEvent = Event<Vec2d>;
|
||||
|
@ -680,7 +671,7 @@ public:
|
|||
|
||||
#if ENABLE_RAYCAST_PICKING
|
||||
std::shared_ptr<SceneRaycasterItem> add_raycaster_for_picking(SceneRaycaster::EType type, int id, const MeshRaycaster& raycaster,
|
||||
const Transform3d& trafo, bool use_back_faces = false) {
|
||||
const Transform3d& trafo = Transform3d::Identity(), bool use_back_faces = false) {
|
||||
return m_scene_raycaster.add_raycaster(type, id, raycaster, trafo, use_back_faces);
|
||||
}
|
||||
void remove_raycasters_for_picking(SceneRaycaster::EType type, int id) {
|
||||
|
|
|
@ -257,6 +257,21 @@ void GLModel::Geometry::remove_vertex(size_t id)
|
|||
}
|
||||
}
|
||||
|
||||
indexed_triangle_set GLModel::Geometry::get_as_indexed_triangle_set() const
|
||||
{
|
||||
indexed_triangle_set its;
|
||||
its.vertices.reserve(vertices_count());
|
||||
for (size_t i = 0; i < vertices_count(); ++i) {
|
||||
its.vertices.emplace_back(extract_position_3(i));
|
||||
}
|
||||
its.indices.reserve(indices_count() / 3);
|
||||
for (size_t i = 0; i < indices_count() / 3; ++i) {
|
||||
const size_t tri_id = i * 3;
|
||||
its.indices.emplace_back(extract_index(tri_id), extract_index(tri_id + 1), extract_index(tri_id + 2));
|
||||
}
|
||||
return its;
|
||||
}
|
||||
|
||||
size_t GLModel::Geometry::vertex_stride_floats(const Format& format)
|
||||
{
|
||||
switch (format.vertex_layout)
|
||||
|
@ -2253,6 +2268,128 @@ GLModel::Geometry smooth_sphere(unsigned int resolution, float radius)
|
|||
|
||||
return data;
|
||||
}
|
||||
|
||||
GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height)
|
||||
{
|
||||
resolution = std::max<unsigned int>(4, resolution);
|
||||
|
||||
const unsigned int sectorCount = resolution;
|
||||
const float sectorStep = 2.0f * float(M_PI) / float(sectorCount);
|
||||
|
||||
GLModel::Geometry data;
|
||||
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
|
||||
data.reserve_vertices(sectorCount * 4 + 2);
|
||||
data.reserve_indices(sectorCount * 4 * 3);
|
||||
|
||||
auto generate_vertices_on_circle = [sectorCount, sectorStep](float radius) {
|
||||
std::vector<Vec3f> ret;
|
||||
ret.reserve(sectorCount);
|
||||
for (unsigned int i = 0; i < sectorCount; ++i) {
|
||||
// from 0 to 2pi
|
||||
const float sectorAngle = sectorStep * i;
|
||||
ret.emplace_back(radius * std::cos(sectorAngle), radius * std::sin(sectorAngle), 0.0f);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
const std::vector<Vec3f> base_vertices = generate_vertices_on_circle(radius);
|
||||
const Vec3f h = height * Vec3f::UnitZ();
|
||||
|
||||
// stem vertices
|
||||
for (unsigned int i = 0; i < sectorCount; ++i) {
|
||||
const Vec3f& v = base_vertices[i];
|
||||
const Vec3f n = v.normalized();
|
||||
data.add_vertex(v, n);
|
||||
data.add_vertex(v + h, n);
|
||||
}
|
||||
|
||||
// stem triangles
|
||||
for (unsigned int i = 0; i < sectorCount; ++i) {
|
||||
unsigned int v1 = i * 2;
|
||||
unsigned int v2 = (i < sectorCount - 1) ? v1 + 2 : 0;
|
||||
unsigned int v3 = v2 + 1;
|
||||
unsigned int v4 = v1 + 1;
|
||||
data.add_triangle(v1, v2, v3);
|
||||
data.add_triangle(v1, v3, v4);
|
||||
}
|
||||
|
||||
// bottom cap vertices
|
||||
Vec3f cap_center = Vec3f::Zero();
|
||||
unsigned int cap_center_id = data.vertices_count();
|
||||
Vec3f normal = -Vec3f::UnitZ();
|
||||
|
||||
data.add_vertex(cap_center, normal);
|
||||
for (unsigned int i = 0; i < sectorCount; ++i) {
|
||||
data.add_vertex(base_vertices[i], normal);
|
||||
}
|
||||
|
||||
// bottom cap triangles
|
||||
for (unsigned int i = 0; i < sectorCount; ++i) {
|
||||
data.add_triangle(cap_center_id, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1, cap_center_id + i + 1);
|
||||
}
|
||||
|
||||
// top cap vertices
|
||||
cap_center += h;
|
||||
cap_center_id = data.vertices_count();
|
||||
normal = -normal;
|
||||
|
||||
data.add_vertex(cap_center, normal);
|
||||
for (unsigned int i = 0; i < sectorCount; ++i) {
|
||||
data.add_vertex(base_vertices[i] + h, normal);
|
||||
}
|
||||
|
||||
// top cap triangles
|
||||
for (unsigned int i = 0; i < sectorCount; ++i) {
|
||||
data.add_triangle(cap_center_id, cap_center_id + i + 1, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness)
|
||||
{
|
||||
const unsigned int torus_sector_count = std::max<unsigned int>(4, primary_resolution);
|
||||
const float torus_sector_step = 2.0f * float(M_PI) / float(torus_sector_count);
|
||||
const unsigned int section_sector_count = std::max<unsigned int>(4, secondary_resolution);
|
||||
const float section_sector_step = 2.0f * float(M_PI) / float(section_sector_count);
|
||||
|
||||
GLModel::Geometry data;
|
||||
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
|
||||
data.reserve_vertices(torus_sector_count * section_sector_count);
|
||||
data.reserve_indices(torus_sector_count * section_sector_count * 2 * 3);
|
||||
|
||||
// vertices
|
||||
for (unsigned int i = 0; i < torus_sector_count; ++i) {
|
||||
const float section_angle = torus_sector_step * i;
|
||||
const float csa = std::cos(section_angle);
|
||||
const float ssa = std::sin(section_angle);
|
||||
const Vec3f section_center(radius * csa, radius * ssa, 0.0f);
|
||||
for (unsigned int j = 0; j < section_sector_count; ++j) {
|
||||
const float circle_angle = section_sector_step * j;
|
||||
const float thickness_xy = thickness * std::cos(circle_angle);
|
||||
const float thickness_z = thickness * std::sin(circle_angle);
|
||||
const Vec3f v(thickness_xy * csa, thickness_xy * ssa, thickness_z);
|
||||
data.add_vertex(section_center + v, (Vec3f)v.normalized());
|
||||
}
|
||||
}
|
||||
|
||||
// triangles
|
||||
for (unsigned int i = 0; i < torus_sector_count; ++i) {
|
||||
const unsigned int ii = i * section_sector_count;
|
||||
const unsigned int ii_next = ((i + 1) % torus_sector_count) * section_sector_count;
|
||||
for (unsigned int j = 0; j < section_sector_count; ++j) {
|
||||
const unsigned int j_next = (j + 1) % section_sector_count;
|
||||
const unsigned int i0 = ii + j;
|
||||
const unsigned int i1 = ii_next + j;
|
||||
const unsigned int i2 = ii_next + j_next;
|
||||
const unsigned int i3 = ii + j_next;
|
||||
data.add_triangle(i0, i1, i2);
|
||||
data.add_triangle(i0, i2, i3);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
@ -140,6 +140,8 @@ namespace GUI {
|
|||
size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); }
|
||||
size_t indices_size_bytes() const { return indices.size() * index_stride_bytes(*this); }
|
||||
|
||||
indexed_triangle_set get_as_indexed_triangle_set() const;
|
||||
|
||||
static size_t vertex_stride_floats(const Format& format);
|
||||
static size_t vertex_stride_bytes(const Format& format) { return vertex_stride_floats(format) * sizeof(float); }
|
||||
|
||||
|
@ -374,9 +376,17 @@ namespace GUI {
|
|||
GLModel::Geometry diamond(unsigned int resolution);
|
||||
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
// create a sphere with the given resolution and smooth normals
|
||||
// create a sphere with smooth normals
|
||||
// the origin of the sphere is in its center
|
||||
GLModel::Geometry smooth_sphere(unsigned int resolution, float radius);
|
||||
// create a cylinder with smooth normals
|
||||
// the axis of the cylinder is the Z axis
|
||||
// the origin of the cylinder is the center of its bottom cap face
|
||||
GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height);
|
||||
// create a torus with smooth normals
|
||||
// the axis of the torus is the Z axis
|
||||
// the origin of the torus is in its center
|
||||
GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness);
|
||||
#endif // ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
@ -1713,11 +1713,16 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
|
|||
new_volume->set_transformation(Geometry::Transformation::volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb));
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
// Set the modifier position.
|
||||
auto offset = (type_name == "Slab") ?
|
||||
// Slab: Lift to print bed
|
||||
Vec3d(0., 0., 0.5 * mesh_bb.size().z() + instance_bb.min.z() - v->get_instance_offset().z()) :
|
||||
Vec3d offset;
|
||||
if (type_name == "Slab") {
|
||||
Vec3d inst_center = instance_bb.center() - v->get_instance_offset();
|
||||
// Slab: Lift to print bed and and push to the center of instance
|
||||
offset = Vec3d(inst_center.x(), inst_center.y(), 0.5 * mesh_bb.size().z() + instance_bb.min.z() - v->get_instance_offset().z());
|
||||
}
|
||||
else {
|
||||
// Translate the new modifier to be pickable: move to the left front corner of the instance's bounding box, lift to print bed.
|
||||
Vec3d(instance_bb.max.x(), instance_bb.min.y(), instance_bb.min.z()) + 0.5 * mesh_bb.size() - v->get_instance_offset();
|
||||
offset = Vec3d(instance_bb.max.x(), instance_bb.min.y(), instance_bb.min.z()) + 0.5 * mesh_bb.size() - v->get_instance_offset();
|
||||
}
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
new_volume->set_offset(v->get_instance_transformation().get_matrix_no_offset().inverse() * offset);
|
||||
#else
|
||||
|
@ -2904,6 +2909,9 @@ static bool can_add_volumes_to_object(const ModelObject* object)
|
|||
|
||||
wxDataViewItemArray ObjectList::add_volumes_to_object_in_list(size_t obj_idx, std::function<bool(const ModelVolume*)> add_to_selection/* = nullptr*/)
|
||||
{
|
||||
const bool is_prevent_list_events = m_prevent_list_events;
|
||||
m_prevent_list_events = true;
|
||||
|
||||
wxDataViewItem object_item = m_objects_model->GetItemById(int(obj_idx));
|
||||
m_objects_model->DeleteVolumeChildren(object_item);
|
||||
|
||||
|
@ -2932,6 +2940,7 @@ wxDataViewItemArray ObjectList::add_volumes_to_object_in_list(size_t obj_idx, st
|
|||
Expand(object_item);
|
||||
}
|
||||
|
||||
m_prevent_list_events = is_prevent_list_events;
|
||||
return items;
|
||||
}
|
||||
|
||||
|
|
|
@ -416,6 +416,16 @@ public:
|
|||
~TaskTimer();
|
||||
};
|
||||
|
||||
class KeyAutoRepeatFilter
|
||||
{
|
||||
size_t m_count{ 0 };
|
||||
|
||||
public:
|
||||
void increase_count() { ++m_count; }
|
||||
void reset_count() { m_count = 0; }
|
||||
bool is_first() const { return m_count == 0; }
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -864,7 +864,10 @@ void GLGizmoCut3D::on_set_state()
|
|||
m_parent.request_extra_frame();
|
||||
}
|
||||
else {
|
||||
m_c->object_clipper()->release();
|
||||
if (auto oc = m_c->object_clipper()) {
|
||||
oc->set_behavior(true, true, 0.);
|
||||
oc->release();
|
||||
}
|
||||
m_selected.clear();
|
||||
}
|
||||
force_update_clipper_on_render = m_state == On;
|
||||
|
@ -1000,7 +1003,7 @@ bool GLGizmoCut3D::on_is_activable() const
|
|||
{
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const int object_idx = selection.get_object_idx();
|
||||
if (object_idx < 0)
|
||||
if (object_idx < 0 || selection.is_wipe_tower())
|
||||
return false;
|
||||
|
||||
bool is_dowel_object = false;
|
||||
|
@ -1391,7 +1394,7 @@ void GLGizmoCut3D::on_render()
|
|||
void GLGizmoCut3D::render_debug_input_window()
|
||||
{
|
||||
m_imgui->begin(wxString("DEBUG"));
|
||||
|
||||
/*
|
||||
static bool hide_clipped = false;
|
||||
static bool fill_cut = false;
|
||||
static float contour_width = 0.4f;
|
||||
|
@ -1405,7 +1408,7 @@ void GLGizmoCut3D::render_debug_input_window()
|
|||
oc->set_behavior(hide_clipped || m_connectors_editing, fill_cut || m_connectors_editing, double(contour_width));
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
*/
|
||||
if (m_imgui->checkbox(_L("Render cut plane as circle"), m_cut_plane_as_circle))
|
||||
m_plane.reset();
|
||||
|
||||
|
@ -1531,6 +1534,14 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
|
|||
unselect_all_connectors();
|
||||
set_connectors_editing(false);
|
||||
}
|
||||
|
||||
ImGui::SameLine(2.75f * m_label_width);
|
||||
|
||||
if (m_imgui->button(_L("Cancel"))) {
|
||||
m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal();
|
||||
reset_connectors();
|
||||
set_connectors_editing(false);
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoCut3D::render_build_size()
|
||||
|
@ -1573,6 +1584,8 @@ void GLGizmoCut3D::set_connectors_editing(bool connectors_editing)
|
|||
m_connectors_editing = connectors_editing;
|
||||
update_raycasters_for_picking();
|
||||
|
||||
m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width));
|
||||
|
||||
m_parent.request_extra_frame();
|
||||
}
|
||||
|
||||
|
@ -1603,6 +1616,13 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
|
|||
reset_cut_plane();
|
||||
m_imgui->disabled_end();
|
||||
|
||||
ImGui::SameLine(2.25f * m_label_width);
|
||||
ImGui::PushItemWidth(0.75f * m_label_width);
|
||||
m_is_contour_changed = m_imgui->slider_float("contour width", &m_contour_width, 0.f, 3.f);
|
||||
|
||||
if (auto oc = m_c->object_clipper(); oc && m_is_contour_changed)
|
||||
oc->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width));
|
||||
|
||||
if (wxGetApp().plater()->printer_technology() == ptFFF) {
|
||||
m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower);
|
||||
if (m_imgui->button(_L("Add/Edit connectors")))
|
||||
|
@ -1641,7 +1661,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
|
|||
|
||||
ImGui::Separator();
|
||||
|
||||
m_imgui->disabled_begin(!can_perform_cut());
|
||||
m_imgui->disabled_begin(!m_is_contour_changed && !can_perform_cut());
|
||||
if(m_imgui->button(_L("Perform cut")))
|
||||
perform_cut(m_parent.get_selection());
|
||||
m_imgui->disabled_end();
|
||||
|
@ -1729,6 +1749,8 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors)
|
|||
|
||||
void GLGizmoCut3D::render_input_window_warning() const
|
||||
{
|
||||
if (m_is_contour_changed)
|
||||
return;
|
||||
if (wxGetApp().plater()->printer_technology() == ptFFF && m_has_invalid_connector) {
|
||||
wxString out = wxString(ImGui::WarningMarkerSmall) + _L("Invalid connectors detected") + ":";
|
||||
if (m_info_stats.outside_cut_contour > size_t(0))
|
||||
|
@ -1829,7 +1851,8 @@ void GLGizmoCut3D::render_connectors()
|
|||
{
|
||||
::glEnable(GL_DEPTH_TEST);
|
||||
|
||||
if (cut_line_processing() || m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info())
|
||||
if (m_is_contour_changed || cut_line_processing() ||
|
||||
m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info())
|
||||
return;
|
||||
|
||||
const ModelObject* mo = m_c->selection_info()->model_object();
|
||||
|
|
|
@ -96,7 +96,7 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||
|
||||
bool m_hide_cut_plane{ false };
|
||||
bool m_connectors_editing{ false };
|
||||
bool m_cut_plane_as_circle{ false };
|
||||
bool m_cut_plane_as_circle{ true };
|
||||
|
||||
float m_connector_depth_ratio{ 3.f };
|
||||
float m_connector_size{ 2.5f };
|
||||
|
@ -109,6 +109,9 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||
bool m_imperial_units{ false };
|
||||
bool force_update_clipper_on_render{false};
|
||||
|
||||
float m_contour_width{ 0.4f };
|
||||
bool m_is_contour_changed{ false };
|
||||
|
||||
mutable std::vector<bool> m_selected; // which pins are currently selected
|
||||
int m_selected_count{ 0 };
|
||||
|
||||
|
@ -170,7 +173,6 @@ public:
|
|||
void put_connectors_on_cut_plane(const Vec3d& cp_normal, double cp_offset);
|
||||
void update_clipper();
|
||||
void update_clipper_on_render();
|
||||
void set_connectors_editing() { m_connectors_editing = true; }
|
||||
void invalidate_cut_plane();
|
||||
|
||||
BoundingBoxf3 bounding_box() const;
|
||||
|
|
|
@ -25,6 +25,8 @@ class GLGizmoFlatten : public GLGizmoBase
|
|||
|
||||
private:
|
||||
|
||||
GLModel arrow;
|
||||
|
||||
struct PlaneData {
|
||||
std::vector<Vec3d> vertices; // should be in fact local in update_planes()
|
||||
#if ENABLE_LEGACY_OPENGL_REMOVAL
|
||||
|
|
|
@ -114,7 +114,7 @@ void GLGizmoHollow::on_register_raycasters_for_picking()
|
|||
if (info != nullptr && !info->model_object()->sla_drain_holes.empty()) {
|
||||
const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes;
|
||||
for (int i = 0; i < (int)drain_holes.size(); ++i) {
|
||||
m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster, Transform3d::Identity()));
|
||||
m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster));
|
||||
}
|
||||
update_raycasters_for_picking_transform();
|
||||
}
|
||||
|
|
1697
src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp
Normal file
1697
src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp
Normal file
File diff suppressed because it is too large
Load diff
164
src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp
Normal file
164
src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp
Normal file
|
@ -0,0 +1,164 @@
|
|||
#ifndef slic3r_GLGizmoMeasure_hpp_
|
||||
#define slic3r_GLGizmoMeasure_hpp_
|
||||
|
||||
#include "GLGizmoBase.hpp"
|
||||
#include "slic3r/GUI/GLModel.hpp"
|
||||
#include "slic3r/GUI/GUI_Utils.hpp"
|
||||
#include "libslic3r/Measure.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class ModelVolume;
|
||||
|
||||
enum class ModelVolumeType : int;
|
||||
|
||||
namespace Measure { class Measuring; }
|
||||
|
||||
|
||||
namespace GUI {
|
||||
|
||||
enum class SLAGizmoEventType : unsigned char;
|
||||
|
||||
class GLGizmoMeasure : public GLGizmoBase
|
||||
{
|
||||
enum class EMode : unsigned char
|
||||
{
|
||||
BasicSelection,
|
||||
ExtendedSelection
|
||||
};
|
||||
|
||||
struct SelectedFeatures
|
||||
{
|
||||
struct Item
|
||||
{
|
||||
std::string source;
|
||||
std::optional<Measure::SurfaceFeature> feature;
|
||||
|
||||
bool operator == (const Item& other) const {
|
||||
if (this->source != other.source) return false;
|
||||
return this->feature == other.feature;
|
||||
}
|
||||
|
||||
bool operator != (const Item& other) const {
|
||||
return !operator == (other);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
source.clear();
|
||||
feature.reset();
|
||||
}
|
||||
};
|
||||
|
||||
Item first;
|
||||
Item second;
|
||||
|
||||
void reset() {
|
||||
first.reset();
|
||||
second.reset();
|
||||
}
|
||||
|
||||
bool operator == (const SelectedFeatures & other) const {
|
||||
if (this->first != other.first) return false;
|
||||
return this->second == other.second;
|
||||
}
|
||||
|
||||
bool operator != (const SelectedFeatures & other) const {
|
||||
return !operator == (other);
|
||||
}
|
||||
};
|
||||
|
||||
EMode m_mode{ EMode::BasicSelection };
|
||||
Measure::MeasurementResult m_measurement_result;
|
||||
|
||||
std::unique_ptr<Measure::Measuring> m_measuring; // PIMPL
|
||||
|
||||
PickingModel m_sphere;
|
||||
PickingModel m_cylinder;
|
||||
PickingModel m_circle;
|
||||
PickingModel m_plane;
|
||||
struct Dimensioning
|
||||
{
|
||||
GLModel line;
|
||||
GLModel triangle;
|
||||
GLModel arc;
|
||||
};
|
||||
Dimensioning m_dimensioning;
|
||||
|
||||
Transform3d m_volume_matrix{ Transform3d::Identity() };
|
||||
std::vector<GLModel> m_plane_models_cache;
|
||||
std::map<int, std::shared_ptr<SceneRaycasterItem>> m_raycasters;
|
||||
std::vector<std::shared_ptr<SceneRaycasterItem>> m_selection_raycasters;
|
||||
std::optional<Measure::SurfaceFeature> m_curr_feature;
|
||||
std::optional<Vec3d> m_curr_point_on_feature_position;
|
||||
struct SceneRaycasterState
|
||||
{
|
||||
std::shared_ptr<SceneRaycasterItem> raycaster{ nullptr };
|
||||
bool state{true};
|
||||
|
||||
};
|
||||
std::vector<SceneRaycasterState> m_scene_raycasters;
|
||||
|
||||
// These hold information to decide whether recalculation is necessary:
|
||||
std::vector<Transform3d> m_volumes_matrices;
|
||||
std::vector<ModelVolumeType> m_volumes_types;
|
||||
Vec3d m_first_instance_scale{ Vec3d::Ones() };
|
||||
Vec3d m_first_instance_mirror{ Vec3d::Ones() };
|
||||
float m_last_inv_zoom{ 0.0f };
|
||||
std::optional<Measure::SurfaceFeature> m_last_circle;
|
||||
int m_last_plane_idx{ -1 };
|
||||
|
||||
bool m_mouse_left_down{ false }; // for detection left_up of this gizmo
|
||||
const ModelObject* m_old_model_object{ nullptr };
|
||||
const ModelVolume* m_old_model_volume{ nullptr };
|
||||
|
||||
Vec2d m_mouse_pos{ Vec2d::Zero() };
|
||||
|
||||
KeyAutoRepeatFilter m_ctrl_kar_filter;
|
||||
|
||||
SelectedFeatures m_selected_features;
|
||||
bool m_pending_scale{ false };
|
||||
bool m_editing_distance{ false };
|
||||
bool m_is_editing_distance_first_frame{ true };
|
||||
|
||||
void update_if_needed();
|
||||
|
||||
void disable_scene_raycasters();
|
||||
void restore_scene_raycasters_state();
|
||||
|
||||
void render_dimensioning();
|
||||
|
||||
#if ENABLE_MEASURE_GIZMO_DEBUG
|
||||
void render_debug_dialog();
|
||||
#endif // ENABLE_MEASURE_GIZMO_DEBUG
|
||||
|
||||
public:
|
||||
GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
||||
|
||||
/// <summary>
|
||||
/// Apply rotation on select plane
|
||||
/// </summary>
|
||||
/// <param name="mouse_event">Keep information about mouse click</param>
|
||||
/// <returns>Return True when use the information otherwise False.</returns>
|
||||
bool on_mouse(const wxMouseEvent &mouse_event) override;
|
||||
|
||||
void data_changed() override;
|
||||
|
||||
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
|
||||
|
||||
protected:
|
||||
bool on_init() override;
|
||||
std::string on_get_name() const override;
|
||||
bool on_is_activable() const override;
|
||||
void on_render() override;
|
||||
void on_set_state() override;
|
||||
CommonGizmosDataID on_get_requirements() const override;
|
||||
|
||||
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
|
||||
virtual void on_register_raycasters_for_picking() override;
|
||||
virtual void on_unregister_raycasters_for_picking() override;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_GLGizmoMeasure_hpp_
|
|
@ -148,8 +148,8 @@ void GLGizmoSlaSupports::on_register_raycasters_for_picking()
|
|||
|
||||
if (m_editing_mode && !m_editing_cache.empty()) {
|
||||
for (size_t i = 0; i < m_editing_cache.size(); ++i) {
|
||||
m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster, Transform3d::Identity()),
|
||||
m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster, Transform3d::Identity()));
|
||||
m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster),
|
||||
m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster));
|
||||
}
|
||||
update_raycasters_for_picking_transform();
|
||||
}
|
||||
|
|
|
@ -119,21 +119,25 @@ void SelectionInfo::on_update()
|
|||
const Selection& selection = get_pool()->get_canvas()->get_selection();
|
||||
if (selection.is_single_full_instance()) {
|
||||
m_model_object = selection.get_model()->objects[selection.get_object_idx()];
|
||||
m_model_volume = nullptr;
|
||||
m_z_shift = selection.get_first_volume()->get_sla_shift_z();
|
||||
}
|
||||
else
|
||||
else {
|
||||
m_model_object = nullptr;
|
||||
if (selection.is_single_volume())
|
||||
m_model_volume = selection.get_model()->objects[selection.get_object_idx()]->volumes[selection.get_first_volume()->volume_idx()];
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionInfo::on_release()
|
||||
{
|
||||
m_model_object = nullptr;
|
||||
m_model_volume = nullptr;
|
||||
}
|
||||
|
||||
int SelectionInfo::get_active_instance() const
|
||||
{
|
||||
const Selection& selection = get_pool()->get_canvas()->get_selection();
|
||||
return selection.get_instance_idx();
|
||||
return get_pool()->get_canvas()->get_selection().get_instance_idx();
|
||||
}
|
||||
|
||||
|
||||
|
@ -329,12 +333,18 @@ void Raycaster::on_update()
|
|||
{
|
||||
wxBusyCursor wait;
|
||||
const ModelObject* mo = get_pool()->selection_info()->model_object();
|
||||
const ModelVolume* mv = get_pool()->selection_info()->model_volume();
|
||||
|
||||
if (! mo)
|
||||
if (mo == nullptr && mv == nullptr)
|
||||
return;
|
||||
|
||||
std::vector<ModelVolume*> mvs;
|
||||
if (mv != nullptr)
|
||||
mvs.push_back(const_cast<ModelVolume*>(mv));
|
||||
else
|
||||
mvs = mo->volumes;
|
||||
|
||||
std::vector<const TriangleMesh*> meshes;
|
||||
const std::vector<ModelVolume*>& mvs = mo->volumes;
|
||||
if (mvs.size() == 1) {
|
||||
assert(mvs.front()->is_model_part());
|
||||
const HollowedMesh* hollowed_mesh_tracker = get_pool()->hollowed_mesh();
|
||||
|
@ -342,9 +352,9 @@ void Raycaster::on_update()
|
|||
meshes.push_back(hollowed_mesh_tracker->get_hollowed_mesh());
|
||||
}
|
||||
if (meshes.empty()) {
|
||||
for (const ModelVolume* mv : mvs) {
|
||||
if (mv->is_model_part())
|
||||
meshes.push_back(&mv->mesh());
|
||||
for (const ModelVolume* v : mvs) {
|
||||
if (v->is_model_part())
|
||||
meshes.push_back(&v->mesh());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
namespace Slic3r {
|
||||
|
||||
class ModelObject;
|
||||
|
||||
class ModelVolume;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
|
@ -24,6 +24,8 @@ enum class SLAGizmoEventType : unsigned char {
|
|||
Dragging,
|
||||
Delete,
|
||||
SelectAll,
|
||||
CtrlDown,
|
||||
CtrlUp,
|
||||
ShiftUp,
|
||||
AltUp,
|
||||
ApplyChanges,
|
||||
|
@ -153,7 +155,10 @@ public:
|
|||
explicit SelectionInfo(CommonGizmosDataPool* cgdp)
|
||||
: CommonGizmosDataBase(cgdp) {}
|
||||
|
||||
// Returns a non-null pointer if the selection is a single full instance
|
||||
ModelObject* model_object() const { return m_model_object; }
|
||||
// Returns a non-null pointer if the selection is a single volume
|
||||
ModelVolume* model_volume() const { return m_model_volume; }
|
||||
int get_active_instance() const;
|
||||
float get_sla_shift() const { return m_z_shift; }
|
||||
|
||||
|
@ -163,6 +168,7 @@ protected:
|
|||
|
||||
private:
|
||||
ModelObject* m_model_object = nullptr;
|
||||
ModelVolume* m_model_volume = nullptr;
|
||||
// int m_active_inst = -1;
|
||||
float m_z_shift = 0.f;
|
||||
};
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoEmboss.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp"
|
||||
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
@ -106,6 +107,7 @@ bool GLGizmosManager::init()
|
|||
m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "fdm_supports.svg", 7));
|
||||
m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8));
|
||||
m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "mmu_segmentation.svg", 9));
|
||||
m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, "measure.svg", 10));
|
||||
m_gizmos.emplace_back(new GLGizmoEmboss(m_parent));
|
||||
m_gizmos.emplace_back(new GLGizmoSimplify(m_parent));
|
||||
|
||||
|
@ -290,6 +292,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
|
|||
return dynamic_cast<GLGizmoSeam*>(m_gizmos[Seam].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == MmuSegmentation)
|
||||
return dynamic_cast<GLGizmoMmuSegmentation*>(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == Measure)
|
||||
return dynamic_cast<GLGizmoMeasure*>(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == Cut)
|
||||
return dynamic_cast<GLGizmoCut3D*>(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else
|
||||
|
@ -497,8 +501,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
|
|||
|
||||
bool processed = false;
|
||||
|
||||
if ((evt.GetModifiers() & ctrlMask) != 0)
|
||||
{
|
||||
if ((evt.GetModifiers() & ctrlMask) != 0) {
|
||||
switch (keyCode)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
|
@ -516,15 +519,13 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (!evt.HasModifiers())
|
||||
{
|
||||
else if (!evt.HasModifiers()) {
|
||||
switch (keyCode)
|
||||
{
|
||||
// key ESC
|
||||
case WXK_ESCAPE:
|
||||
{
|
||||
if (m_current != Undefined)
|
||||
{
|
||||
if (m_current != Undefined) {
|
||||
if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges))
|
||||
reset_all_states();
|
||||
|
||||
|
@ -561,8 +562,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
|
|||
case 'A':
|
||||
case 'a':
|
||||
{
|
||||
if (m_current == SlaSupports)
|
||||
{
|
||||
if (m_current == SlaSupports) {
|
||||
gizmo_event(SLAGizmoEventType::AutomaticGeneration);
|
||||
// set as processed no matter what's returned by gizmo_event() to avoid the calling canvas to process 'A' as arrange
|
||||
processed = true;
|
||||
|
@ -580,8 +580,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
|
|||
case 'F':
|
||||
case 'f':
|
||||
{
|
||||
if (m_current == Scale)
|
||||
{
|
||||
if (m_current == Scale) {
|
||||
if (!is_dragging())
|
||||
wxGetApp().plater()->scale_selection_to_fit_print_volume();
|
||||
|
||||
|
@ -593,8 +592,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
|
|||
}
|
||||
}
|
||||
|
||||
if (!processed && !evt.HasModifiers())
|
||||
{
|
||||
if (!processed && !evt.HasModifiers()) {
|
||||
if (handle_shortcut(keyCode))
|
||||
processed = true;
|
||||
}
|
||||
|
@ -618,19 +616,20 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
|
|||
const bool is_editing = m_current == Hollow ? true : gizmo->is_in_editing_mode();
|
||||
const bool is_rectangle_dragging = gizmo->is_selection_rectangle_dragging();
|
||||
|
||||
if (keyCode == WXK_SHIFT)
|
||||
{
|
||||
if (keyCode == WXK_SHIFT) {
|
||||
// shift has been just released - SLA gizmo might want to close rectangular selection.
|
||||
if (gizmo_event(SLAGizmoEventType::ShiftUp) || (is_editing && is_rectangle_dragging))
|
||||
processed = true;
|
||||
}
|
||||
else if (keyCode == WXK_ALT)
|
||||
{
|
||||
else if (keyCode == WXK_ALT) {
|
||||
// alt has been just released - SLA gizmo might want to close rectangular selection.
|
||||
if (gizmo_event(SLAGizmoEventType::AltUp) || (is_editing && is_rectangle_dragging))
|
||||
processed = true;
|
||||
}
|
||||
}
|
||||
else if (m_current == Measure && keyCode == WXK_CONTROL) {
|
||||
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), false);
|
||||
}
|
||||
|
||||
// if (processed)
|
||||
// m_parent.set_cursor(GLCanvas3D::Standard);
|
||||
|
@ -643,8 +642,7 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
|
|||
// m_parent.set_cursor(GLCanvas3D::Cross);
|
||||
processed = true;
|
||||
}
|
||||
else if (m_current == Cut)
|
||||
{
|
||||
else if (m_current == Cut) {
|
||||
auto do_move = [this, &processed](double delta_z) {
|
||||
GLGizmoCut3D* cut = dynamic_cast<GLGizmoCut3D*>(get_current());
|
||||
cut->shift_cut_z(delta_z);
|
||||
|
@ -660,11 +658,15 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
|
|||
}
|
||||
default: { break; }
|
||||
}
|
||||
} else if (m_current == Simplify && keyCode == WXK_ESCAPE) {
|
||||
}
|
||||
else if (m_current == Simplify && keyCode == WXK_ESCAPE) {
|
||||
GLGizmoSimplify *simplify = dynamic_cast<GLGizmoSimplify *>(get_current());
|
||||
if (simplify != nullptr)
|
||||
processed = simplify->on_esc_key_down();
|
||||
}
|
||||
else if (m_current == Measure && keyCode == WXK_CONTROL) {
|
||||
gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), true);
|
||||
}
|
||||
}
|
||||
|
||||
if (processed)
|
||||
|
|
|
@ -81,6 +81,7 @@ public:
|
|||
MmuSegmentation,
|
||||
Emboss,
|
||||
Simplify,
|
||||
Measure,
|
||||
Undefined
|
||||
};
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ static const std::map<const wchar_t, std::string> font_icons = {
|
|||
{ImGui::PreferencesHoverButton, "notification_preferences_hover"},
|
||||
{ImGui::SliderFloatEditBtnIcon, "edit_button" },
|
||||
{ImGui::SliderFloatEditBtnPressedIcon, "edit_button_pressed" },
|
||||
{ImGui::ClipboardBtnIcon , "copy_menu" },
|
||||
{ImGui::ExpandBtn , "expand_btn" },
|
||||
{ImGui::CollapseBtn , "collapse_btn" },
|
||||
{ImGui::RevertButton , "undo" },
|
||||
|
@ -460,36 +461,6 @@ bool ImGuiWrapper::draw_radio_button(const std::string& name, float size, bool a
|
|||
return pressed;
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format)
|
||||
{
|
||||
return ImGui::InputDouble(label.c_str(), const_cast<double*>(&value), 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::input_double(const wxString &label, const double &value, const std::string &format)
|
||||
{
|
||||
auto label_utf8 = into_u8(label);
|
||||
return input_double(label_utf8, value, format);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format)
|
||||
{
|
||||
bool value_changed = false;
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
std::string item_label = (i == 0) ? "X" : ((i == 1) ? "Y" : "Z");
|
||||
ImGui::PushID(i);
|
||||
ImGui::PushItemWidth(width);
|
||||
value_changed |= ImGui::InputDouble(item_label.c_str(), const_cast<double*>(&value(i)), 0.0f, 0.0f, format.c_str());
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
return value_changed;
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::checkbox(const wxString &label, bool &value)
|
||||
{
|
||||
auto label_utf8 = into_u8(label);
|
||||
|
@ -548,20 +519,20 @@ void ImGuiWrapper::text_wrapped(const wxString &label, float wrap_width)
|
|||
|
||||
void ImGuiWrapper::tooltip(const char *label, float wrap_width)
|
||||
{
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 4.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 8.0f, 8.0f });
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(wrap_width);
|
||||
ImGui::TextUnformatted(label);
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
ImGui::PopStyleVar(3);
|
||||
}
|
||||
|
||||
void ImGuiWrapper::tooltip(const wxString &label, float wrap_width)
|
||||
{
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(wrap_width);
|
||||
ImGui::TextUnformatted(label.ToUTF8().data());
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
tooltip(label.ToUTF8().data(), wrap_width);
|
||||
}
|
||||
|
||||
ImVec2 ImGuiWrapper::get_slider_icon_size() const
|
||||
|
@ -703,6 +674,29 @@ bool ImGuiWrapper::image_button(ImTextureID user_texture_id, const ImVec2& size,
|
|||
return image_button_ex(id, user_texture_id, size, uv0, uv1, padding, bg_col, tint_col, flags);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::image_button(const wchar_t icon, const wxString& tooltip)
|
||||
{
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
const ImTextureID tex_id = io.Fonts->TexID;
|
||||
assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0);
|
||||
const float inv_tex_w = 1.0f / float(io.Fonts->TexWidth);
|
||||
const float inv_tex_h = 1.0f / float(io.Fonts->TexHeight);
|
||||
const ImFontAtlasCustomRect* const rect = GetTextureCustomRect(icon);
|
||||
const ImVec2 size = { float(rect->Width), float(rect->Height) };
|
||||
const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h);
|
||||
const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, 1.0f });
|
||||
const bool res = image_button(tex_id, size, uv0, uv1);
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
if (!tooltip.empty() && ImGui::IsItemHovered())
|
||||
this->tooltip(tooltip, ImGui::GetFontSize() * 20.0f);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags)
|
||||
{
|
||||
// this is to force the label to the left of the widget:
|
||||
|
@ -1165,6 +1159,11 @@ ImFontAtlasCustomRect* ImGuiWrapper::GetTextureCustomRect(const wchar_t& tex_id)
|
|||
return (item != m_custom_glyph_rects_ids.end()) ? ImGui::GetIO().Fonts->GetCustomRectByIndex(m_custom_glyph_rects_ids[tex_id]) : nullptr;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::disable_background_fadeout_animation()
|
||||
{
|
||||
GImGui->DimBgRatio = 1.0f;
|
||||
}
|
||||
|
||||
ImU32 ImGuiWrapper::to_ImU32(const ColorRGBA& color)
|
||||
{
|
||||
return ImGui::GetColorU32({ color.r(), color.g(), color.b(), color.a() });
|
||||
|
|
|
@ -96,9 +96,6 @@ public:
|
|||
bool button(const wxString& label, const ImVec2 &size, bool enable); // default size = ImVec2(0.f, 0.f)
|
||||
bool radio_button(const wxString &label, bool active);
|
||||
bool draw_radio_button(const std::string& name, float size, bool active, std::function<void(ImGuiWindow& window, const ImVec2& pos, float size)> draw_callback);
|
||||
bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f");
|
||||
bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f");
|
||||
bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f");
|
||||
bool checkbox(const wxString &label, bool &value);
|
||||
static void text(const char *label);
|
||||
static void text(const std::string &label);
|
||||
|
@ -119,6 +116,7 @@ public:
|
|||
bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true);
|
||||
|
||||
bool image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0.0, 0.0), const ImVec2& uv1 = ImVec2(1.0, 1.0), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0.0, 0.0, 0.0, 0.0), const ImVec4& tint_col = ImVec4(1.0, 1.0, 1.0, 1.0), ImGuiButtonFlags flags = 0);
|
||||
bool image_button(const wchar_t icon, const wxString& tooltip = L"");
|
||||
|
||||
// Use selection = -1 to not mark any option as selected
|
||||
bool combo(const wxString& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags = 0);
|
||||
|
@ -210,6 +208,8 @@ public:
|
|||
void set_requires_extra_frame() { m_requires_extra_frame = true; }
|
||||
void reset_requires_extra_frame() { m_requires_extra_frame = false; }
|
||||
|
||||
void disable_background_fadeout_animation();
|
||||
|
||||
static ImU32 to_ImU32(const ColorRGBA& color);
|
||||
static ImVec4 to_ImVec4(const ColorRGBA& color);
|
||||
static ColorRGBA from_ImU32(const ImU32& color);
|
||||
|
|
|
@ -538,7 +538,7 @@ void MainFrame::update_layout()
|
|||
case ESettingsLayout::GCodeViewer:
|
||||
{
|
||||
m_main_sizer->Add(m_plater, 1, wxEXPAND);
|
||||
m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, 0.0, {}, {}, true);
|
||||
m_plater->set_default_bed_shape();
|
||||
m_plater->get_collapse_toolbar().set_enabled(false);
|
||||
m_plater->collapse_sidebar(true);
|
||||
m_plater->Show();
|
||||
|
|
|
@ -19,6 +19,14 @@ wxDEFINE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent);
|
|||
|
||||
BitmapCache* m_bitmap_cache = nullptr;
|
||||
|
||||
wxBitmapBundle* find_bndl(const std::string& bmp_name)
|
||||
{
|
||||
if (!m_bitmap_cache)
|
||||
m_bitmap_cache = new BitmapCache;
|
||||
|
||||
return m_bitmap_cache->find_bndl(bmp_name);
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// ----------------------------------------------------------------------------
|
||||
// ObjectDataViewModelNode
|
||||
|
@ -188,7 +196,7 @@ void ObjectDataViewModelNode::update_settings_digest_bitmaps()
|
|||
std::string scaled_bitmap_name = m_name.ToUTF8().data();
|
||||
scaled_bitmap_name += (wxGetApp().dark_mode() ? "-dm" : "");
|
||||
|
||||
wxBitmapBundle *bmp = m_bitmap_cache->find_bndl(scaled_bitmap_name);
|
||||
wxBitmapBundle *bmp = find_bndl(scaled_bitmap_name);
|
||||
if (bmp == nullptr) {
|
||||
std::vector<wxBitmapBundle*> bmps;
|
||||
for (auto& category : m_opt_categories)
|
||||
|
@ -323,8 +331,6 @@ static int get_root_idx(ObjectDataViewModelNode *parent_node, const ItemType roo
|
|||
|
||||
ObjectDataViewModel::ObjectDataViewModel()
|
||||
{
|
||||
m_bitmap_cache = new Slic3r::GUI::BitmapCache;
|
||||
|
||||
m_volume_bmps = MenuFactory::get_volume_bitmaps();
|
||||
m_text_volume_bmps = MenuFactory::get_text_volume_bitmaps();
|
||||
m_warning_bmp = *get_bmp_bundle(WarningIcon);
|
||||
|
@ -362,7 +368,7 @@ void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode* node)
|
|||
scaled_bitmap_name += std::to_string(vol_type);
|
||||
scaled_bitmap_name += (wxGetApp().dark_mode() ? "-dm" : "-lm");
|
||||
|
||||
wxBitmapBundle* bmp = m_bitmap_cache->find_bndl(scaled_bitmap_name);
|
||||
wxBitmapBundle* bmp = find_bndl(scaled_bitmap_name);
|
||||
if (!bmp) {
|
||||
std::vector<wxBitmapBundle*> bmps;
|
||||
if (node->has_warning_icon())
|
||||
|
|
|
@ -3034,6 +3034,7 @@ void Plater::priv::delete_all_objects_from_model()
|
|||
gcode_result.reset();
|
||||
|
||||
view3D->get_canvas3d()->reset_sequential_print_clearance();
|
||||
view3D->get_canvas3d()->reset_all_gizmos();
|
||||
|
||||
m_worker.cancel_all();
|
||||
|
||||
|
@ -4180,19 +4181,14 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
|
|||
combo->update();
|
||||
}
|
||||
else if (select_preset) {
|
||||
if (preset_type == Preset::TYPE_PRINTER) {
|
||||
PhysicalPrinterCollection& physical_printers = wxGetApp().preset_bundle->physical_printers;
|
||||
if(combo->is_selected_physical_printer())
|
||||
preset_name = physical_printers.get_selected_printer_preset_name();
|
||||
else
|
||||
physical_printers.unselect_printer();
|
||||
}
|
||||
wxWindowUpdateLocker noUpdates(sidebar->presets_panel());
|
||||
wxGetApp().get_tab(preset_type)->select_preset(preset_name);
|
||||
}
|
||||
|
||||
if (preset_type != Preset::TYPE_PRINTER || select_preset) {
|
||||
// update plater with new config
|
||||
q->on_config_change(wxGetApp().preset_bundle->full_config());
|
||||
}
|
||||
if (preset_type == Preset::TYPE_PRINTER) {
|
||||
/* Settings list can be changed after printer preset changing, so
|
||||
* update all settings items for all item had it.
|
||||
|
@ -4948,9 +4944,13 @@ bool Plater::priv::can_increase_instances() const
|
|||
|| q->canvas3D()->get_gizmos_manager().is_in_editing_mode())
|
||||
return false;
|
||||
|
||||
// Disallow arrange and add instance when emboss gizmo is opend
|
||||
// Prevent strobo effect during editing emboss parameters.
|
||||
if (q->canvas3D()->get_gizmos_manager().get_current_type() == GLGizmosManager::Emboss) return false;
|
||||
|
||||
return !sidebar->obj_list()->has_selected_cut_object();
|
||||
const int obj_idx = get_selected_object_idx();
|
||||
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) &&
|
||||
!sidebar->obj_list()->has_selected_cut_object();
|
||||
}
|
||||
|
||||
bool Plater::priv::can_decrease_instances() const
|
||||
|
@ -4959,7 +4959,10 @@ bool Plater::priv::can_decrease_instances() const
|
|||
|| q->canvas3D()->get_gizmos_manager().is_in_editing_mode())
|
||||
return false;
|
||||
|
||||
return !sidebar->obj_list()->has_selected_cut_object();
|
||||
const int obj_idx = get_selected_object_idx();
|
||||
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) &&
|
||||
(model.objects[obj_idx]->instances.size() > 1) &&
|
||||
!sidebar->obj_list()->has_selected_cut_object();
|
||||
}
|
||||
|
||||
bool Plater::priv::can_split_to_objects() const
|
||||
|
@ -5490,10 +5493,27 @@ void Plater::load_gcode(const wxString& filename)
|
|||
p->gcode_result = std::move(processor.extract_result());
|
||||
|
||||
// show results
|
||||
try
|
||||
{
|
||||
p->preview->reload_print(false);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
wxEndBusyCursor();
|
||||
p->gcode_result.reset();
|
||||
reset_gcode_toolpaths();
|
||||
set_default_bed_shape();
|
||||
p->preview->reload_print(false);
|
||||
p->get_current_canvas3D()->render();
|
||||
MessageDialog(this, _L("The selected file") + ":\n" + filename + "\n" + _L("does not contain valid gcode."),
|
||||
wxString(GCODEVIEWER_APP_NAME) + " - " + _L("Error while loading .gcode file"), wxOK | wxICON_WARNING | wxCENTRE).ShowModal();
|
||||
set_project_filename(wxEmptyString);
|
||||
return;
|
||||
}
|
||||
p->preview->get_canvas3d()->zoom_to_gcode();
|
||||
|
||||
if (p->preview->get_canvas3d()->get_gcode_layers_zs().empty()) {
|
||||
wxEndBusyCursor();
|
||||
//wxMessageDialog(this, _L("The selected file") + ":\n" + filename + "\n" + _L("does not contain valid gcode."),
|
||||
MessageDialog(this, _L("The selected file") + ":\n" + filename + "\n" + _L("does not contain valid gcode."),
|
||||
wxString(GCODEVIEWER_APP_NAME) + " - " + _L("Error while loading .gcode file"), wxOK | wxICON_WARNING | wxCENTRE).ShowModal();
|
||||
|
@ -6659,6 +6679,11 @@ void Plater::set_bed_shape(const Pointfs& shape, const double max_print_height,
|
|||
p->set_bed_shape(shape, max_print_height, custom_texture, custom_model, force_as_custom);
|
||||
}
|
||||
|
||||
void Plater::set_default_bed_shape() const
|
||||
{
|
||||
set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, 0.0, {}, {}, true);
|
||||
}
|
||||
|
||||
void Plater::force_filament_colors_update()
|
||||
{
|
||||
bool update_scheduled = false;
|
||||
|
|
|
@ -399,6 +399,7 @@ public:
|
|||
|
||||
void set_bed_shape() const;
|
||||
void set_bed_shape(const Pointfs& shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const;
|
||||
void set_default_bed_shape() const;
|
||||
|
||||
NotificationManager * get_notification_manager();
|
||||
const NotificationManager * get_notification_manager() const;
|
||||
|
|
|
@ -515,12 +515,23 @@ bool PresetComboBox::is_selected_physical_printer()
|
|||
|
||||
bool PresetComboBox::selection_is_changed_according_to_physical_printers()
|
||||
{
|
||||
if (m_type != Preset::TYPE_PRINTER || !is_selected_physical_printer())
|
||||
if (m_type != Preset::TYPE_PRINTER)
|
||||
return false;
|
||||
|
||||
const std::string selected_string = into_u8(this->GetString(this->GetSelection()));
|
||||
PhysicalPrinterCollection& physical_printers = m_preset_bundle->physical_printers;
|
||||
Tab* tab = wxGetApp().get_tab(Preset::TYPE_PRINTER);
|
||||
|
||||
std::string selected_string = this->GetString(this->GetSelection()).ToUTF8().data();
|
||||
if (!is_selected_physical_printer()) {
|
||||
if (!physical_printers.has_selection())
|
||||
return false;
|
||||
|
||||
const bool is_changed = selected_string == physical_printers.get_selected_printer_preset_name();
|
||||
physical_printers.unselect_printer();
|
||||
if (is_changed)
|
||||
tab->select_preset(selected_string);
|
||||
return is_changed;
|
||||
}
|
||||
|
||||
std::string old_printer_full_name, old_printer_preset;
|
||||
if (physical_printers.has_selection()) {
|
||||
|
@ -535,20 +546,21 @@ bool PresetComboBox::selection_is_changed_according_to_physical_printers()
|
|||
|
||||
// if new preset wasn't selected, there is no need to call update preset selection
|
||||
if (old_printer_preset == preset_name) {
|
||||
tab->update_preset_choice();
|
||||
wxGetApp().plater()->show_action_buttons(false);
|
||||
|
||||
// we need just to update according Plater<->Tab PresetComboBox
|
||||
if (dynamic_cast<PlaterPresetComboBox*>(this)!=nullptr) {
|
||||
wxGetApp().get_tab(m_type)->update_preset_choice();
|
||||
// Synchronize config.ini with the current selections.
|
||||
m_preset_bundle->export_selections(*wxGetApp().app_config);
|
||||
this->update();
|
||||
}
|
||||
else if (dynamic_cast<TabPresetComboBox*>(this)!=nullptr)
|
||||
wxGetApp().sidebar().update_presets(m_type);
|
||||
|
||||
this->update();
|
||||
return true;
|
||||
}
|
||||
|
||||
Tab* tab = wxGetApp().get_tab(Preset::TYPE_PRINTER);
|
||||
if (tab)
|
||||
tab->select_preset(preset_name, false, old_printer_full_name);
|
||||
return true;
|
||||
|
|
|
@ -173,6 +173,31 @@ void SceneRaycaster::render_hit(const Camera& camera)
|
|||
|
||||
shader->stop_using();
|
||||
}
|
||||
|
||||
size_t SceneRaycaster::active_beds_count() const {
|
||||
size_t count = 0;
|
||||
for (const auto& b : m_bed) {
|
||||
if (b->is_active())
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
size_t SceneRaycaster::active_volumes_count() const {
|
||||
size_t count = 0;
|
||||
for (const auto& v : m_volumes) {
|
||||
if (v->is_active())
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
size_t SceneRaycaster::active_gizmos_count() const {
|
||||
size_t count = 0;
|
||||
for (const auto& g : m_gizmos) {
|
||||
if (g->is_active())
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
|
||||
std::vector<std::shared_ptr<SceneRaycasterItem>>* SceneRaycaster::get_raycasters(EType type)
|
||||
|
|
|
@ -105,9 +105,10 @@ public:
|
|||
size_t active_gizmos_count() const;
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
|
||||
static int decode_id(EType type, int id);
|
||||
|
||||
private:
|
||||
static int encode_id(EType type, int id);
|
||||
static int decode_id(EType type, int id);
|
||||
static int base_id(EType type);
|
||||
};
|
||||
|
||||
|
|
|
@ -594,8 +594,6 @@ void SearchDialog::ProcessSelection(wxDataViewItem selection)
|
|||
|
||||
void SearchDialog::OnInputText(wxCommandEvent&)
|
||||
{
|
||||
search_line->SetInsertionPointEnd();
|
||||
|
||||
wxString input_string = search_line->GetValue();
|
||||
if (input_string == default_string)
|
||||
input_string.Clear();
|
||||
|
|
|
@ -330,6 +330,7 @@ public:
|
|||
#if ENABLE_WORLD_COORDINATE
|
||||
bool is_single_volume_or_modifier() const { return is_single_volume() || is_single_modifier(); }
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
bool is_single_volume_instance() const { return is_single_full_instance() && m_list.size() == 1; }
|
||||
bool is_single_text() const;
|
||||
|
||||
bool contains_volume(unsigned int volume_idx) const { return m_list.find(volume_idx) != m_list.end(); }
|
||||
|
|
|
@ -215,26 +215,30 @@ void Tab::create_preset_tab()
|
|||
sizer->Add(m_hsizer, 0, wxEXPAND | wxBOTTOM, 3);
|
||||
m_hsizer->Add(m_presets_choice, 0, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3);
|
||||
m_hsizer->AddSpacer(int(4*scale_factor));
|
||||
m_hsizer->Add(m_btn_save_preset, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_hsizer->AddSpacer(int(4*scale_factor));
|
||||
m_hsizer->Add(m_btn_rename_preset, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_hsizer->AddSpacer(int(4 * scale_factor));
|
||||
m_hsizer->Add(m_btn_delete_preset, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
m_h_buttons_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_h_buttons_sizer->Add(m_btn_save_preset, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_h_buttons_sizer->AddSpacer(int(4*scale_factor));
|
||||
m_h_buttons_sizer->Add(m_btn_rename_preset, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_h_buttons_sizer->AddSpacer(int(4 * scale_factor));
|
||||
m_h_buttons_sizer->Add(m_btn_delete_preset, 0, wxALIGN_CENTER_VERTICAL);
|
||||
if (m_btn_edit_ph_printer) {
|
||||
m_hsizer->AddSpacer(int(4 * scale_factor));
|
||||
m_hsizer->Add(m_btn_edit_ph_printer, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_h_buttons_sizer->AddSpacer(int(4 * scale_factor));
|
||||
m_h_buttons_sizer->Add(m_btn_edit_ph_printer, 0, wxALIGN_CENTER_VERTICAL);
|
||||
}
|
||||
m_hsizer->AddSpacer(int(/*16*/8 * scale_factor));
|
||||
m_hsizer->Add(m_btn_hide_incompatible_presets, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_hsizer->AddSpacer(int(8 * scale_factor));
|
||||
m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_hsizer->AddSpacer(int(32 * scale_factor));
|
||||
m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_hsizer->AddSpacer(int(32 * scale_factor));
|
||||
m_hsizer->Add(m_search_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_hsizer->AddSpacer(int(8*scale_factor));
|
||||
m_hsizer->Add(m_btn_compare_preset, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_h_buttons_sizer->AddSpacer(int(/*16*/8 * scale_factor));
|
||||
m_h_buttons_sizer->Add(m_btn_hide_incompatible_presets, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_h_buttons_sizer->AddSpacer(int(8 * scale_factor));
|
||||
m_h_buttons_sizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_h_buttons_sizer->AddSpacer(int(32 * scale_factor));
|
||||
m_h_buttons_sizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_h_buttons_sizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_h_buttons_sizer->AddSpacer(int(32 * scale_factor));
|
||||
m_h_buttons_sizer->Add(m_search_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_h_buttons_sizer->AddSpacer(int(8*scale_factor));
|
||||
m_h_buttons_sizer->Add(m_btn_compare_preset, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
m_hsizer->Add(m_h_buttons_sizer, 1, wxEXPAND);
|
||||
m_hsizer->AddSpacer(int(16*scale_factor));
|
||||
// m_hsizer->AddStretchSpacer(32);
|
||||
// StretchSpacer has a strange behavior under OSX, so
|
||||
|
@ -3192,6 +3196,7 @@ void Tab::update_btns_enabling()
|
|||
if (m_btn_edit_ph_printer)
|
||||
m_btn_edit_ph_printer->SetToolTip( m_preset_bundle->physical_printers.has_selection() ?
|
||||
_L("Edit physical printer") : _L("Add physical printer"));
|
||||
m_h_buttons_sizer->Layout();
|
||||
}
|
||||
|
||||
void Tab::update_preset_choice()
|
||||
|
|
|
@ -173,6 +173,7 @@ protected:
|
|||
ScalableButton* m_btn_edit_ph_printer {nullptr};
|
||||
ScalableButton* m_btn_hide_incompatible_presets;
|
||||
wxBoxSizer* m_hsizer;
|
||||
wxBoxSizer* m_h_buttons_sizer;
|
||||
wxBoxSizer* m_left_sizer;
|
||||
wxTreeCtrl* m_treectrl;
|
||||
|
||||
|
|
|
@ -7,12 +7,35 @@
|
|||
// though C++20 format uses a different template pattern for position independent parameters.
|
||||
// This wrapper also manages implicit conversion from wxString to UTF8 and format_wxstr() variants are provided to format into wxString.
|
||||
|
||||
#include <libslic3r/format.hpp>
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
namespace Slic3r::internal::format {
|
||||
// Wrapper around wxScopedCharBuffer to indicate that the content is UTF8 formatted.
|
||||
struct utf8_buffer {
|
||||
// wxScopedCharBuffer is reference counted, therefore copying by value is cheap.
|
||||
wxScopedCharBuffer data;
|
||||
};
|
||||
inline std::ostream& operator<<(std::ostream& os, const utf8_buffer &v) {
|
||||
os << v.data.data();
|
||||
return os;
|
||||
}
|
||||
// Accept wxString and convert it to UTF8 to be processed by Slic3r::format().
|
||||
inline const utf8_buffer cook(const wxString& arg) {
|
||||
return utf8_buffer{ arg.ToUTF8() };
|
||||
}
|
||||
// Vojtech seemingly does not understand perfect forwarding:
|
||||
// Why Slic3r::internal::format::cook(T&& arg) is taken for non-const wxString reference?
|
||||
inline const utf8_buffer cook(wxString& arg) {
|
||||
return utf8_buffer{ arg.ToUTF8() };
|
||||
}
|
||||
inline const utf8_buffer cook(wxString&& arg) {
|
||||
return utf8_buffer{ arg.ToUTF8() };
|
||||
}
|
||||
}
|
||||
|
||||
#include <libslic3r/format.hpp>
|
||||
|
||||
namespace Slic3r::GUI {
|
||||
|
||||
// Format input mixing UTF8 encoded strings (const char*, std::string) and wxStrings, return a wxString.
|
||||
template<typename... TArgs>
|
||||
|
@ -42,42 +65,6 @@ inline std::string format(const wxString& fmt, TArgs&&... args) {
|
|||
return Slic3r::format(fmt.ToUTF8().data(), std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
|
||||
namespace internal {
|
||||
namespace format {
|
||||
// Wrapper around wxScopedCharBuffer to indicate that the content is UTF8 formatted.
|
||||
struct utf8_buffer {
|
||||
// wxScopedCharBuffer is reference counted, therefore copying by value is cheap.
|
||||
wxScopedCharBuffer data;
|
||||
};
|
||||
// Accept wxString and convert it to UTF8 to be processed by Slic3r::format().
|
||||
inline const utf8_buffer cook(const wxString &arg) {
|
||||
return utf8_buffer { arg.ToUTF8() };
|
||||
}
|
||||
// Vojtech seemingly does not understand perfect forwarding:
|
||||
// Why Slic3r::internal::format::cook(T&& arg) is taken for non-const wxString reference?
|
||||
inline const utf8_buffer cook(wxString &arg) {
|
||||
return utf8_buffer { arg.ToUTF8() };
|
||||
}
|
||||
inline const utf8_buffer cook(wxString &&arg) {
|
||||
return utf8_buffer{ arg.ToUTF8() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
namespace boost {
|
||||
namespace io {
|
||||
namespace detail {
|
||||
// Adaptor for boost::format to accept wxString converted to UTF8.
|
||||
inline std::ostream& operator<<(std::ostream& os, const Slic3r::internal::format::utf8_buffer& str) {
|
||||
os << str.data.data();
|
||||
return os;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Slic3r::GUI
|
||||
|
||||
#endif /* slic3r_GUI_format_hpp_ */
|
||||
|
|
|
@ -27,6 +27,7 @@ add_executable(${_TEST_NAME}_tests
|
|||
test_voronoi.cpp
|
||||
test_optimizers.cpp
|
||||
test_png_io.cpp
|
||||
test_surface_mesh.cpp
|
||||
test_timeutils.cpp
|
||||
test_quadric_edge_collapse.cpp
|
||||
test_triangulation.cpp
|
||||
|
|
122
tests/libslic3r/test_surface_mesh.cpp
Normal file
122
tests/libslic3r/test_surface_mesh.cpp
Normal file
|
@ -0,0 +1,122 @@
|
|||
#include <catch2/catch.hpp>
|
||||
#include <test_utils.hpp>
|
||||
|
||||
|
||||
#include <libslic3r/SurfaceMesh.hpp>
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
|
||||
// Generate a broken cube mesh. Face 8 is inverted, face 11 is missing.
|
||||
indexed_triangle_set its_make_cube_broken(double xd, double yd, double zd)
|
||||
{
|
||||
auto x = float(xd), y = float(yd), z = float(zd);
|
||||
return {
|
||||
{ {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7},
|
||||
{0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2},
|
||||
{2, 5, 6}, {2, 5, 3}, {4, 0, 3} /*missing face*/ },
|
||||
{ {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0},
|
||||
{x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST_CASE("SurfaceMesh on a cube", "[SurfaceMesh]") {
|
||||
indexed_triangle_set cube = its_make_cube(1., 1., 1.);
|
||||
SurfaceMesh sm(cube);
|
||||
const Halfedge_index hi_first = sm.halfedge(Face_index(0));
|
||||
Halfedge_index hi = hi_first;
|
||||
|
||||
REQUIRE(! hi_first.is_invalid());
|
||||
|
||||
SECTION("next / prev halfedge") {
|
||||
hi = sm.next(hi);
|
||||
REQUIRE(hi != hi_first);
|
||||
hi = sm.next(hi);
|
||||
hi = sm.next(hi);
|
||||
REQUIRE(hi == hi_first);
|
||||
hi = sm.prev(hi);
|
||||
REQUIRE(hi != hi_first);
|
||||
hi = sm.prev(hi);
|
||||
hi = sm.prev(hi);
|
||||
REQUIRE(hi == hi_first);
|
||||
}
|
||||
|
||||
SECTION("next_around_target") {
|
||||
// Check that we get to the same halfedge after applying next_around_target
|
||||
// four times.
|
||||
const Vertex_index target_vert = sm.target(hi_first);
|
||||
for (int i=0; i<4;++i) {
|
||||
hi = sm.next_around_target(hi);
|
||||
REQUIRE((hi == hi_first) == (i == 3));
|
||||
REQUIRE(sm.is_same_vertex(sm.target(hi), target_vert));
|
||||
REQUIRE(! sm.is_border(hi));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("iterate around target and source") {
|
||||
hi = sm.next_around_target(hi);
|
||||
hi = sm.prev_around_target(hi);
|
||||
hi = sm.prev_around_source(hi);
|
||||
hi = sm.next_around_source(hi);
|
||||
REQUIRE(hi == hi_first);
|
||||
}
|
||||
|
||||
SECTION("opposite") {
|
||||
const Vertex_index target = sm.target(hi);
|
||||
const Vertex_index source = sm.source(hi);
|
||||
hi = sm.opposite(hi);
|
||||
REQUIRE(sm.is_same_vertex(target, sm.source(hi)));
|
||||
REQUIRE(sm.is_same_vertex(source, sm.target(hi)));
|
||||
hi = sm.opposite(hi);
|
||||
REQUIRE(hi == hi_first);
|
||||
}
|
||||
|
||||
SECTION("halfedges walk") {
|
||||
for (int i=0; i<4; ++i) {
|
||||
hi = sm.next(hi);
|
||||
hi = sm.opposite(hi);
|
||||
}
|
||||
REQUIRE(hi == hi_first);
|
||||
}
|
||||
|
||||
SECTION("point accessor") {
|
||||
Halfedge_index hi = sm.halfedge(Face_index(0));
|
||||
hi = sm.opposite(hi);
|
||||
hi = sm.prev(hi);
|
||||
hi = sm.opposite(hi);
|
||||
REQUIRE(hi.face() == Face_index(6));
|
||||
REQUIRE(sm.point(sm.target(hi)).isApprox(cube.vertices[7]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TEST_CASE("SurfaceMesh on a broken cube", "[SurfaceMesh]") {
|
||||
indexed_triangle_set cube = its_make_cube_broken(1., 1., 1.);
|
||||
SurfaceMesh sm(cube);
|
||||
|
||||
SECTION("Check inverted face") {
|
||||
Halfedge_index hi = sm.halfedge(Face_index(8));
|
||||
for (int i=0; i<3; ++i) {
|
||||
REQUIRE(! hi.is_invalid());
|
||||
REQUIRE(sm.is_border(hi));
|
||||
}
|
||||
REQUIRE(hi == sm.halfedge(Face_index(8)));
|
||||
hi = sm.opposite(hi);
|
||||
REQUIRE(hi.is_invalid());
|
||||
}
|
||||
|
||||
SECTION("missing face") {
|
||||
Halfedge_index hi = sm.halfedge(Face_index(0));
|
||||
for (int i=0; i<3; ++i)
|
||||
hi = sm.next_around_source(hi);
|
||||
hi = sm.next(hi);
|
||||
REQUIRE(sm.is_border(hi));
|
||||
REQUIRE(! hi.is_invalid());
|
||||
hi = sm.opposite(hi);
|
||||
REQUIRE(hi.is_invalid());
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue