kopia lustrzana https://github.com/vilemduha/blendercam
Add files via upload
rodzic
a4c393849b
commit
11444b957f
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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%")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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="")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
Ładowanie…
Reference in New Issue