From 098b1540a0bf082801dbf57a9332d48165a57d7c Mon Sep 17 00:00:00 2001 From: migo101 Date: Wed, 13 Apr 2022 18:34:17 +0200 Subject: [PATCH 01/13] Quick update --- scripts/addons/cam/ui.py | 73 +------------------ scripts/addons/cam/ui_panels/buttons_panel.py | 9 ++- scripts/addons/cam/ui_panels/feedrate.py | 23 ++++++ scripts/addons/cam/ui_panels/info.py | 8 +- scripts/addons/cam/ui_panels/optimisation.py | 50 +++++++++++++ 5 files changed, 84 insertions(+), 79 deletions(-) create mode 100644 scripts/addons/cam/ui_panels/feedrate.py create mode 100644 scripts/addons/cam/ui_panels/optimisation.py diff --git a/scripts/addons/cam/ui.py b/scripts/addons/cam/ui.py index adc73248..1048f07d 100644 --- a/scripts/addons/cam/ui.py +++ b/scripts/addons/cam/ui.py @@ -42,6 +42,8 @@ from cam.ui_panels.material import CAM_MATERIAL_Panel from cam.ui_panels.chains import CAM_UL_operations, CAM_UL_chains, CAM_CHAINS_Panel from cam.ui_panels.op_properties import CAM_OPERATION_PROPERTIES_Panel from cam.ui_panels.movement import CAM_MOVEMENT_Panel +from cam.ui_panels.feedrate import CAM_FEEDRATE_Panel +from cam.ui_panels.optimisation import CAM_OPTIMISATION_Panel class CAM_UL_orientations(UIList): @@ -55,77 +57,6 @@ class CAM_UL_orientations(UIList): -class CAM_FEEDRATE_Panel(CAMButtonsPanel, bpy.types.Panel): - """CAM feedrate panel""" - bl_label = "CAM feedrate" - bl_idname = "WORLD_PT_CAM_FEEDRATE" - - COMPAT_ENGINES = {'BLENDERCAM_RENDER'} - - def draw(self, context): - layout = self.layout - scene = bpy.context.scene - if len(scene.cam_operations) == 0: - layout.label(text='Add operation first') - if len(scene.cam_operations) > 0: - ao = scene.cam_operations[scene.cam_active_operation] - if ao.valid: - layout.prop(ao, 'feedrate') - layout.prop(ao, 'do_simulation_feedrate') - layout.prop(ao, 'plunge_feedrate') - layout.prop(ao, 'plunge_angle') - layout.prop(ao, 'spindle_rpm') - - -class CAM_OPTIMISATION_Panel(CAMButtonsPanel, bpy.types.Panel): - """CAM optimisation panel""" - bl_label = "CAM optimisation" - bl_idname = "WORLD_PT_CAM_OPTIMISATION" - - COMPAT_ENGINES = {'BLENDERCAM_RENDER'} - - def draw(self, context): - layout = self.layout - scene = bpy.context.scene - - if len(scene.cam_operations) == 0: - layout.label(text='Add operation first') - if len(scene.cam_operations) > 0: - ao = scene.cam_operations[scene.cam_active_operation] - if ao.valid: - layout.prop(ao, 'optimize') - if ao.optimize: - layout.prop(ao, 'optimize_threshold') - if ao.geometry_source == 'OBJECT' or ao.geometry_source == 'COLLECTION': - exclude_exact = ao.strategy in ['MEDIAL_AXIS', 'POCKET', 'WATERLINE', 'CUTOUT', 'DRILL', 'PENCIL', - 'CURVE'] - if not exclude_exact: - if not ao.use_exact: - layout.prop(ao, 'use_exact') - layout.label(text="Exact mode must be set for opencamlib to work ") - - if "ocl" in sys.modules: - layout.label(text="Opencamlib is available ") - layout.prop(ao, 'use_opencamlib') - else: - layout.label(text="Opencamlib is NOT available ") - layout.prop(ao, 'exact_subdivide_edges') - - if exclude_exact or not ao.use_exact: - layout.prop(ao, 'pixsize') - layout.prop(ao, 'imgres_limit') - - sx = ao.max.x - ao.min.x - sy = ao.max.y - ao.min.y - resx = int(sx / ao.pixsize) - resy = int(sy / ao.pixsize) - l = 'resolution: ' + str(resx) + ' x ' + str(resy) - layout.label(text=l) - - layout.prop(ao, 'simulation_detail') - layout.prop(ao, 'circle_detail') - - class CAM_AREA_Panel(CAMButtonsPanel, bpy.types.Panel): """CAM operation area panel""" bl_label = "CAM operation area " diff --git a/scripts/addons/cam/ui_panels/buttons_panel.py b/scripts/addons/cam/ui_panels/buttons_panel.py index 3c5ff37c..7a1d4aa0 100644 --- a/scripts/addons/cam/ui_panels/buttons_panel.py +++ b/scripts/addons/cam/ui_panels/buttons_panel.py @@ -1,5 +1,6 @@ import bpy - +import sys +import ocl # Panel definitions class CAMButtonsPanel: bl_space_type = 'PROPERTIES' @@ -33,3 +34,9 @@ class CAMButtonsPanel: def has_operations(self): return (self.operations_count() > 0) + + def opencamlib_version(self): + if "ocl" in sys.modules: + return(f"Opencamlib v{ocl.version()} installed") + else: + return("Opencamlib is not installed") diff --git a/scripts/addons/cam/ui_panels/feedrate.py b/scripts/addons/cam/ui_panels/feedrate.py new file mode 100644 index 00000000..7c1f1134 --- /dev/null +++ b/scripts/addons/cam/ui_panels/feedrate.py @@ -0,0 +1,23 @@ +import bpy +from cam.ui_panels.buttons_panel import CAMButtonsPanel + +class CAM_FEEDRATE_Panel(CAMButtonsPanel, bpy.types.Panel): + """CAM feedrate panel""" + bl_label = "CAM feedrate" + bl_idname = "WORLD_PT_CAM_FEEDRATE" + + COMPAT_ENGINES = {'BLENDERCAM_RENDER'} + + def draw(self, context): + if not self.has_operations(): + self.layout.label(text='Add operation first') + return + + ao = self.active_operation() + if not ao.valid: return + + self.layout.prop(ao, 'feedrate') + self.layout.prop(ao, 'do_simulation_feedrate') + self.layout.prop(ao, 'plunge_feedrate') + self.layout.prop(ao, 'plunge_angle') + self.layout.prop(ao, 'spindle_rpm') diff --git a/scripts/addons/cam/ui_panels/info.py b/scripts/addons/cam/ui_panels/info.py index 2db6a207..ca4d7a13 100644 --- a/scripts/addons/cam/ui_panels/info.py +++ b/scripts/addons/cam/ui_panels/info.py @@ -1,6 +1,3 @@ - -import sys -import ocl import bpy from cam.simple import strInUnits @@ -27,10 +24,7 @@ class CAM_INFO_Panel(CAMButtonsPanel, bpy.types.Panel): self.layout.label(text='No CAM operation created') def draw_opencamlib_version(self): - if "ocl" in sys.modules: - self.layout.label(text = f"Opencamlib v{ocl.version()} installed") - else: - self.layout.label(text = "Opencamlib is not installed") + self.layout.label(text = self.opencamlib_version()) def draw_active_op_warnings(self): active_op = self.active_operation() diff --git a/scripts/addons/cam/ui_panels/optimisation.py b/scripts/addons/cam/ui_panels/optimisation.py new file mode 100644 index 00000000..0f62abb0 --- /dev/null +++ b/scripts/addons/cam/ui_panels/optimisation.py @@ -0,0 +1,50 @@ +import bpy +from cam.ui_panels.buttons_panel import CAMButtonsPanel + +class CAM_OPTIMISATION_Panel(CAMButtonsPanel, bpy.types.Panel): + """CAM optimisation panel""" + bl_label = "CAM optimisation" + bl_idname = "WORLD_PT_CAM_OPTIMISATION" + + COMPAT_ENGINES = {'BLENDERCAM_RENDER'} + + def draw(self, context): + layout = self.layout + scene = bpy.context.scene + + if len(scene.cam_operations) == 0: + layout.label(text='Add operation first') + if len(scene.cam_operations) > 0: + ao = scene.cam_operations[scene.cam_active_operation] + if ao.valid: + layout.prop(ao, 'optimize') + if ao.optimize: + layout.prop(ao, 'optimize_threshold') + if ao.geometry_source == 'OBJECT' or ao.geometry_source == 'COLLECTION': + exclude_exact = ao.strategy in ['MEDIAL_AXIS', 'POCKET', 'WATERLINE', 'CUTOUT', 'DRILL', 'PENCIL', + 'CURVE'] + if not exclude_exact: + if not ao.use_exact: + layout.prop(ao, 'use_exact') + layout.label(text="Exact mode must be set for opencamlib to work ") + + if "ocl" in sys.modules: + layout.label(text="Opencamlib is available ") + layout.prop(ao, 'use_opencamlib') + else: + layout.label(text="Opencamlib is NOT available ") + layout.prop(ao, 'exact_subdivide_edges') + + if exclude_exact or not ao.use_exact: + layout.prop(ao, 'pixsize') + layout.prop(ao, 'imgres_limit') + + sx = ao.max.x - ao.min.x + sy = ao.max.y - ao.min.y + resx = int(sx / ao.pixsize) + resy = int(sy / ao.pixsize) + l = 'resolution: ' + str(resx) + ' x ' + str(resy) + layout.label(text=l) + + layout.prop(ao, 'simulation_detail') + layout.prop(ao, 'circle_detail') From d9b112f499f69d3cac67378b2f0e228eb7b941e5 Mon Sep 17 00:00:00 2001 From: migo101 Date: Wed, 13 Apr 2022 19:04:10 +0200 Subject: [PATCH 02/13] opencamlib version function created --- scripts/addons/cam/ui_panels/buttons_panel.py | 5 ++--- scripts/addons/cam/ui_panels/info.py | 6 +++++- scripts/addons/cam/ui_panels/optimisation.py | 9 +++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/scripts/addons/cam/ui_panels/buttons_panel.py b/scripts/addons/cam/ui_panels/buttons_panel.py index 7a1d4aa0..7ad4e570 100644 --- a/scripts/addons/cam/ui_panels/buttons_panel.py +++ b/scripts/addons/cam/ui_panels/buttons_panel.py @@ -37,6 +37,5 @@ class CAMButtonsPanel: def opencamlib_version(self): if "ocl" in sys.modules: - return(f"Opencamlib v{ocl.version()} installed") - else: - return("Opencamlib is not installed") + return(ocl.version()) + return diff --git a/scripts/addons/cam/ui_panels/info.py b/scripts/addons/cam/ui_panels/info.py index ca4d7a13..cee1d50e 100644 --- a/scripts/addons/cam/ui_panels/info.py +++ b/scripts/addons/cam/ui_panels/info.py @@ -24,7 +24,11 @@ class CAM_INFO_Panel(CAMButtonsPanel, bpy.types.Panel): self.layout.label(text='No CAM operation created') def draw_opencamlib_version(self): - self.layout.label(text = self.opencamlib_version()) + opencamlib_version = self.opencamlib_version() + if opencamlib_version is None: + self.layout.label(text = "Opencamlib is not installed") + else: + self.layout.label(text = f"Opencamlib v{opencamlib_version} installed") def draw_active_op_warnings(self): active_op = self.active_operation() diff --git a/scripts/addons/cam/ui_panels/optimisation.py b/scripts/addons/cam/ui_panels/optimisation.py index 0f62abb0..d394709b 100644 --- a/scripts/addons/cam/ui_panels/optimisation.py +++ b/scripts/addons/cam/ui_panels/optimisation.py @@ -28,12 +28,13 @@ class CAM_OPTIMISATION_Panel(CAMButtonsPanel, bpy.types.Panel): layout.prop(ao, 'use_exact') layout.label(text="Exact mode must be set for opencamlib to work ") - if "ocl" in sys.modules: - layout.label(text="Opencamlib is available ") - layout.prop(ao, 'use_opencamlib') - else: + opencamlib_version = self.opencamlib_version() + if opencamlib_version is None: layout.label(text="Opencamlib is NOT available ") layout.prop(ao, 'exact_subdivide_edges') + else: + layout.label(text=f"Opencamlib v{opencamlib_version} installed") + layout.prop(ao, 'use_opencamlib') if exclude_exact or not ao.use_exact: layout.prop(ao, 'pixsize') From 6805acd276a0a13ba46c205ddd1dec846a332273 Mon Sep 17 00:00:00 2001 From: abosafia Date: Wed, 20 Apr 2022 22:19:06 +0200 Subject: [PATCH 03/13] cutter collides itself_fix --- scripts/addons/cam/collision.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/scripts/addons/cam/collision.py b/scripts/addons/cam/collision.py index abfdf6e3..4e401d52 100644 --- a/scripts/addons/cam/collision.py +++ b/scripts/addons/cam/collision.py @@ -46,9 +46,8 @@ def getCutterBullet(o): if type == 'END': bpy.ops.mesh.primitive_cylinder_add(vertices=32, radius= o.cutter_diameter / 2, depth= o.cutter_diameter, end_fill_type='NGON', - align='WORLD', enter_editmode=False, location=(0 , 0, o.cutter_diameter / 2), + align='WORLD', enter_editmode=False, location = CUTTER_OFFSET, rotation=(0, 0, 0)) - #bpy.ops.object.duplicate() # show bit cutter = bpy.context.active_object cutter.scale *= BULLET_SCALE bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) @@ -62,8 +61,7 @@ def getCutterBullet(o): # the actual collision shape is capsule in this case. bpy.ops.mesh.primitive_ico_sphere_add(subdivisions = 3, radius = o.cutter_diameter / 2, align='WORLD', enter_editmode=False, - location=(0, 0, o.cutter_diameter / 2), rotation=(0, 0, 0)) - #bpy.ops.object.duplicate() # show bit + location = CUTTER_OFFSET, rotation=(0, 0, 0)) cutter = bpy.context.active_object cutter.scale *= BULLET_SCALE bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) @@ -81,9 +79,8 @@ def getCutterBullet(o): cone_d = o.cutter_diameter * s bpy.ops.mesh.primitive_cone_add(vertices=32, radius1 = o.cutter_diameter / 2, radius2=0, depth = cone_d, end_fill_type='NGON', - align='WORLD', enter_editmode=False, location = (0, 0, cone_d / 2), + align='WORLD', enter_editmode=False, location = CUTTER_OFFSET, rotation=(math.pi, 0, 0)) - #bpy.ops.object.duplicate() # show bit cutter = bpy.context.active_object cutter.scale *= BULLET_SCALE bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) @@ -100,8 +97,7 @@ def getCutterBullet(o): radius2 = o.cylcone_diameter / 2, depth = cylcone_d, end_fill_type='NGON', align = 'WORLD', enter_editmode = False, - location = (0 , 0 , cylcone_d / 2),rotation = (math.pi, 0, 0)) - #bpy.ops.object.duplicate() # show bit + location = CUTTER_OFFSET,rotation = (math.pi, 0, 0)) cutter = bpy.context.active_object cutter.scale *= BULLET_SCALE bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) @@ -109,6 +105,7 @@ def getCutterBullet(o): bpy.ops.rigidbody.object_add(type='ACTIVE') cutter = bpy.context.active_object cutter.rigid_body.collision_shape = 'CONVEX_HULL' + cutter.location = CUTTER_OFFSET elif type == 'BALLCONE': angle = math.radians(o.cutter_tip_angle)/2 cutter_R = o.cutter_diameter/2 @@ -137,8 +134,7 @@ def getCutterBullet(o): ob_scr.merge_threshold = 0 ob_scr.use_merge_vertices = True bpy.ops.object.modifier_apply(modifier='scr') - bpy.data.objects['BallConeTool'].select_set(True) - #bpy.ops.object.duplicate() # show bit + bpy.data.objects['BallConeTool'].select_set(True) cutter = bpy.context.active_object cutter.scale *= BULLET_SCALE bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) @@ -146,7 +142,7 @@ def getCutterBullet(o): bpy.ops.rigidbody.object_add(type='ACTIVE') cutter.location = CUTTER_OFFSET cutter.rigid_body.collision_shape = 'CONVEX_HULL' - + cutter.location = CUTTER_OFFSET elif type == 'CUSTOM': cutob = bpy.data.objects[o.cutter_object_name] activate(cutob) From 0314c97b9171645f1ccebbec3845fbed4c238842 Mon Sep 17 00:00:00 2001 From: migo101 Date: Mon, 25 Apr 2022 12:04:30 +0200 Subject: [PATCH 04/13] UI cleanup --- scripts/addons/cam/ui.py | 63 +--------------------------------------- 1 file changed, 1 insertion(+), 62 deletions(-) diff --git a/scripts/addons/cam/ui.py b/scripts/addons/cam/ui.py index 664b93ff..7efac936 100644 --- a/scripts/addons/cam/ui.py +++ b/scripts/addons/cam/ui.py @@ -44,6 +44,7 @@ from cam.ui_panels.op_properties import CAM_OPERATION_PROPERTIES_Panel from cam.ui_panels.movement import CAM_MOVEMENT_Panel from cam.ui_panels.feedrate import CAM_FEEDRATE_Panel from cam.ui_panels.optimisation import CAM_OPTIMISATION_Panel +from cam.ui_panels.area import CAM_AREA_Panel class CAM_UL_orientations(UIList): @@ -57,68 +58,6 @@ class CAM_UL_orientations(UIList): -class CAM_AREA_Panel(CAMButtonsPanel, bpy.types.Panel): - """CAM operation area panel""" - bl_label = "CAM operation area " - bl_idname = "WORLD_PT_CAM_OPERATION_AREA" - - COMPAT_ENGINES = {'BLENDERCAM_RENDER'} - - def draw(self, context): - layout = self.layout - scene = bpy.context.scene - if len(scene.cam_operations) == 0: - layout.label(text='Add operation first') - if len(scene.cam_operations) > 0: - ao = scene.cam_operations[scene.cam_active_operation] - if ao.valid: - layout.prop(ao, 'use_layers') - if ao.use_layers: - layout.prop(ao, 'stepdown') - - layout.prop(ao, 'ambient_behaviour') - if ao.ambient_behaviour == 'AROUND': - layout.prop(ao, 'ambient_radius') - - layout.prop(ao, 'maxz') # experimental - if ao.maxz > ao.free_movement_height: - layout.prop(ao, 'free_movement_height') - layout.label(text='Depth start > Free movement') - layout.label(text='POSSIBLE COLLISION') - if ao.geometry_source in ['OBJECT', 'COLLECTION']: - if ao.strategy == 'CURVE': - layout.label(text="cannot use depth from object using CURVES") - - if not ao.minz_from_ob: - if not ao.minz_from_material: - layout.prop(ao, 'minz') - layout.prop(ao, 'minz_from_material') - if not ao.minz_from_material: - layout.prop(ao, 'minz_from_ob') - else: - layout.prop(ao, 'source_image_scale_z') - layout.prop(ao, 'source_image_size_x') - if ao.source_image_name != '': - i = bpy.data.images[ao.source_image_name] - if i is not None: - sy = int((ao.source_image_size_x / i.size[0]) * i.size[1] * 1000000) / 1000 - - layout.label(text='image size on y axis: ' + strInUnits(sy, 8)) - layout.separator() - layout.prop(ao, 'source_image_offset') - col = layout.column(align=True) - col.prop(ao, 'source_image_crop', text='Crop source image') - if ao.source_image_crop: - col.prop(ao, 'source_image_crop_start_x', text='start x') - col.prop(ao, 'source_image_crop_start_y', text='start y') - col.prop(ao, 'source_image_crop_end_x', text='end x') - col.prop(ao, 'source_image_crop_end_y', text='end y') - layout.prop(ao, 'use_limit_curve') - if ao.use_limit_curve: - layout.prop_search(ao, "limit_curve", bpy.data, "objects") - layout.prop(ao, "ambient_cutter_restrict") - - class CAM_GCODE_Panel(CAMButtonsPanel, bpy.types.Panel): """CAM operation g-code options panel""" bl_label = "CAM g-code options " From ff1b59b97a0d96b9ffa0fd8beee4d588ded9fa6b Mon Sep 17 00:00:00 2001 From: migo101 Date: Mon, 25 Apr 2022 12:12:50 +0200 Subject: [PATCH 05/13] Fix AttributeError: 'list' object has no attribute 'geoms' --- scripts/addons/cam/utils.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/addons/cam/utils.py b/scripts/addons/cam/utils.py index 3be0ec38..ecec6683 100644 --- a/scripts/addons/cam/utils.py +++ b/scripts/addons/cam/utils.py @@ -1316,7 +1316,13 @@ def getObjectOutline(radius, o, Offset): # FIXME: make this one operation indep join = 2 else: join = 1 - for p1 in polygons.geoms: # sort by size before this??? + + if isinstance(polygons, list): + polygon_list = polygons + else: + polygon_list = polygons.geoms + + for p1 in polygon_list: # sort by size before this??? # print(p1.type, len(polygons)) i += 1 if radius > 0: From 3d11e94273923b78b84438e641ef6adec6d3e3a5 Mon Sep 17 00:00:00 2001 From: migo101 Date: Mon, 2 May 2022 20:05:06 +0200 Subject: [PATCH 06/13] Cleaned up operations panel --- scripts/addons/cam/ui_panels/operations.py | 88 +++++++++++++++++----- 1 file changed, 70 insertions(+), 18 deletions(-) diff --git a/scripts/addons/cam/ui_panels/operations.py b/scripts/addons/cam/ui_panels/operations.py index a743640c..99e492d0 100644 --- a/scripts/addons/cam/ui_panels/operations.py +++ b/scripts/addons/cam/ui_panels/operations.py @@ -2,6 +2,14 @@ import bpy from cam.ui_panels.buttons_panel import CAMButtonsPanel +# Operations panel +# This panel displays the list of operations created by the user +# Functionnalities are: +# - list Operations +# - create/delete/duplicate/reorder operations +# - display preset operations +# +# For each operation, generate the corresponding gcode and export the gcode file class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.Panel): """CAM operations panel""" @@ -10,10 +18,36 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.Panel): COMPAT_ENGINES = {'BLENDERCAM_RENDER'} + # Main draw function def draw(self, context): - layout = self.layout + self.context = context + self.draw_operations_list() + use_experimental = bpy.context.preferences.addons['cam'].preferences.experimental - row = layout.row() + if (not self.has_operations()): return + ao = self.active_operation() + if ao is None: return + + self.draw_presets() + + self.draw_output_buttons() + + layout = self.layout + sub = layout.column() + sub.active = not ao.computing + + # Draw operation name and filename + sub.prop(ao, 'name') + sub.prop(ao, 'filename') + + self.draw_operation_source() + self.draw_operation_options() + + + # Draw the list of operations and the associated buttons: + # create, delete, duplicate, reorder + def draw_operations_list(self): + row = self.layout.row() row.template_list("CAM_UL_operations", '', self.scene, "cam_operations", self.scene, 'cam_active_operation') col = row.column(align=True) col.operator("scene.cam_operation_add", icon='ADD', text="") @@ -23,18 +57,27 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.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' - use_experimental = bpy.context.preferences.addons['cam'].preferences.experimental - if (not self.has_operations()): return - ao = self.active_operation() - if ao is None: return - - row = layout.row(align=True) + # Draw the list of preset operations, and preset add and remove buttons + def draw_presets(self): + 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 - if not ao.computing: + + # Draw buttons "Calculate path & export Gcode", "Export Gcode ", and "Simulate this operation" + def draw_output_buttons(self): + layout = self.layout + ao = self.active_operation() + + # FIXME This does not seem to work - there is never a "Computing" label displayed + # while an operation is being calculated + if ao.computing: + row = layout.row(align=True) + row.label(text='computing') + row.operator('object.kill_calculate_cam_paths_background', text="", icon='CANCEL') + else: if ao.valid: layout.operator("object.calculate_cam_path", text="Calculate path & export Gcode") if ao.name is not None: @@ -44,16 +87,14 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.Panel): layout.operator("object.cam_simulate", text="Simulate this operation") else: layout.label(text="operation invalid, can't compute") - else: - row = layout.row(align=True) - row.label(text='computing') - row.operator('object.kill_calculate_cam_paths_background', text="", icon='CANCEL') - sub = layout.column() - sub.active = not ao.computing - sub.prop(ao, 'name') - sub.prop(ao, '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): + layout = self.layout + ao = self.active_operation() layout.prop(ao, 'geometry_source') @@ -75,18 +116,29 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.Panel): else: layout.prop_search(ao, "source_image_name", bpy.data, "images") - if ao.strategy in ['CARVE', 'PROJECTED_CURVE']: layout.prop_search(ao, "curve_object", bpy.data, "objects") if ao.strategy == 'PROJECTED_CURVE': layout.prop_search(ao, "curve_object1", bpy.data, "objects") + # Draw Operation options: + # Remove redundant points (optimizes operation) + # Use modifiers of the object + # Hide all other paths + # Parent path to object (?) + + def draw_operation_options(self): + layout = self.layout + ao = self.active_operation() + + # TODO This should be in some optimization menu layout.prop(ao, 'remove_redundant_points') if ao.remove_redundant_points: layout.label(text='Revise your Code before running!') layout.label(text='Quality will suffer if tolerance') layout.label(text='is high') layout.prop(ao, 'simplify_tol') + if ao.geometry_source in ['OBJECT', 'COLLECTION']: layout.prop(ao, 'use_modifiers') layout.prop(ao, 'hide_all_others') From fcf6a56c4fee75e50d8e7a01a52f449c3fb6b571 Mon Sep 17 00:00:00 2001 From: migo101 Date: Tue, 3 May 2022 12:00:52 +0200 Subject: [PATCH 07/13] Operation name now contains the object name --- scripts/addons/cam/ops.py | 20 ++++++++++++-------- scripts/addons/cam/ui_panels/operations.py | 2 ++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/scripts/addons/cam/ops.py b/scripts/addons/cam/ops.py index e48170b4..805fb0b0 100644 --- a/scripts/addons/cam/ops.py +++ b/scripts/addons/cam/ops.py @@ -591,21 +591,25 @@ class CamOperationAdd(bpy.types.Operator): s = bpy.context.scene fixUnits() - if s.objects.get('CAM_machine') is None: - utils.addMachineAreaObject() - # if len(s.cam_material)==0: - # s.cam_material.add() - s.cam_operations.add() o = s.cam_operations[-1] - s.cam_active_operation = len(s.cam_operations) - 1 - o.name = 'Operation_' + str(s.cam_active_operation + 1) - o.filename = o.name ob = bpy.context.active_object if ob is not None: o.object_name = ob.name minx, miny, minz, maxx, maxy, maxz = utils.getBoundsWorldspace([ob]) o.minz = minz + else: + o.object_name = "unknown" + + s.cam_active_operation = len(s.cam_operations) - 1 + o.name = f"Op_{o.object_name}_{s.cam_active_operation + 1}" + o.filename = o.name + + if s.objects.get('CAM_machine') is None: + utils.addMachineAreaObject() + # if len(s.cam_material)==0: + # s.cam_material.add() + return {'FINISHED'} diff --git a/scripts/addons/cam/ui_panels/operations.py b/scripts/addons/cam/ui_panels/operations.py index 99e492d0..da4c4ca3 100644 --- a/scripts/addons/cam/ui_panels/operations.py +++ b/scripts/addons/cam/ui_panels/operations.py @@ -22,6 +22,8 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.Panel): def draw(self, context): self.context = context self.draw_operations_list() + + # FIXME: is this ever used ? use_experimental = bpy.context.preferences.addons['cam'].preferences.experimental if (not self.has_operations()): return From d0adf83adcf0523774a6f364312471bc173863ae Mon Sep 17 00:00:00 2001 From: abosafia Date: Tue, 3 May 2022 19:39:37 +0200 Subject: [PATCH 08/13] Fix ocl_import if not installed --- scripts/addons/cam/ui_panels/info.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/addons/cam/ui_panels/info.py b/scripts/addons/cam/ui_panels/info.py index 2db6a207..6c18be06 100644 --- a/scripts/addons/cam/ui_panels/info.py +++ b/scripts/addons/cam/ui_panels/info.py @@ -1,6 +1,6 @@ import sys -import ocl + import bpy from cam.simple import strInUnits @@ -28,6 +28,7 @@ class CAM_INFO_Panel(CAMButtonsPanel, bpy.types.Panel): def draw_opencamlib_version(self): if "ocl" in sys.modules: + import ocl self.layout.label(text = f"Opencamlib v{ocl.version()} installed") else: self.layout.label(text = "Opencamlib is not installed") From a6a6d3d24a4bd408860fe1160737ee0b02e2cd45 Mon Sep 17 00:00:00 2001 From: migo101 Date: Tue, 3 May 2022 19:53:36 +0200 Subject: [PATCH 09/13] Fix: missing ocl causes crash --- scripts/addons/cam/ui_panels/buttons_panel.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/addons/cam/ui_panels/buttons_panel.py b/scripts/addons/cam/ui_panels/buttons_panel.py index 7ad4e570..1cce37a8 100644 --- a/scripts/addons/cam/ui_panels/buttons_panel.py +++ b/scripts/addons/cam/ui_panels/buttons_panel.py @@ -1,6 +1,6 @@ import bpy import sys -import ocl + # Panel definitions class CAMButtonsPanel: bl_space_type = 'PROPERTIES' @@ -17,6 +17,7 @@ class CAMButtonsPanel: def __init__(self): self.scene = bpy.context.scene + def active_operation_index(self): return(self.scene.cam_active_operation) @@ -36,6 +37,8 @@ class CAMButtonsPanel: return (self.operations_count() > 0) def opencamlib_version(self): - if "ocl" in sys.modules: - return(ocl.version()) - return + try: + import ocl + if "ocl" in sys.modules: return(ocl.version()) + except ImportError as e: + return From 2ddc0d8acaa42f0f72eb6a1faf7c859b395bd5de Mon Sep 17 00:00:00 2001 From: migo101 Date: Tue, 3 May 2022 19:54:41 +0200 Subject: [PATCH 10/13] Deal with no object selected --- scripts/addons/cam/ops.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/addons/cam/ops.py b/scripts/addons/cam/ops.py index 805fb0b0..164dd06f 100644 --- a/scripts/addons/cam/ops.py +++ b/scripts/addons/cam/ops.py @@ -599,7 +599,11 @@ class CamOperationAdd(bpy.types.Operator): minx, miny, minz, maxx, maxy, maxz = utils.getBoundsWorldspace([ob]) o.minz = minz else: - o.object_name = "unknown" + # FIXME Creating an operation without any object selected + # This should actually display a modal dialog + # and cancel the operation creation + o.object_name = "none" + o.minz = 0 s.cam_active_operation = len(s.cam_operations) - 1 o.name = f"Op_{o.object_name}_{s.cam_active_operation + 1}" @@ -607,9 +611,6 @@ class CamOperationAdd(bpy.types.Operator): if s.objects.get('CAM_machine') is None: utils.addMachineAreaObject() - # if len(s.cam_material)==0: - # s.cam_material.add() - return {'FINISHED'} From c80a2eea5358b3d21178a1fa66a78e374abd1b66 Mon Sep 17 00:00:00 2001 From: migo101 Date: Wed, 4 May 2022 09:55:51 +0200 Subject: [PATCH 11/13] Fix merge --- scripts/addons/cam/ui_panels/info.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/addons/cam/ui_panels/info.py b/scripts/addons/cam/ui_panels/info.py index 70f9c0f0..6a7b72e2 100644 --- a/scripts/addons/cam/ui_panels/info.py +++ b/scripts/addons/cam/ui_panels/info.py @@ -1,9 +1,4 @@ -<<<<<<< HEAD -======= - import sys - ->>>>>>> 6bf95c0f69b1ecef7355bd0b5ac27722a6fcfd21 import bpy from cam.simple import strInUnits From ed7fc6d5aa045b1e4d95c749a30a7a732a4fd873 Mon Sep 17 00:00:00 2001 From: migo101 Date: Wed, 4 May 2022 10:04:10 +0200 Subject: [PATCH 12/13] Better import statement --- scripts/addons/cam/ui_panels/buttons_panel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/addons/cam/ui_panels/buttons_panel.py b/scripts/addons/cam/ui_panels/buttons_panel.py index 1cce37a8..1681521b 100644 --- a/scripts/addons/cam/ui_panels/buttons_panel.py +++ b/scripts/addons/cam/ui_panels/buttons_panel.py @@ -39,6 +39,6 @@ class CAMButtonsPanel: def opencamlib_version(self): try: import ocl - if "ocl" in sys.modules: return(ocl.version()) + return(ocl.version()) except ImportError as e: return From f1edf11a1bd92bcd23de3dab8d8016926484e95a Mon Sep 17 00:00:00 2001 From: migo101 Date: Tue, 10 May 2022 16:30:57 +0200 Subject: [PATCH 13/13] Reformating Area Panel --- scripts/addons/cam/__init__.py | 4 +- scripts/addons/cam/ui_panels/area.py | 78 ++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 scripts/addons/cam/ui_panels/area.py diff --git a/scripts/addons/cam/__init__.py b/scripts/addons/cam/__init__.py index f16a587e..7717d848 100644 --- a/scripts/addons/cam/__init__.py +++ b/scripts/addons/cam/__init__.py @@ -685,7 +685,7 @@ class camOperation(bpy.types.PropertyGroup): # movement and ramps use_layers: bpy.props.BoolProperty(name="Use Layers", description="Use layers for roughing", default=True, update=updateRest) - stepdown: bpy.props.FloatProperty(name="Step down", default=0.01, min=0.00001, max=32, precision=PRECISION, + stepdown: bpy.props.FloatProperty(name="", description="Layer height", default=0.01, min=0.00001, max=32, precision=PRECISION, unit="LENGTH", update=updateRest) first_down: bpy.props.BoolProperty(name="First down", description="First go down on a contour, then go to the next one", @@ -1527,7 +1527,7 @@ def unregister(): for p in classes: bpy.utils.unregister_class(p) s = bpy.types.Scene - + # cam chains are defined hardly now. del s.cam_chains del s.cam_active_chain diff --git a/scripts/addons/cam/ui_panels/area.py b/scripts/addons/cam/ui_panels/area.py new file mode 100644 index 00000000..42ee9b83 --- /dev/null +++ b/scripts/addons/cam/ui_panels/area.py @@ -0,0 +1,78 @@ + +import bpy +from cam.ui_panels.buttons_panel import CAMButtonsPanel + +class CAM_AREA_Panel(CAMButtonsPanel, bpy.types.Panel): + """CAM operation area panel""" + bl_label = "CAM operation area " + bl_idname = "WORLD_PT_CAM_OPERATION_AREA" + + COMPAT_ENGINES = {'BLENDERCAM_RENDER'} + + def draw(self, context): + self.scene = bpy.context.scene + if not self.has_operations(): + self.layout.label(text='Add operation first') + return + + self.ao = self.active_operation() + + if not self.ao.valid: + return + + self.draw_z_limits() + self.draw_xy_limits() + + # Draw layers option: use layers(y/n) and choose the stepdown + def draw_z_limits(self): + row = self.layout.row(align=True) + row.prop(self.ao, 'use_layers') + if self.ao.use_layers: + row.prop(self.ao, 'stepdown') + + self.layout.prop(self.ao, 'maxz') + + if self.ao.maxz > self.ao.free_movement_height: + self.layout.prop(self.ao, 'free_movement_height') + self.layout.label(text='Depth start > Free movement') + self.layout.label(text='POSSIBLE COLLISION') + + if self.ao.geometry_source in ['OBJECT', 'COLLECTION']: + if self.ao.strategy == 'CURVE': + self.layout.label(text="cannot use depth from object using CURVES") + + if not self.ao.minz_from_ob: + if not self.ao.minz_from_material: + self.layout.prop(self.ao, 'minz') + self.layout.prop(self.ao, 'minz_from_material') + if not self.ao.minz_from_material: + self.layout.prop(self.ao, 'minz_from_ob') + else: + self.layout.prop(self.ao, 'source_image_scale_z') + self.layout.prop(self.ao, 'source_image_size_x') + if self.ao.source_image_name != '': + i = bpy.data.images[self.ao.source_image_name] + if i is not None: + sy = int((self.ao.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.ao, 'source_image_offset') + col = self.layout.column(align=True) + col.prop(self.ao, 'source_image_crop', text='Crop source image') + if self.ao.source_image_crop: + col.prop(self.ao, 'source_image_crop_start_x', text='start x') + col.prop(self.ao, 'source_image_crop_start_y', text='start y') + col.prop(self.ao, 'source_image_crop_end_x', text='end x') + col.prop(self.ao, 'source_image_crop_end_y', text='end y') + + def draw_xy_limits(self): + self.layout.prop(self.ao, 'ambient_behaviour') + if self.ao.ambient_behaviour == 'AROUND': + self.layout.prop(self.ao, 'ambient_radius') + + self.layout.prop(self.ao, 'use_limit_curve') + + if self.ao.use_limit_curve: + self.layout.prop_search(self.ao, "limit_curve", bpy.data, "objects") + + self.layout.prop(self.ao, "ambient_cutter_restrict")