Remodeled display calculation, converted operations, working on op_properties

pull/248/head
migo101 2023-07-04 19:16:13 +02:00
rodzic b4643389e0
commit e828a138f4
12 zmienionych plików z 440 dodań i 355 usunięć

Wyświetl plik

@ -9,26 +9,26 @@ class CAM_AREA_Panel(CAMButtonsPanel, bpy.types.Panel):
panel_interface_level = 0
prop_level = {
'use_layers': 0,
'maxz': 1,
'minz': 1,
'ambient': 1,
'limit_curve': 1
'draw_use_layers': 0,
'draw_maxz': 1,
'draw_minz': 1,
'draw_ambient': 1,
'draw_limit_curve': 1
}
def draw_use_layers(self):
if not self.has_correct_level('use_layers'): return
if not self.has_correct_level(): return
row = self.layout.row(align=True)
row.prop(self.op, 'use_layers')
if self.op.use_layers:
row.prop(self.op, 'stepdown')
def draw_maxz(self):
if not self.has_correct_level('maxz'): return
if not self.has_correct_level(): return
self.layout.prop(self.op, 'maxz')
def draw_minz(self):
if not self.has_correct_level('minz'): return
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")
@ -58,7 +58,7 @@ class CAM_AREA_Panel(CAMButtonsPanel, bpy.types.Panel):
col.prop(self.op, 'source_image_crop_end_y', text='end y')
def draw_ambient(self):
if not self.has_correct_level('ambient'): return
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':
@ -66,7 +66,7 @@ class CAM_AREA_Panel(CAMButtonsPanel, bpy.types.Panel):
self.layout.prop(self.op, "ambient_cutter_restrict")
def draw_limit_curve(self):
if not self.has_correct_level('limit_curve'): return
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:

Wyświetl plik

@ -1,4 +1,5 @@
import bpy
import inspect
# Panel definitions
class CAMButtonsPanel:
@ -47,9 +48,13 @@ class CAMButtonsPanel:
def has_operations(self):
return (self.operations_count() > 0)
def has_correct_level(self, prop_name):
def has_correct_level(self):
if not hasattr(self, 'prop_level'):
return True
if not prop_name in self.prop_level:
caller_function = inspect.stack()[1][3]
if not caller_function in self.prop_level:
return True
return self.prop_level[prop_name] <= int(self.context.scene.interface.level)
return self.prop_level[caller_function] <= int(self.context.scene.interface.level)

Wyświetl plik

@ -38,8 +38,6 @@ class CAM_CHAINS_Panel(CAMButtonsPanel, bpy.types.Panel):
bl_idname = "WORLD_PT_CAM_CHAINS"
panel_interface_level = 1
COMPAT_ENGINES = {'BLENDERCAM_RENDER'}
def draw(self, context):
layout = self.layout

Wyświetl plik

@ -10,60 +10,59 @@ class CAM_CUTTER_Panel(CAMButtonsPanel, bpy.types.Panel):
panel_interface_level = 0
prop_level = {
'preset_menu': 1,
'cutter_id': 2,
'cutter_type': 0,
'ball_radius': 0,
'bull_radius': 0,
'cylcone_diameter': 0,
'tip_angle': 0,
'laser': 0,
'plasma': 0,
'custom': 0,
'diameter': 0,
'flutes': 1,
'description': 1,
'engagement': 0
'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('preset_menu'): return
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('cutter_id'): return
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('cutter_type'): return
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('ball_radius'): return
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('bull_radius'): return
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('cylcone_diameter'): return
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('tip_angle'): return
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('laser'): return
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')
@ -71,7 +70,7 @@ class CAM_CUTTER_Panel(CAMButtonsPanel, bpy.types.Panel):
self.layout.prop(self.op, 'Laser_delay')
def draw_plasma(self):
if not self.has_correct_level('plasma'): return
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')
@ -81,7 +80,7 @@ class CAM_CUTTER_Panel(CAMButtonsPanel, bpy.types.Panel):
self.layout.prop(self.op, 'lead_out')
def draw_custom(self):
if not self.has_correct_level('custom'): return
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')
@ -90,20 +89,20 @@ class CAM_CUTTER_Panel(CAMButtonsPanel, bpy.types.Panel):
self.layout.prop_search(self.op, "cutter_object_name", bpy.data, "objects")
def draw_cutter_diameter(self):
if not self.has_correct_level('diameter'): return
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('flutes'): return
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('description'): return
if not self.has_correct_level(): return
self.layout.prop(self.op, 'cutter_description')
def draw_engagement(self):
if not self.has_correct_level('engagement'): return
if not self.has_correct_level(): return
if self.op.cutter_type in ['LASER', 'PLASMA']: return
if self.op.strategy in ['CUTOUT']: return

Wyświetl plik

@ -8,31 +8,31 @@ class CAM_FEEDRATE_Panel(CAMButtonsPanel, bpy.types.Panel):
panel_interface_level = 0
prop_level = {
'feedrate': 0,
'sim_feedrate': 2,
'plunge_feedrate': 1,
'plunge_angle': 1,
'spindle_rpm': 0
'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('feedrate'): return
if not self.has_correct_level(): return
self.layout.prop(self.op, 'feedrate')
def draw_sim_feedrate(self):
if not self.has_correct_level('sim_feedrate'): return
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('plunge_feedrate'): return
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('plunge_angle'): return
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('spindle_rpm'): return
if not self.has_correct_level(): return
self.layout.prop(self.op, 'spindle_rpm')
def draw(self, context):

Wyświetl plik

@ -10,41 +10,41 @@ class CAM_GCODE_Panel(CAMButtonsPanel, bpy.types.Panel):
panel_interface_level = 1
prop_level = {
'output_header': 1,
'output_trailer': 1,
'enable_dust': 1,
'enable_hold': 1,
'enable_mist': 1
'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('output_header'): return
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('output_trailer'): return
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('enable_dust'): return
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('enable_hold'): return
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('enable_mist'): return
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')

Wyświetl plik

@ -33,16 +33,16 @@ class CAM_INFO_Panel(CAMButtonsPanel, bpy.types.Panel):
panel_interface_level = 0
prop_level = {
'opencamlib_version': 1,
'warnings': 0,
'time': 0,
'chipload': 0,
'money_cost': 1
'draw_opencamlib_version': 1,
'draw_op_warnings': 0,
'draw_op_time': 0,
'draw_op_chipload': 0,
'draw_op_money_cost': 1
}
# Display the OpenCamLib version
def draw_opencamlib_version(self):
if not self.has_correct_level('opencamlib_version'): return
if not self.has_correct_level(): return
opencamlib_version = cam.utils.opencamlib_version()
if opencamlib_version is None:
self.layout.label(text="Opencamlib is not installed")
@ -52,14 +52,14 @@ class CAM_INFO_Panel(CAMButtonsPanel, bpy.types.Panel):
# Display warnings related to the current operation
def draw_op_warnings(self):
if not self.has_correct_level('warnings'): return
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('time'): return
if not self.has_correct_level(): return
if not int(self.op.info.duration * 60) > 0:
return
@ -74,7 +74,7 @@ class CAM_INFO_Panel(CAMButtonsPanel, bpy.types.Panel):
# Display the chipload (does this work ?)
def draw_op_chipload(self):
if not self.has_correct_level('chipload'): return
if not self.has_correct_level(): return
if not self.op.info.chipload > 0:
return
@ -83,7 +83,7 @@ class CAM_INFO_Panel(CAMButtonsPanel, bpy.types.Panel):
# Display the current operation money cost
def draw_op_money_cost(self):
if not self.has_correct_level('money_cost'): return
if not self.has_correct_level(): return
if not int(self.op.info.duration * 60) > 0:
return

Wyświetl plik

@ -4,62 +4,116 @@ from cam.ui_panels.buttons_panel import CAMButtonsPanel
class CAM_MACHINE_Panel(CAMButtonsPanel, bpy.types.Panel):
"""CAM machine panel"""
bl_label = " "
bl_label = "CAM Machine"
bl_idname = "WORLD_PT_CAM_MACHINE"
always_show_panel = True
panel_interface_level = 0
COMPAT_ENGINES = {'BLENDERCAM_RENDER'}
prop_level = {
'draw_presets': 1,
'draw_post_processor': 0,
'draw_split_files': 2,
'draw_system': 1,
'draw_position_definitions': 2,
'draw_working_area': 2,
'draw_feedrates': 2,
'draw_splindle_speeds': 2,
'draw_tool_options': 2,
'draw_suplemental_axis': 2,
'draw_collet_size': 2,
'draw_block_numbers': 2,
'draw_hourly_rate': 2
}
def draw_header(self, context):
self.layout.menu("CAM_MACHINE_MT_presets", text="CAM Machine")
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):
layout = self.layout
s = bpy.context.scene
us = s.unit_settings
self.context = context
self.machine = bpy.context.scene.cam_machine
ao = s.cam_machine
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()
if ao:
# machine preset
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
layout.prop(ao, 'post_processor')
layout.prop(ao, 'eval_splitting')
if ao.eval_splitting:
layout.prop(ao, 'split_limit')
layout.prop(us, 'system')
layout.prop(ao, 'use_position_definitions')
if ao.use_position_definitions:
layout.prop(ao, 'starting_position')
layout.prop(ao, 'mtc_position')
layout.prop(ao, 'ending_position')
layout.prop(ao, 'working_area')
layout.prop(ao, 'feedrate_min')
layout.prop(ao, 'feedrate_max')
layout.prop(ao, 'feedrate_default')
# TODO: spindle default and feedrate default should become part of the cutter definition...
layout.prop(ao, 'spindle_min')
layout.prop(ao, 'spindle_max')
layout.prop(ao, 'spindle_start_time')
layout.prop(ao, 'spindle_default')
layout.prop(ao, 'output_tool_definitions')
layout.prop(ao, 'output_tool_change')
if ao.output_tool_change:
layout.prop(ao, 'output_g43_on_tool_change')
if self.use_experimental:
layout.prop(ao, 'axis4')
layout.prop(ao, 'axis5')
layout.prop(ao, 'collet_size')
layout.prop(ao, 'output_block_numbers')
if ao.output_block_numbers:
layout.prop(ao, 'start_block_number')
layout.prop(ao, 'block_number_increment')
layout.prop(ao, 'hourly_rate')

Wyświetl plik

@ -85,31 +85,31 @@ class CAM_MATERIAL_Panel(CAMButtonsPanel, bpy.types.Panel):
panel_interface_level = 0
prop_level = {
'estimate_from_model': 0,
'radius_around_model': 1,
'position_object': 0
'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']:
if not self.has_correct_level('estimate_from_model'): return
self.layout.prop(self.op.material, 'estimate_from_model')
if self.op.material.estimate_from_model:
row_radius = self.layout.row()
if self.has_correct_level('radius_around_model'):
row_radius.label(text="Additional radius")
row_radius.prop(self.op.material, 'radius_around_model', text='')
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('position_object'): return
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')
@ -120,9 +120,6 @@ class CAM_MATERIAL_Panel(CAMButtonsPanel, bpy.types.Panel):
def draw(self, context):
self.context = context
if self.op is None:
return
# FIXME: This function displays the progression of a job with a progress bar
# Commenting because it makes no sense here
# Consider removing it entirely

Wyświetl plik

@ -111,50 +111,50 @@ class CAM_MOVEMENT_Panel(CAMButtonsPanel, bpy.types.Panel):
panel_interface_level = 0
prop_level = {
'type': 1,
'spindle_rotation': 2,
'free_height': 0,
'useG64': 2,
'parallel_step_back': 1,
'first_down': 1,
'helix_enter': 2,
'ramp': 1,
'retract_tangential': 2,
'stay_low': 1,
'protect_vertical': 1
'draw_cut_type': 1,
'draw_spindle_rotation': 2,
'draw_free_height': 0,
'draw_use_g64': 2,
'draw_parallel_stepback': 1,
'draw_first_down': 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('type'): return
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('spindle_rotation'): return
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('free_height'): return
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('useG64'): return
if not self.has_correct_level(): return
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('parallel_step_back'): return
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('helix_enter'): return
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:
@ -162,12 +162,12 @@ class CAM_MOVEMENT_Panel(CAMButtonsPanel, bpy.types.Panel):
self.layout.prop(self.op.movement, 'helix_diameter')
def draw_first_down(self):
if not self.has_correct_level('first_down'): return
if not self.has_correct_level(): return
if self.op.strategy in ['CUTOUT','POCKET','MEDIAL_AXIS']:
self.layout.prop(self.op.movement, 'first_down')
def draw_ramp(self):
if not self.has_correct_level('ramp'): return
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')
@ -176,7 +176,7 @@ class CAM_MOVEMENT_Panel(CAMButtonsPanel, bpy.types.Panel):
self.layout.prop(self.movement, 'ramp_out_angle')
def draw_retract_tangential(self):
if not self.has_correct_level('retract_tangential'): return
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:
@ -184,13 +184,13 @@ class CAM_MOVEMENT_Panel(CAMButtonsPanel, bpy.types.Panel):
self.layout.prop(self.op.movement, 'retract_height')
def draw_stay_low(self):
if not self.has_correct_level('stay_low'): return
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('protect_vertical'): return
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:
@ -199,9 +199,6 @@ class CAM_MOVEMENT_Panel(CAMButtonsPanel, bpy.types.Panel):
def draw(self, context):
self.context = context
if not self.op.valid:
return
self.draw_cut_type()
self.draw_spindle_rotation()
self.draw_free_height()

Wyświetl plik

@ -9,169 +9,179 @@ class CAM_OPERATION_PROPERTIES_Panel(CAMButtonsPanel, bpy.types.Panel):
bl_idname = "WORLD_PT_CAM_OPERATION"
panel_interface_level = 0
COMPAT_ENGINES = {'BLENDERCAM_RENDER'}
prop_level = {
'draw_cutter_engagement': 0,
'draw_machine_axis': 2,
'draw_strategy': 0
}
# Displays percentage of the cutter which is engaged with the material
# Displays a warning for engagements greater than 50%
def EngagementDisplay(self, operat, layout):
ao = operat
def draw_cutter_engagement(self):
if not self.has_correct_level(): return
if ao.cutter_type == 'BALLCONE':
if ao.dist_between_paths > ao.ball_radius:
layout.label(text="CAUTION: CUTTER ENGAGEMENT")
layout.label(text="GREATER THAN 50%")
layout.label(text="Cutter engagement: " + str(round(100 * ao.dist_between_paths / ao.ball_radius, 1)) + "%")
if self.op.cutter_type in ['BALLCONE']:
engagement = round(100 * self.op.dist_between_paths / self.op.ball_radius, 1)
else:
if ao.dist_between_paths > ao.cutter_diameter / 2:
layout.label(text="CAUTION: CUTTER ENGAGEMENT")
layout.label(text="GREATER THAN 50%")
layout.label(text="Cutter Engagement: " + str(round(100 * ao.dist_between_paths / ao.cutter_diameter, 1)) + "%")
engagement = round(100 * self.op.dist_between_paths / self.op.cutter_diameter, 1)
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')
def draw(self, context):
layout = self.layout
scene = bpy.context.scene
self.context = context
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:
if self.use_experimental:
layout.prop(ao, 'machine_axes')
if ao.machine_axes == '3':
layout.prop(ao, 'strategy')
elif ao.machine_axes == '4':
layout.prop(ao, 'strategy4axis')
if ao.strategy4axis == 'INDEXED':
layout.prop(ao, 'strategy')
layout.prop(ao, 'rotary_axis_1')
self.draw_machine_axis()
self.draw_strategy()
elif ao.machine_axes == '5':
layout.prop(ao, 'strategy5axis')
if ao.strategy5axis == 'INDEXED':
layout.prop(ao, 'strategy')
layout.prop(ao, 'rotary_axis_1')
layout.prop(ao, 'rotary_axis_2')
if ao.strategy in ['BLOCK', 'SPIRAL', 'CIRCLES', 'OUTLINEFILL']:
layout.prop(ao.movement, 'insideout')
if ao.strategy in ['CUTOUT', 'CURVE']:
if ao.strategy == 'CUTOUT':
layout.prop(ao, 'cut_type')
layout.label(text="Overshoot works best with curve")
layout.label(text="having C remove doubles")
layout.prop(ao, 'straight')
layout.prop(ao, 'profile_start')
layout.label(text="Lead in / out not fully working")
layout.prop(ao, 'lead_in')
layout.prop(ao, 'lead_out')
layout.prop(ao, 'enable_A')
if ao.enable_A:
layout.prop(ao, 'rotation_A')
layout.prop(ao, 'A_along_x')
if ao.A_along_x:
layout.label(text='A || X - B || Y')
else:
layout.label(text='A || Y - B ||X')
layout.prop(ao, 'enable_B')
if ao.enable_B:
layout.prop(ao, 'rotation_B')
if self.op.strategy in ['BLOCK', 'SPIRAL', 'CIRCLES', 'OUTLINEFILL']:
self.layout.prop(self.op.movement, 'insideout')
layout.prop(ao, 'outlines_count')
if ao.outlines_count > 1:
layout.prop(ao, 'dist_between_paths')
self.EngagementDisplay(ao, layout)
layout.prop(ao.movement, 'insideout')
layout.prop(ao, 'dont_merge')
if self.op.strategy in ['CUTOUT', 'CURVE']:
if self.op.strategy == 'CUTOUT':
self.layout.prop(self.op, 'cut_type')
self.layout.label(text="Overshoot works best with curve")
self.layout.label(text="having C remove doubles")
self.layout.prop(self.op, 'straight')
self.layout.prop(self.op, 'profile_start')
self.layout.label(text="Lead in / out not fully working")
self.layout.prop(self.op, 'lead_in')
self.layout.prop(self.op, 'lead_out')
elif ao.strategy == 'WATERLINE':
layout.label(text="OCL doesn't support fill areas")
if not ao.optimisation.use_opencamlib:
layout.prop(ao, 'slice_detail')
layout.prop(ao, 'waterline_fill')
if ao.waterline_fill:
layout.prop(ao, 'dist_between_paths')
self.EngagementDisplay(ao, layout)
layout.prop(ao, 'waterline_project')
elif ao.strategy == 'CARVE':
layout.prop(ao, 'carve_depth')
layout.prop(ao, 'dist_along_paths')
elif ao.strategy == 'MEDIAL_AXIS':
layout.prop(ao, 'medial_axis_threshold')
layout.prop(ao, 'medial_axis_subdivision')
layout.prop(ao, 'add_pocket_for_medial')
layout.prop(ao, 'add_mesh_for_medial')
elif ao.strategy == 'DRILL':
layout.prop(ao, 'drill_type')
layout.prop(ao, 'enable_A')
if ao.enable_A:
layout.prop(ao, 'rotation_A')
layout.prop(ao, 'A_along_x')
if ao.A_along_x:
layout.label(text='A || X - B || Y')
else:
layout.label(text='A || Y - B ||X')
layout.prop(ao, 'enable_B')
if ao.enable_B:
layout.prop(ao, 'rotation_B')
elif ao.strategy == 'POCKET':
layout.prop(ao, 'pocket_option')
layout.prop(ao, 'pocketToCurve')
layout.prop(ao, 'dist_between_paths')
self.EngagementDisplay(ao, layout)
layout.prop(ao, 'enable_A')
if ao.enable_A:
layout.prop(ao, 'rotation_A')
layout.prop(ao, 'A_along_x')
if ao.A_along_x:
layout.label(text='A || X - B || Y')
else:
layout.label(text='A || Y - B ||X')
layout.prop(ao, 'enable_B')
if ao.enable_B:
layout.prop(ao, 'rotation_B')
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:
layout.prop(ao, 'dist_between_paths')
self.EngagementDisplay(ao, layout)
layout.prop(ao, 'dist_along_paths')
if ao.strategy == 'PARALLEL' or ao.strategy == 'CROSS':
layout.prop(ao, 'parallel_angle')
layout.prop(ao, 'enable_A')
if ao.enable_A:
layout.prop(ao, 'rotation_A')
layout.prop(ao, 'A_along_x')
if ao.A_along_x:
layout.label(text='A || X - B || Y')
else:
layout.label(text='A || Y - B ||X')
layout.prop(ao, 'enable_B')
if ao.enable_B:
layout.prop(ao, 'rotation_B')
self.layout.label(text='A || Y - B ||X')
layout.prop(ao, 'inverse')
if ao.strategy not in ['POCKET', 'DRILL', 'CURVE', 'MEDIAL_AXIS']:
layout.prop(ao, 'use_bridges')
if ao.use_bridges:
layout.prop(ao, 'bridges_width')
layout.prop(ao, 'bridges_height')
self.layout.prop(self.op, 'enable_B')
if self.op.enable_B:
self.layout.prop(self.op, 'rotation_B')
layout.prop_search(ao, "bridges_collection_name", bpy.data, "collections")
layout.prop(ao, 'use_bridge_modifiers')
layout.operator("scene.cam_bridges_add", text="Autogenerate bridges")
if ao.strategy == 'WATERLINE':
layout.label(text="Waterline roughing strategy")
layout.label(text="needs a skin margin")
layout.prop(ao, 'skin')
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')
self.layout.prop(self.op, 'dont_merge')
if ao.machine_axes == '3':
layout.prop(ao, 'array')
if ao.array:
layout.prop(ao, 'array_x_count')
layout.prop(ao, 'array_x_distance')
layout.prop(ao, 'array_y_count')
layout.prop(ao, 'array_y_distance')
elif self.op.strategy == '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.draw_cutter_engagement()
self.layout.prop(self.op, 'waterline_project')
elif self.op.strategy == 'CARVE':
self.layout.prop(self.op, 'carve_depth')
self.layout.prop(self.op, 'dist_along_paths')
elif self.op.strategy == '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')
elif self.op.strategy == 'DRILL':
self.layout.prop(self.op, 'drill_type')
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')
self.layout.prop(self.op, 'enable_B')
if self.op.enable_B:
self.layout.prop(self.op, 'rotation_B')
elif self.op.strategy == 'POCKET':
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.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')
self.layout.prop(self.op, 'enable_B')
if self.op.enable_B:
self.layout.prop(self.op, 'rotation_B')
else:
self.layout.prop(self.op, 'dist_between_paths')
self.draw_cutter_engagement()
self.layout.prop(self.op, 'dist_along_paths')
if self.op.strategy == 'PARALLEL' or self.op.strategy == 'CROSS':
self.layout.prop(self.op, 'parallel_angle')
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')
self.layout.prop(self.op, 'enable_B')
if self.op.enable_B:
self.layout.prop(self.op, 'rotation_B')
self.layout.prop(self.op, 'inverse')
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")
if self.op.strategy == 'WATERLINE':
self.layout.label(text="Waterline roughing strategy")
self.layout.label(text="needs a skin margin")
self.layout.prop(self.op, 'skin')
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')

Wyświetl plik

@ -18,25 +18,20 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.Panel):
always_show_panel = True
panel_interface_level = 0
# Main draw function
def draw(self, context):
self.draw_operations_list()
if (not self.has_operations()): return
if self.op is None: return
self.draw_presets()
self.draw_output_buttons()
sub = self.layout.column()
sub.active = not self.op.computing
# Draw operation name and filename
sub.prop(self.op, 'name')
sub.prop(self.op, 'filename')
self.draw_operation_source()
self.draw_operation_options()
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,
'draw_simplify_gcode': 1,
'draw_use_modifiers': 1,
'draw_hide_all_others': 2,
'draw_parent_path_to_object': 2
}
# Draw the list of operations and the associated buttons:
# create, delete, duplicate, reorder
@ -54,37 +49,47 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.Panel):
# 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
# Draw buttons "Calculate path & export Gcode", "Export Gcode ", and "Simulate this operation"
def draw_output_buttons(self):
# FIXME This does not seem to work - there is never a "Computing" label displayed
# while an operation is being calculated
if self.op.computing:
row = self.layout.row(align=True)
row.label(text='computing')
row.operator('object.kill_calculate_cam_paths_background', text="", icon='CANCEL')
def draw_calculate_path(self):
if not self.has_correct_level(): return
if self.op.valid:
self.layout.operator("object.calculate_cam_path", text="Calculate path & export Gcode")
else:
if self.op.valid:
self.layout.operator("object.calculate_cam_path", text="Calculate path & export Gcode")
if self.op.name is not None:
name = "cam_path_{}".format(self.op.name)
if bpy.context.scene.objects.get(name) is not None:
self.layout.operator("object.cam_export", text="Export Gcode ")
self.layout.operator("object.cam_simulate", text="Simulate this operation")
else:
self.layout.label(text="operation invalid, can't compute")
self.layout.label(text="operation invalid, can't compute")
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':
@ -110,25 +115,45 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.Panel):
if self.op.strategy == 'PROJECTED_CURVE':
self.layout.prop_search(self.op, "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_simplify_gcode(self):
if not self.has_correct_level(): return
def draw_operation_options(self):
# TODO This should be in some optimization menu
if self.op.strategy != 'DRILL':
if self.op.strategy not in ['DRILL']:
self.layout.prop(self.op, 'remove_redundant_points')
if self.op.remove_redundant_points:
self.layout.label(text='Revise your Code before running!')
self.layout.label(text='Quality will suffer if tolerance')
self.layout.label(text='is high')
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
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()
self.draw_simplify_gcode()
self.draw_use_modifiers()
self.draw_hide_all_others()
self.draw_parent_path_to_object()