From 11444b957f11b27ac0fc9d86f759cc2d1ff89ba7 Mon Sep 17 00:00:00 2001 From: SpectralVectors Date: Wed, 23 Oct 2024 17:37:33 -0400 Subject: [PATCH] Add files via upload --- scripts/addons/cam/ui/panels/area.py | 160 +++----- scripts/addons/cam/ui/panels/buttons_panel.py | 57 +-- scripts/addons/cam/ui/panels/chains.py | 73 ++-- scripts/addons/cam/ui/panels/cutter.py | 226 ++++------ scripts/addons/cam/ui/panels/feedrate.py | 56 +-- scripts/addons/cam/ui/panels/gcode.py | 83 ++-- scripts/addons/cam/ui/panels/info.py | 146 +++---- scripts/addons/cam/ui/panels/interface.py | 8 +- scripts/addons/cam/ui/panels/machine.py | 198 ++++----- scripts/addons/cam/ui/panels/material.py | 80 ++-- scripts/addons/cam/ui/panels/movement.py | 186 ++++----- scripts/addons/cam/ui/panels/op_properties.py | 386 +++++++----------- scripts/addons/cam/ui/panels/operations.py | 180 ++++---- scripts/addons/cam/ui/panels/optimisation.py | 169 ++++---- scripts/addons/cam/ui/panels/pack.py | 25 +- scripts/addons/cam/ui/panels/slice.py | 14 +- 16 files changed, 798 insertions(+), 1249 deletions(-) diff --git a/scripts/addons/cam/ui/panels/area.py b/scripts/addons/cam/ui/panels/area.py index 7aa7f8f6..4c252633 100644 --- a/scripts/addons/cam/ui/panels/area.py +++ b/scripts/addons/cam/ui/panels/area.py @@ -15,98 +15,74 @@ class CAM_AREA_Panel(CAMButtonsPanel, Panel): bl_label = "CAM Operation Area" bl_idname = "WORLD_PT_CAM_OPERATION_AREA" - panel_interface_level = 0 - - prop_level = { - "draw_use_layers": 0, - "draw_maxz": 1, - "draw_minz": 1, - "draw_ambient": 1, - "draw_limit_curve": 1, - "draw_first_down": 1, - } - - def draw_use_layers(self): - if not self.has_correct_level(): - return - col = self.layout.column(align=True) - row = col.row(align=True) - row.prop(self.op, "use_layers") - if self.op.use_layers: - row.prop(self.op, "stepdown") - self.draw_first_down(col) - - def draw_first_down(self, col): - if not self.has_correct_level(): - return - if self.op.strategy in ["CUTOUT", "POCKET", "MEDIAL_AXIS"]: - row = col.row(align=True) - row.label(text="") - row.prop(self.op, "first_down") - - def draw_maxz(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "maxz") - self.layout.prop(self.op.movement, "free_height") - if self.op.maxz > self.op.movement.free_height: - self.layout.label(text="!ERROR! COLLISION!") - self.layout.label(text="Depth Start > Free Movement Height") - self.layout.label(text="!ERROR! COLLISION!") - - def draw_minz(self): - if not self.has_correct_level(): - return - if self.op.geometry_source in ["OBJECT", "COLLECTION"]: - if self.op.strategy == "CURVE": - self.layout.label(text="Cannot Use Depth from Object Using Curves") - - row = self.layout.row(align=True) - row.label(text="Set Max Depth from") - row.prop(self.op, "minz_from", text="") - if self.op.minz_from == "CUSTOM": - self.layout.prop(self.op, "minz") - - else: - self.layout.prop(self.op, "source_image_scale_z") - self.layout.prop(self.op, "source_image_size_x") - if self.op.source_image_name != "": - i = bpy.data.images[self.op.source_image_name] - if i is not None: - sy = int((self.op.source_image_size_x / i.size[0]) * i.size[1] * 1000000) / 1000 - self.layout.label(text="Image Size on Y Axis: " + strInUnits(sy, 8)) - self.layout.separator() - self.layout.prop(self.op, "source_image_offset") - col = self.layout.column(align=True) - col.prop(self.op, "source_image_crop", text="Crop Source Image") - if self.op.source_image_crop: - col.prop(self.op, "source_image_crop_start_x", text="Start X") - col.prop(self.op, "source_image_crop_start_y", text="Start Y") - col.prop(self.op, "source_image_crop_end_x", text="End X") - col.prop(self.op, "source_image_crop_end_y", text="End Y") - - def draw_ambient(self): - if not self.has_correct_level(): - return - if self.op.strategy in ["BLOCK", "SPIRAL", "CIRCLES", "PARALLEL", "CROSS"]: - self.layout.prop(self.op, "ambient_behaviour") - if self.op.ambient_behaviour == "AROUND": - self.layout.prop(self.op, "ambient_radius") - self.layout.prop(self.op, "ambient_cutter_restrict") - - def draw_limit_curve(self): - if not self.has_correct_level(): - return - if self.op.strategy in ["BLOCK", "SPIRAL", "CIRCLES", "PARALLEL", "CROSS"]: - self.layout.prop(self.op, "use_limit_curve") - if self.op.use_limit_curve: - self.layout.prop_search(self.op, "limit_curve", bpy.data, "objects") def draw(self, context): - self.context = context + layout = self.layout + if self.op is not None: + # Use Layers + col = layout.column(align=True) + row = col.row(align=True) + row.prop(self.op, "use_layers") + if self.op.use_layers: + row.prop(self.op, "stepdown") - self.draw_use_layers() - self.draw_maxz() - self.draw_minz() - self.draw_ambient() - self.draw_limit_curve() + # First Down + if self.level >= 1 and self.op.strategy in ["CUTOUT", "POCKET", "MEDIAL_AXIS"]: + row = col.row(align=True) + row.label(text="") + row.prop(self.op, "first_down") + + # Max Z + if self.level >= 1: + col = layout.column(align=True) + col.prop(self.op, "maxz") + col.prop(self.op.movement, "free_height") + if self.op.maxz > self.op.movement.free_height: + col.label(text="!ERROR! COLLISION!") + col.label(text="Depth Start > Free Movement Height") + col.label(text="!ERROR! COLLISION!") + + # Min Z + if self.level >= 1: + if self.op.geometry_source in ["OBJECT", "COLLECTION"]: + if self.op.strategy == "CURVE": + col.label(text="Cannot Use Depth from Object Using Curves") + + row = col.row(align=True) + row.label(text="Set Max Depth from") + row.prop(self.op, "minz_from", text="") + if self.op.minz_from == "CUSTOM": + row.prop(self.op, "minz") + + else: + col.prop(self.op, "source_image_scale_z") + col.prop(self.op, "source_image_size_x") + if self.op.source_image_name != "": + i = bpy.data.images[self.op.source_image_name] + if i is not None: + size_x = self.op.source_image_size_x / i.size[0] + size_y = int(x_size * i.size[1] * 1000000) / 1000 + col.label(text="Image Size on Y Axis: " + strInUnits(size_y, 8)) + col.separator() + col.prop(self.op, "source_image_offset") + col.prop(self.op, "source_image_crop", text="Crop Source Image") + if self.op.source_image_crop: + col.prop(self.op, "source_image_crop_start_x", text="Start X") + col.prop(self.op, "source_image_crop_start_y", text="Start Y") + col.prop(self.op, "source_image_crop_end_x", text="End X") + col.prop(self.op, "source_image_crop_end_y", text="End Y") + + # Draw Ambient + if self.level >= 1: + if self.op.strategy in ["BLOCK", "SPIRAL", "CIRCLES", "PARALLEL", "CROSS"]: + col.prop(self.op, "ambient_behaviour") + if self.op.ambient_behaviour == "AROUND": + col.prop(self.op, "ambient_radius") + col.prop(self.op, "ambient_cutter_restrict") + + # Draw Limit Curve + if self.level >= 1: + if self.op.strategy in ["BLOCK", "SPIRAL", "CIRCLES", "PARALLEL", "CROSS"]: + col.prop(self.op, "use_limit_curve") + if self.op.use_limit_curve: + col.prop_search(self.op, "limit_curve", bpy.data, "objects") diff --git a/scripts/addons/cam/ui/panels/buttons_panel.py b/scripts/addons/cam/ui/panels/buttons_panel.py index afe0b23d..810b8951 100644 --- a/scripts/addons/cam/ui/panels/buttons_panel.py +++ b/scripts/addons/cam/ui/panels/buttons_panel.py @@ -14,56 +14,23 @@ class CAMButtonsPanel: bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "render" - always_show_panel = False COMPAT_ENGINES = {"CNCCAM_RENDER"} - # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here - @classmethod def poll(cls, context): - rd = bpy.context.scene.render - if rd.engine in cls.COMPAT_ENGINES: - if cls.always_show_panel: - return True - op = cls.active_operation() - if op and op.valid: - if hasattr(cls, "panel_interface_level"): - return cls.panel_interface_level <= int(context.scene.interface.level) - else: - return True - return False - - @classmethod - def active_operation_index(cls): - return bpy.context.scene.cam_active_operation - - @classmethod - def active_operation(cls): - active_op = None - try: - active_op = bpy.context.scene.cam_operations[cls.active_operation_index()] - except IndexError: - pass - return active_op + engine = context.scene.render.engine + return [True if engine in cls.COMPAT_ENGINES else False] def __init__(self): - self.op = self.active_operation() - addon_prefs = bpy.context.preferences.addons["bl_ext.user_default.blendercam"].preferences + context = bpy.context + + self.level = int(context.scene.interface.level) + self.machine = context.scene.cam_machine + + addon_prefs = context.preferences.addons["bl_ext.user_default.blendercam"].preferences self.use_experimental = addon_prefs.experimental - def operations_count(self): - return len(bpy.context.scene.cam_operations) - - def has_operations(self): - return self.operations_count() > 0 - - def has_correct_level(self): - if not hasattr(self, "prop_level"): - return True - - caller_function = inspect.stack()[1][3] - - if caller_function not in self.prop_level: - return True - - return self.prop_level[caller_function] <= int(self.context.scene.interface.level) + operations = context.scene.cam_operations + operations_count = len(operations) + operation_index = context.scene.cam_active_operation + self.op = operations[operation_index] if operations_count > 0 else None diff --git a/scripts/addons/cam/ui/panels/chains.py b/scripts/addons/cam/ui/panels/chains.py index e17e33b2..8a0f11b9 100644 --- a/scripts/addons/cam/ui/panels/chains.py +++ b/scripts/addons/cam/ui/panels/chains.py @@ -43,49 +43,48 @@ class CAM_CHAINS_Panel(CAMButtonsPanel, Panel): bl_label = "CAM Chains" bl_idname = "WORLD_PT_CAM_CHAINS" - panel_interface_level = 1 - always_show_panel = True def draw(self, context): - layout = self.layout + if self.level >= 1 and self.op is not None: + layout = self.layout - row = layout.row() - scene = bpy.context.scene + row = layout.row() + scene = bpy.context.scene - row.template_list("CAM_UL_chains", "", scene, "cam_chains", scene, "cam_active_chain") - col = row.column(align=True) - col.operator("scene.cam_chain_add", icon="ADD", text="") - col.operator("scene.cam_chain_remove", icon="REMOVE", text="") + row.template_list("CAM_UL_chains", "", scene, "cam_chains", scene, "cam_active_chain") + col = row.column(align=True) + col.operator("scene.cam_chain_add", icon="ADD", text="") + col.operator("scene.cam_chain_remove", icon="REMOVE", text="") - if len(scene.cam_chains) > 0: - chain = scene.cam_chains[scene.cam_active_chain] - row = layout.row(align=True) + if len(scene.cam_chains) > 0: + chain = scene.cam_chains[scene.cam_active_chain] + row = layout.row(align=True) - if chain: - row.template_list( - "CAM_UL_operations", "", chain, "operations", chain, "active_operation" - ) - col = row.column(align=True) - col.operator("scene.cam_chain_operation_add", icon="ADD", text="") - col.operator("scene.cam_chain_operation_remove", icon="REMOVE", text="") - if len(chain.operations) > 0: - col.operator("scene.cam_chain_operation_up", icon="TRIA_UP", text="") - col.operator("scene.cam_chain_operation_down", icon="TRIA_DOWN", text="") - - if not chain.computing: - layout.operator( - "object.calculate_cam_paths_chain", - text="Calculate Chain Paths & Export Gcode", + if chain: + row.template_list( + "CAM_UL_operations", "", chain, "operations", chain, "active_operation" ) - layout.operator("object.cam_export_paths_chain", text="Export Chain G-code") - layout.operator("object.cam_simulate_chain", text="Simulate This Chain") + col = row.column(align=True) + col.operator("scene.cam_chain_operation_add", icon="ADD", text="") + col.operator("scene.cam_chain_operation_remove", icon="REMOVE", text="") + if len(chain.operations) > 0: + col.operator("scene.cam_chain_operation_up", icon="TRIA_UP", text="") + col.operator("scene.cam_chain_operation_down", icon="TRIA_DOWN", text="") - valid, reason = isChainValid(chain, context) - if not valid: - layout.label(icon="ERROR", text=f"Can't Compute Chain - Reason:\n") - layout.label(text=reason) - else: - layout.label(text="Chain Is Currently Computing") + if not chain.computing: + layout.operator( + "object.calculate_cam_paths_chain", + text="Calculate Chain Paths & Export Gcode", + ) + layout.operator("object.cam_export_paths_chain", text="Export Chain G-code") + layout.operator("object.cam_simulate_chain", text="Simulate This Chain") - layout.prop(chain, "name") - layout.prop(chain, "filename") + valid, reason = isChainValid(chain, context) + if not valid: + layout.label(icon="ERROR", text=f"Can't Compute Chain - Reason:\n") + layout.label(text=reason) + else: + layout.label(text="Chain Is Currently Computing") + + layout.prop(chain, "name") + layout.prop(chain, "filename") diff --git a/scripts/addons/cam/ui/panels/cutter.py b/scripts/addons/cam/ui/panels/cutter.py index a0add992..e4fd9fb1 100644 --- a/scripts/addons/cam/ui/panels/cutter.py +++ b/scripts/addons/cam/ui/panels/cutter.py @@ -14,147 +14,91 @@ class CAM_CUTTER_Panel(CAMButtonsPanel, Panel): bl_label = "CAM Cutter" bl_idname = "WORLD_PT_CAM_CUTTER" - panel_interface_level = 0 - - prop_level = { - "draw_cutter_preset_menu": 1, - "draw_cutter_id": 2, - "draw_cutter_type": 0, - "draw_ball_radius": 0, - "draw_bull_radius": 0, - "draw_cylcone_diameter": 0, - "draw_cutter_tip_angle": 0, - "draw_laser": 0, - "draw_plasma": 0, - "draw_custom": 0, - "draw_cutter_diameter": 0, - "draw_cutter_flutes": 1, - "draw_cutter_description": 1, - "draw_engagement": 0, - } - - def draw_cutter_preset_menu(self): - if not self.has_correct_level(): - return - row = self.layout.row(align=True) - row.menu("CAM_CUTTER_MT_presets", text=bpy.types.CAM_CUTTER_MT_presets.bl_label) - row.operator("render.cam_preset_cutter_add", text="", icon="ADD") - row.operator("render.cam_preset_cutter_add", text="", icon="REMOVE").remove_active = True - - def draw_cutter_id(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "cutter_id") - - def draw_cutter_type(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "cutter_type") - - def draw_ball_radius(self): - if not self.has_correct_level(): - return - if self.op.cutter_type in ["BALLCONE"]: - self.layout.prop(self.op, "ball_radius") - - def draw_bull_radius(self): - if not self.has_correct_level(): - return - if self.op.cutter_type in ["BULLNOSE"]: - self.layout.prop(self.op, "bull_corner_radius") - - def draw_cylcone_diameter(self): - if not self.has_correct_level(): - return - if self.op.cutter_type in ["CYLCONE"]: - self.layout.prop(self.op, "cylcone_diameter") - - def draw_cutter_tip_angle(self): - if not self.has_correct_level(): - return - if self.op.cutter_type in ["VCARVE", "BALLCONE", "BULLNOSE", "CYLCONE"]: - self.layout.prop(self.op, "cutter_tip_angle") - - def draw_laser(self): - if not self.has_correct_level(): - return - if self.op.cutter_type in ["LASER"]: - self.layout.prop(self.op, "Laser_on") - self.layout.prop(self.op, "Laser_off") - self.layout.prop(self.op, "Laser_cmd") - self.layout.prop(self.op, "Laser_delay") - - def draw_plasma(self): - if not self.has_correct_level(): - return - if self.op.cutter_type in ["PLASMA"]: - self.layout.prop(self.op, "Plasma_on") - self.layout.prop(self.op, "Plasma_off") - self.layout.prop(self.op, "Plasma_delay") - self.layout.prop(self.op, "Plasma_dwell") - self.layout.prop(self.op, "lead_in") - self.layout.prop(self.op, "lead_out") - - def draw_custom(self): - if not self.has_correct_level(): - return - if self.op.cutter_type in ["CUSTOM"]: - if self.op.optimisation.use_exact: - self.layout.label( - text="Warning - only Convex Shapes Are Supported. ", icon="COLOR_RED" - ) - self.layout.label(text="If Your Custom Cutter Is Concave,") - self.layout.label(text="Switch Exact Mode Off.") - self.layout.prop_search(self.op, "cutter_object_name", bpy.data, "objects") - - def draw_cutter_diameter(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "cutter_diameter") - - def draw_cutter_flutes(self): - if not self.has_correct_level(): - return - if self.op.cutter_type not in ["LASER", "PLASMA"]: - self.layout.prop(self.op, "cutter_flutes") - - def draw_cutter_description(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "cutter_description") - - def draw_engagement(self): - if not self.has_correct_level(): - return - if self.op.cutter_type in ["LASER", "PLASMA"]: - return - if self.op.strategy in ["CUTOUT"]: - return - - if self.op.cutter_type in ["BALLCONE"]: - engagement = round(100 * self.op.dist_between_paths / self.op.ball_radius, 1) - else: - engagement = round(100 * self.op.dist_between_paths / self.op.cutter_diameter, 1) - - self.layout.label(text=f"Cutter Engagement: {engagement}%") - - if engagement > 50: - self.layout.label(text="WARNING: CUTTER ENGAGEMENT > 50%") def draw(self, context): - self.context = context + layout = self.layout + if self.op is not None: + # Cutter Preset Menu + if self.level >= 1: + row = layout.row(align=True) + row.menu("CAM_CUTTER_MT_presets", text=bpy.types.CAM_CUTTER_MT_presets.bl_label) + row.operator("render.cam_preset_cutter_add", text="", icon="ADD") + row.operator( + "render.cam_preset_cutter_add", text="", icon="REMOVE" + ).remove_active = True - self.draw_cutter_preset_menu() - self.draw_cutter_id() - self.draw_cutter_type() - self.draw_ball_radius() - self.draw_bull_radius() - self.draw_cylcone_diameter() - self.draw_cutter_tip_angle() - self.draw_laser() - self.draw_plasma() - self.draw_custom() - self.draw_cutter_diameter() - self.draw_cutter_flutes() - self.draw_cutter_description() - self.draw_engagement() + # Cutter ID + if self.level >= 2: + layout.prop(self.op, "cutter_id") + + # Cutter Type + layout.prop(self.op, "cutter_type") + + # Ball Radius + if self.op.cutter_type in ["BALLCONE"]: + layout.prop(self.op, "ball_radius") + + # Bullnose Radius + if self.op.cutter_type in ["BULLNOSE"]: + layout.prop(self.op, "bull_corner_radius") + + # Cyclone Diameter + if self.op.cutter_type in ["CYLCONE"]: + layout.prop(self.op, "cylcone_diameter") + + # Cutter Tip Angle + if self.op.cutter_type in ["VCARVE", "BALLCONE", "BULLNOSE", "CYLCONE"]: + layout.prop(self.op, "cutter_tip_angle") + + # Laser + if self.op.cutter_type in ["LASER"]: + layout.prop(self.op, "Laser_on") + layout.prop(self.op, "Laser_off") + layout.prop(self.op, "Laser_cmd") + layout.prop(self.op, "Laser_delay") + + # Plasma + if self.op.cutter_type in ["PLASMA"]: + layout.prop(self.op, "Plasma_on") + layout.prop(self.op, "Plasma_off") + layout.prop(self.op, "Plasma_delay") + layout.prop(self.op, "Plasma_dwell") + layout.prop(self.op, "lead_in") + layout.prop(self.op, "lead_out") + + # Custom + if self.op.cutter_type in ["CUSTOM"]: + if self.op.optimisation.use_exact: + layout.label( + text="Warning - only Convex Shapes Are Supported. ", icon="COLOR_RED" + ) + layout.label(text="If Your Custom Cutter Is Concave,") + layout.label(text="Switch Exact Mode Off.") + layout.prop_search(self.op, "cutter_object_name", bpy.data, "objects") + + # Cutter Diameter + layout.prop(self.op, "cutter_diameter") + + # Cutter Flutes + if self.level >= 1: + if self.op.cutter_type not in ["LASER", "PLASMA"]: + layout.prop(self.op, "cutter_flutes") + + # Cutter Description + layout.prop(self.op, "cutter_description") + + # Cutter Engagement + if self.op.cutter_type in ["LASER", "PLASMA"]: + return + if self.op.strategy in ["CUTOUT"]: + return + + if self.op.cutter_type in ["BALLCONE"]: + engagement = round(100 * self.op.dist_between_paths / self.op.ball_radius, 1) + else: + engagement = round(100 * self.op.dist_between_paths / self.op.cutter_diameter, 1) + + layout.label(text=f"Cutter Engagement: {engagement}%") + + if engagement > 50: + layout.label(text="WARNING: CUTTER ENGAGEMENT > 50%") diff --git a/scripts/addons/cam/ui/panels/feedrate.py b/scripts/addons/cam/ui/panels/feedrate.py index ab7182cd..8d09f2bf 100644 --- a/scripts/addons/cam/ui/panels/feedrate.py +++ b/scripts/addons/cam/ui/panels/feedrate.py @@ -14,46 +14,22 @@ class CAM_FEEDRATE_Panel(CAMButtonsPanel, Panel): bl_label = "CAM Feedrate" bl_idname = "WORLD_PT_CAM_FEEDRATE" - panel_interface_level = 0 - - prop_level = { - "draw_feedrate": 0, - "draw_sim_feedrate": 2, - "draw_plunge_feedrate": 1, - "draw_plunge_angle": 1, - "draw_spindle_rpm": 0, - } - - def draw_feedrate(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "feedrate") - - def draw_sim_feedrate(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "do_simulation_feedrate") - - def draw_plunge_feedrate(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "plunge_feedrate") - - def draw_plunge_angle(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "plunge_angle") - - def draw_spindle_rpm(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "spindle_rpm") def draw(self, context): - self.context = context + layout = self.layout + if self.op is not None: + # Feedrate + layout.prop(self.op, "feedrate") - self.draw_feedrate() - self.draw_sim_feedrate() - self.draw_plunge_feedrate() - self.draw_plunge_angle() - self.draw_spindle_rpm() + # Sim Feedrate + if self.level >= 2: + layout.prop(self.op, "do_simulation_feedrate") + + # Plunge Feedrate + if self.level >= 1: + layout.prop(self.op, "plunge_feedrate") + # Plunge Angle + layout.prop(self.op, "plunge_angle") + + # Spindle RPM + layout.prop(self.op, "spindle_rpm") diff --git a/scripts/addons/cam/ui/panels/gcode.py b/scripts/addons/cam/ui/panels/gcode.py index fd72199b..2712da66 100644 --- a/scripts/addons/cam/ui/panels/gcode.py +++ b/scripts/addons/cam/ui/panels/gcode.py @@ -14,59 +14,36 @@ class CAM_GCODE_Panel(CAMButtonsPanel, Panel): bl_label = "CAM G-code Options" bl_idname = "WORLD_PT_CAM_GCODE" - panel_interface_level = 1 - - prop_level = { - "draw_output_header": 1, - "draw_output_trailer": 1, - "draw_enable_dust": 1, - "draw_enable_hold": 1, - "draw_enable_mist": 1, - } - - def draw_output_header(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "output_header") - if self.op.output_header: - self.layout.prop(self.op, "gcode_header") - - def draw_output_trailer(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "output_trailer") - if self.op.output_trailer: - self.layout.prop(self.op, "gcode_trailer") - - def draw_enable_dust(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "enable_dust") - if self.op.enable_dust: - self.layout.prop(self.op, "gcode_start_dust_cmd") - self.layout.prop(self.op, "gcode_stop_dust_cmd") - - def draw_enable_hold(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "enable_hold") - if self.op.enable_hold: - self.layout.prop(self.op, "gcode_start_hold_cmd") - self.layout.prop(self.op, "gcode_stop_hold_cmd") - - def draw_enable_mist(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "enable_mist") - if self.op.enable_mist: - self.layout.prop(self.op, "gcode_start_mist_cmd") - self.layout.prop(self.op, "gcode_stop_mist_cmd") def draw(self, context): - self.context = context + layout = self.layout - self.draw_output_header() - self.draw_output_trailer() - self.draw_enable_dust() - self.draw_enable_hold() - self.draw_enable_mist() + if self.level >= 1 and self.op is not None: + + # Output Header + layout.prop(self.op, "output_header") + if self.op.output_header: + layout.prop(self.op, "gcode_header") + + # Output Trailer + layout.prop(self.op, "output_trailer") + if self.op.output_trailer: + layout.prop(self.op, "gcode_trailer") + + # Enable Dust + layout.prop(self.op, "enable_dust") + if self.op.enable_dust: + layout.prop(self.op, "gcode_start_dust_cmd") + layout.prop(self.op, "gcode_stop_dust_cmd") + + # Enable Hold + layout.prop(self.op, "enable_hold") + if self.op.enable_hold: + layout.prop(self.op, "gcode_start_hold_cmd") + layout.prop(self.op, "gcode_stop_hold_cmd") + + # Enable Mist + layout.prop(self.op, "enable_mist") + if self.op.enable_mist: + layout.prop(self.op, "gcode_start_mist_cmd") + layout.prop(self.op, "gcode_stop_mist_cmd") diff --git a/scripts/addons/cam/ui/panels/info.py b/scripts/addons/cam/ui/panels/info.py index e6d74e5d..f0e6ad6e 100644 --- a/scripts/addons/cam/ui/panels/info.py +++ b/scripts/addons/cam/ui/panels/info.py @@ -60,98 +60,60 @@ class CAM_INFO_Properties(PropertyGroup): class CAM_INFO_Panel(CAMButtonsPanel, Panel): bl_label = "CAM Info & Warnings" bl_idname = "WORLD_PT_CAM_INFO" - panel_interface_level = 0 - always_show_panel = True - - prop_level = { - "draw_cnccam_version": 0, - "draw_opencamlib_version": 1, - "draw_op_warnings": 0, - "draw_op_time": 0, - "draw_op_chipload": 0, - "draw_op_money_cost": 1, - } - - # Draw CNC CAM version (and whether there are updates available) - def draw_cnccam_version(self): - addon_prefs = bpy.context.preferences.addons["bl_ext.user_default.blendercam"].preferences - if not self.has_correct_level(): - return - self.layout.label(text=f'CNC CAM v{".".join([str(x) for x in cam_version])}') - if len(addon_prefs.new_version_available) > 0: - self.layout.label(text=f"New Version Available:") - self.layout.label(text=f" {addon_prefs.new_version_available}") - self.layout.operator("render.cam_update_now") - - # Display the OpenCamLib version - def draw_opencamlib_version(self): - if not self.has_correct_level(): - return - ocl_version = opencamlib_version() - if ocl_version is None: - self.layout.label(text="OpenCAMLib is not Installed") - else: - self.layout.label(text=f"OpenCAMLib v{ocl_version}") - - # Display warnings related to the current operation - def draw_op_warnings(self): - if not self.has_correct_level(): - return - for line in self.op.info.warnings.rstrip("\n").split("\n"): - if len(line) > 0: - self.layout.label(text=line, icon="ERROR") - - # Display the time estimation for the current operation - def draw_op_time(self): - if not self.has_correct_level(): - return - if not int(self.op.info.duration * 60) > 0: - return - - time_estimate = f"Operation Duration: {int(self.op.info.duration*60)}s " - if self.op.info.duration > 60: - time_estimate += f" ({int(self.op.info.duration / 60)}h" - time_estimate += f" {round(self.op.info.duration % 60)}min)" - elif self.op.info.duration > 1: - time_estimate += f" ({round(self.op.info.duration % 60)}min)" - - self.layout.label(text=time_estimate) - - # Display the chipload (does this work ?) - def draw_op_chipload(self): - if not self.has_correct_level(): - return - if not self.op.info.chipload > 0: - return - - chipload = f"Chipload: {strInUnits(self.op.info.chipload, 4)}/tooth" - self.layout.label(text=chipload) - - # Display the current operation money cost - def draw_op_money_cost(self): - if not self.has_correct_level(): - return - if not int(self.op.info.duration * 60) > 0: - return - - row = self.layout.row() - row.label(text="Hourly Rate") - row.prop(bpy.context.scene.cam_machine, "hourly_rate", text="") - - if float(bpy.context.scene.cam_machine.hourly_rate) < 0.01: - return - - cost_per_second = bpy.context.scene.cam_machine.hourly_rate / 3600 - total_cost = self.op.info.duration * 60 * cost_per_second - op_cost = f"Operation Cost: ${total_cost:.2f} (${cost_per_second:.2f}/s)" - self.layout.label(text=op_cost) # Display the Info Panel def draw(self, context): - self.context = context - self.draw_cnccam_version() - self.draw_opencamlib_version() - if self.op: - self.draw_op_warnings() - self.draw_op_time() - self.draw_op_money_cost() + layout = self.layout + if self.op is not None: + # CNC CAM Version + layout.label(text=f'CNC CAM v{".".join([str(x) for x in cam_version])}') + + # OpenCAMLib Version + if self.level >= 1: + ocl_version = opencamlib_version() + if ocl_version is None: + layout.label(text="OpenCAMLib is not Installed") + else: + layout.label(text=f"OpenCAMLib v{ocl_version}") + + # Operation Warnings + for line in self.op.info.warnings.rstrip("\n").split("\n"): + if len(line) > 0: + layout.label(text=line, icon="ERROR") + + # Operation Time Estimate + if not int(self.op.info.duration * 60) > 0: + return + + time_estimate = f"Operation Duration: {int(self.op.info.duration*60)}s " + if self.op.info.duration > 60: + time_estimate += f" ({int(self.op.info.duration / 60)}h" + time_estimate += f" {round(self.op.info.duration % 60)}min)" + elif self.op.info.duration > 1: + time_estimate += f" ({round(self.op.info.duration % 60)}min)" + + layout.label(text=time_estimate) + + # Operation Chipload + if not self.op.info.chipload > 0: + return + + chipload = f"Chipload: {strInUnits(self.op.info.chipload, 4)}/tooth" + layout.label(text=chipload) + + # Operation Money Cost + if self.level >= 1: + if not int(self.op.info.duration * 60) > 0: + return + + row = self.layout.row() + row.label(text="Hourly Rate") + row.prop(bpy.context.scene.cam_machine, "hourly_rate", text="") + + if float(bpy.context.scene.cam_machine.hourly_rate) < 0.01: + return + + cost_per_second = bpy.context.scene.cam_machine.hourly_rate / 3600 + total_cost = self.op.info.duration * 60 * cost_per_second + op_cost = f"Operation Cost: ${total_cost:.2f} (${cost_per_second:.2f}/s)" + layout.label(text=op_cost) diff --git a/scripts/addons/cam/ui/panels/interface.py b/scripts/addons/cam/ui/panels/interface.py index f62fb2a2..98bce04e 100644 --- a/scripts/addons/cam/ui/panels/interface.py +++ b/scripts/addons/cam/ui/panels/interface.py @@ -35,11 +35,7 @@ class CAM_INTERFACE_Properties(PropertyGroup): class CAM_INTERFACE_Panel(CAMButtonsPanel, Panel): bl_label = "Interface" bl_idname = "WORLD_PT_CAM_INTERFACE" - always_show_panel = True - - def draw_interface_level(self): - self.layout.prop(self.context.scene.interface, "level", text="") def draw(self, context): - self.context = context - self.draw_interface_level() + layout = self.layout + layout.prop(context.scene.interface, "level", text="") diff --git a/scripts/addons/cam/ui/panels/machine.py b/scripts/addons/cam/ui/panels/machine.py index 35b3ba94..885ca04d 100644 --- a/scripts/addons/cam/ui/panels/machine.py +++ b/scripts/addons/cam/ui/panels/machine.py @@ -14,126 +14,84 @@ class CAM_MACHINE_Panel(CAMButtonsPanel, Panel): bl_label = "CAM Machine" bl_idname = "WORLD_PT_CAM_MACHINE" - always_show_panel = True - panel_interface_level = 0 - - prop_level = { - "draw_presets": 1, - "draw_post_processor": 0, - "draw_split_files": 2, - "draw_system": 0, - "draw_position_definitions": 2, - "draw_working_area": 0, - "draw_feedrates": 1, - "draw_splindle_speeds": 0, - "draw_tool_options": 2, - "draw_suplemental_axis": 3, - "draw_collet_size": 2, - "draw_block_numbers": 2, - "draw_hourly_rate": 1, - } - - def draw_presets(self): - if not self.has_correct_level(): - return - row = self.layout.row(align=True) - row.menu("CAM_MACHINE_MT_presets", text=bpy.types.CAM_MACHINE_MT_presets.bl_label) - row.operator("render.cam_preset_machine_add", text="", icon="ADD") - row.operator("render.cam_preset_machine_add", text="", icon="REMOVE").remove_active = True - - def draw_post_processor(self): - if not self.has_correct_level(): - return - self.layout.prop(self.machine, "post_processor") - - def draw_split_files(self): - if not self.has_correct_level(): - return - self.layout.prop(self.machine, "eval_splitting") - if self.machine.eval_splitting: - self.layout.prop(self.machine, "split_limit") - - def draw_system(self): - if not self.has_correct_level(): - return - self.layout.prop(bpy.context.scene.unit_settings, "system") - - def draw_position_definitions(self): - if not self.has_correct_level(): - return - self.layout.prop(self.machine, "use_position_definitions") - if self.machine.use_position_definitions: - self.layout.prop(self.machine, "starting_position") - self.layout.prop(self.machine, "mtc_position") - self.layout.prop(self.machine, "ending_position") - - def draw_working_area(self): - if not self.has_correct_level(): - return - self.layout.prop(self.machine, "working_area") - - def draw_feedrates(self): - if not self.has_correct_level(): - return - self.layout.prop(self.machine, "feedrate_min") - self.layout.prop(self.machine, "feedrate_max") - self.layout.prop(self.machine, "feedrate_default") - - def draw_splindle_speeds(self): - if not self.has_correct_level(): - return - # TODO: spindle default and feedrate default should become part of the cutter definition... - self.layout.prop(self.machine, "spindle_min") - self.layout.prop(self.machine, "spindle_max") - self.layout.prop(self.machine, "spindle_start_time") - self.layout.prop(self.machine, "spindle_default") - - def draw_tool_options(self): - if not self.has_correct_level(): - return - self.layout.prop(self.machine, "output_tool_definitions") - self.layout.prop(self.machine, "output_tool_change") - if self.machine.output_tool_change: - self.layout.prop(self.machine, "output_g43_on_tool_change") - - def draw_suplemental_axis(self): - if not self.has_correct_level(): - return - self.layout.prop(self.machine, "axis4") - self.layout.prop(self.machine, "axis5") - - def draw_collet_size(self): - if not self.has_correct_level(): - return - self.layout.prop(self.machine, "collet_size") - - def draw_block_numbers(self): - if not self.has_correct_level(): - return - self.layout.prop(self.machine, "output_block_numbers") - if self.machine.output_block_numbers: - self.layout.prop(self.machine, "start_block_number") - self.layout.prop(self.machine, "block_number_increment") - - def draw_hourly_rate(self): - if not self.has_correct_level(): - return - self.layout.prop(self.machine, "hourly_rate") def draw(self, context): - self.context = context - self.machine = bpy.context.scene.cam_machine + layout = self.layout - self.draw_presets() - self.draw_post_processor() - self.draw_split_files() - self.draw_system() - self.draw_position_definitions() - self.draw_working_area() - self.draw_feedrates() - self.draw_splindle_speeds() - self.draw_tool_options() - self.draw_suplemental_axis() - self.draw_collet_size() - self.draw_block_numbers() - self.draw_hourly_rate() + # Presets + if self.level >= 1: + row = layout.row(align=True) + row.menu("CAM_MACHINE_MT_presets", text=bpy.types.CAM_MACHINE_MT_presets.bl_label) + row.operator( + "render.cam_preset_machine_add", + text="", + icon="ADD", + ) + row.operator( + "render.cam_preset_machine_add", + text="", + icon="REMOVE", + ).remove_active = True + + # Post Processor + layout.prop(self.machine, "post_processor") + + # Split Files + if self.level >= 2: + layout.prop(self.machine, "eval_splitting") + if self.machine.eval_splitting: + layout.prop(self.machine, "split_limit") + + # System + layout.prop(bpy.context.scene.unit_settings, "system") + + # Position Definitions + if self.level >= 2: + layout.prop(self.machine, "use_position_definitions") + if self.machine.use_position_definitions: + layout.prop(self.machine, "starting_position") + layout.prop(self.machine, "mtc_position") + layout.prop(self.machine, "ending_position") + + # Working Area + layout.prop(self.machine, "working_area") + + # Feedrates + if self.level >= 1: + layout.prop(self.machine, "feedrate_min") + layout.prop(self.machine, "feedrate_max") + layout.prop(self.machine, "feedrate_default") + + # Spindle Speeds + # TODO: spindle default and feedrate default should become part of the cutter definition... + layout.prop(self.machine, "spindle_min") + layout.prop(self.machine, "spindle_max") + layout.prop(self.machine, "spindle_start_time") + layout.prop(self.machine, "spindle_default") + + # Tool Options + if self.level >= 2: + layout.prop(self.machine, "output_tool_definitions") + layout.prop(self.machine, "output_tool_change") + if self.machine.output_tool_change: + layout.prop(self.machine, "output_g43_on_tool_change") + + # Supplemental Axis + if self.level >= 3: + layout.prop(self.machine, "axis4") + layout.prop(self.machine, "axis5") + + # Collet Size + if self.level >= 2: + layout.prop(self.machine, "collet_size") + + # Block Numbers + if self.level >= 2: + layout.prop(self.machine, "output_block_numbers") + if self.machine.output_block_numbers: + layout.prop(self.machine, "start_block_number") + layout.prop(self.machine, "block_number_increment") + + # Hourly Rate + if self.level >= 1: + layout.prop(self.machine, "hourly_rate") diff --git a/scripts/addons/cam/ui/panels/material.py b/scripts/addons/cam/ui/panels/material.py index 8c63063e..7716d380 100644 --- a/scripts/addons/cam/ui/panels/material.py +++ b/scripts/addons/cam/ui/panels/material.py @@ -68,7 +68,6 @@ class CAM_MATERIAL_Properties(PropertyGroup): update=update_material, ) - # material_origin origin: FloatVectorProperty( name="Material Origin", default=(0, 0, 0), @@ -78,7 +77,6 @@ class CAM_MATERIAL_Properties(PropertyGroup): update=update_material, ) - # material_size size: FloatVectorProperty( name="Material Size", default=(0.200, 0.200, 0.100), @@ -97,11 +95,10 @@ class CAM_MATERIAL_PositionObject(Operator): bl_idname = "object.material_cam_position" bl_label = "Position Object for CAM Operation" bl_options = {"REGISTER", "UNDO"} - interface_level = 0 def execute(self, context): - s = bpy.context.scene - operation = s.cam_operations[s.cam_active_operation] + scene = context.scene + operation = scene.cam_operations[scene.cam_active_operation] if operation.object_name in bpy.data.objects: positionObject(operation) else: @@ -109,60 +106,37 @@ class CAM_MATERIAL_PositionObject(Operator): return {"FINISHED"} def draw(self, context): - if not self.interface_level <= int(self.context.scene.interface.level): - return - self.layout.prop_search(self, "operation", bpy.context.scene, "cam_operations") + layout = self.layout + layout.prop_search(self, "operation", context.scene, "cam_operations") class CAM_MATERIAL_Panel(CAMButtonsPanel, Panel): bl_label = "CAM Material Size and Position" bl_idname = "WORLD_PT_CAM_MATERIAL" - panel_interface_level = 0 - - prop_level = { - "draw_estimate_from_image": 0, - "draw_estimate_from_object": 1, - "draw_axis_alignment": 0, - } - - def draw_estimate_from_image(self): - if not self.has_correct_level(): - return - if self.op.geometry_source not in ["OBJECT", "COLLECTION"]: - self.layout.label(text="Estimated from Image") - - def draw_estimate_from_object(self): - if not self.has_correct_level(): - return - if self.op.geometry_source in ["OBJECT", "COLLECTION"]: - self.layout.prop(self.op.material, "estimate_from_model") - if self.op.material.estimate_from_model: - row_radius = self.layout.row() - row_radius.label(text="Additional Radius") - row_radius.prop(self.op.material, "radius_around_model", text="") - else: - self.layout.prop(self.op.material, "origin") - self.layout.prop(self.op.material, "size") - - # Display Axis alignment section - def draw_axis_alignment(self): - if not self.has_correct_level(): - return - if self.op.geometry_source in ["OBJECT", "COLLECTION"]: - row_axis = self.layout.row() - row_axis.prop(self.op.material, "center_x") - row_axis.prop(self.op.material, "center_y") - self.layout.prop(self.op.material, "z_position") - self.layout.operator("object.material_cam_position", text="Position Object") def draw(self, context): - self.context = context + layout = self.layout + if self.op is not None: + # Estimate from Image + if self.op.geometry_source not in ["OBJECT", "COLLECTION"]: + layout.label(text="Estimated from Image") - # FIXME: This function displays the progression of a job with a progress bar - # Commenting because it makes no sense here - # Consider removing it entirely - # self.layout.template_running_jobs() + # Estimate from Object + if self.level >= 1: + if self.op.geometry_source in ["OBJECT", "COLLECTION"]: + layout.prop(self.op.material, "estimate_from_model") + if self.op.material.estimate_from_model: + row_radius = layout.row() + row_radius.label(text="Additional Radius") + row_radius.prop(self.op.material, "radius_around_model", text="") + else: + layout.prop(self.op.material, "origin") + layout.prop(self.op.material, "size") - self.draw_estimate_from_image() - self.draw_estimate_from_object() - self.draw_axis_alignment() + # Axis Alignment + if self.op.geometry_source in ["OBJECT", "COLLECTION"]: + row_axis = layout.row() + row_axis.prop(self.op.material, "center_x") + row_axis.prop(self.op.material, "center_y") + layout.prop(self.op.material, "z_position") + layout.operator("object.material_cam_position", text="Position Object") diff --git a/scripts/addons/cam/ui/panels/movement.py b/scripts/addons/cam/ui/panels/movement.py index 22752b72..50fcb681 100644 --- a/scripts/addons/cam/ui/panels/movement.py +++ b/scripts/addons/cam/ui/panels/movement.py @@ -31,7 +31,11 @@ class CAM_MOVEMENT_Properties(PropertyGroup): "Conventional / Up milling", "Cutter rotates against the direction of the feed", ), - ("CLIMB", "Climb / Down milling", "Cutter rotates with the direction of the feed"), + ( + "CLIMB", + "Climb / Down milling", + "Cutter rotates with the direction of the feed", + ), ( "MEANDER", "Meander / Zig Zag", @@ -45,7 +49,10 @@ class CAM_MOVEMENT_Properties(PropertyGroup): insideout: EnumProperty( name="Direction", - items=(("INSIDEOUT", "Inside out", "a"), ("OUTSIDEIN", "Outside in", "a")), + items=( + ("INSIDEOUT", "Inside out", "a"), + ("OUTSIDEIN", "Outside in", "a"), + ), description="Approach to the piece", default="INSIDEOUT", update=update_operation, @@ -53,7 +60,10 @@ class CAM_MOVEMENT_Properties(PropertyGroup): spindle_rotation: EnumProperty( name="Spindle Rotation", - items=(("CW", "Clockwise", "a"), ("CCW", "Counter clockwise", "a")), + items=( + ("CW", "Clockwise", "a"), + ("CCW", "Counter clockwise", "a"), + ), description="Spindle rotation direction", default="CW", update=update_operation, @@ -216,109 +226,73 @@ class CAM_MOVEMENT_Panel(CAMButtonsPanel, Panel): bl_label = "CAM Movement" bl_idname = "WORLD_PT_CAM_MOVEMENT" - panel_interface_level = 0 - - prop_level = { - "draw_cut_type": 1, - "draw_spindle_rotation": 2, - "draw_free_height": 0, - "draw_use_g64": 2, - "draw_parallel_stepback": 1, - "draw_helix_enter": 2, - "draw_ramp": 1, - "draw_retract_tangential": 2, - "draw_stay_low": 1, - "draw_protect_vertical": 1, - } - - def draw_cut_type(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op.movement, "type") - if self.op.movement.type in ["BLOCK", "SPIRAL", "CIRCLES"]: - self.layout.prop(self.op.movement, "insideout") - - def draw_spindle_rotation(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op.movement, "spindle_rotation") - - def draw_free_height(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op.movement, "free_height") - if self.op.maxz > self.op.movement.free_height: - self.layout.label(text="Depth Start > Free Movement") - self.layout.label(text="POSSIBLE COLLISION") - - def draw_use_g64(self): - if not self.has_correct_level(): - return - if self.context.scene.cam_machine.post_processor not in G64_INCOMPATIBLE_MACHINES: - self.layout.prop(self.op.movement, "useG64") - if self.op.movement.useG64: - self.layout.prop(self.op.movement, "G64") - - def draw_parallel_stepback(self): - if not self.has_correct_level(): - return - if self.op.strategy in ["PARALLEL", "CROSS"]: - if not self.op.movement.ramp: - self.layout.prop(self.op.movement, "parallel_step_back") - - def draw_helix_enter(self): - if not self.has_correct_level(): - return - if self.op.strategy in ["POCKET"]: - self.layout.prop(self.op.movement, "helix_enter") - if self.op.movement.helix_enter: - self.layout.prop(self.op.movement, "ramp_in_angle") - self.layout.prop(self.op.movement, "helix_diameter") - - def draw_ramp(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op.movement, "ramp") - if self.op.movement.ramp: - self.layout.prop(self.op.movement, "ramp_in_angle") - self.layout.prop(self.op.movement, "ramp_out") - if self.op.movement.ramp_out: - self.layout.prop(self.op.movement, "ramp_out_angle") - - def draw_retract_tangential(self): - if not self.has_correct_level(): - return - if self.op.strategy in ["POCKET"]: - self.layout.prop(self.op.movement, "retract_tangential") - if self.op.movement.retract_tangential: - self.layout.prop(self.op.movement, "retract_radius") - self.layout.prop(self.op.movement, "retract_height") - - def draw_stay_low(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op.movement, "stay_low") - if self.op.movement.stay_low: - self.layout.prop(self.op.movement, "merge_dist") - - def draw_protect_vertical(self): - if not self.has_correct_level(): - return - if self.op.cutter_type not in ["BALLCONE"]: - self.layout.prop(self.op.movement, "protect_vertical") - if self.op.movement.protect_vertical: - self.layout.prop(self.op.movement, "protect_vertical_limit") def draw(self, context): - self.context = context + layout = self.layout + if self.op is not None: + # Cut Type + if self.level >= 1: + layout.prop(self.op.movement, "type") + if self.op.movement.type in ["BLOCK", "SPIRAL", "CIRCLES"]: + layout.prop(self.op.movement, "insideout") - self.draw_cut_type() - self.draw_spindle_rotation() - self.draw_free_height() - self.draw_use_g64() - self.draw_parallel_stepback() - self.draw_ramp() - self.draw_helix_enter() - self.draw_retract_tangential() - self.draw_stay_low() - self.draw_protect_vertical() + # Spindle Rotation + if self.level >= 2: + layout.prop(self.op.movement, "spindle_rotation") + + # Free Height + layout.prop(self.op.movement, "free_height") + if self.op.maxz > self.op.movement.free_height: + layout.label(text="Depth Start > Free Movement") + layout.label(text="POSSIBLE COLLISION") + + # Use G64 + if self.level >= 2: + if context.scene.cam_machine.post_processor not in G64_INCOMPATIBLE_MACHINES: + layout.prop(self.op.movement, "useG64") + if self.op.movement.useG64: + layout.prop(self.op.movement, "G64") + + # Parallel Stepback + if self.level >= 1: + if self.op.strategy in ["PARALLEL", "CROSS"]: + if not self.op.movement.ramp: + layout.prop(self.op.movement, "parallel_step_back") + + # Helix Enter + if self.level >= 2: + if self.op.strategy in ["POCKET"]: + layout.prop(self.op.movement, "helix_enter") + if self.op.movement.helix_enter: + layout.prop(self.op.movement, "ramp_in_angle") + layout.prop(self.op.movement, "helix_diameter") + + # Ramp + if self.level >= 1: + layout.prop(self.op.movement, "ramp") + if self.op.movement.ramp: + layout.prop(self.op.movement, "ramp_in_angle") + layout.prop(self.op.movement, "ramp_out") + if self.op.movement.ramp_out: + layout.prop(self.op.movement, "ramp_out_angle") + + # Retract Tangential + if self.level >= 2: + if self.op.strategy in ["POCKET"]: + layout.prop(self.op.movement, "retract_tangential") + if self.op.movement.retract_tangential: + layout.prop(self.op.movement, "retract_radius") + layout.prop(self.op.movement, "retract_height") + + # Stay Low + if self.level >= 1: + layout.prop(self.op.movement, "stay_low") + if self.op.movement.stay_low: + layout.prop(self.op.movement, "merge_dist") + + # Protect Vertical + if self.level >= 1: + if self.op.cutter_type not in ["BALLCONE"]: + layout.prop(self.op.movement, "protect_vertical") + if self.op.movement.protect_vertical: + layout.prop(self.op.movement, "protect_vertical_limit") diff --git a/scripts/addons/cam/ui/panels/op_properties.py b/scripts/addons/cam/ui/panels/op_properties.py index 273fee60..546c93d2 100644 --- a/scripts/addons/cam/ui/panels/op_properties.py +++ b/scripts/addons/cam/ui/panels/op_properties.py @@ -14,250 +14,174 @@ class CAM_OPERATION_PROPERTIES_Panel(CAMButtonsPanel, Panel): bl_label = "CAM Operation Setup" bl_idname = "WORLD_PT_CAM_OPERATION" - panel_interface_level = 0 - prop_level = { - "draw_cutter_engagement": 0, - "draw_machine_axis": 2, - "draw_strategy": 0, - "draw_enable_A_B_axis": 1, - "draw_cutout_options": 0, - "draw_waterline_options": 0, - "draw_medial_axis_options": 0, - "draw_drill_options": 0, - "draw_pocket_options": 0, - "draw_default_options": 0, - "draw_bridges_options": 1, - "draw_skin": 1, - "draw_array": 1, - "draw_cutout_type": 0, - "draw_overshoot": 1, - "draw_startpoint": 1, - "draw_lead_in_out": 3, - "draw_outlines": 2, - "draw_merge": 2, - } - - # Displays percentage of the cutter which is engaged with the material - # Displays a warning for engagements greater than 50% def draw_cutter_engagement(self): - if not self.has_correct_level(): - return + layout = self.layout + if self.op is not None: + # Cutter Engagement + # Warns if cutter engagement is greater than 50% + if self.op.cutter_type in ["BALLCONE"]: + engagement = round(100 * self.op.dist_between_paths / self.op.ball_radius, 1) + else: + engagement = round(100 * self.op.dist_between_paths / self.op.cutter_diameter, 1) - if self.op.cutter_type in ["BALLCONE"]: - engagement = round(100 * self.op.dist_between_paths / self.op.ball_radius, 1) - else: - engagement = round(100 * self.op.dist_between_paths / self.op.cutter_diameter, 1) + if engagement > 50: + layout.label(text="Warning: High Cutter Engagement") - if engagement > 50: - self.layout.label(text="Warning: High Cutter Engagement") - - self.layout.label(text=f"Cutter Engagement: {engagement}%") - - def draw_machine_axis(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "machine_axes") - - def draw_strategy(self): - if not self.has_correct_level(): - return - if self.op.machine_axes == "4": - self.layout.prop(self.op, "strategy4axis") - if self.op.strategy4axis == "INDEXED": - self.layout.prop(self.op, "strategy") - self.layout.prop(self.op, "rotary_axis_1") - elif self.op.machine_axes == "5": - self.layout.prop(self.op, "strategy5axis") - if self.op.strategy5axis == "INDEXED": - self.layout.prop(self.op, "strategy") - self.layout.prop(self.op, "rotary_axis_1") - self.layout.prop(self.op, "rotary_axis_2") - else: - self.layout.prop(self.op, "strategy") + layout.label(text=f"Cutter Engagement: {engagement}%") def draw_enable_A_B_axis(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "enable_A") - if self.op.enable_A: - self.layout.prop(self.op, "rotation_A") - self.layout.prop(self.op, "A_along_x") - if self.op.A_along_x: - self.layout.label(text="A || X - B || Y") - else: - self.layout.label(text="A || Y - B ||X") + layout = self.layout - self.layout.prop(self.op, "enable_B") - if self.op.enable_B: - self.layout.prop(self.op, "rotation_B") + # Enable A & B Axes + if self.level >= 1: + layout.prop(self.op, "enable_A") + if self.op.enable_A: + layout.prop(self.op, "rotation_A") + layout.prop(self.op, "A_along_x") + if self.op.A_along_x: + layout.label(text="A || X - B || Y") + else: + layout.label(text="A || Y - B || X") - def draw_cutout_type(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "cut_type") + layout.prop(self.op, "enable_B") + if self.op.enable_B: + layout.prop(self.op, "rotation_B") def draw_overshoot(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "straight") + layout = self.layout - def draw_startpoint(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "profile_start") - - def draw_lead_in_out(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "lead_in") - self.layout.prop(self.op, "lead_out") - - def draw_outlines(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "outlines_count") - if self.op.outlines_count > 1: - self.layout.prop(self.op, "dist_between_paths") - self.draw_cutter_engagement() - self.layout.prop(self.op.movement, "insideout") - - def draw_merge(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "dont_merge") - - def draw_cutout_options(self): - if not self.has_correct_level(): - return - if self.op.strategy in ["CUTOUT"]: - self.draw_cutout_type() - if self.op.cut_type in ["OUTSIDE", "INSIDE"]: - self.draw_overshoot() - self.draw_startpoint() - self.draw_lead_in_out() - - if self.op.strategy in ["CUTOUT", "CURVE"]: - self.draw_enable_A_B_axis() - self.draw_outlines() - self.draw_merge() - - def draw_waterline_options(self): - if not self.has_correct_level(): - return - if self.op.strategy in ["WATERLINE"]: - self.layout.label(text="Ocl Doesn't Support Fill Areas") - if not self.op.optimisation.use_opencamlib: - self.layout.prop(self.op, "slice_detail") - self.layout.prop(self.op, "waterline_fill") - if self.op.waterline_fill: - self.layout.prop(self.op, "dist_between_paths") - self.layout.prop(self.op, "waterline_project") - self.layout.label(text="Waterline Needs a Skin Margin") - - def draw_carve_options(self): - if not self.has_correct_level(): - return - if self.op.strategy in ["CARVE"]: - self.layout.prop(self.op, "carve_depth") - self.layout.prop(self.op, "dist_along_paths") - - def draw_medial_axis_options(self): - if not self.has_correct_level(): - return - if self.op.strategy in ["MEDIAL_AXIS"]: - self.layout.prop(self.op, "medial_axis_threshold") - self.layout.prop(self.op, "medial_axis_subdivision") - self.layout.prop(self.op, "add_pocket_for_medial") - self.layout.prop(self.op, "add_mesh_for_medial") - - def draw_drill_options(self): - if not self.has_correct_level(): - return - if self.op.strategy in ["DRILL"]: - self.layout.prop(self.op, "drill_type") - self.draw_enable_A_B_axis() - - def draw_pocket_options(self): - if not self.has_correct_level(): - return - if self.op.strategy in ["POCKET"]: - self.draw_overshoot() - self.layout.prop(self.op, "pocketType") - if self.op.pocketType == "PARALLEL": - self.layout.label(text="Warning:Parallel pocket Experimental", icon="ERROR") - self.layout.prop(self.op, "parallelPocketCrosshatch") - self.layout.prop(self.op, "parallelPocketContour") - self.layout.prop(self.op, "parallelPocketAngle") - else: - self.layout.prop(self.op, "pocket_option") - self.layout.prop(self.op, "pocketToCurve") - self.layout.prop(self.op, "dist_between_paths") - self.draw_cutter_engagement() - self.draw_enable_A_B_axis() - - def draw_default_options(self): - if not self.has_correct_level(): - return - if self.op.strategy not in [ - "CUTOUT", - "CURVE", - "WATERLINE", - "CARVE", - "MEDIAL_AXIS", - "DRILL", - "POCKET", - ]: - self.layout.prop(self.op, "dist_between_paths") - self.draw_cutter_engagement() - self.layout.prop(self.op, "dist_along_paths") - if self.op.strategy in ["PARALLEL", "CROSS"]: - self.layout.prop(self.op, "parallel_angle") - self.draw_enable_A_B_axis() - self.layout.prop(self.op, "inverse") - - def draw_bridges_options(self): - if not self.has_correct_level(): - return - if self.op.strategy not in ["POCKET", "DRILL", "CURVE", "MEDIAL_AXIS"]: - self.layout.prop(self.op, "use_bridges") - if self.op.use_bridges: - self.layout.prop(self.op, "bridges_width") - self.layout.prop(self.op, "bridges_height") - self.layout.prop_search(self.op, "bridges_collection_name", bpy.data, "collections") - self.layout.prop(self.op, "use_bridge_modifiers") - self.layout.operator("scene.cam_bridges_add", text="Autogenerate Bridges / Tabs") - - def draw_skin(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "skin") - - def draw_array(self): - if not self.has_correct_level(): - return - if self.op.machine_axes == "3": - self.layout.prop(self.op, "array") - if self.op.array: - self.layout.prop(self.op, "array_x_count") - self.layout.prop(self.op, "array_x_distance") - self.layout.prop(self.op, "array_y_count") - self.layout.prop(self.op, "array_y_distance") + # Overshoot + layout.prop(self.op, "straight") def draw(self, context): - self.context = context + layout = self.layout + if self.op is not None: + # Machine Axis + if self.level >= 2: + layout.prop(self.op, "machine_axes") - self.draw_machine_axis() - self.draw_strategy() + # Strategy + if self.op.machine_axes == "4": + layout.prop(self.op, "strategy4axis") + if self.op.strategy4axis == "INDEXED": + layout.prop(self.op, "strategy") + layout.prop(self.op, "rotary_axis_1") + elif self.op.machine_axes == "5": + layout.prop(self.op, "strategy5axis") + if self.op.strategy5axis == "INDEXED": + layout.prop(self.op, "strategy") + layout.prop(self.op, "rotary_axis_1") + layout.prop(self.op, "rotary_axis_2") + else: + layout.prop(self.op, "strategy") - self.draw_cutout_options() - self.draw_waterline_options() - self.draw_carve_options() - self.draw_medial_axis_options() - self.draw_drill_options() - self.draw_pocket_options() - self.draw_default_options() + # Cutout Options + if self.op.strategy in ["CUTOUT"]: + # Cutout Type + layout.prop(self.op, "cut_type") + if self.op.cut_type in ["OUTSIDE", "INSIDE"]: + self.draw_overshoot() + # Startpoint + layout.prop(self.op, "profile_start") + # Lead In & Out + layout.prop(self.op, "lead_in") + layout.prop(self.op, "lead_out") - self.draw_bridges_options() - self.draw_skin() - self.draw_array() + if self.op.strategy in ["CUTOUT", "CURVE"]: + self.draw_enable_A_B_axis() + # Outlines + layout.prop(self.op, "outlines_count") + if self.op.outlines_count > 1: + layout.prop(self.op, "dist_between_paths") + self.draw_cutter_engagement() + layout.prop(self.op.movement, "insideout") + # Merge + layout.prop(self.op, "dont_merge") + + # Waterline Options + if self.op.strategy in ["WATERLINE"]: + layout.label(text="Ocl Doesn't Support Fill Areas") + if not self.op.optimisation.use_opencamlib: + layout.prop(self.op, "slice_detail") + layout.prop(self.op, "waterline_fill") + if self.op.waterline_fill: + layout.prop(self.op, "dist_between_paths") + layout.prop(self.op, "waterline_project") + layout.label(text="Waterline Needs a Skin Margin") + + # Carve Options + if self.op.strategy in ["CARVE"]: + layout.prop(self.op, "carve_depth") + layout.prop(self.op, "dist_along_paths") + + # Medial Axis Options + if self.op.strategy in ["MEDIAL_AXIS"]: + layout.prop(self.op, "medial_axis_threshold") + layout.prop(self.op, "medial_axis_subdivision") + layout.prop(self.op, "add_pocket_for_medial") + layout.prop(self.op, "add_mesh_for_medial") + + # Drill Options + if self.op.strategy in ["DRILL"]: + layout.prop(self.op, "drill_type") + self.draw_enable_A_B_axis() + + # Pocket Options + if self.op.strategy in ["POCKET"]: + self.draw_overshoot() + layout.prop(self.op, "pocketType") + if self.op.pocketType == "PARALLEL": + layout.label(text="Warning:Parallel pocket Experimental", icon="ERROR") + layout.prop(self.op, "parallelPocketCrosshatch") + layout.prop(self.op, "parallelPocketContour") + layout.prop(self.op, "parallelPocketAngle") + else: + layout.prop(self.op, "pocket_option") + layout.prop(self.op, "pocketToCurve") + layout.prop(self.op, "dist_between_paths") + self.draw_cutter_engagement() + self.draw_enable_A_B_axis() + + # Default Options + if self.op.strategy not in [ + "CUTOUT", + "CURVE", + "WATERLINE", + "CARVE", + "MEDIAL_AXIS", + "DRILL", + "POCKET", + ]: + layout.prop(self.op, "dist_between_paths") + self.draw_cutter_engagement() + layout.prop(self.op, "dist_along_paths") + if self.op.strategy in ["PARALLEL", "CROSS"]: + layout.prop(self.op, "parallel_angle") + self.draw_enable_A_B_axis() + layout.prop(self.op, "inverse") + + # Bridges Options + if self.level >= 1: + if self.op.strategy not in ["POCKET", "DRILL", "CURVE", "MEDIAL_AXIS"]: + layout.prop(self.op, "use_bridges") + if self.op.use_bridges: + layout.prop(self.op, "bridges_width") + layout.prop(self.op, "bridges_height") + layout.prop_search( + self.op, "bridges_collection_name", bpy.data, "collections" + ) + layout.prop(self.op, "use_bridge_modifiers") + layout.operator("scene.cam_bridges_add", text="Autogenerate Bridges / Tabs") + + # Skin + self.layout.prop(self.op, "skin") + + # Array + if self.op.machine_axes == "3": + layout.prop(self.op, "array") + if self.op.array: + layout.prop(self.op, "array_x_count") + layout.prop(self.op, "array_x_distance") + layout.prop(self.op, "array_y_count") + layout.prop(self.op, "array_y_distance") diff --git a/scripts/addons/cam/ui/panels/operations.py b/scripts/addons/cam/ui/panels/operations.py index a46bccd2..5be3232b 100644 --- a/scripts/addons/cam/ui/panels/operations.py +++ b/scripts/addons/cam/ui/panels/operations.py @@ -9,8 +9,8 @@ from bpy.types import Panel from .buttons_panel import CAMButtonsPanel # Operations panel -# This panel displays the list of operations created by the user -# Functionnalities are: +# Displays the list of operations created by the user +# Functionality: # - list Operations # - create/delete/duplicate/reorder operations # - display preset operations @@ -23,24 +23,21 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, Panel): bl_label = "CAM Operations" bl_idname = "WORLD_PT_CAM_OPERATIONS" - always_show_panel = True - panel_interface_level = 0 - prop_level = { - "draw_presets": 1, - "draw_operations_list": 0, - "draw_calculate_path": 0, - "draw_export_gcode": 1, - "draw_simulate_op": 1, - "draw_op_name": 1, - "draw_op_filename": 0, - "draw_operation_source": 0, - } + def draw(self, context): + layout = self.layout + # Presets + if self.level >= 1: + row = layout.row(align=True) + row.menu("CAM_OPERATION_MT_presets", text=bpy.types.CAM_OPERATION_MT_presets.bl_label) + row.operator("render.cam_preset_operation_add", text="", icon="ADD") + row.operator( + "render.cam_preset_operation_add", text="", icon="REMOVE" + ).remove_active = True - # Draw the list of operations and the associated buttons: - # create, delete, duplicate, reorder - def draw_operations_list(self): - row = self.layout.row() + # Operations List + # create, delete, duplicate, reorder + row = layout.row() row.template_list( "CAM_UL_operations", "", @@ -57,99 +54,60 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, Panel): col.operator("scene.cam_operation_move", icon="TRIA_UP", text="").direction = "UP" col.operator("scene.cam_operation_move", icon="TRIA_DOWN", text="").direction = "DOWN" - # Draw the list of preset operations, and preset add and remove buttons - - def draw_presets(self): - if not self.has_correct_level(): - return - row = self.layout.row(align=True) - row.menu("CAM_OPERATION_MT_presets", text=bpy.types.CAM_OPERATION_MT_presets.bl_label) - row.operator("render.cam_preset_operation_add", text="", icon="ADD") - row.operator("render.cam_preset_operation_add", text="", icon="REMOVE").remove_active = True - - def draw_calculate_path(self): - if not self.has_correct_level(): - return - if self.op.maxz > self.op.movement.free_height: - self.layout.label(text="!ERROR! COLLISION!") - self.layout.label(text="Depth Start > Free Movement Height") - self.layout.label(text="!ERROR! COLLISION!") - self.layout.prop(self.op.movement, "free_height") - - if not self.op.valid: - self.layout.label(text="Select a Valid Object to Calculate the Path.") - # will be disable if not valid - self.layout.operator("object.calculate_cam_path", text="Calculate Path & Export Gcode") - - def draw_export_gcode(self): - if not self.has_correct_level(): - return - if self.op.valid: - if self.op.name is not None: - name = f"cam_path_{self.op.name}" - if bpy.context.scene.objects.get(name) is not None: - self.layout.operator("object.cam_export", text="Export Gcode ") - - def draw_simulate_op(self): - if not self.has_correct_level(): - return - if self.op.valid: - self.layout.operator("object.cam_simulate", text="Simulate This Operation") - - def draw_op_name(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "name") - - def draw_op_filename(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "filename") - - # Draw a list of objects which will be used as the source of the operation - # FIXME Right now, cameras or lights may be used, which crashes - # The user should only be able to choose meshes and curves - - def draw_operation_source(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "geometry_source") - - if self.op.strategy == "CURVE": - if self.op.geometry_source == "OBJECT": - self.layout.prop_search(self.op, "object_name", bpy.data, "objects") - elif self.op.geometry_source == "COLLECTION": - self.layout.prop_search(self.op, "collection_name", bpy.data, "collections") - else: - if self.op.geometry_source == "OBJECT": - self.layout.prop_search(self.op, "object_name", bpy.data, "objects") - if self.op.enable_A: - self.layout.prop(self.op, "rotation_A") - if self.op.enable_B: - self.layout.prop(self.op, "rotation_B") - - elif self.op.geometry_source == "COLLECTION": - self.layout.prop_search(self.op, "collection_name", bpy.data, "collections") - else: - self.layout.prop_search(self.op, "source_image_name", bpy.data, "images") - - if self.op.strategy in ["CARVE", "PROJECTED_CURVE"]: - self.layout.prop_search(self.op, "curve_object", bpy.data, "objects") - if self.op.strategy == "PROJECTED_CURVE": - self.layout.prop_search(self.op, "curve_object1", bpy.data, "objects") - - def draw(self, context): - self.context = context - - self.draw_presets() - self.draw_operations_list() - if self.op is None: return - self.draw_calculate_path() - self.draw_export_gcode() - self.draw_simulate_op() - self.draw_op_name() - self.draw_op_filename() - self.draw_operation_source() + # Calculate Path + if self.op.maxz > self.op.movement.free_height: + layout.label(text="!ERROR! COLLISION!") + layout.label(text="Depth Start > Free Movement Height") + layout.label(text="!ERROR! COLLISION!") + layout.prop(self.op.movement, "free_height") + + if not self.op.valid: + layout.label(text="Select a Valid Object to Calculate the Path.") + # will be disabled if not valid + layout.operator("object.calculate_cam_path", text="Calculate Path & Export Gcode") + + # Export Gcode + if self.level >= 1: + if self.op.valid: + if self.op.name is not None: + name = f"cam_path_{self.op.name}" + if bpy.context.scene.objects.get(name) is not None: + layout.operator("object.cam_export", text="Export Gcode ") + + # Simulate Op + layout.operator("object.cam_simulate", text="Simulate This Operation") + + # Op Name + layout.prop(self.op, "name") + + # Op Filename + layout.prop(self.op, "filename") + + # Op Source + layout.prop(self.op, "geometry_source") + + if self.op.strategy == "CURVE": + if self.op.geometry_source == "OBJECT": + layout.prop_search(self.op, "object_name", bpy.data, "objects") + elif self.op.geometry_source == "COLLECTION": + layout.prop_search(self.op, "collection_name", bpy.data, "collections") + else: + if self.op.geometry_source == "OBJECT": + layout.prop_search(self.op, "object_name", bpy.data, "objects") + if self.op.enable_A: + layout.prop(self.op, "rotation_A") + if self.op.enable_B: + layout.prop(self.op, "rotation_B") + + elif self.op.geometry_source == "COLLECTION": + layout.prop_search(self.op, "collection_name", bpy.data, "collections") + else: + layout.prop_search(self.op, "source_image_name", bpy.data, "images") + + if self.op.strategy in ["CARVE", "PROJECTED_CURVE"]: + layout.prop_search(self.op, "curve_object", bpy.data, "objects") + if self.op.strategy == "PROJECTED_CURVE": + layout.prop_search(self.op, "curve_object1", bpy.data, "objects") diff --git a/scripts/addons/cam/ui/panels/optimisation.py b/scripts/addons/cam/ui/panels/optimisation.py index 47ecb328..84d7d745 100644 --- a/scripts/addons/cam/ui/panels/optimisation.py +++ b/scripts/addons/cam/ui/panels/optimisation.py @@ -110,104 +110,75 @@ class CAM_OPTIMISATION_Panel(CAMButtonsPanel, Panel): bl_idname = "WORLD_PT_CAM_OPTIMISATION" panel_interface_level = 2 - def draw_optimize(self): - if not self.has_correct_level(): - return - - self.layout.prop(self.op.optimisation, "optimize") - if self.op.optimisation.optimize: - self.layout.prop(self.op.optimisation, "optimize_threshold") - - def draw_exact_mode(self): - if not self.has_correct_level(): - return - - if not self.op.geometry_source == "OBJECT" or self.op.geometry_source == "COLLECTION": - return - - self.exact_possible = self.op.strategy not in [ - "MEDIAL_AXIS", - "POCKET", - "CUTOUT", - "DRILL", - "PENCIL", - "CURVE", - ] - - if self.exact_possible: - self.layout.prop(self.op.optimisation, "use_exact") - - if not self.exact_possible or not self.op.optimisation.use_exact: - self.layout.prop(self.op.optimisation, "pixsize") - self.layout.prop(self.op.optimisation, "imgres_limit") - - sx = self.op.max.x - self.op.min.x - sy = self.op.max.y - self.op.min.y - resx = int(sx / self.op.optimisation.pixsize) - resy = int(sy / self.op.optimisation.pixsize) - - if resx > 0 and resy > 0: - resolution = "Resolution: " + str(resx) + " x " + str(resy) - self.layout.label(text=resolution) - - def draw_use_opencamlib(self): - if not self.has_correct_level(): - return - - if not (self.exact_possible and self.op.optimisation.use_exact): - return - - ocl_version = opencamlib_version() - - if ocl_version is None: - self.layout.label(text="OpenCAMLib is not Available ") - self.layout.prop(self.op.optimisation, "exact_subdivide_edges") - else: - self.layout.prop(self.op.optimisation, "use_opencamlib") - - def draw_simulation_detail(self): - if not self.has_correct_level(): - return - - self.layout.prop(self.op.optimisation, "simulation_detail") - self.layout.prop(self.op.optimisation, "circle_detail") - - def draw_simplify_gcode(self): - if not self.has_correct_level(): - return - - if self.op.strategy not in ["DRILL"]: - self.layout.prop(self.op, "remove_redundant_points") - - if self.op.remove_redundant_points: - self.layout.prop(self.op, "simplify_tol") - - def draw_use_modifiers(self): - if not self.has_correct_level(): - return - if self.op.geometry_source in ["OBJECT", "COLLECTION"]: - self.layout.prop(self.op, "use_modifiers") - - def draw_hide_all_others(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "hide_all_others") - - def draw_parent_path_to_object(self): - if not self.has_correct_level(): - return - self.layout.prop(self.op, "parent_path_to_object") - def draw(self, context): - self.context = context + layout = self.layout - self.draw_optimize() - self.layout.separator() - self.draw_exact_mode() - self.draw_use_opencamlib() - self.layout.separator() - self.draw_simulation_detail() - self.draw_simplify_gcode() - self.draw_use_modifiers() - self.draw_hide_all_others() - self.draw_parent_path_to_object() + if self.level >= 2 and self.op is not None: + # Optimize + layout.prop(self.op.optimisation, "optimize") + if self.op.optimisation.optimize: + layout.prop(self.op.optimisation, "optimize_threshold") + layout.separator() + + # Exact Mode + if not self.op.geometry_source == "OBJECT" or self.op.geometry_source == "COLLECTION": + return + + self.exact_possible = self.op.strategy not in [ + "MEDIAL_AXIS", + "POCKET", + "CUTOUT", + "DRILL", + "PENCIL", + "CURVE", + ] + + if self.exact_possible: + layout.prop(self.op.optimisation, "use_exact") + + if not self.exact_possible or not self.op.optimisation.use_exact: + layout.prop(self.op.optimisation, "pixsize") + layout.prop(self.op.optimisation, "imgres_limit") + + sx = self.op.max.x - self.op.min.x + sy = self.op.max.y - self.op.min.y + resx = int(sx / self.op.optimisation.pixsize) + resy = int(sy / self.op.optimisation.pixsize) + + if resx > 0 and resy > 0: + resolution = "Resolution: " + str(resx) + " x " + str(resy) + layout.label(text=resolution) + + # Use OpenCAMLib + if not (self.exact_possible and self.op.optimisation.use_exact): + return + + ocl_version = opencamlib_version() + + if ocl_version is None: + layout.label(text="OpenCAMLib is not Available ") + layout.prop(self.op.optimisation, "exact_subdivide_edges") + else: + layout.prop(self.op.optimisation, "use_opencamlib") + layout.separator() + + # Simulation Detail + layout.prop(self.op.optimisation, "simulation_detail") + layout.prop(self.op.optimisation, "circle_detail") + + # Simplify Gcode + if self.op.strategy not in ["DRILL"]: + layout.prop(self.op, "remove_redundant_points") + + if self.op.remove_redundant_points: + layout.prop(self.op, "simplify_tol") + + # Use Modifiers + if self.op.geometry_source in ["OBJECT", "COLLECTION"]: + layout.prop(self.op, "use_modifiers") + + # Hide All Others + layout.prop(self.op, "hide_all_others") + + # Parent Path to Object + layout.prop(self.op, "parent_path_to_object") diff --git a/scripts/addons/cam/ui/panels/pack.py b/scripts/addons/cam/ui/panels/pack.py index 557e97c6..8512e67d 100644 --- a/scripts/addons/cam/ui/panels/pack.py +++ b/scripts/addons/cam/ui/panels/pack.py @@ -14,23 +14,18 @@ class CAM_PACK_Panel(CAMButtonsPanel, Panel): bl_label = "Pack Curves on Sheet" bl_idname = "WORLD_PT_CAM_PACK" - panel_interface_level = 2 - - COMPAT_ENGINES = {"CNCCAM_RENDER"} def draw(self, context): layout = self.layout scene = bpy.context.scene settings = scene.cam_pack - layout.label(text="Warning - Algorithm Is Slow.") - layout.label(text="Only for Curves Now.") - - layout.operator("object.cam_pack_objects") - layout.prop(settings, "sheet_fill_direction") - layout.prop(settings, "sheet_x") - layout.prop(settings, "sheet_y") - layout.prop(settings, "distance") - layout.prop(settings, "tolerance") - layout.prop(settings, "rotate") - if settings.rotate: - layout.prop(settings, "rotate_angle") + if self.level >= 2: + layout.operator("object.cam_pack_objects") + layout.prop(settings, "sheet_fill_direction") + layout.prop(settings, "sheet_x") + layout.prop(settings, "sheet_y") + layout.prop(settings, "distance") + layout.prop(settings, "tolerance") + layout.prop(settings, "rotate") + if settings.rotate: + layout.prop(settings, "rotate_angle") diff --git a/scripts/addons/cam/ui/panels/slice.py b/scripts/addons/cam/ui/panels/slice.py index c72ab0ad..af66377c 100644 --- a/scripts/addons/cam/ui/panels/slice.py +++ b/scripts/addons/cam/ui/panels/slice.py @@ -14,17 +14,15 @@ class CAM_SLICE_Panel(CAMButtonsPanel, Panel): bl_label = "Slice Model to Plywood Sheets" bl_idname = "WORLD_PT_CAM_SLICE" - panel_interface_level = 2 - - COMPAT_ENGINES = {"CNCCAM_RENDER"} def draw(self, context): layout = self.layout scene = bpy.context.scene settings = scene.cam_slice - layout.operator("object.cam_slice_objects") - layout.prop(settings, "slice_distance") - layout.prop(settings, "slice_above0") - layout.prop(settings, "slice_3d") - layout.prop(settings, "indexes") + if self.level >= 2: + layout.operator("object.cam_slice_objects") + layout.prop(settings, "slice_distance") + layout.prop(settings, "slice_above0") + layout.prop(settings, "slice_3d") + layout.prop(settings, "indexes")