Add files via upload

pull/276/head
SpectralVectors 2024-10-23 17:37:33 -04:00 zatwierdzone przez GitHub
rodzic a4c393849b
commit 11444b957f
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
16 zmienionych plików z 798 dodań i 1249 usunięć

Wyświetl plik

@ -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")

Wyświetl plik

@ -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

Wyświetl plik

@ -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")

Wyświetl plik

@ -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%")

Wyświetl plik

@ -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")

Wyświetl plik

@ -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")

Wyświetl plik

@ -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)

Wyświetl plik

@ -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="")

Wyświetl plik

@ -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")

Wyświetl plik

@ -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")

Wyświetl plik

@ -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")

Wyświetl plik

@ -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")

Wyświetl plik

@ -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")

Wyświetl plik

@ -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")

Wyświetl plik

@ -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")

Wyświetl plik

@ -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")