diff --git a/scripts/addons/cam/__init__.py b/scripts/addons/cam/__init__.py index 8b5432237..a8c172b17 100644 --- a/scripts/addons/cam/__init__.py +++ b/scripts/addons/cam/__init__.py @@ -42,27 +42,27 @@ # pip install required python stuff subprocess.check_call([sys.executable, "-m", "ensurepip"]) subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", " pip"]) - subprocess.check_call([sys.executable, "-m", "pip", "install", "shapely","Equation","opencamlib"]) + subprocess.check_call([sys.executable, "-m", "pip", "install", + "shapely", "Equation", "opencamlib"]) # install numba if available for this platform, ignore failure subprocess.run([sys.executable, "-m", "pip", "install", "numba"]) - -from bpy.app.handlers import persistent -from bpy.props import * -from bpy.types import Menu, Operator, UIList, AddonPreferences -from bpy_extras.object_utils import object_data_add from cam import ui, ops, curvecamtools, curvecamequation, curvecamcreate, utils, simple, \ polygon_utils_cam, autoupdate, basrelief # , post_processors from mathutils import * from shapely import geometry as sgeometry +from bpy_extras.object_utils import object_data_add +from bpy.types import Menu, Operator, UIList, AddonPreferences +from bpy.props import * +from bpy.app.handlers import persistent -from cam.ui import * from cam.version import __version__ +from cam.ui import * bl_info = { "name": "CAM - gcode generation tools", - "author": "Vilem Novak", - "version":(1,0,3), + "author": "Vilem Novak & Contributors", + "version": (1, 0, 5), "blender": (3, 6, 0), "location": "Properties > render", "description": "Generate machining paths for CNC", @@ -75,8 +75,9 @@ was_hidden_dict = {} + def updateMachine(self, context): - global _IS_LOADING_DEFAULTS + global _IS_LOADING_DEFAULTS print('update machine ') if not _IS_LOADING_DEFAULTS: utils.addMachineAreaObject() @@ -125,7 +126,6 @@ def updateOperation(self, context): print(e) - class CamAddonPreferences(AddonPreferences): # this must match the addon name, use '__package__' # when defining this in a submodule of a python package. @@ -153,7 +153,6 @@ class CamAddonPreferences(AddonPreferences): default="" ) - just_updated: BoolProperty( name="Set to true on update or initial install", default=True @@ -164,8 +163,6 @@ class CamAddonPreferences(AddonPreferences): default="" ) - - default_interface_level: bpy.props.EnumProperty( name="Interface level in new file", description="Choose visible options", @@ -189,19 +186,22 @@ def draw(self, context): layout.prop(self, "update_source") layout.label(text="Choose a preset update source") - UPDATE_SOURCES=[("https://github.com/vilemduha/blendercam", "Stable", "Stable releases (github.com/vilemduja/blendercam)"), - ("https://github.com/pppalain/blendercam", "Unstable", "Unstable releases (github.com/pppalain/blendercam)"), - # comments for searching in github actions release script to automatically set this repo - # if required - ## REPO ON NEXT LINE - ("https://api.github.com/repos/pppalain/blendercam/commits","Direct from git (may not work)","Get from git commits directly"), - ## REPO ON PREV LINE - ("","None","Don't do auto update"), - ] - grid=layout.grid_flow(align=True) - for (url,short,long) in UPDATE_SOURCES: - op=grid.operator("render.cam_set_update_source",text=short) - op.new_source=url + UPDATE_SOURCES = [("https://github.com/vilemduha/blendercam", "Stable", "Stable releases (github.com/vilemduja/blendercam)"), + ("https://github.com/pppalain/blendercam", "Unstable", + "Unstable releases (github.com/pppalain/blendercam)"), + # comments for searching in github actions release script to automatically set this repo + # if required + # REPO ON NEXT LINE + ("https://api.github.com/repos/pppalain/blendercam/commits", + "Direct from git (may not work)", "Get from git commits directly"), + # REPO ON PREV LINE + ("", "None", "Don't do auto update"), + ] + grid = layout.grid_flow(align=True) + for (url, short, long) in UPDATE_SOURCES: + op = grid.operator("render.cam_set_update_source", text=short) + op.new_source = url + class machineSettings(bpy.types.PropertyGroup): """stores all data for machines""" @@ -209,7 +209,8 @@ class machineSettings(bpy.types.PropertyGroup): post_processor: EnumProperty(name='Post processor', items=(('ISO', 'Iso', 'exports standardized gcode ISO 6983 (RS-274)'), ('MACH3', 'Mach3', 'default mach3'), - ('EMC', 'LinuxCNC - EMC2', 'Linux based CNC control software - formally EMC2'), + ('EMC', 'LinuxCNC - EMC2', + 'Linux based CNC control software - formally EMC2'), ('FADAL', 'Fadal', 'Fadal VMC'), ('GRBL', 'grbl', 'optimized gcode for grbl firmware on Arduino with cnc shield'), @@ -324,7 +325,8 @@ class PackObjectsSettings(bpy.types.PropertyGroup): tolerance: FloatProperty(name="Placement Tolerance", description="Tolerance for placement: smaller value slower placemant", min=0.001, max=0.02, default=0.005, precision=cam.constants.PRECISION, unit="LENGTH") - rotate: bpy.props.BoolProperty(name="enable rotation", description="Enable rotation of elements", default=True) + rotate: bpy.props.BoolProperty( + name="enable rotation", description="Enable rotation of elements", default=True) rotate_angle: FloatProperty(name="Placement Angle rotation step", description="bigger rotation angle,faster placemant", default=0.19635 * 4, min=math.pi/180, @@ -337,9 +339,11 @@ class SliceObjectsSettings(bpy.types.PropertyGroup): slice_distance: FloatProperty(name="Slicing distance", description="slices distance in z, should be most often thickness of plywood sheet.", min=0.001, max=10, default=0.005, precision=cam.constants.PRECISION, unit="LENGTH") - slice_above0: bpy.props.BoolProperty(name="Slice above 0", description="only slice model above 0", default=False) + slice_above0: bpy.props.BoolProperty( + name="Slice above 0", description="only slice model above 0", default=False) slice_3d: bpy.props.BoolProperty(name="3d slice", description="for 3d carving", default=False) - indexes: bpy.props.BoolProperty(name="add indexes", description="adds index text of layer + index", default=True) + indexes: bpy.props.BoolProperty( + name="add indexes", description="adds index text of layer + index", default=True) class import_settings(bpy.types.PropertyGroup): @@ -353,8 +357,9 @@ class import_settings(bpy.types.PropertyGroup): max_segment_size: FloatProperty(name="", description="Only Segments bigger then this value get subdivided", default=0.001, min=0.0001, max=1.0, unit="LENGTH") -def isValid(o,context): - valid=True + +def isValid(o, context): + valid = True if o.geometry_source == 'OBJECT': if o.object_name not in bpy.data.objects: valid = False @@ -369,11 +374,12 @@ def isValid(o,context): valid = False return valid + def operationValid(self, context): - scene=context.scene + scene = context.scene o = scene.cam_operations[scene.cam_active_operation] o.changed = True - o.valid = isValid(o,context) + o.valid = isValid(o, context) invalidmsg = "Invalid source object for operation.\n" if o.valid: o.info.warnings = "" @@ -386,20 +392,21 @@ def operationValid(self, context): o.update_zbufferimage_tag = True print('validity ') -def isChainValid(chain,context): + +def isChainValid(chain, context): s = context.scene - if len(chain.operations)==0: - return (False,"") + if len(chain.operations) == 0: + return (False, "") for cho in chain.operations: found_op = None for so in s.cam_operations: if so.name == cho.name: - found_op= so + found_op = so if found_op == None: - return (False,f"Couldn't find operation {cho.name}") - if cam.isValid(found_op,context) is False: - return (False,f"Operation {found_op.name} is not valid") - return (True,"") + return (False, f"Couldn't find operation {cho.name}") + if cam.isValid(found_op, context) is False: + return (False, f"Operation {found_op.name} is not valid") + return (True, "") def updateOperationValid(self, context): @@ -462,6 +469,7 @@ def updateStrategy(o, context): def updateCutout(o, context): pass + def updateExact(o, context): print('update exact ') o.changed = True @@ -484,6 +492,7 @@ def updateOpencamlib(o, context): o.optimisation.use_opencamlib = False print('Current operation cannot use opencamlib') + def updateBridges(o, context): print('update bridges ') o.changed = True @@ -589,10 +598,12 @@ class camOperation(bpy.types.PropertyGroup): curve_object1: bpy.props.StringProperty(name='Curve target', description='curve which will serve as attractor for the cutter when the cutter follows the curve', update=operationValid) - source_image_name: bpy.props.StringProperty(name='image_source', description='image source', update=operationValid) + source_image_name: bpy.props.StringProperty( + name='image_source', description='image source', update=operationValid) geometry_source: EnumProperty(name='Source of data', items=( - ('OBJECT', 'object', 'a'), ('COLLECTION', 'Collection of objects', 'a'), + ('OBJECT', 'object', 'a'), ('COLLECTION', + 'Collection of objects', 'a'), ('IMAGE', 'Image', 'a')), description='Geometry source', default='OBJECT', update=updateOperationValid) @@ -640,7 +651,8 @@ class camOperation(bpy.types.PropertyGroup): update=updateStrategy) strategy5axis: EnumProperty(name='Strategy', items=( - ('INDEXED', 'Indexed 3-axis', 'all 3 axis strategies, just rotated by 4+5th axes'), + ('INDEXED', 'Indexed 3-axis', + 'all 3 axis strategies, just rotated by 4+5th axes'), ), description='5 axis Strategy', default='INDEXED', @@ -684,13 +696,14 @@ class camOperation(bpy.types.PropertyGroup): # pocket options pocket_option: EnumProperty(name='Start Position', items=( ('INSIDE', 'Inside', 'a'), ('OUTSIDE', 'Outside', 'a')), - description='Pocket starting position', default='INSIDE', update=updateRest) + description='Pocket starting position', default='INSIDE', update=updateRest) pocketToCurve: bpy.props.BoolProperty(name="Pocket to curve", description="generates a curve instead of a path", default=False, update=updateRest) # Cutout cut_type: EnumProperty(name='Cut', - items=(('OUTSIDE', 'Outside', 'a'), ('INSIDE', 'Inside', 'a'), ('ONLINE', 'On line', 'a')), + items=(('OUTSIDE', 'Outside', 'a'), ('INSIDE', + 'Inside', 'a'), ('ONLINE', 'On line', 'a')), description='Type of cutter used', default='OUTSIDE', update=updateRest) outlines_count: bpy.props.IntProperty(name="Outlines count", description="Outlines count", default=1, min=1, max=32, update=updateCutout) @@ -720,7 +733,8 @@ class camOperation(bpy.types.PropertyGroup): max=0.035, default=0.005, unit="LENGTH", precision=cam.constants.PRECISION, update=updateOffsetImage) - cutter_description: StringProperty(name="Tool Description", default="", update=updateOffsetImage) + cutter_description: StringProperty( + name="Tool Description", default="", update=updateOffsetImage) Laser_on: bpy.props.StringProperty(name="Laser ON string", default="M68 E0 Q100") Laser_off: bpy.props.StringProperty(name="Laser OFF string", default="M68 E0 Q0") @@ -758,7 +772,8 @@ class camOperation(bpy.types.PropertyGroup): subtype="ANGLE", unit="ROTATION", update=updateRotation) enable_A: bpy.props.BoolProperty(name="Enable A axis", description="Rotate A axis", default=False, update=updateRotation) - A_along_x: bpy.props.BoolProperty(name="A Along X ", description="A Parallel to X", default=True, update=updateRest) + A_along_x: bpy.props.BoolProperty( + name="A Along X ", description="A Parallel to X", default=True, update=updateRest) rotation_B: bpy.props.FloatProperty(name="B axis angle", description="Rotate B axis\nto specified angle", default=0, min=-360, max=360, precision=0, @@ -772,9 +787,10 @@ class camOperation(bpy.types.PropertyGroup): # drill only drill_type: EnumProperty(name='Holes on', items=( - ('MIDDLE_SYMETRIC', 'Middle of symetric curves', 'a'), ('MIDDLE_ALL', 'Middle of all curve parts', 'a'), + ('MIDDLE_SYMETRIC', 'Middle of symetric curves', + 'a'), ('MIDDLE_ALL', 'Middle of all curve parts', 'a'), ('ALL_POINTS', 'All points in curve', 'a')), description='Strategy to detect holes to drill', - default='MIDDLE_SYMETRIC', update=updateRest) + default='MIDDLE_SYMETRIC', update=updateRest) # waterline only slice_detail: bpy.props.FloatProperty(name="Distance betwen slices", default=0.001, min=0.00001, max=32, precision=cam.constants.PRECISION, unit="LENGTH", update=updateRest) @@ -802,20 +818,20 @@ class camOperation(bpy.types.PropertyGroup): # helix_angle: bpy.props.FloatProperty(name="Helix ramp angle", default=3*math.pi/180, min=0.00001, max=math.pi*0.4999,precision=1, subtype="ANGLE" , unit="ROTATION" , update = updateRest) minz: bpy.props.FloatProperty(name="Operation depth end", - default=-0.01, min=-3, max=3, precision=cam.constants.PRECISION, - unit="LENGTH", - update=updateRest) + default=-0.01, min=-3, max=3, precision=cam.constants.PRECISION, + unit="LENGTH", + update=updateRest) minz_from: bpy.props.EnumProperty(name='Set max depth from', - description = 'Set maximum operation depth', - items=( - ('OBJECT', 'Object', 'Set max operation depth from Object'), - ('MATERIAL', 'Material', 'Set max operation depth from Material'), - ('CUSTOM', 'Custom', 'Custom max depth'), - ), - default='OBJECT', - update=updateRest - ) + description='Set maximum operation depth', + items=( + ('OBJECT', 'Object', 'Set max operation depth from Object'), + ('MATERIAL', 'Material', 'Set max operation depth from Material'), + ('CUSTOM', 'Custom', 'Custom max depth'), + ), + default='OBJECT', + update=updateRest + ) start_type: bpy.props.EnumProperty(name='Start type', items=( @@ -832,9 +848,8 @@ class camOperation(bpy.types.PropertyGroup): update=updateRest) # EXPERIMENTAL first_down: bpy.props.BoolProperty(name="First down", - description="First go down on a contour, then go to the next one", - default=False, update=cam.utils.update_operation) - + description="First go down on a contour, then go to the next one", + default=False, update=cam.utils.update_operation) ####################################################### # Image related @@ -866,7 +881,6 @@ class camOperation(bpy.types.PropertyGroup): # Toolpath and area related ##################################################### - ambient_behaviour: EnumProperty(name='Ambient', items=(('ALL', 'All', 'a'), ('AROUND', 'Around', 'a')), description='handling ambient surfaces', default='ALL', update=updateZbufferImage) @@ -972,20 +986,15 @@ class camOperation(bpy.types.PropertyGroup): # material settings - - - - ############################################################################## # MATERIAL SETTINGS min: bpy.props.FloatVectorProperty( name='Operation minimum', default=(0, 0, 0), unit='LENGTH', precision=cam.constants.PRECISION, - subtype="XYZ") + subtype="XYZ") max: bpy.props.FloatVectorProperty(name='Operation maximum', default=(0, 0, 0), unit='LENGTH', precision=cam.constants.PRECISION, subtype="XYZ") - # g-code options for operation output_header: BoolProperty(name="output g-code header", description="output user defined g-code command header at start of operation", @@ -996,16 +1005,16 @@ class camOperation(bpy.types.PropertyGroup): default="G53 G0") enable_dust: BoolProperty(name="Dust collector", - description="output user defined g-code command header at start of operation", - default=False) + description="output user defined g-code command header at start of operation", + default=False) gcode_start_dust_cmd: StringProperty(name="Start dust collector", - description="commands to start dust collection. Use ; for line breaks", - default="M100") + description="commands to start dust collection. Use ; for line breaks", + default="M100") gcode_stop_dust_cmd: StringProperty(name="Stop dust collector", - description="command to stop dust collection. Use ; for line breaks", - default="M101") + description="command to stop dust collection. Use ; for line breaks", + default="M101") enable_hold: BoolProperty(name="Hold down", description="output hold down command at start of operation", @@ -1068,8 +1077,10 @@ class camOperation(bpy.types.PropertyGroup): update_bullet_collision_tag: bpy.props.BoolProperty(name="mark bullet collisionworld for update", description="mark for update", default=True) - valid: bpy.props.BoolProperty(name="Valid", description="True if operation is ok for calculation", default=True); - changedata: bpy.props.StringProperty(name='changedata', description='change data for checking if stuff changed.') + valid: bpy.props.BoolProperty( + name="Valid", description="True if operation is ok for calculation", default=True) + changedata: bpy.props.StringProperty( + name='changedata', description='change data for checking if stuff changed.') # process related data @@ -1078,20 +1089,26 @@ class camOperation(bpy.types.PropertyGroup): outtext: bpy.props.StringProperty(name='outtext', description='outtext', default='') -class opReference(bpy.types.PropertyGroup): # this type is defined just to hold reference to operations for chains +# this type is defined just to hold reference to operations for chains +class opReference(bpy.types.PropertyGroup): name: bpy.props.StringProperty(name="Operation name", default="Operation") computing = False # for UiList display -class camChain(bpy.types.PropertyGroup): # chain is just a set of operations which get connected on export into 1 file. - index: bpy.props.IntProperty(name="index", description="index in the hard-defined camChains", default=-1) +# chain is just a set of operations which get connected on export into 1 file. +class camChain(bpy.types.PropertyGroup): + index: bpy.props.IntProperty( + name="index", description="index in the hard-defined camChains", default=-1) active_operation: bpy.props.IntProperty(name="active operation", description="active operation in chain", default=-1) name: bpy.props.StringProperty(name="Chain Name", default="Chain") filename: bpy.props.StringProperty(name="File name", default="Chain") # filename of - valid: bpy.props.BoolProperty(name="Valid", description="True if whole chain is ok for calculation", default=True); + valid: bpy.props.BoolProperty( + name="Valid", description="True if whole chain is ok for calculation", default=True) computing: bpy.props.BoolProperty(name="Computing right now", description="", default=False) - operations: bpy.props.CollectionProperty(type=opReference) # this is to hold just operation names. + # this is to hold just operation names. + operations: bpy.props.CollectionProperty(type=opReference) + class CAM_CUTTER_MT_presets(Menu): bl_label = "Cutter presets" @@ -1099,6 +1116,7 @@ class CAM_CUTTER_MT_presets(Menu): preset_operator = "script.execute_preset" draw = Menu.draw_preset + class CAM_MACHINE_MT_presets(Menu): bl_label = "Machine presets" preset_subdir = "cam_machines" @@ -1106,15 +1124,16 @@ class CAM_MACHINE_MT_presets(Menu): draw = Menu.draw_preset @classmethod - def post_cb(cls,context): + def post_cb(cls, context): name = cls.bl_label filepath = bpy.utils.preset_find(name, - cls.preset_subdir, - display_name=True, - ext=".py") - context.preferences.addons['cam'].preferences.default_machine_preset=filepath + cls.preset_subdir, + display_name=True, + ext=".py") + context.preferences.addons['cam'].preferences.default_machine_preset = filepath bpy.ops.wm.save_userpref() + class AddPresetCamCutter(bl_operators.presets.AddPresetBase, Operator): """Add a Cutter Preset""" bl_idname = "render.cam_preset_cutter_add" @@ -1151,7 +1170,8 @@ class AddPresetCamOperation(bl_operators.presets.AddPresetBase, Operator): bl_label = "Add Operation Preset" preset_menu = "CAM_OPERATION_MT_presets" - preset_defines = ["o = bpy.context.scene.cam_operations[bpy.context.scene.cam_active_operation]"] + preset_defines = [ + "o = bpy.context.scene.cam_operations[bpy.context.scene.cam_active_operation]"] preset_values = ['o.use_layers', 'o.info.duration', 'o.info.chipload', 'o.material.estimate_from_model', 'o.movement.stay_low', 'o.carve_depth', 'o.dist_along_paths', 'o.source_image_crop_end_x', 'o.source_image_crop_end_y', 'o.material.size', @@ -1216,7 +1236,9 @@ class BLENDERCAM_ENGINE(bpy.types.RenderEngine): bl_idname = 'BLENDERCAM_RENDER' bl_label = "Cam" -_IS_LOADING_DEFAULTS=False + +_IS_LOADING_DEFAULTS = False + @bpy.app.handlers.persistent def check_operations_on_load(context): @@ -1228,14 +1250,16 @@ def check_operations_on_load(context): o.computing = False # set interface level to previously used level for a new file if not bpy.data.filepath: - _IS_LOADING_DEFAULTS=True + _IS_LOADING_DEFAULTS = True s.interface.level = bpy.context.preferences.addons['cam'].preferences.default_interface_level - machine_preset=bpy.context.preferences.addons['cam'].preferences.machine_preset=bpy.context.preferences.addons['cam'].preferences.default_machine_preset - if len(machine_preset)>0: - print("Loading preset:",machine_preset) + machine_preset = bpy.context.preferences.addons[ + 'cam'].preferences.machine_preset = bpy.context.preferences.addons['cam'].preferences.default_machine_preset + if len(machine_preset) > 0: + print("Loading preset:", machine_preset) # load last used machine preset - bpy.ops.script.execute_preset(filepath=machine_preset,menu_idname="CAM_MACHINE_MT_presets") - _IS_LOADING_DEFAULTS=False + bpy.ops.script.execute_preset(filepath=machine_preset, + menu_idname="CAM_MACHINE_MT_presets") + _IS_LOADING_DEFAULTS = False # check for updated version of the plugin bpy.ops.render.cam_check_updates() # copy presets if not there yet @@ -1243,16 +1267,16 @@ def check_operations_on_load(context): preset_source_path = Path(__file__).parent / 'presets' preset_target_path = Path(bpy.utils.script_path_user()) / 'presets' - def copy_if_not_exists(src,dst): - if Path(dst).exists()==False: - shutil.copy2(src,dst) - shutil.copytree(preset_source_path,preset_target_path,copy_function=copy_if_not_exists,dirs_exist_ok=True) + def copy_if_not_exists(src, dst): + if Path(dst).exists() == False: + shutil.copy2(src, dst) + shutil.copytree(preset_source_path, preset_target_path, + copy_function=copy_if_not_exists, dirs_exist_ok=True) - bpy.context.preferences.addons['cam'].preferences.just_updated=False + bpy.context.preferences.addons['cam'].preferences.just_updated = False bpy.ops.wm.save_userpref() - def get_panels(): # convenience function for bot register and unregister functions # types = bpy.types return ( @@ -1404,7 +1428,7 @@ def compatible_panels(): t.MATERIAL_PT_strand, t.MATERIAL_PT_options, t.MATERIAL_PT_shadow, - + t.MATERIAL_PT_transp_game, t.MATERIAL_PT_volume_density, t.MATERIAL_PT_volume_shading, @@ -1571,7 +1595,8 @@ def register(): s = bpy.types.Scene s.cam_chains = bpy.props.CollectionProperty(type=camChain) - s.cam_active_chain = bpy.props.IntProperty(name="CAM Active Chain", description="The selected chain") + s.cam_active_chain = bpy.props.IntProperty( + name="CAM Active Chain", description="The selected chain") s.cam_operations = bpy.props.CollectionProperty(type=camOperation) diff --git a/scripts/addons/cam/async_op.py b/scripts/addons/cam/async_op.py index c2239adef..6e212fa51 100644 --- a/scripts/addons/cam/async_op.py +++ b/scripts/addons/cam/async_op.py @@ -3,24 +3,27 @@ import types + @types.coroutine -def progress_async(text, n=None,value_type='%'): +def progress_async(text, n=None, value_type='%'): """function for reporting during the script, works for background operations in the header.""" - throw_exception=yield ('progress',{'text':text,'n':n,"value_type":value_type}) + throw_exception = yield ('progress', {'text': text, 'n': n, "value_type": value_type}) if throw_exception is not None: raise throw_exception + class AsyncCancelledException(Exception): pass + class AsyncOperatorMixin: def __init__(self): - self.timer=None - self.coroutine=None - self._is_cancelled=False + self.timer = None + self.coroutine = None + self._is_cancelled = False - def modal(self,context,event): + def modal(self, context, event): if bpy.app.background: return {'PASS_THROUGH'} @@ -35,10 +38,10 @@ def modal(self,context,event): except Exception as e: context.window_manager.event_timer_remove(self.timer) bpy.context.workspace.status_text_set(None) - self.report({'ERROR'},str(e)) + self.report({'ERROR'}, str(e)) return {'FINISHED'} elif event.type == 'ESC': - self._is_cancelled=True + self._is_cancelled = True self.tick(context) context.window_manager.event_timer_remove(self.timer) bpy.context.workspace.status_text_set(None) @@ -48,7 +51,7 @@ def modal(self,context,event): else: return {'PASS_THROUGH'} - def show_progress(self,context,text, n,value_type): + def show_progress(self, context, text, n, value_type): if n is not None: progress_text = f"{text}: {n:.2f}{value_type}" else: @@ -57,45 +60,46 @@ def show_progress(self,context,text, n,value_type): sys.stdout.write(f"Progress: {progress_text}\n") sys.stdout.flush() - def tick(self,context): - if self.coroutine==None: - self.coroutine=self.execute_async(context) + def tick(self, context): + if self.coroutine == None: + self.coroutine = self.execute_async(context) try: if self._is_cancelled: - (msg,args)=self.coroutine.send(AsyncCancelledException("Cancelled with ESC key")) + (msg, args) = self.coroutine.send(AsyncCancelledException("Cancelled with ESC key")) raise StopIteration else: - (msg,args)=self.coroutine.send(None) - if msg=='progress': - self.show_progress(context,**args) + (msg, args) = self.coroutine.send(None) + if msg == 'progress': + self.show_progress(context, **args) else: sys.stdout.write(f"{msg},{args}") return True except StopIteration: return False except Exception as e: - print("Exception thrown in tick:",e) + print("Exception thrown in tick:", e) def execute(self, context): if bpy.app.background: # running in background - don't run as modal, # otherwise tests all fail - while self.tick(context)==True: + while self.tick(context) == True: pass return {'FINISHED'} else: - self.timer=context.window_manager.event_timer_add(.001, window=context.window) + self.timer = context.window_manager.event_timer_add(.001, window=context.window) context.window_manager.modal_handler_add(self) return {'RUNNING_MODAL'} -class AsyncTestOperator(bpy.types.Operator,AsyncOperatorMixin): + +class AsyncTestOperator(bpy.types.Operator, AsyncOperatorMixin): """test async operator""" bl_idname = "object.cam_async_test_operator" bl_label = "Test operator for async stuff" - bl_options = {'REGISTER', 'UNDO','BLOCKING'} + bl_options = {'REGISTER', 'UNDO', 'BLOCKING'} - async def execute_async(self,context): + async def execute_async(self, context): for x in range(100): - await progress_async("Async test:",x) + await progress_async("Async test:", x) -#bpy.utils.register_class(AsyncTestOperator) \ No newline at end of file +# bpy.utils.register_class(AsyncTestOperator) diff --git a/scripts/addons/cam/autoupdate.py b/scripts/addons/cam/autoupdate.py index 9b9361b2f..25e0a2c0f 100644 --- a/scripts/addons/cam/autoupdate.py +++ b/scripts/addons/cam/autoupdate.py @@ -11,6 +11,7 @@ import sys import calendar + class UpdateChecker(bpy.types.Operator): """check for updates""" bl_idname = "render.cam_check_updates" @@ -21,31 +22,31 @@ def execute(self, context): if bpy.app.background: return {"FINISHED"} last_update_check = bpy.context.preferences.addons['cam'].preferences.last_update_check - today=date.today().toordinal() + today = date.today().toordinal() update_source = bpy.context.preferences.addons['cam'].preferences.update_source - match = re.match(r"https://github.com/([^/]+/[^/]+)",update_source) + match = re.match(r"https://github.com/([^/]+/[^/]+)", update_source) if match: update_source = f"https://api.github.com/repos/{match.group(1)}/releases" - + print(f"update check: {update_source}") - if update_source=="None" or len(update_source)==0: + if update_source == "None" or len(update_source) == 0: return {'FINISHED'} bpy.context.preferences.addons['cam'].preferences.new_version_available = "" bpy.ops.wm.save_userpref() # get list of releases from github release if update_source.endswith("/releases"): - with urlopen(update_source,timeout=2.0) as response: + with urlopen(update_source, timeout=2.0) as response: body = response.read().decode("UTF-8") # find the tag name - release_list=json.loads(body) + release_list = json.loads(body) if len(release_list) > 0: release = release_list[0] tag = release["tag_name"] print(f"Found release: {tag}") - match = re.match(r".*(\d+)\.(\s*\d+)\.(\s*\d+)",tag) + match = re.match(r".*(\d+)\.(\s*\d+)\.(\s*\d+)", tag) if match: - version_num = tuple(map(int,match.groups())) + version_num = tuple(map(int, match.groups())) print(f"Found version: {version_num}") bpy.context.preferences.addons['cam'].preferences.last_update_check = today @@ -54,17 +55,18 @@ def execute(self, context): [str(x) for x in version_num]) bpy.ops.wm.save_userpref() elif update_source.endswith("/commits"): - with urlopen(update_source+"?per_page=1",timeout=2) as response: + with urlopen(update_source+"?per_page=1", timeout=2) as response: body = response.read().decode("UTF-8") # find the tag name - commit_list=json.loads(body) - commit_sha=commit_list[0]['sha'] - commit_date=commit_list[0]['commit']['author']['date'] + commit_list = json.loads(body) + commit_sha = commit_list[0]['sha'] + commit_date = commit_list[0]['commit']['author']['date'] if bpy.context.preferences.addons['cam'].preferences.last_commit_hash != commit_sha: - bpy.context.preferences.addons['cam'].preferences.new_version_available=commit_date + bpy.context.preferences.addons['cam'].preferences.new_version_available = commit_date bpy.ops.wm.save_userpref() return {'FINISHED'} + class Updater(bpy.types.Operator): """update to newer version if possible """ bl_idname = "render.cam_update_now" @@ -74,27 +76,27 @@ class Updater(bpy.types.Operator): def execute(self, context): print("update check") last_update_check = bpy.context.preferences.addons['cam'].preferences.last_update_check - today=date.today().toordinal() + today = date.today().toordinal() update_source = bpy.context.preferences.addons['cam'].preferences.update_source - if update_source=="None" or len(update_source)==0: + if update_source == "None" or len(update_source) == 0: return {'FINISHED'} - match = re.match(r"https://github.com/([^/]+/[^/]+)",update_source) + match = re.match(r"https://github.com/([^/]+/[^/]+)", update_source) if match: update_source = f"https://api.github.com/repos/{match.group(1)}/releases" # get list of releases from github release if update_source.endswith("/releases"): - with urlopen(update_source,timeout=2) as response: + with urlopen(update_source, timeout=2) as response: body = response.read().decode("UTF-8") # find the tag name - release_list=json.loads(body) + release_list = json.loads(body) if len(release_list) > 0: release = release_list[0] tag = release["tag_name"] print(f"Found release: {tag}") - match = re.match(r".*(\d+)\.(\s*\d+)\.(\s*\d+)",tag) + match = re.match(r".*(\d+)\.(\s*\d+)\.(\s*\d+)", tag) if match: - version_num = tuple(map(int,match.groups())) + version_num = tuple(map(int, match.groups())) print(f"Found version: {version_num}") bpy.context.preferences.addons['cam'].preferences.last_update_check = today bpy.ops.wm.save_userpref() @@ -105,62 +107,64 @@ def execute(self, context): self.install_zip_from_url(zip_url) return {'FINISHED'} elif update_source.endswith("/commits"): - with urlopen(update_source+"?per_page=1",timeout=2) as response: + with urlopen(update_source+"?per_page=1", timeout=2) as response: body = response.read().decode("UTF-8") # find the tag name - commit_list=json.loads(body) - commit_sha=commit_list[0]['sha'] + commit_list = json.loads(body) + commit_sha = commit_list[0]['sha'] if bpy.context.preferences.addons['cam'].preferences.last_commit_hash != commit_sha: # get zipball from this commit - zip_url = update_source.replace("/commits",f"/zipball/{commit_sha}") + zip_url = update_source.replace("/commits", f"/zipball/{commit_sha}") self.install_zip_from_url(zip_url) bpy.context.preferences.addons['cam'].preferences.last_commit_hash = commit_sha bpy.ops.wm.save_userpref() return {'FINISHED'} - - def install_zip_from_url(self,zip_url): + + def install_zip_from_url(self, zip_url): with urlopen(zip_url) as zip_response: - zip_body=zip_response.read() - buffer= io.BytesIO(zip_body) - zf=zipfile.ZipFile(buffer,mode='r') - files=zf.infolist() + zip_body = zip_response.read() + buffer = io.BytesIO(zip_body) + zf = zipfile.ZipFile(buffer, mode='r') + files = zf.infolist() cam_addon_path = pathlib.Path(__file__).parent for fileinfo in files: - filename=fileinfo.filename + filename = fileinfo.filename if fileinfo.is_dir() == False: - path_pos=filename.replace("\\","/").find("/scripts/addons/cam/") - if path_pos!=-1: - relative_path=filename[path_pos+len("/scripts/addons/cam/"):] + path_pos = filename.replace("\\", "/").find("/scripts/addons/cam/") + if path_pos != -1: + relative_path = filename[path_pos+len("/scripts/addons/cam/"):] out_path = cam_addon_path / relative_path print(out_path) # check folder exists - out_path.parent.mkdir(parents=True,exist_ok=True) - with zf.open(filename,"r") as in_file, open(out_path,"wb") as out_file: - time_struct=(*fileinfo.date_time,0,0,0) - mtime=calendar.timegm(time_struct) + out_path.parent.mkdir(parents=True, exist_ok=True) + with zf.open(filename, "r") as in_file, open(out_path, "wb") as out_file: + time_struct = (*fileinfo.date_time, 0, 0, 0) + mtime = calendar.timegm(time_struct) out_file.write(in_file.read()) - os.utime(out_path,times=(mtime,mtime)) + os.utime(out_path, times=(mtime, mtime)) # TODO: check for newer times # TODO: what about if a file is deleted... # updated everything, now mark as updated and reload scripts - bpy.context.preferences.addons['cam'].preferences.just_updated=True + bpy.context.preferences.addons['cam'].preferences.just_updated = True bpy.context.preferences.addons['cam'].preferences.new_version_available = "" bpy.ops.wm.save_userpref() # unload ourself from python module system - delete_list=[] + delete_list = [] for m in sys.modules.keys(): - if m.startswith("cam.") or m=='cam': + if m.startswith("cam.") or m == 'cam': delete_list.append(m) for d in delete_list: del sys.modules[d] bpy.ops.script.reload() + class UpdateSourceOperator(bpy.types.Operator): bl_idname = "render.cam_set_update_source" bl_label = "Set blendercam update source" new_source: bpy.props.StringProperty(default='') - def execute(self,context): - bpy.context.preferences.addons['cam'].preferences.update_source=self.new_source + + def execute(self, context): + bpy.context.preferences.addons['cam'].preferences.update_source = self.new_source bpy.ops.wm.save_userpref() return {'FINISHED'} diff --git a/scripts/addons/cam/basrelief.py b/scripts/addons/cam/basrelief.py index 1a163b90d..d6551a807 100644 --- a/scripts/addons/cam/basrelief.py +++ b/scripts/addons/cam/basrelief.py @@ -1,4 +1,5 @@ -import bpy,time +import bpy +import time import numpy import math import re @@ -6,1149 +7,1183 @@ from bpy.props import * -##//////////////////////////////////////////////////////////////////// -#// Full Multigrid Algorithm for solving partial differential equations -#////////////////////////////////////////////////////////////////////// -#MODYF = 0 #/* 1 or 0 (1 is better) */ -#MINS = 16 #/* minimum size 4 6 or 100 */ +# //////////////////////////////////////////////////////////////////// +# // Full Multigrid Algorithm for solving partial differential equations +# ////////////////////////////////////////////////////////////////////// +# MODYF = 0 #/* 1 or 0 (1 is better) */ +# MINS = 16 #/* minimum size 4 6 or 100 */ -#SMOOTH_IT = 2 #/* minimum 1 */ -#V_CYCLE = 10 #/* number of v-cycles 2*/ +# SMOOTH_IT = 2 #/* minimum 1 */ +# V_CYCLE = 10 #/* number of v-cycles 2*/ #ITERATIONS = 5 -#// precision +# // precision EPS = 1.0e-32 -PRECISION=5 -NUMPYALG=False -#PLANAR_CONST=True +PRECISION = 5 +NUMPYALG = False +# PLANAR_CONST=True -def copy_compbuf_data(inbuf, outbuf): - outbuf[:]=inbuf[:] - -def restrictbuf( inbuf, outbuf ):#scale down array.... - - inx = inbuf.shape[0] - iny = inbuf.shape[1] - outx = outbuf.shape[0] - outy = outbuf.shape[1] +def copy_compbuf_data(inbuf, outbuf): + outbuf[:] = inbuf[:] - dx=inx/outx - dy=iny/outy - filterSize = 0.5 - xfiltersize=dx*filterSize +def restrictbuf(inbuf, outbuf): # scale down array.... - sy=dy/2-0.5 - if dx==2 and dy==2:#much simpler method - #if dx<2: - #restricted= - #num=restricted.shape[0]*restricted.shape[1] - outbuf[:]=(inbuf[::2,::2]+inbuf[1::2,::2]+inbuf[::2,1::2]+inbuf[1::2,1::2])/4.0 + inx = inbuf.shape[0] + iny = inbuf.shape[1] - elif NUMPYALG:#numpy method - yrange=numpy.arange(0,outy) - xrange=numpy.arange(0,outx) + outx = outbuf.shape[0] + outy = outbuf.shape[1] - w=0 - sx = dx/2-0.5 + dx = inx/outx + dy = iny/outy - sxrange=xrange*dx+sx - syrange=yrange*dy+sy + filterSize = 0.5 + xfiltersize = dx*filterSize - sxstartrange=numpy.array(numpy.ceil(sxrange-xfiltersize),dtype=int) - sxstartrange[sxstartrange<0]=0 - sxendrange=numpy.array(numpy.floor(sxrange+xfiltersize)+1,dtype=int) - sxendrange[sxendrange>inx]=inx + sy = dy/2-0.5 + if dx == 2 and dy == 2: # much simpler method + # if dx<2: + # restricted= + # num=restricted.shape[0]*restricted.shape[1] + outbuf[:] = (inbuf[::2, ::2]+inbuf[1::2, ::2]+inbuf[::2, 1::2]+inbuf[1::2, 1::2])/4.0 - systartrange=numpy.array(numpy.ceil(syrange-xfiltersize),dtype=int) - systartrange[systartrange<0]=0 - syendrange=numpy.array(numpy.floor(syrange+xfiltersize)+1,dtype=int) - syendrange[syendrange>iny]=iny - #np.arange(8*6*3).reshape((8, 6, 3)) - - indices=numpy.arange(outx*outy*2*3).reshape((2,outx*outy,3))#3is the maximum value...?pff. - - r=sxendrange-sxstartrange - - indices[0]=sxstartrange.repeat(outy) - - indices[1]=systartrange.repeat(outx).reshape(outx,outy).swapaxes(0,1).flatten() - - #systartrange=numpy.max(0,numpy.ceil(syrange-xfiltersize)) - #syendrange=numpy.min(numpy.floor(syrange+xfiltersize),iny-1)+1 - - outbuf.fill(0) - tempbuf=inbuf[indices[0],indices[1]] - tempbuf+=inbuf[indices[0]+1,indices[1]] - tempbuf+=inbuf[indices[0],indices[1]+1] - tempbuf+=inbuf[indices[0]+1,indices[1]+1] - tempbuf/=4.0 - outbuf[:]=tempbuf.reshape((outx,outy)) - #outbuf[:,:]=inbuf[]#inbuf[sxstartrange,systartrange] #+ inbuf[sxstartrange+1,systartrange] + inbuf[sxstartrange,systartrange+1] + inbuf[sxstartrange+1,systartrange+1])/4.0 - - - - else:#old method - for y in range(0,outy): - - sx = dx/2-0.5 - for x in range(0,outx): - pixVal = 0 - w = 0 + elif NUMPYALG: # numpy method + yrange = numpy.arange(0, outy) + xrange = numpy.arange(0, outx) - # - for ix in range(max( 0, ceil(sx-dx*filterSize)),min( floor( sx+dx*filterSize ), inx-1)+1): - for iy in range(max( 0, ceil( sy-dx*filterSize ) ),min( floor( sy+dx*filterSize), iny-1)+1): - pixVal += inbuf[ix,iy] - w += 1 - outbuf[x ,y] = pixVal/w + w = 0 + sx = dx/2-0.5 - sx+=dx - sy+=dy + sxrange = xrange*dx+sx + syrange = yrange*dy+sy -def prolongate( inbuf, outbuf ): + sxstartrange = numpy.array(numpy.ceil(sxrange-xfiltersize), dtype=int) + sxstartrange[sxstartrange < 0] = 0 + sxendrange = numpy.array(numpy.floor(sxrange+xfiltersize)+1, dtype=int) + sxendrange[sxendrange > inx] = inx - inx = inbuf.shape[0] - iny = inbuf.shape[1] + systartrange = numpy.array(numpy.ceil(syrange-xfiltersize), dtype=int) + systartrange[systartrange < 0] = 0 + syendrange = numpy.array(numpy.floor(syrange+xfiltersize)+1, dtype=int) + syendrange[syendrange > iny] = iny + #np.arange(8*6*3).reshape((8, 6, 3)) + + # 3is the maximum value...?pff. + indices = numpy.arange(outx*outy*2*3).reshape((2, outx*outy, 3)) + + r = sxendrange-sxstartrange + + indices[0] = sxstartrange.repeat(outy) + + indices[1] = systartrange.repeat(outx).reshape(outx, outy).swapaxes(0, 1).flatten() + + # systartrange=numpy.max(0,numpy.ceil(syrange-xfiltersize)) + # syendrange=numpy.min(numpy.floor(syrange+xfiltersize),iny-1)+1 + + outbuf.fill(0) + tempbuf = inbuf[indices[0], indices[1]] + tempbuf += inbuf[indices[0]+1, indices[1]] + tempbuf += inbuf[indices[0], indices[1]+1] + tempbuf += inbuf[indices[0]+1, indices[1]+1] + tempbuf /= 4.0 + outbuf[:] = tempbuf.reshape((outx, outy)) + # outbuf[:,:]=inbuf[]#inbuf[sxstartrange,systartrange] #+ inbuf[sxstartrange+1,systartrange] + inbuf[sxstartrange,systartrange+1] + inbuf[sxstartrange+1,systartrange+1])/4.0 + + else: # old method + for y in range(0, outy): + + sx = dx/2-0.5 + for x in range(0, outx): + pixVal = 0 + w = 0 - outx = outbuf.shape[0] - outy = outbuf.shape[1] + # + for ix in range(max(0, ceil(sx-dx*filterSize)), min(floor(sx+dx*filterSize), inx-1)+1): + for iy in range(max(0, ceil(sy-dx*filterSize)), min(floor(sy+dx*filterSize), iny-1)+1): + pixVal += inbuf[ix, iy] + w += 1 + outbuf[x, y] = pixVal/w - dx=inx/outx - dy=iny/outy + sx += dx + sy += dy - filterSize = 1 - xfiltersize=dx*filterSize - #outx[:]= - #outbuf.put(inbuf.repeat(4)) - if dx==0.5 and dy==0.5: - outbuf[::2,::2]=inbuf - outbuf[1::2,::2]=inbuf - outbuf[::2,1::2]=inbuf - outbuf[1::2,1::2]=inbuf - #x=inbuf::.flatten().repeat(2) - elif NUMPYALG:#numpy method - sy=-dy/2 - sx=-dx/2 - xrange=numpy.arange(0,outx) - yrange=numpy.arange(0,outy) +def prolongate(inbuf, outbuf): - sxrange=xrange*dx+sx - syrange=yrange*dy+sy - - sxstartrange=numpy.array(numpy.ceil(sxrange-xfiltersize),dtype=int) - sxstartrange[sxstartrange<0]=0 - sxendrange=numpy.array(numpy.floor(sxrange+xfiltersize)+1,dtype=int) - sxendrange[sxendrange>=inx]=inx-1 - systartrange=numpy.array(numpy.ceil(syrange-xfiltersize),dtype=int) - systartrange[systartrange<0]=0 - syendrange=numpy.array(numpy.floor(syrange+xfiltersize)+1,dtype=int) - syendrange[syendrange>=iny]=iny-1 + inx = inbuf.shape[0] + iny = inbuf.shape[1] - indices=numpy.arange(outx*outy*2).reshape((2,outx*outy)) - indices[0]=sxstartrange.repeat(outy) - indices[1]=systartrange.repeat(outx).reshape(outx,outy).swapaxes(0,1).flatten() + outx = outbuf.shape[0] + outy = outbuf.shape[1] - #systartrange=numpy.max(0,numpy.ceil(syrange-xfiltersize)) - #syendrange=numpy.min(numpy.floor(syrange+xfiltersize),iny-1)+1 - #outbuf.fill(0) - tempbuf=inbuf[indices[0],indices[1]] - #tempbuf+=inbuf[indices[0]+1,indices[1]] - #tempbuf+=inbuf[indices[0],indices[1]+1] - #tempbuf+=inbuf[indices[0]+1,indices[1]+1] - tempbuf/=4.0 - outbuf[:]=tempbuf.reshape((outx,outy)) + dx = inx/outx + dy = iny/outy - #outbuf.fill(0) - #outbuf[xrange,yrange]=inbuf[sxstartrange,systartrange]# + inbuf[sxendrange,systartrange] + inbuf[sxstartrange,syendrange] + inbuf[sxendrange,syendrange])/4.0 + filterSize = 1 + xfiltersize = dx*filterSize + # outx[:]= - else: - sy=-dy/2 - for y in range(0,outy): - sx=-dx/2 - for x in range(0,outx): - pixVal = 0 - weight = 0 - - for ix in range(max( 0, ceil( sx-filterSize ) ),min( floor(sx+filterSize), inx-1 )+1): - for iy in range(max( 0, ceil( sy-filterSize ) ),min( floor( sy+filterSize), iny-1 )+1): - fx = abs( sx - ix ) - fy = abs( sy - iy ) - - fval = (1-fx)*(1-fy) - - pixVal += inbuf[ix,iy] * fval - weight += fval - #if weight==0: - # print('error' ) - # return - outbuf[x,y]=pixVal/weight - sx+=dx - sy+=dy - -def idx(r,c,cols): - - return r*cols+c+1 - - -## smooth u using f at level -def smooth( U, F , linbcgiterations, planar): - - iter=0 - err=0 - - rows = U.shape[1] - cols = U.shape[0] - - n = U.size - - linbcg( n, F, U, 2, 0.001, linbcgiterations, iter, err , rows, cols, planar) - - - -def calculate_defect( D, U, F ): - - - sx = F.shape[0] - sy = F.shape[1] - - h = 1.0/sqrt(sx*sy*1.0) - h2i = 1.0/(h*h) - - h2i = 1 - D[1:-1,1:-1]=F[1:-1,1:-1] - U[:-2,1:-1] - U[2:,1:-1] - U[1:-1, :-2] - U[1:-1,2:] + 4*U[1:-1,1:-1] - #sides - D[1:-1,0]=F[1:-1,0] - U[:-2,0] - U[2:,0] - U[1:-1, 1] + 3*U[1:-1,0] - D[1:-1,-1]=F[1:-1,-1] - U[:-2,-1] - U[2:,-1] - U[1:-1, -2] + 3*U[1:-1,-1] - D[0,1:-1] = F[0,1:-1] - U[0,:-2] - U[0,:-2] - U[1,1:-1] + 3*U[0,1:-1] - D[-1,1:-1] = F[-1,1:-1] - U[-1,:-2] - U[-1,:-2] - U[-1,1:-1] + 3*U[-1,1:-1] - #coners - D[0,0]=F[0,0] - U[0,1] - U[1,0] + 2*U[0,0] - D[0,-1]=F[0,-1] - U[1,-1] - U[0,-2] + 2*U[0,-1] - D[-1,0]=F[-1,0] - U[-2,0] - U[-1,1] + 2*U[-1,0] - D[-1,-1]=F[-1,-1] - U[-2,-1] - U[-1,-2] + 2*U[-1,-1] - - # for y in range(0,sy): - # for x in range(0,sx): - # - # w = max(0,x-1) - # n = max(0,y-1) - # e = min(sx, x+1) - # s = min(sy, y+1) - # - # - # D[x,y] = F[x,y] -( U[e,y] + U[w,y] + U[x,n] + U[x,s] - 4.0*U[x,y]) - -def add_correction( U, C ): - U+=C + # outbuf.put(inbuf.repeat(4)) + if dx == 0.5 and dy == 0.5: + outbuf[::2, ::2] = inbuf + outbuf[1::2, ::2] = inbuf + outbuf[::2, 1::2] = inbuf + outbuf[1::2, 1::2] = inbuf + # x=inbuf::.flatten().repeat(2) + elif NUMPYALG: # numpy method + sy = -dy/2 + sx = -dx/2 + xrange = numpy.arange(0, outx) + yrange = numpy.arange(0, outy) + + sxrange = xrange*dx+sx + syrange = yrange*dy+sy + + sxstartrange = numpy.array(numpy.ceil(sxrange-xfiltersize), dtype=int) + sxstartrange[sxstartrange < 0] = 0 + sxendrange = numpy.array(numpy.floor(sxrange+xfiltersize)+1, dtype=int) + sxendrange[sxendrange >= inx] = inx-1 + systartrange = numpy.array(numpy.ceil(syrange-xfiltersize), dtype=int) + systartrange[systartrange < 0] = 0 + syendrange = numpy.array(numpy.floor(syrange+xfiltersize)+1, dtype=int) + syendrange[syendrange >= iny] = iny-1 + + indices = numpy.arange(outx*outy*2).reshape((2, outx*outy)) + indices[0] = sxstartrange.repeat(outy) + indices[1] = systartrange.repeat(outx).reshape(outx, outy).swapaxes(0, 1).flatten() + + # systartrange=numpy.max(0,numpy.ceil(syrange-xfiltersize)) + # syendrange=numpy.min(numpy.floor(syrange+xfiltersize),iny-1)+1 + # outbuf.fill(0) + tempbuf = inbuf[indices[0], indices[1]] + # tempbuf+=inbuf[indices[0]+1,indices[1]] + # tempbuf+=inbuf[indices[0],indices[1]+1] + # tempbuf+=inbuf[indices[0]+1,indices[1]+1] + tempbuf /= 4.0 + outbuf[:] = tempbuf.reshape((outx, outy)) + + # outbuf.fill(0) + # outbuf[xrange,yrange]=inbuf[sxstartrange,systartrange]# + inbuf[sxendrange,systartrange] + inbuf[sxstartrange,syendrange] + inbuf[sxendrange,syendrange])/4.0 + + else: + sy = -dy/2 + for y in range(0, outy): + sx = -dx/2 + for x in range(0, outx): + pixVal = 0 + weight = 0 + + for ix in range(max(0, ceil(sx-filterSize)), min(floor(sx+filterSize), inx-1)+1): + for iy in range(max(0, ceil(sy-filterSize)), min(floor(sy+filterSize), iny-1)+1): + fx = abs(sx - ix) + fy = abs(sy - iy) + + fval = (1-fx)*(1-fy) + + pixVal += inbuf[ix, iy] * fval + weight += fval + # if weight==0: + # print('error' ) + # return + outbuf[x, y] = pixVal/weight + sx += dx + sy += dy + + +def idx(r, c, cols): + + return r*cols+c+1 + + +# smooth u using f at level +def smooth(U, F, linbcgiterations, planar): + + iter = 0 + err = 0 + + rows = U.shape[1] + cols = U.shape[0] + + n = U.size + + linbcg(n, F, U, 2, 0.001, linbcgiterations, iter, err, rows, cols, planar) + + +def calculate_defect(D, U, F): + + sx = F.shape[0] + sy = F.shape[1] + + h = 1.0/sqrt(sx*sy*1.0) + h2i = 1.0/(h*h) + + h2i = 1 + D[1:-1, 1:-1] = F[1:-1, 1:-1] - U[:-2, 1:-1] - U[2:, 1:-1] - \ + U[1:-1, :-2] - U[1:-1, 2:] + 4*U[1:-1, 1:-1] + # sides + D[1:-1, 0] = F[1:-1, 0] - U[:-2, 0] - U[2:, 0] - U[1:-1, 1] + 3*U[1:-1, 0] + D[1:-1, -1] = F[1:-1, -1] - U[:-2, -1] - U[2:, -1] - U[1:-1, -2] + 3*U[1:-1, -1] + D[0, 1:-1] = F[0, 1:-1] - U[0, :-2] - U[0, :-2] - U[1, 1:-1] + 3*U[0, 1:-1] + D[-1, 1:-1] = F[-1, 1:-1] - U[-1, :-2] - U[-1, :-2] - U[-1, 1:-1] + 3*U[-1, 1:-1] + # coners + D[0, 0] = F[0, 0] - U[0, 1] - U[1, 0] + 2*U[0, 0] + D[0, -1] = F[0, -1] - U[1, -1] - U[0, -2] + 2*U[0, -1] + D[-1, 0] = F[-1, 0] - U[-2, 0] - U[-1, 1] + 2*U[-1, 0] + D[-1, -1] = F[-1, -1] - U[-2, -1] - U[-1, -2] + 2*U[-1, -1] + + # for y in range(0,sy): + # for x in range(0,sx): + # + # w = max(0,x-1) + # n = max(0,y-1) + # e = min(sx, x+1) + # s = min(sy, y+1) + # + # + # D[x,y] = F[x,y] -( U[e,y] + U[w,y] + U[x,n] + U[x,s] - 4.0*U[x,y]) + + +def add_correction(U, C): + U += C -#def alloc_compbuf(xmax,ymax,pix, 1): +# def alloc_compbuf(xmax,ymax,pix, 1): # ar=numpy.array() -def solve_pde_multigrid( F, U , vcycleiterations, linbcgiterations, smoothiterations, mins, levels, useplanar, planar): - - xmax = F.shape[0] - ymax = F.shape[1] - - #int i # index for simple loops - #int k # index for iterating through levels - #int k2 # index for iterating through levels in V-cycles - - ## 1. restrict f to coarse-grid (by the way count the number of levels) - ## k=0: fine-grid = f - ## k=levels: coarsest-grid - #pix = CB_VAL#what is this>??? - #int cycle - #int sx, sy - - RHS=[] - IU=[] - VF=[] - PLANAR=[] - for a in range(0,levels+1): - RHS.append(None) - IU.append(None) - VF.append(None) - PLANAR.append(None) - VF[0] = numpy.zeros((xmax,ymax), dtype=numpy.float) - #numpy.fill(pix)!? TODO - - RHS[0] = F.copy() - IU[0] = U.copy() - PLANAR[0] = planar.copy() - - sx=xmax - sy=ymax - #print(planar) - for k in range(0,levels): - # calculate size of next level - sx=int(sx/2) - sy=int(sy/2) - PLANAR[k+1] = numpy.zeros((sx,sy), dtype=numpy.float) - RHS[k+1] = numpy.zeros((sx,sy), dtype=numpy.float) - IU[k+1] = numpy.zeros((sx,sy), dtype=numpy.float) - VF[k+1] = numpy.zeros((sx,sy), dtype=numpy.float) - - # restrict from level k to level k+1 (coarser-grid) - restrictbuf(PLANAR[k], PLANAR[k+1]) - PLANAR[k+1]=PLANAR[k+1]>0 - #numpytoimage(PLANAR[k+1],'planar') - #print(PLANAR[k+1]) - restrictbuf( RHS[k], RHS[k+1] ) - #numpytoimage(RHS[k+1],'rhs') - - - - # 2. find exact sollution at the coarsest-grid (k=levels) - IU[levels].fill(0.0)#this was replaced to easify code. exact_sollution( RHS[levels], IU[levels] ) - - # 3. nested iterations - - for k in range(levels-1,-1,-1): - print('K:', str(k)) - - # 4. interpolate sollution from last coarse-grid to finer-grid - # interpolate from level k+1 to level k (finer-grid) - prolongate( IU[k+1], IU[k] ) - #print('k',k) - # 4.1. first target function is the equation target function - # (following target functions are the defect) - copy_compbuf_data( RHS[k], VF[k] ) +def solve_pde_multigrid(F, U, vcycleiterations, linbcgiterations, smoothiterations, mins, levels, useplanar, planar): + + xmax = F.shape[0] + ymax = F.shape[1] + + # int i # index for simple loops + # int k # index for iterating through levels + # int k2 # index for iterating through levels in V-cycles + + # 1. restrict f to coarse-grid (by the way count the number of levels) + # k=0: fine-grid = f + # k=levels: coarsest-grid + # pix = CB_VAL#what is this>??? + # int cycle + # int sx, sy + + RHS = [] + IU = [] + VF = [] + PLANAR = [] + for a in range(0, levels+1): + RHS.append(None) + IU.append(None) + VF.append(None) + PLANAR.append(None) + VF[0] = numpy.zeros((xmax, ymax), dtype=numpy.float) + # numpy.fill(pix)!? TODO + + RHS[0] = F.copy() + IU[0] = U.copy() + PLANAR[0] = planar.copy() + + sx = xmax + sy = ymax + # print(planar) + for k in range(0, levels): + # calculate size of next level + sx = int(sx/2) + sy = int(sy/2) + PLANAR[k+1] = numpy.zeros((sx, sy), dtype=numpy.float) + RHS[k+1] = numpy.zeros((sx, sy), dtype=numpy.float) + IU[k+1] = numpy.zeros((sx, sy), dtype=numpy.float) + VF[k+1] = numpy.zeros((sx, sy), dtype=numpy.float) + + # restrict from level k to level k+1 (coarser-grid) + restrictbuf(PLANAR[k], PLANAR[k+1]) + PLANAR[k+1] = PLANAR[k+1] > 0 + # numpytoimage(PLANAR[k+1],'planar') + # print(PLANAR[k+1]) + restrictbuf(RHS[k], RHS[k+1]) + # numpytoimage(RHS[k+1],'rhs') + + # 2. find exact sollution at the coarsest-grid (k=levels) + # this was replaced to easify code. exact_sollution( RHS[levels], IU[levels] ) + IU[levels].fill(0.0) + + # 3. nested iterations + + for k in range(levels-1, -1, -1): + print('K:', str(k)) + + # 4. interpolate sollution from last coarse-grid to finer-grid + # interpolate from level k+1 to level k (finer-grid) + prolongate(IU[k+1], IU[k]) + # print('k',k) + # 4.1. first target function is the equation target function + # (following target functions are the defect) + copy_compbuf_data(RHS[k], VF[k]) + + #print('lanar ') + + # 5. V-cycle (twice repeated) + + for cycle in range(0, vcycleiterations): + print('v-cycle iteration:', str(cycle)) + + # 6. downward stroke of V + for k2 in range(k, levels): + # 7. pre-smoothing of initial sollution using target function + # zero for initial guess at smoothing + # (except for level k when iu contains prolongated result) + if(k2 != k): + IU[k2].fill(0.0) + + for i in range(0, smoothiterations): + smooth(IU[k2], VF[k2], linbcgiterations, PLANAR[k2]) + + # 8. calculate defect at level + # d[k2] = Lh * ~u[k2] - f[k2] + + D = numpy.zeros_like(IU[k2]) + # if k2==0: + # IU[k2][planar[k2]]=IU[k2].max() + # print(IU[0]) + if useplanar and k2 == 0: + IU[k2][PLANAR[k2]] = IU[k2].min() + # if k2==0 : + + # VF[k2][PLANAR[k2]]=0.0 + # print(IU[0]) + calculate_defect(D, IU[k2], VF[k2]) + + # 9. restrict deffect as target function for next coarser-grid + # def -> f[k2+1] + restrictbuf(D, VF[k2+1]) + + # 10. solve on coarsest-grid (target function is the deffect) + # iu[levels] should contain sollution for + # the f[levels] - last deffect, iu will now be the correction + IU[levels].fill(0.0) # exact_sollution(VF[levels], IU[levels] ) + + # 11. upward stroke of V + for k2 in range(levels-1, k-1, -1): + print('k2: ', str(k2)) + # 12. interpolate correction from last coarser-grid to finer-grid + # iu[k2+1] -> cor + C = numpy.zeros_like(IU[k2]) + prolongate(IU[k2+1], C) + + # 13. add interpolated correction to initial sollution at level k2 + add_correction(IU[k2], C) + + # 14. post-smoothing of current sollution using target function + for i in range(0, smoothiterations): + + smooth(IU[k2], VF[k2], linbcgiterations, PLANAR[k2]) - #print('lanar ') + if useplanar and k2 == 0: + IU[0][planar] = IU[0].min() + # print(IU[0]) - # 5. V-cycle (twice repeated) + # --- end of V-cycle + + # --- end of nested iteration - for cycle in range(0,vcycleiterations): - print('v-cycle iteration:', str(cycle)) + # 15. final sollution + # IU[0] contains the final sollution - # 6. downward stroke of V - for k2 in range(k,levels): - # 7. pre-smoothing of initial sollution using target function - # zero for initial guess at smoothing - # (except for level k when iu contains prolongated result) - if( k2!=k ): - IU[k2].fill(0.0) + U[:] = IU[0] - for i in range(0,smoothiterations): - smooth( IU[k2], VF[k2], linbcgiterations, PLANAR[k2]) - # 8. calculate defect at level - # d[k2] = Lh * ~u[k2] - f[k2] - - D = numpy.zeros_like(IU[k2]) - #if k2==0: - #IU[k2][planar[k2]]=IU[k2].max() - #print(IU[0]) - if useplanar and k2==0: - IU[k2][PLANAR[k2]]=IU[k2].min() - #if k2==0 : - - # VF[k2][PLANAR[k2]]=0.0 - # print(IU[0]) - calculate_defect( D, IU[k2], VF[k2] ) - - # 9. restrict deffect as target function for next coarser-grid - # def -> f[k2+1] - restrictbuf( D, VF[k2+1] ) - - # 10. solve on coarsest-grid (target function is the deffect) - # iu[levels] should contain sollution for - # the f[levels] - last deffect, iu will now be the correction - IU[levels].fill(0.0)#exact_sollution(VF[levels], IU[levels] ) - - # 11. upward stroke of V - for k2 in range(levels-1,k-1,-1): - print('k2: ',str(k2)) - # 12. interpolate correction from last coarser-grid to finer-grid - # iu[k2+1] -> cor - C = numpy.zeros_like(IU[k2]) - prolongate( IU[k2+1], C ) +def asolve(b, x): + x[:] = -4*b - # 13. add interpolated correction to initial sollution at level k2 - add_correction( IU[k2], C ) +def atimes(x, res): + res[1:-1, 1:-1] = x[:-2, 1:-1]+x[2:, 1:-1]+x[1:-1, :-2]+x[1:-1, 2:] - 4*x[1:-1, 1:-1] + # sides + res[1:-1, 0] = x[0:-2, 0]+x[2:, 0]+x[1:-1, 1] - 3*x[1:-1, 0] + res[1:-1, -1] = x[0:-2, -1]+x[2:, -1]+x[1:-1, -2] - 3*x[1:-1, -1] + res[0, 1:-1] = x[0, :-2] + x[0, 2:] + x[1, 1:-1] - 3*x[0, 1:-1] + res[-1, 1:-1] = x[-1, :-2] + x[-1, 2:] + x[-2, 1:-1] - 3*x[-1, 1:-1] + # corners + res[0, 0] = x[1, 0]+x[0, 1]-2*x[0, 0] + res[-1, 0] = x[-2, 0]+x[-1, 1]-2*x[-1, 0] + res[0, -1] = x[0, -2]+x[1, -1]-2*x[0, -1] + res[-1, -1] = x[-1, -2]+x[-2, -1]-2*x[-1, -1] - # 14. post-smoothing of current sollution using target function - for i in range(0, smoothiterations): - smooth( IU[k2], VF[k2] ,linbcgiterations,PLANAR[k2]) +def snrm(n, sx, itol): + if (itol <= 3): + temp = sx*sx + ans = temp.sum() + return sqrt(ans) + else: + temp = numpy.abs(sx) + return temp.max() - if useplanar and k2==0: - IU[0][planar]=IU[0].min() - #print(IU[0]) +# /** +# * Biconjugate Gradient Method +# * from Numerical Recipes in C +# */ - #--- end of V-cycle - #--- end of nested iteration +def linbcg(n, b, x, itol, tol, itmax, iter, err, rows, cols, planar): - # 15. final sollution - # IU[0] contains the final sollution + p = numpy.zeros((cols, rows)) + pp = numpy.zeros((cols, rows)) + r = numpy.zeros((cols, rows)) + rr = numpy.zeros((cols, rows)) + z = numpy.zeros((cols, rows)) + zz = numpy.zeros((cols, rows)) - U[:]=IU[0] + iter = 0 + atimes(x, r) + r[:] = b-r + rr[:] = r + atimes(r, rr) # minimum residual -def asolve(b, x): - x[:] = -4*b + znrm = 1.0 + + if (itol == 1): + bnrm = snrm(n, b, itol) + + elif (itol == 2): + asolve(b, z) + bnrm = snrm(n, z, itol) + + elif (itol == 3 or itol == 4): + asolve(b, z) + bnrm = snrm(n, z, itol) + asolve(r, z) + znrm = snrm(n, z, itol) + else: + print("illegal itol in linbcg") + + asolve(r, z) + + while (iter <= itmax): + #print('linbcg iteration:', str(iter)) + iter += 1 + zm1nrm = znrm + asolve(rr, zz) + + bknum = 0.0 + + temp = z*rr + + bknum = temp.sum() # -z[0]*rr[0]???? + + if (iter == 1): + p[:] = z + pp[:] = zz + + else: + bk = bknum/bkden + p = bk*p+z + pp = bk*pp+zz + bkden = bknum + atimes(p, z) + temp = z*pp + akden = temp.sum() + ak = bknum/akden + atimes(pp, zz) + + x += ak*p + r -= ak*z + rr -= ak*zz + + asolve(r, z) + + if (itol == 1 or itol == 2): + znrm = 1.0 + err = snrm(n, r, itol)/bnrm + elif (itol == 3 or itol == 4): + znrm = snrm(n, z, itol) + if (abs(zm1nrm-znrm) > EPS*znrm): + dxnrm = abs(ak)*snrm(n, p, itol) + err = znrm/abs(zm1nrm-znrm)*dxnrm + else: + err = znrm/bnrm + continue + xnrm = snrm(n, x, itol) + + if (err <= 0.5*xnrm): + err /= xnrm + else: + err = znrm/bnrm + continue + if (err <= tol): + break + # if PLANAR_CONST and planar.shape==rr.shape: + # x[planar]=0.0 + + +# -------------------------------------------------------------------- + + +def numpysave(a, iname): + inamebase = bpy.path.basename(iname) + + i = numpytoimage(a, inamebase) + + r = bpy.context.scene.render + + r.image_settings.file_format = 'OPEN_EXR' + r.image_settings.color_mode = 'BW' + r.image_settings.color_depth = '32' + + i.save_render(iname) -def atimes(x, res): - res[1:-1,1:-1]=x[:-2,1:-1]+x[2:,1:-1]+x[1:-1,:-2]+x[1:-1,2:] - 4*x[1:-1,1:-1] - #sides - res[1:-1,0]=x[0:-2,0]+x[2:,0]+x[1:-1,1] - 3*x[1:-1,0] - res[1:-1,-1] = x[0:-2,-1]+x[2:,-1]+x[1:-1,-2]- 3*x[1:-1,-1] - res[0,1:-1] = x[0, :-2] + x[0, 2:] + x[1, 1:-1] -3*x [0,1:-1] - res[-1,1:-1] = x[-1, :-2] + x[-1, 2:] + x[-2, 1:-1] -3*x [-1,1:-1] - #corners - res[0,0]=x[1,0]+x[0,1]-2*x[0,0] - res[-1,0]=x[-2,0]+x[-1,1]-2*x[-1,0] - res[0,-1]=x[0,-2]+x[1,-1]-2*x[0,-1] - res[-1,-1]=x[-1,-2]+x[-2,-1]-2*x[-1,-1] -def snrm(n, sx, itol): +def numpytoimage(a, iname): + t = time.time() + print('numpy to image - here') + t = time.time() + print(a.shape[0], a.shape[1]) + foundimage = False + for image in bpy.data.images: + + if image.name[:len(iname)] == iname and image.size[0] == a.shape[0] and image.size[1] == a.shape[1]: + i = image + foundimage = True + if not foundimage: + bpy.ops.image.new(name=iname, width=a.shape[0], height=a.shape[1], color=( + 0, 0, 0, 1), alpha=True, generated_type='BLANK', float=True) + for image in bpy.data.images: + + if image.name[:len(iname)] == iname and image.size[0] == a.shape[0] and image.size[1] == a.shape[1]: + i = image + + d = a.shape[0]*a.shape[1] + a = a.swapaxes(0, 1) + a = a.reshape(d) + a = a.repeat(4) + a[3::4] = 1 + # i.pixels=a + i.pixels[:] = a[:] # this gives big speedup! + print('\ntime '+str(time.time()-t)) + return i - if (itol <= 3): - temp=sx*sx - ans=temp.sum() - return sqrt(ans) - else: - temp=numpy.abs(sx) - return temp.max() -#/** -# * Biconjugate Gradient Method -# * from Numerical Recipes in C -# */ -def linbcg(n, b, x, itol, tol, itmax, iter, err, rows, cols, planar): +def imagetonumpy(i): + t = time.time() + inc = 0 + + width = i.size[0] + height = i.size[1] + x = 0 + y = 0 + count = 0 + na = numpy.array((0.1), dtype=float) + + size = width*height + na.resize(size*4) + + # these 2 lines are about 15% faster than na=i.pixels[:].... whyyyyyyyy!!?!?!?!?! Blender image data access is evil. + p = i.pixels[:] + na[:] = p + # na=numpy.array(i.pixels[:])#this was terribly slow... at least I know why now, it probably + na = na[::4] + na = na.reshape(height, width) + na = na.swapaxes(0, 1) + + print('\ntime of image to numpy '+str(time.time()-t)) + return na + + +def tonemap(i, exponent): + # if depth buffer never got written it gets set + # to a great big value (10000000000.0) + # filter out anything within an order of magnitude of it + # so we only have things that are actually drawn + maxheight = i.max(where=i < 1000000000.0, initial=0) + minheight = i.min() + i[:] = numpy.clip(i, minheight, maxheight) + + i[:] = ((i-minheight))/(maxheight-minheight) + i[:] **= exponent + + +def vert(column, row, z, XYscaling, Zscaling): + """ Create a single vert """ + return column * XYscaling, row * XYscaling, z * Zscaling + + +def buildMesh(mesh_z, br): + global rows + global size + scale = 1 + scalez = 1 + decimateRatio = br.decimate_ratio # get variable from interactive table + bpy.ops.object.select_all(action='DESELECT') + for object in bpy.data.objects: + if re.search("BasReliefMesh", str(object)): + bpy.data.objects.remove(object) + print("old basrelief removed") + + print("Building mesh") + numY = mesh_z.shape[1] + numX = mesh_z.shape[0] + print(numX, numY) + + verts = list() + faces = list() + + for i, row in enumerate(mesh_z): + for j, col in enumerate(row): + verts.append(vert(i, j, col, scale, scalez)) + + count = 0 + for i in range(0, numY * (numX-1)): + if count < numY-1: + A = i # the first vertex + B = i+1 # the second vertex + C = (i+numY)+1 # the third vertex + D = (i+numY) # the fourth vertex + + face = (A, B, C, D) + faces.append(face) + count = count + 1 + else: + count = 0 + + # Create Mesh Datablock + mesh = bpy.data.meshes.new("displacement") + mesh.from_pydata(verts, [], faces) + + mesh.update() + + # make object from mesh + new_object = bpy.data.objects.new('BasReliefMesh', mesh) + scene = bpy.context.scene + scene.collection.objects.link(new_object) + + # mesh object is made - preparing to decimate. + ob = bpy.data.objects['BasReliefMesh'] + ob.select_set(True) + bpy.context.view_layer.objects.active = ob + bpy.context.active_object.dimensions = (br.widthmm/1000, br.heightmm/1000, br.thicknessmm/1000) + bpy.context.active_object.location = (float( + br.justifyx)*br.widthmm/1000, float(br.justifyy)*br.heightmm/1000, float(br.justifyz)*br.thicknessmm/1000) + + print("faces:" + str(len(ob.data.polygons))) + print("vertices:" + str(len(ob.data.vertices))) + if decimateRatio > 0.95: + print("skipping decimate ratio > 0.95") + else: + m = ob.modifiers.new(name="Foo", type='DECIMATE') + m.ratio = decimateRatio + print("decimating with ratio:"+str(decimateRatio)) + bpy.ops.object.modifier_apply(modifier=m.name) + print("decimated") + print("faces:" + str(len(ob.data.polygons))) + print("vertices:" + str(len(ob.data.vertices))) - p=numpy.zeros((cols,rows)) - pp=numpy.zeros((cols,rows)) - r=numpy.zeros((cols,rows)) - rr=numpy.zeros((cols,rows)) - z=numpy.zeros((cols,rows)) - zz=numpy.zeros((cols,rows)) +# Switches to cycles render to CYCLES to render the sceen then switches it back to BLENDERCAM_RENDER for basRelief - iter=0 - atimes(x,r) - r[:]=b-r - rr[:]=r - - atimes(r,rr) # minimum residual - znrm=1.0 - - if (itol == 1): - bnrm=snrm(n,b,itol) - - elif (itol == 2): - asolve(b,z) - bnrm=snrm(n,z,itol) - - elif (itol == 3 or itol == 4): - asolve(b,z) - bnrm=snrm(n,z,itol) - asolve(r,z) - znrm=snrm(n,z,itol) - else: - print("illegal itol in linbcg") - - asolve(r,z) - - while (iter <= itmax): - #print('linbcg iteration:', str(iter)) - iter+=1 - zm1nrm=znrm - asolve(rr,zz) - - bknum=0.0 - - temp=z*rr - - bknum=temp.sum()#-z[0]*rr[0]???? - - if (iter == 1): - p[:]=z - pp[:]=zz - - else: - bk=bknum/bkden - p=bk*p+z - pp=bk*pp+zz - bkden=bknum - atimes(p,z) - temp=z*pp - akden = temp.sum() - ak=bknum/akden - atimes(pp,zz) - - x+=ak*p - r-= ak*z - rr -= ak*zz - - asolve(r,z) - - if (itol == 1 or itol == 2): - znrm=1.0 - err=snrm(n,r,itol)/bnrm - elif (itol == 3 or itol == 4): - znrm=snrm(n,z,itol) - if (abs(zm1nrm-znrm) > EPS*znrm): - dxnrm=abs(ak)*snrm(n,p,itol) - err=znrm/abs(zm1nrm-znrm)*dxnrm - else: - err=znrm/bnrm - continue - xnrm=snrm(n,x,itol) - - if (err <= 0.5*xnrm): - err /= xnrm - else: - err=znrm/bnrm - continue - if (err <= tol): - break - #if PLANAR_CONST and planar.shape==rr.shape: - # x[planar]=0.0 - - -#-------------------------------------------------------------------- - - -def numpysave(a,iname): - inamebase=bpy.path.basename(iname) - - i=numpytoimage(a,inamebase) - - r=bpy.context.scene.render - - r.image_settings.file_format='OPEN_EXR' - r.image_settings.color_mode='BW' - r.image_settings.color_depth='32' - - i.save_render(iname) - -def numpytoimage(a,iname): - t=time.time() - print('numpy to image - here') - t=time.time() - print(a.shape[0],a.shape[1]) - foundimage=False - for image in bpy.data.images: - - if image.name[:len(iname)]==iname and image.size[0]==a.shape[0] and image.size[1]==a.shape[1]: - i=image - foundimage=True - if not foundimage: - bpy.ops.image.new(name=iname, width=a.shape[0], height=a.shape[1], color=(0, 0, 0, 1), alpha=True, generated_type='BLANK', float=True) - for image in bpy.data.images: - - if image.name[:len(iname)]==iname and image.size[0]==a.shape[0] and image.size[1]==a.shape[1]: - i=image - - d=a.shape[0]*a.shape[1] - a=a.swapaxes(0,1) - a=a.reshape(d) - a=a.repeat(4) - a[3::4]=1 - #i.pixels=a - i.pixels[:]=a[:]#this gives big speedup! - print('\ntime '+str(time.time()-t)) - return i +def renderScene(width, height, bit_diameter, passes_per_radius, make_nodes, view_layer): + print("rendering scene") + scene = bpy.context.scene + # make sure we're in object mode or else bad things happen + if bpy.context.active_object: + bpy.ops.object.mode_set(mode='OBJECT') + + scene.render.engine = 'CYCLES' + our_viewer = None + our_renderer = None + if make_nodes: + # make depth render node and viewer node + if scene.use_nodes == False: + scene.use_nodes = True + node_tree = scene.node_tree + nodes = node_tree.nodes + our_viewer = node_tree.nodes.new(type='CompositorNodeViewer') + our_viewer.label = "CAM_basrelief_viewer" + our_renderer = node_tree.nodes.new(type='CompositorNodeRLayers') + our_renderer.label = "CAM_basrelief_renderlayers" + our_renderer.layer = view_layer + node_tree.links.new(our_renderer.outputs[our_renderer.outputs.find( + 'Depth')], our_viewer.inputs[our_viewer.inputs.find("Image")]) + scene.view_layers[view_layer].use_pass_z = True + # set our viewer as active so that it is what gets rendered to viewer node image + nodes.active = our_viewer + + # Set render resolution + passes = bit_diameter/(2*passes_per_radius) + x = round(width/passes) + y = round(height/passes) + print(x, y, passes) + scene.render.resolution_x = x + scene.render.resolution_y = y + scene.render.resolution_percentage = 100 + bpy.ops.render.render(animation=False, write_still=False, use_viewport=True, layer="", scene="") + if our_renderer is not None: + nodes.remove(our_renderer) + if our_viewer is not None: + nodes.remove(our_viewer) + bpy.context.scene.render.engine = 'BLENDERCAM_RENDER' + print("done rendering") -def imagetonumpy(i): - t=time.time() - inc=0 - - width=i.size[0] - height=i.size[1] - x=0 - y=0 - count=0 - na=numpy.array((0.1),dtype=float) - - size=width*height - na.resize(size*4) - - p=i.pixels[:]#these 2 lines are about 15% faster than na=i.pixels[:].... whyyyyyyyy!!?!?!?!?! Blender image data access is evil. - na[:]=p - #na=numpy.array(i.pixels[:])#this was terribly slow... at least I know why now, it probably - na=na[::4] - na=na.reshape(height,width) - na=na.swapaxes(0,1) - - print('\ntime of image to numpy '+str(time.time()-t)) - return na - -def tonemap(i,exponent): - # if depth buffer never got written it gets set - # to a great big value (10000000000.0) - # filter out anything within an order of magnitude of it - # so we only have things that are actually drawn - maxheight=i.max(where=i<1000000000.0,initial=0) - minheight=i.min() - i[:]=numpy.clip(i,minheight,maxheight) - - i[:]=((i-minheight))/(maxheight-minheight) - i[:]**=exponent - -def vert(column, row, z,XYscaling,Zscaling): - """ Create a single vert """ - return column * XYscaling, row * XYscaling, z * Zscaling - -def buildMesh(mesh_z,br): - global rows - global size - scale=1 - scalez=1 - decimateRatio= br.decimate_ratio #get variable from interactive table - bpy.ops.object.select_all(action='DESELECT') - for object in bpy.data.objects: - if re.search("BasReliefMesh",str(object)): - bpy.data.objects.remove(object) - print("old basrelief removed") - - - - print("Building mesh") - numY = mesh_z.shape[1] - numX = mesh_z.shape[0] - print(numX,numY) - - verts = list() - faces = list() - - for i, row in enumerate(mesh_z): - for j, col in enumerate(row): - verts.append(vert(i, j, col,scale,scalez)) - - count = 0 - for i in range (0, numY *(numX-1)): - if count < numY-1: - A = i # the first vertex - B = i+1 # the second vertex - C = (i+numY)+1 # the third vertex - D = (i+numY) # the fourth vertex - - face = (A,B,C,D) - faces.append(face) - count = count + 1 - else: - count = 0 - - # Create Mesh Datablock - mesh = bpy.data.meshes.new("displacement") - mesh.from_pydata(verts, [], faces) - - mesh.update() - - # make object from mesh - new_object = bpy.data.objects.new('BasReliefMesh', mesh) - scene = bpy.context.scene - scene.collection.objects.link(new_object) - - #mesh object is made - preparing to decimate. - ob=bpy.data.objects['BasReliefMesh'] - ob.select_set(True) - bpy.context.view_layer.objects.active = ob - bpy.context.active_object.dimensions= (br.widthmm/1000,br.heightmm/1000,br.thicknessmm/1000) - bpy.context.active_object.location= (float(br.justifyx)*br.widthmm/1000,float(br.justifyy)*br.heightmm/1000,float(br.justifyz)*br.thicknessmm/1000) - - - print("faces:" + str(len(ob.data.polygons))) - print("vertices:" + str(len(ob.data.vertices))) - if decimateRatio > 0.95: - print("skipping decimate ratio > 0.95") - else: - m = ob.modifiers.new(name="Foo", type='DECIMATE') - m.ratio=decimateRatio - print("decimating with ratio:"+str(decimateRatio)) - bpy.ops.object.modifier_apply(modifier=m.name) - print("decimated") - print("faces:" + str(len(ob.data.polygons))) - print("vertices:" + str(len(ob.data.vertices))) - -# Switches to cycles render to CYCLES to render the sceen then switches it back to BLENDERCAM_RENDER for basRelief -def renderScene(width,height,bit_diameter,passes_per_radius,make_nodes,view_layer): - print("rendering scene") - scene = bpy.context.scene - # make sure we're in object mode or else bad things happen - if bpy.context.active_object: - bpy.ops.object.mode_set(mode='OBJECT') - - scene.render.engine = 'CYCLES' - our_viewer=None - our_renderer=None - if make_nodes: - # make depth render node and viewer node - if scene.use_nodes==False: - scene.use_nodes=True - node_tree=scene.node_tree - nodes=node_tree.nodes - our_viewer=node_tree.nodes.new(type = 'CompositorNodeViewer') - our_viewer.label="CAM_basrelief_viewer" - our_renderer=node_tree.nodes.new(type= 'CompositorNodeRLayers') - our_renderer.label="CAM_basrelief_renderlayers" - our_renderer.layer=view_layer - node_tree.links.new(our_renderer.outputs[our_renderer.outputs.find('Depth')],our_viewer.inputs[our_viewer.inputs.find("Image")]) - scene.view_layers[view_layer].use_pass_z=True - # set our viewer as active so that it is what gets rendered to viewer node image - nodes.active=our_viewer - - # Set render resolution - passes=bit_diameter/(2*passes_per_radius) - x=round(width/passes) - y=round(height/passes) - print(x,y,passes) - scene.render.resolution_x = x - scene.render.resolution_y = y - scene.render.resolution_percentage = 100 - bpy.ops.render.render(animation=False, write_still=False, use_viewport=True, layer="", scene="") - if our_renderer is not None: - nodes.remove(our_renderer) - if our_viewer is not None: - nodes.remove(our_viewer) - bpy.context.scene.render.engine = 'BLENDERCAM_RENDER' - print("done rendering") - - def problemAreas(br): - t=time.time() - if br.use_image_source: - i=bpy.data.images[br.source_image_name] - else: - i=bpy.data.images["Viewer Node"] - silh_thres=br.silhouette_threshold - recover_silh=br.recover_silhouettes - silh_scale=br.silhouette_scale - MINS=br.min_gridsize - smoothiterations=br.smooth_iterations - vcycleiterations=br.vcycle_iterations - linbcgiterations=br.linbcg_iterations - useplanar=br.use_planar - #scale down before: - if br.gradient_scaling_mask_use: - m=bpy.data.images[br.gradient_scaling_mask_name] - #mask=nar=imagetonumpy(m) - - #if br.scale_down_before_use: - # i.scale(int(i.size[0]*br.scale_down_before),int(i.size[1]*br.scale_down_before)) - # if br.gradient_scaling_mask_use: - # m.scale(int(m.size[0]*br.scale_down_before),int(m.size[1]*br.scale_down_before)) - - nar=imagetonumpy(i) - #return - if br.gradient_scaling_mask_use: - mask=imagetonumpy(m) - #put image to scale - tonemap(nar,br.depth_exponent) - nar=1-nar# reverse z buffer+ add something - print(nar.min(),nar.max()) - gx=nar.copy() - gx.fill(0) - gx[:-1,:]=nar[1:,:]-nar[:-1,:] - gy=nar.copy() - gy.fill(0) - gy[:,:-1]=nar[:,1:]-nar[:,:-1] - - #it' ok, we can treat neg and positive silh separately here: - a=br.attenuation - planar=nar<(nar.min()+0.0001)#numpy.logical_or(silhxplanar,silhyplanar)# - #sqrt for silhouettes recovery: - sqrarx=numpy.abs(gx) - for iter in range(0,br.silhouette_exponent): - sqrarx=numpy.sqrt(sqrarx) - sqrary=numpy.abs(gy) - for iter in range(0,br.silhouette_exponent): - sqrary=numpy.sqrt(sqrary) - - - #detect and also recover silhouettes: - silhxpos=gx>silh_thres - gx=gx*(-silhxpos)+recover_silh*(silhxpos*silh_thres*silh_scale)*sqrarx - silhxneg=gx<-silh_thres - gx=gx*(-silhxneg)-recover_silh*(silhxneg*silh_thres*silh_scale)*sqrarx - silhx=numpy.logical_or(silhxpos,silhxneg) - gx=gx*silhx+(1.0/a*numpy.log(1.+a*(gx)))*(-silhx)#attenuate - - #if br.fade_distant_objects: - # gx*=(nar) - # gy*=(nar) - - silhypos=gy>silh_thres - gy=gy*(-silhypos)+recover_silh*(silhypos*silh_thres*silh_scale)*sqrary - silhyneg=gy<-silh_thres - gy=gy*(-silhyneg)-recover_silh*(silhyneg*silh_thres*silh_scale)*sqrary - silhy=numpy.logical_or(silhypos,silhyneg)#both silh - gy=gy*silhy+(1.0/a*numpy.log(1.+a*(gy)))*(-silhy)#attenuate - - #now scale slopes... - if br.gradient_scaling_mask_use: - gx*=mask - gy*=mask - - - divg=gx+gy - divga=numpy.abs(divg) - divgp= divga>silh_thres/4.0 - divgp=1-divgp - for a in range(0,2): - atimes(divgp,divga) - divga=divgp - - numpytoimage(divga,'problem') + t = time.time() + if br.use_image_source: + i = bpy.data.images[br.source_image_name] + else: + i = bpy.data.images["Viewer Node"] + silh_thres = br.silhouette_threshold + recover_silh = br.recover_silhouettes + silh_scale = br.silhouette_scale + MINS = br.min_gridsize + smoothiterations = br.smooth_iterations + vcycleiterations = br.vcycle_iterations + linbcgiterations = br.linbcg_iterations + useplanar = br.use_planar + # scale down before: + if br.gradient_scaling_mask_use: + m = bpy.data.images[br.gradient_scaling_mask_name] + # mask=nar=imagetonumpy(m) + + # if br.scale_down_before_use: + # i.scale(int(i.size[0]*br.scale_down_before),int(i.size[1]*br.scale_down_before)) + # if br.gradient_scaling_mask_use: + # m.scale(int(m.size[0]*br.scale_down_before),int(m.size[1]*br.scale_down_before)) + + nar = imagetonumpy(i) + # return + if br.gradient_scaling_mask_use: + mask = imagetonumpy(m) + # put image to scale + tonemap(nar, br.depth_exponent) + nar = 1-nar # reverse z buffer+ add something + print(nar.min(), nar.max()) + gx = nar.copy() + gx.fill(0) + gx[:-1, :] = nar[1:, :]-nar[:-1, :] + gy = nar.copy() + gy.fill(0) + gy[:, :-1] = nar[:, 1:]-nar[:, :-1] + + # it' ok, we can treat neg and positive silh separately here: + a = br.attenuation + planar = nar < (nar.min()+0.0001) # numpy.logical_or(silhxplanar,silhyplanar)# + # sqrt for silhouettes recovery: + sqrarx = numpy.abs(gx) + for iter in range(0, br.silhouette_exponent): + sqrarx = numpy.sqrt(sqrarx) + sqrary = numpy.abs(gy) + for iter in range(0, br.silhouette_exponent): + sqrary = numpy.sqrt(sqrary) + + # detect and also recover silhouettes: + silhxpos = gx > silh_thres + gx = gx*(-silhxpos)+recover_silh*(silhxpos*silh_thres*silh_scale)*sqrarx + silhxneg = gx < -silh_thres + gx = gx*(-silhxneg)-recover_silh*(silhxneg*silh_thres*silh_scale)*sqrarx + silhx = numpy.logical_or(silhxpos, silhxneg) + gx = gx*silhx+(1.0/a*numpy.log(1.+a*(gx)))*(-silhx) # attenuate + + # if br.fade_distant_objects: + # gx*=(nar) + # gy*=(nar) + + silhypos = gy > silh_thres + gy = gy*(-silhypos)+recover_silh*(silhypos*silh_thres*silh_scale)*sqrary + silhyneg = gy < -silh_thres + gy = gy*(-silhyneg)-recover_silh*(silhyneg*silh_thres*silh_scale)*sqrary + silhy = numpy.logical_or(silhypos, silhyneg) # both silh + gy = gy*silhy+(1.0/a*numpy.log(1.+a*(gy)))*(-silhy) # attenuate + + # now scale slopes... + if br.gradient_scaling_mask_use: + gx *= mask + gy *= mask + + divg = gx+gy + divga = numpy.abs(divg) + divgp = divga > silh_thres/4.0 + divgp = 1-divgp + for a in range(0, 2): + atimes(divgp, divga) + divga = divgp + + numpytoimage(divga, 'problem') def relief(br): - t=time.time() - - if br.use_image_source: - i=bpy.data.images[br.source_image_name] - else: - i=bpy.data.images["Viewer Node"] - silh_thres=br.silhouette_threshold - recover_silh=br.recover_silhouettes - silh_scale=br.silhouette_scale - MINS=br.min_gridsize - smoothiterations=br.smooth_iterations - vcycleiterations=br.vcycle_iterations - linbcgiterations=br.linbcg_iterations - useplanar=br.use_planar - #scale down before: - if br.gradient_scaling_mask_use: - m=bpy.data.images[br.gradient_scaling_mask_name] - #mask=nar=imagetonumpy(m) - - #if br.scale_down_before_use: - # i.scale(int(i.size[0]*br.scale_down_before),int(i.size[1]*br.scale_down_before)) - # if br.gradient_scaling_mask_use: - # m.scale(int(m.size[0]*br.scale_down_before),int(m.size[1]*br.scale_down_before)) - - nar=imagetonumpy(i) - #return - if br.gradient_scaling_mask_use: - mask=imagetonumpy(m) - #put image to scale - tonemap(nar,br.depth_exponent) - nar=1-nar# reverse z buffer+ add something - print("Range:",nar.min(),nar.max()) - if nar.min() - nar.max() ==0: - raise ReliefError("Input image is blank - check you have the correct view layer or input image set.") - - gx=nar.copy() - gx.fill(0) - gx[:-1,:]=nar[1:,:]-nar[:-1,:] - gy=nar.copy() - gy.fill(0) - gy[:,:-1]=nar[:,1:]-nar[:,:-1] - - #it' ok, we can treat neg and positive silh separately here: - a=br.attenuation - planar=nar<(nar.min()+0.0001)#numpy.logical_or(silhxplanar,silhyplanar)# - #sqrt for silhouettes recovery: - sqrarx=numpy.abs(gx) - for iter in range(0,br.silhouette_exponent): - sqrarx=numpy.sqrt(sqrarx) - sqrary=numpy.abs(gy) - for iter in range(0,br.silhouette_exponent): - sqrary=numpy.sqrt(sqrary) - - - #detect and also recover silhouettes: - silhxpos=gx>silh_thres - print("*** silhxpos is %s" %silhxpos) - gx=gx*(~silhxpos)+recover_silh*(silhxpos*silh_thres*silh_scale)*sqrarx - silhxneg=gx<-silh_thres - gx=gx*(~silhxneg)-recover_silh*(silhxneg*silh_thres*silh_scale)*sqrarx - silhx=numpy.logical_or(silhxpos,silhxneg) - gx=gx*silhx+(1.0/a*numpy.log(1.+a*(gx)))*(~silhx)#attenuate - - #if br.fade_distant_objects: - # gx*=(nar) - # gy*=(nar) - - silhypos=gy>silh_thres - gy=gy*(~silhypos)+recover_silh*(silhypos*silh_thres*silh_scale)*sqrary - silhyneg=gy<-silh_thres - gy=gy*(~silhyneg)-recover_silh*(silhyneg*silh_thres*silh_scale)*sqrary - silhy=numpy.logical_or(silhypos,silhyneg)#both silh - gy=gy*silhy+(1.0/a*numpy.log(1.+a*(gy)))*(~silhy)#attenuate - - #now scale slopes... - if br.gradient_scaling_mask_use: - gx*=mask - gy*=mask - - # - #print(silhx) - #silhx=abs(gx)>silh_thres - #gx=gx*(-silhx) - #silhy=abs(gy)>silh_thres - #gy=gy*(-silhy) - - - divg=gx+gy - divg[1:,:]=divg[1:,:]-gx[:-1,:] #subtract x - divg[:,1:]=divg[:,1:]-gy[:,:-1] #subtract y - - if br.detail_enhancement_use:# fourier stuff here!disabled by now - print("detail enhancement") - rows,cols=gx.shape - crow,ccol = int(rows/2), int(cols/2) - #dist=int(br.detail_enhancement_freq*gx.shape[0]/(2)) - #bandwidth=.1 - #dist= - divgmin=divg.min() - divg+=divgmin - divgf=numpy.fft.fft2(divg) - divgfshift=numpy.fft.fftshift(divgf) - #mspectrum = 20*numpy.log(numpy.abs(divgfshift)) - #numpytoimage(mspectrum,'mspectrum') - mask=divg.copy() - pos=numpy.array((crow,ccol)) - - #bpy.context.scene.view_settings.curve_mapping.initialize() - #cur=bpy.context.scene.view_settings.curve_mapping.curves[0] - def filterwindow(x,y, cx = 0, cy = 0):#, curve=None): - return abs((cx-x))+abs((cy-y)) - #v=(abs((cx-x)/(cx))+abs((cy-y)/(cy))) - #return v - - mask=numpy.fromfunction(filterwindow,divg.shape, cx=crow, cy=ccol)#, curve=cur) - mask=numpy.sqrt(mask) - #for x in range(mask.shape[0]): - # for y in range(mask.shape[1]): - # mask[x,y]=cur.evaluate(mask[x,y]) - maskmin=mask.min() - maskmax=mask.max() - mask=(mask-maskmin)/(maskmax-maskmin) - mask*=br.detail_enhancement_amount - mask+=1-mask.max() - #mask+=1 - mask[crow-1:crow+1,ccol-1:ccol+1]=1#to preserve basic freqencies. - #numpytoimage(mask,'mask') - divgfshift = divgfshift*mask - divgfshift = numpy.fft.ifftshift(divgfshift) - divg = numpy.abs(numpy.fft.ifft2(divgfshift)) - divg-=divgmin - divg=-divg - print("detail enhancement finished") - - levels=0 - mins = min(nar.shape[0],nar.shape[1]) - while (mins>=MINS): - levels+=1 - mins = mins/2 - - - target=numpy.zeros_like(divg) - - solve_pde_multigrid( divg, target ,vcycleiterations, linbcgiterations, smoothiterations, mins, levels, useplanar, planar) - - tonemap(target,1) - - buildMesh(target,br) + t = time.time() + + if br.use_image_source: + i = bpy.data.images[br.source_image_name] + else: + i = bpy.data.images["Viewer Node"] + silh_thres = br.silhouette_threshold + recover_silh = br.recover_silhouettes + silh_scale = br.silhouette_scale + MINS = br.min_gridsize + smoothiterations = br.smooth_iterations + vcycleiterations = br.vcycle_iterations + linbcgiterations = br.linbcg_iterations + useplanar = br.use_planar + # scale down before: + if br.gradient_scaling_mask_use: + m = bpy.data.images[br.gradient_scaling_mask_name] + # mask=nar=imagetonumpy(m) + + # if br.scale_down_before_use: + # i.scale(int(i.size[0]*br.scale_down_before),int(i.size[1]*br.scale_down_before)) + # if br.gradient_scaling_mask_use: + # m.scale(int(m.size[0]*br.scale_down_before),int(m.size[1]*br.scale_down_before)) + + nar = imagetonumpy(i) + # return + if br.gradient_scaling_mask_use: + mask = imagetonumpy(m) + # put image to scale + tonemap(nar, br.depth_exponent) + nar = 1-nar # reverse z buffer+ add something + print("Range:", nar.min(), nar.max()) + if nar.min() - nar.max() == 0: + raise ReliefError( + "Input image is blank - check you have the correct view layer or input image set.") + + gx = nar.copy() + gx.fill(0) + gx[:-1, :] = nar[1:, :]-nar[:-1, :] + gy = nar.copy() + gy.fill(0) + gy[:, :-1] = nar[:, 1:]-nar[:, :-1] + + # it' ok, we can treat neg and positive silh separately here: + a = br.attenuation + planar = nar < (nar.min()+0.0001) # numpy.logical_or(silhxplanar,silhyplanar)# + # sqrt for silhouettes recovery: + sqrarx = numpy.abs(gx) + for iter in range(0, br.silhouette_exponent): + sqrarx = numpy.sqrt(sqrarx) + sqrary = numpy.abs(gy) + for iter in range(0, br.silhouette_exponent): + sqrary = numpy.sqrt(sqrary) + + # detect and also recover silhouettes: + silhxpos = gx > silh_thres + print("*** silhxpos is %s" % silhxpos) + gx = gx*(~silhxpos)+recover_silh*(silhxpos*silh_thres*silh_scale)*sqrarx + silhxneg = gx < -silh_thres + gx = gx*(~silhxneg)-recover_silh*(silhxneg*silh_thres*silh_scale)*sqrarx + silhx = numpy.logical_or(silhxpos, silhxneg) + gx = gx*silhx+(1.0/a*numpy.log(1.+a*(gx)))*(~silhx) # attenuate + + # if br.fade_distant_objects: + # gx*=(nar) + # gy*=(nar) + + silhypos = gy > silh_thres + gy = gy*(~silhypos)+recover_silh*(silhypos*silh_thres*silh_scale)*sqrary + silhyneg = gy < -silh_thres + gy = gy*(~silhyneg)-recover_silh*(silhyneg*silh_thres*silh_scale)*sqrary + silhy = numpy.logical_or(silhypos, silhyneg) # both silh + gy = gy*silhy+(1.0/a*numpy.log(1.+a*(gy)))*(~silhy) # attenuate + + # now scale slopes... + if br.gradient_scaling_mask_use: + gx *= mask + gy *= mask + + # + # print(silhx) + # silhx=abs(gx)>silh_thres + # gx=gx*(-silhx) + # silhy=abs(gy)>silh_thres + # gy=gy*(-silhy) + + divg = gx+gy + divg[1:, :] = divg[1:, :]-gx[:-1, :] # subtract x + divg[:, 1:] = divg[:, 1:]-gy[:, :-1] # subtract y + + if br.detail_enhancement_use: # fourier stuff here!disabled by now + print("detail enhancement") + rows, cols = gx.shape + crow, ccol = int(rows/2), int(cols/2) + # dist=int(br.detail_enhancement_freq*gx.shape[0]/(2)) + # bandwidth=.1 + # dist= + divgmin = divg.min() + divg += divgmin + divgf = numpy.fft.fft2(divg) + divgfshift = numpy.fft.fftshift(divgf) + #mspectrum = 20*numpy.log(numpy.abs(divgfshift)) + # numpytoimage(mspectrum,'mspectrum') + mask = divg.copy() + pos = numpy.array((crow, ccol)) + + # bpy.context.scene.view_settings.curve_mapping.initialize() + # cur=bpy.context.scene.view_settings.curve_mapping.curves[0] + def filterwindow(x, y, cx=0, cy=0): # , curve=None): + return abs((cx-x))+abs((cy-y)) + # v=(abs((cx-x)/(cx))+abs((cy-y)/(cy))) + # return v + + mask = numpy.fromfunction(filterwindow, divg.shape, cx=crow, cy=ccol) # , curve=cur) + mask = numpy.sqrt(mask) + # for x in range(mask.shape[0]): + # for y in range(mask.shape[1]): + # mask[x,y]=cur.evaluate(mask[x,y]) + maskmin = mask.min() + maskmax = mask.max() + mask = (mask-maskmin)/(maskmax-maskmin) + mask *= br.detail_enhancement_amount + mask += 1-mask.max() + # mask+=1 + mask[crow-1:crow+1, ccol-1:ccol+1] = 1 # to preserve basic freqencies. + # numpytoimage(mask,'mask') + divgfshift = divgfshift*mask + divgfshift = numpy.fft.ifftshift(divgfshift) + divg = numpy.abs(numpy.fft.ifft2(divgfshift)) + divg -= divgmin + divg = -divg + print("detail enhancement finished") + + levels = 0 + mins = min(nar.shape[0], nar.shape[1]) + while (mins >= MINS): + levels += 1 + mins = mins/2 + + target = numpy.zeros_like(divg) + + solve_pde_multigrid(divg, target, vcycleiterations, linbcgiterations, + smoothiterations, mins, levels, useplanar, planar) + + tonemap(target, 1) + + buildMesh(target, br) # ipath=bpy.path.abspath(i.filepath)[:-len(bpy.path.basename(i.filepath))]+br.output_image_name+'.exr' # numpysave(target,ipath) - t=time.time()-t - print('total time:'+ str(t)+'\n') - #numpytoimage(target,br.output_image_name) + t = time.time()-t + print('total time:' + str(t)+'\n') + # numpytoimage(target,br.output_image_name) class BasReliefsettings(bpy.types.PropertyGroup): - use_image_source: bpy.props.BoolProperty(name="Use image source",description="", default=False) - source_image_name: bpy.props.StringProperty(name='Image source', description='image source') - view_layer_name: bpy.props.StringProperty(name='View layer source',description='Make a bas-relief from whatever is on this view layer') - bit_diameter: FloatProperty(name="Diameter of ball end in mm", description="Diameter of bit which will be used for carving", min=0.01, max=50.0, default=3.175, precision=PRECISION) - pass_per_radius: bpy.props.IntProperty(name="Passes per radius", description="Amount of passes per radius\n(more passes, more mesh precision)",default=2, min=1, max=10) - widthmm: bpy.props.IntProperty(name="Desired width in mm", default=200, min=5, max=4000) - heightmm: bpy.props.IntProperty(name="Desired height in mm", default=150, min=5, max=4000) - thicknessmm: bpy.props.IntProperty(name="Thickness in mm", default=15, min=5, max=100) - - justifyx: bpy.props.EnumProperty(name="X",items=[('1', 'Left','', 0),('-0.5', 'Centered','', 1),('-1', 'Right','', 2)],default='-1') - justifyy: bpy.props.EnumProperty(name="Y",items=[('1', 'Bottom','', 0),('-0.5', 'Centered','', 2),('-1', 'Top','', 1),],default='-1') - justifyz: bpy.props.EnumProperty(name="Z",items=[('-1', 'Below 0','', 0),('-0.5', 'Centered','', 2),('1', 'Above 0','', 1),],default='-1') - - depth_exponent: FloatProperty(name="Depth exponent", description="Initial depth map is taken to this power. Higher = sharper relief",min=0.5,max=10.0,default=1.0,precision=PRECISION) - - silhouette_threshold: FloatProperty(name="Silhouette threshold", description="Silhouette threshold", min=0.000001, max=1.0, default=0.003, precision=PRECISION) - recover_silhouettes: bpy.props.BoolProperty(name="Recover silhouettes",description="", default=True) - silhouette_scale: FloatProperty(name="Silhouette scale", description="Silhouette scale", min=0.000001, max=5.0, default=0.3, precision=PRECISION) - silhouette_exponent: bpy.props.IntProperty(name="Silhouette square exponent", description="If lower, true depht distances between objects will be more visibe in the relief", default=3, min=0, max=5) - attenuation: FloatProperty(name="Gradient attenuation", description="Gradient attenuation", min=0.000001, max=100.0, default=1.0, precision=PRECISION) - min_gridsize: bpy.props.IntProperty(name="Minimum grid size", default=16, min=2, max=512) - smooth_iterations: bpy.props.IntProperty(name="Smooth iterations", default=1, min=1, max=64) - vcycle_iterations: bpy.props.IntProperty(name="V-cycle iterations",description="set up higher for plananr constraint", default=2, min=1, max=128) - linbcg_iterations: bpy.props.IntProperty(name="Linbcg iterations",description="set lower for flatter relief, and when using planar constraint", default=5, min=1, max=64) - use_planar: bpy.props.BoolProperty(name="Use planar constraint",description="", default=False) - gradient_scaling_mask_use: bpy.props.BoolProperty(name="Scale gradients with mask",description="", default=False) - decimate_ratio: FloatProperty(name="Decimate Ratio", description="Simplify the mesh using the Decimate modifier. The lower the value the more simplyfied", min=0.01, max=1.0, default=0.1, precision=PRECISION) - - - gradient_scaling_mask_name: bpy.props.StringProperty(name='Scaling mask name', description='mask name') - scale_down_before_use: bpy.props.BoolProperty(name="Scale down image before processing",description="", default=False) - scale_down_before: FloatProperty(name="Image scale", description="Image scale", min=0.025, max=1.0, default=.5, precision=PRECISION) - detail_enhancement_use: bpy.props.BoolProperty(name="Enhance details ",description="enhance details by frequency analysis", default=False) - #detail_enhancement_freq=FloatProperty(name="frequency limit", description="Image scale", min=0.025, max=1.0, default=.5, precision=PRECISION) - detail_enhancement_amount: FloatProperty(name="amount", description="Image scale", min=0.025, max=1.0, default=.5, precision=PRECISION) - - advanced: bpy.props.BoolProperty(name="Advanced options",description="show advanced options", default=True) + use_image_source: bpy.props.BoolProperty(name="Use image source", description="", default=False) + source_image_name: bpy.props.StringProperty(name='Image source', description='image source') + view_layer_name: bpy.props.StringProperty( + name='View layer source', description='Make a bas-relief from whatever is on this view layer') + bit_diameter: FloatProperty(name="Diameter of ball end in mm", description="Diameter of bit which will be used for carving", + min=0.01, max=50.0, default=3.175, precision=PRECISION) + pass_per_radius: bpy.props.IntProperty( + name="Passes per radius", description="Amount of passes per radius\n(more passes, more mesh precision)", default=2, min=1, max=10) + widthmm: bpy.props.IntProperty(name="Desired width in mm", default=200, min=5, max=4000) + heightmm: bpy.props.IntProperty(name="Desired height in mm", default=150, min=5, max=4000) + thicknessmm: bpy.props.IntProperty(name="Thickness in mm", default=15, min=5, max=100) + + justifyx: bpy.props.EnumProperty(name="X", items=[( + '1', 'Left', '', 0), ('-0.5', 'Centered', '', 1), ('-1', 'Right', '', 2)], default='-1') + justifyy: bpy.props.EnumProperty(name="Y", items=[( + '1', 'Bottom', '', 0), ('-0.5', 'Centered', '', 2), ('-1', 'Top', '', 1), ], default='-1') + justifyz: bpy.props.EnumProperty(name="Z", items=[( + '-1', 'Below 0', '', 0), ('-0.5', 'Centered', '', 2), ('1', 'Above 0', '', 1), ], default='-1') + + depth_exponent: FloatProperty(name="Depth exponent", description="Initial depth map is taken to this power. Higher = sharper relief", + min=0.5, max=10.0, default=1.0, precision=PRECISION) + + silhouette_threshold: FloatProperty( + name="Silhouette threshold", description="Silhouette threshold", min=0.000001, max=1.0, default=0.003, precision=PRECISION) + recover_silhouettes: bpy.props.BoolProperty( + name="Recover silhouettes", description="", default=True) + silhouette_scale: FloatProperty(name="Silhouette scale", description="Silhouette scale", + min=0.000001, max=5.0, default=0.3, precision=PRECISION) + silhouette_exponent: bpy.props.IntProperty( + name="Silhouette square exponent", description="If lower, true depht distances between objects will be more visibe in the relief", default=3, min=0, max=5) + attenuation: FloatProperty(name="Gradient attenuation", description="Gradient attenuation", + min=0.000001, max=100.0, default=1.0, precision=PRECISION) + min_gridsize: bpy.props.IntProperty(name="Minimum grid size", default=16, min=2, max=512) + smooth_iterations: bpy.props.IntProperty(name="Smooth iterations", default=1, min=1, max=64) + vcycle_iterations: bpy.props.IntProperty( + name="V-cycle iterations", description="set up higher for plananr constraint", default=2, min=1, max=128) + linbcg_iterations: bpy.props.IntProperty( + name="Linbcg iterations", description="set lower for flatter relief, and when using planar constraint", default=5, min=1, max=64) + use_planar: bpy.props.BoolProperty(name="Use planar constraint", description="", default=False) + gradient_scaling_mask_use: bpy.props.BoolProperty( + name="Scale gradients with mask", description="", default=False) + decimate_ratio: FloatProperty(name="Decimate Ratio", description="Simplify the mesh using the Decimate modifier. The lower the value the more simplyfied", + min=0.01, max=1.0, default=0.1, precision=PRECISION) + + gradient_scaling_mask_name: bpy.props.StringProperty( + name='Scaling mask name', description='mask name') + scale_down_before_use: bpy.props.BoolProperty( + name="Scale down image before processing", description="", default=False) + scale_down_before: FloatProperty( + name="Image scale", description="Image scale", min=0.025, max=1.0, default=.5, precision=PRECISION) + detail_enhancement_use: bpy.props.BoolProperty( + name="Enhance details ", description="enhance details by frequency analysis", default=False) + #detail_enhancement_freq=FloatProperty(name="frequency limit", description="Image scale", min=0.025, max=1.0, default=.5, precision=PRECISION) + detail_enhancement_amount: FloatProperty( + name="amount", description="Image scale", min=0.025, max=1.0, default=.5, precision=PRECISION) + + advanced: bpy.props.BoolProperty( + name="Advanced options", description="show advanced options", default=True) + class BASRELIEF_Panel(bpy.types.Panel): - """Bas relief panel""" - bl_label = "Bas relief" - bl_idname = "WORLD_PT_BASRELIEF" - - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "render" - - COMPAT_ENGINES = {'BLENDERCAM_RENDER'} - - #def draw_header(self, context): - # self.layout.menu("CAM_CUTTER_MT_presets", text="CAM Cutter") - @classmethod - def poll(cls, context): - rd = context.scene.render - return rd.engine in cls.COMPAT_ENGINES - - def draw(self, context): - layout = self.layout - #print(dir(layout)) - s=bpy.context.scene - - br=s.basreliefsettings - - #if br: - #cutter preset - layout.operator("scene.calculate_bas_relief", text="Calculate relief") - layout.prop(br,'advanced') - layout.prop(br,'use_image_source') - if br.use_image_source: - layout.prop_search(br,'source_image_name', bpy.data, "images") - else: - layout.prop_search(br,'view_layer_name',bpy.context.scene,"view_layers") - layout.prop(br,'depth_exponent') - layout.label(text="Project parameters") - layout.prop(br,'bit_diameter') - layout.prop(br,'pass_per_radius') - layout.prop(br,'widthmm') - layout.prop(br,'heightmm') - layout.prop(br,'thicknessmm') - - layout.label(text="Justification") - layout.prop(br,'justifyx') - layout.prop(br,'justifyy') - layout.prop(br,'justifyz') - - layout.label(text="Silhouette") - layout.prop(br,'silhouette_threshold') - layout.prop(br,'recover_silhouettes') - if br.recover_silhouettes: - layout.prop(br,'silhouette_scale') - if br.advanced: - layout.prop(br,'silhouette_exponent') - #layout.template_curve_mapping(br,'curva') - if br.advanced: - #layout.prop(br,'attenuation') - layout.prop(br,'min_gridsize') - layout.prop(br,'smooth_iterations') - layout.prop(br,'vcycle_iterations') - layout.prop(br,'linbcg_iterations') - layout.prop(br,'use_planar') - layout.prop(br,'decimate_ratio') - - - layout.prop(br,'gradient_scaling_mask_use') - if br.advanced: - if br.gradient_scaling_mask_use: - layout.prop_search(br,'gradient_scaling_mask_name', bpy.data, "images") - layout.prop(br,'detail_enhancement_use') - if br.detail_enhancement_use: - #layout.prop(br,'detail_enhancement_freq') - layout.prop(br,'detail_enhancement_amount') - #print(dir(layout)) - #layout.prop(s.view_settings.curve_mapping,"curves") - #layout.label('Frequency scaling:') - #s.view_settings.curve_mapping.clip_max_y=2 - - #layout.template_curve_mapping(s.view_settings, "curve_mapping") - - #layout.prop(br,'scale_down_before_use') - #if br.scale_down_before_use: - # layout.prop(br,'scale_down_before') + """Bas relief panel""" + bl_label = "Bas relief" + bl_idname = "WORLD_PT_BASRELIEF" + + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "render" + + COMPAT_ENGINES = {'BLENDERCAM_RENDER'} + + # def draw_header(self, context): + # self.layout.menu("CAM_CUTTER_MT_presets", text="CAM Cutter") + @classmethod + def poll(cls, context): + rd = context.scene.render + return rd.engine in cls.COMPAT_ENGINES + + def draw(self, context): + layout = self.layout + # print(dir(layout)) + s = bpy.context.scene + + br = s.basreliefsettings + + # if br: + # cutter preset + layout.operator("scene.calculate_bas_relief", text="Calculate relief") + layout.prop(br, 'advanced') + layout.prop(br, 'use_image_source') + if br.use_image_source: + layout.prop_search(br, 'source_image_name', bpy.data, "images") + else: + layout.prop_search(br, 'view_layer_name', bpy.context.scene, "view_layers") + layout.prop(br, 'depth_exponent') + layout.label(text="Project parameters") + layout.prop(br, 'bit_diameter') + layout.prop(br, 'pass_per_radius') + layout.prop(br, 'widthmm') + layout.prop(br, 'heightmm') + layout.prop(br, 'thicknessmm') + + layout.label(text="Justification") + layout.prop(br, 'justifyx') + layout.prop(br, 'justifyy') + layout.prop(br, 'justifyz') + + layout.label(text="Silhouette") + layout.prop(br, 'silhouette_threshold') + layout.prop(br, 'recover_silhouettes') + if br.recover_silhouettes: + layout.prop(br, 'silhouette_scale') + if br.advanced: + layout.prop(br, 'silhouette_exponent') + # layout.template_curve_mapping(br,'curva') + if br.advanced: + # layout.prop(br,'attenuation') + layout.prop(br, 'min_gridsize') + layout.prop(br, 'smooth_iterations') + layout.prop(br, 'vcycle_iterations') + layout.prop(br, 'linbcg_iterations') + layout.prop(br, 'use_planar') + layout.prop(br, 'decimate_ratio') + + layout.prop(br, 'gradient_scaling_mask_use') + if br.advanced: + if br.gradient_scaling_mask_use: + layout.prop_search(br, 'gradient_scaling_mask_name', bpy.data, "images") + layout.prop(br, 'detail_enhancement_use') + if br.detail_enhancement_use: + # layout.prop(br,'detail_enhancement_freq') + layout.prop(br, 'detail_enhancement_amount') + # print(dir(layout)) + # layout.prop(s.view_settings.curve_mapping,"curves") + #layout.label('Frequency scaling:') + # s.view_settings.curve_mapping.clip_max_y=2 + + #layout.template_curve_mapping(s.view_settings, "curve_mapping") + + # layout.prop(br,'scale_down_before_use') + # if br.scale_down_before_use: + # layout.prop(br,'scale_down_before') + class ReliefError(Exception): - pass + pass + class DoBasRelief(bpy.types.Operator): - """calculate Bas relief""" - bl_idname = "scene.calculate_bas_relief" - bl_label = "calculate Bas relief" - bl_options = {'REGISTER', 'UNDO'} - - processes=[] - - def execute(self, context): - s=bpy.context.scene - br=s.basreliefsettings - if not br.use_image_source and br.view_layer_name=="": - br.view_layer_name=bpy.context.view_layer.name - - try: - renderScene(br.widthmm,br.heightmm,br.bit_diameter,br.pass_per_radius,not br.use_image_source,br.view_layer_name) - except ReliefError as e: - self.report({"ERROR"}, str(e)) - return {"CANCELLED"} - - try: - relief(br) - except ReliefError as e: - self.report({"ERROR"}, str(e)) - return {"CANCELLED"} - return {'FINISHED'} + """calculate Bas relief""" + bl_idname = "scene.calculate_bas_relief" + bl_label = "calculate Bas relief" + bl_options = {'REGISTER', 'UNDO'} + + processes = [] + + def execute(self, context): + s = bpy.context.scene + br = s.basreliefsettings + if not br.use_image_source and br.view_layer_name == "": + br.view_layer_name = bpy.context.view_layer.name + + try: + renderScene(br.widthmm, br.heightmm, br.bit_diameter, br.pass_per_radius, + not br.use_image_source, br.view_layer_name) + except ReliefError as e: + self.report({"ERROR"}, str(e)) + return {"CANCELLED"} + + try: + relief(br) + except ReliefError as e: + self.report({"ERROR"}, str(e)) + return {"CANCELLED"} + return {'FINISHED'} + class ProblemAreas(bpy.types.Operator): - """find Bas relief Problem areas""" - bl_idname = "scene.problemareas_bas_relief" - bl_label = "problem areas Bas relief" - bl_options = {'REGISTER', 'UNDO'} + """find Bas relief Problem areas""" + bl_idname = "scene.problemareas_bas_relief" + bl_label = "problem areas Bas relief" + bl_options = {'REGISTER', 'UNDO'} - processes=[] + processes = [] - #@classmethod - #def poll(cls, context): - # return context.active_object is not None + # @classmethod + # def poll(cls, context): + # return context.active_object is not None - def execute(self, context): - s=bpy.context.scene - br=s.basreliefsettings - problemAreas(br) - return {'FINISHED'} + def execute(self, context): + s = bpy.context.scene + br = s.basreliefsettings + problemAreas(br) + return {'FINISHED'} def get_panels(): - return( - BasReliefsettings, - BASRELIEF_Panel, - DoBasRelief, - ProblemAreas - ) + return( + BasReliefsettings, + BASRELIEF_Panel, + DoBasRelief, + ProblemAreas + ) + def register(): - for p in get_panels(): - bpy.utils.register_class(p) - s=bpy.types.Scene - s.basreliefsettings = bpy.props.PointerProperty(type=BasReliefsettings) + for p in get_panels(): + bpy.utils.register_class(p) + s = bpy.types.Scene + s.basreliefsettings = bpy.props.PointerProperty(type=BasReliefsettings) -def unregister(): - for p in get_panels(): - bpy.utils.unregister_class(p) - s=bpy.types.Scene - del s.basreliefsettings +def unregister(): + for p in get_panels(): + bpy.utils.unregister_class(p) + s = bpy.types.Scene + del s.basreliefsettings diff --git a/scripts/addons/cam/bridges.py b/scripts/addons/cam/bridges.py index cb57a8d5b..dae8b3ae8 100644 --- a/scripts/addons/cam/bridges.py +++ b/scripts/addons/cam/bridges.py @@ -138,7 +138,7 @@ def useBridges(ch, o): vi = 0 newpoints = [] - ch_points=ch.get_points_np() + ch_points = ch.get_points_np() p1 = sgeometry.Point(ch_points[0]) startinside = o.bridgespoly.contains(p1) interrupted = False @@ -169,7 +169,6 @@ def useBridges(ch, o): if o.bridgespoly_boundary_prep.intersects(l): intersections = o.bridgespoly_boundary.intersection(l) - else: intersections = sgeometry.GeometryCollection() diff --git a/scripts/addons/cam/chunk.py b/scripts/addons/cam/chunk.py index d4b1a4769..5427b31cc 100644 --- a/scripts/addons/cam/chunk.py +++ b/scripts/addons/cam/chunk.py @@ -26,7 +26,7 @@ from cam import polygon_utils_cam from cam.simple import * from cam.exception import CamException -from cam.numba_wrapper import jit,prange +from cam.numba_wrapper import jit, prange import math import numpy as np @@ -46,42 +46,45 @@ def Rotate_pbyp(originp, p, ang): # rotate point around another point with angl rot_p = [qx, qy, oz] return rot_p -@jit(nopython=True,parallel=True,fastmath=True,cache=True) -def _internalXyDistanceTo(ourpoints,theirpoints,cutoff): - v1=ourpoints[0] - v2=theirpoints[0] + +@jit(nopython=True, parallel=True, fastmath=True, cache=True) +def _internalXyDistanceTo(ourpoints, theirpoints, cutoff): + v1 = ourpoints[0] + v2 = theirpoints[0] minDistSq = (v1[0]-v2[0])**2 + (v1[1]-v2[1])**2 - cutoffSq= cutoff**2 + cutoffSq = cutoff**2 for v1 in ourpoints: for v2 in theirpoints: - distSq= (v1[0]-v2[0])**2 + (v1[1]-v2[1])**2 - if distSq2 and np.array_equal(self.points[0],self.points[-1]): + chunk = camPathChunk(self.points, self.startpoints, self.endpoints, self.rotations) + if len(self.points) > 2 and np.array_equal(self.points[0], self.points[-1]): chunk.closed = True if self.depth is not None: chunk.depth = self.depth - + return chunk # an actual chunk - stores points as numpy arrays + + class camPathChunk: # parents=[] # children=[] @@ -91,11 +94,11 @@ class camPathChunk: def __init__(self, inpoints, startpoints=None, endpoints=None, rotations=None): # name this as _points so nothing external accesses it directly # for 3 axes, this is only storage of points. For N axes, here go the sampled points - if len(inpoints)==0: - self.points = np.empty(shape=(0,3)) + if len(inpoints) == 0: + self.points = np.empty(shape=(0, 3)) else: - self.points = np.array(inpoints) - self.poly = None # get polygon just in time + self.points = np.array(inpoints) + self.poly = None # get polygon just in time self.simppoly = None if startpoints: self.startpoints = startpoints # from where the sweep test begins, but also retract point for given path @@ -121,29 +124,28 @@ def __init__(self, inpoints, startpoints=None, endpoints=None, rotations=None): def update_poly(self): if len(self.points) > 2: - self.poly = sgeometry.Polygon(self.points[:,0:2]) + self.poly = sgeometry.Polygon(self.points[:, 0:2]) else: self.poly = sgeometry.Polygon() - - def get_point(self,n): + def get_point(self, n): return self.points[n].tolist() - def get_points(self): return self.points.tolist() def get_points_np(self): return self.points - def set_points(self,points): - self.points=np.array(points) + def set_points(self, points): + self.points = np.array(points) def count(self): return len(self.points) def copy(self): - nchunk = camPathChunk(inpoints=self.points.copy(), startpoints=self.startpoints, endpoints=self.endpoints, rotations=self.rotations) + nchunk = camPathChunk(inpoints=self.points.copy(), startpoints=self.startpoints, + endpoints=self.endpoints, rotations=self.rotations) nchunk.closed = self.closed nchunk.children = self.children nchunk.parents = self.parents @@ -152,38 +154,36 @@ def copy(self): return nchunk def shift(self, x, y, z): - self.points = self.points + np.array([x,y,z]) + self.points = self.points + np.array([x, y, z]) for i, p in enumerate(self.startpoints): self.startpoints[i] = (p[0] + x, p[1] + y, p[2] + z) for i, p in enumerate(self.endpoints): self.endpoints[i] = (p[0] + x, p[1] + y, p[2] + z) - def setZ(self, z,if_bigger=False): + def setZ(self, z, if_bigger=False): if if_bigger: - self.points[:,2]=z if z>self.points[:,2] else self.points[:,2] + self.points[:, 2] = z if z > self.points[:, 2] else self.points[:, 2] else: - self.points[:,2]=z - + self.points[:, 2] = z def offsetZ(self, z): - self.points[:,2]+=z + self.points[:, 2] += z def flipX(self, x_centre): - self.points[:,0]= x_centre - self.points[:,0] - + self.points[:, 0] = x_centre - self.points[:, 0] def isbelowZ(self, z): - return np.any(self.points[:,2]2 points # simplify them if they aren't already, to speed up distance finding if self.simppoly is None: - self.simppoly=self.poly.simplify(0.0003).boundary + self.simppoly = self.poly.simplify(0.0003).boundary if other.simppoly is None: - other.simppoly=other.poly.simplify(0.0003).boundary + other.simppoly = other.poly.simplify(0.0003).boundary return self.simppoly.distance(other.simppoly) else: # this is the old method, preferably should be replaced in most cases except parallel # where this method works probably faster. # print('warning, sorting will be slow due to bad parenting in parentChildDist') - return _internalXyDistanceTo(self.points,other.points,cutoff) + return _internalXyDistanceTo(self.points, other.points, cutoff) def adaptdist(self, pos, o): # reorders chunk so that it starts at the closest point to pos. if self.closed: - dist_sq = (pos[0]-self.points[:,0])**2 + (pos[1]-self.points[:,1])**2 + dist_sq = (pos[0]-self.points[:, 0])**2 + (pos[1]-self.points[:, 1])**2 point_idx = np.argmin(dist_sq) - new_points = np.concatenate((self.points[point_idx:],self.points[:point_idx+1])) - self.points=new_points + new_points = np.concatenate((self.points[point_idx:], self.points[:point_idx+1])) + self.points = new_points else: if o.movement.type == 'MEANDER': d1 = dist2d(pos, self.points[0]) d2 = dist2d(pos, self.points[-1]) if d2 < d1: - self.points=np.flip(self.points,axis=0) + self.points = np.flip(self.points, axis=0) def getNextClosest(self, o, pos): # finds closest chunk that can be milled, when inside sorting hierarchy. @@ -281,39 +279,39 @@ def getNextClosest(self, o, pos): def getLength(self): # computes length of the chunk - in 3d - point_differences=self.points[0:-1,:] - self.points[1:,:] - distances=np.linalg.norm(point_differences,axis=1) + point_differences = self.points[0:-1, :] - self.points[1:, :] + distances = np.linalg.norm(point_differences, axis=1) self.length = np.sum(distances) def reverse(self): - self.points=np.flip(self.points,axis=0) + self.points = np.flip(self.points, axis=0) self.startpoints.reverse() self.endpoints.reverse() self.rotations.reverse() def pop(self, index): - print("WARNING: Popping from chunk is slow",self,index) - self.points=np.concatenate((self.points[0:index],self.points[index+1:]),axis=0) + print("WARNING: Popping from chunk is slow", self, index) + self.points = np.concatenate((self.points[0:index], self.points[index+1:]), axis=0) if len(self.startpoints) > 0: self.startpoints.pop(index) self.endpoints.pop(index) self.rotations.pop(index) def dedupePoints(self): - if len(self.points)>1: - keep_points=np.empty(self.points.shape[0],dtype=bool) - keep_points[0]=True - diff_points=np.sum((self.points[1:]-self.points[:1])**2,axis=1) - keep_points[1:]=diff_points>0.000000001 - self.points=self.points[keep_points,:] - - - def insert(self,at_index,point,startpoint=None, endpoint=None, rotation=None): - self.append(point,startpoint=startpoint,endpoint=endpoint,rotation=rotation,at_index=at_index) - - def append(self, point, startpoint=None, endpoint=None, rotation=None,at_index=None): + if len(self.points) > 1: + keep_points = np.empty(self.points.shape[0], dtype=bool) + keep_points[0] = True + diff_points = np.sum((self.points[1:]-self.points[:1])**2, axis=1) + keep_points[1:] = diff_points > 0.000000001 + self.points = self.points[keep_points, :] + + def insert(self, at_index, point, startpoint=None, endpoint=None, rotation=None): + self.append(point, startpoint=startpoint, endpoint=endpoint, + rotation=rotation, at_index=at_index) + + def append(self, point, startpoint=None, endpoint=None, rotation=None, at_index=None): if at_index is None: - self.points=np.concatenate((self.points,np.array([point]))) + self.points = np.concatenate((self.points, np.array([point]))) if startpoint is not None: self.startpoints.append(startpoint) if endpoint is not None: @@ -321,19 +319,20 @@ def append(self, point, startpoint=None, endpoint=None, rotation=None,at_index=N if rotation is not None: self.rotations.append(rotation) else: - self.points=np.concatenate((self.points[0:at_index],np.array([point]),self.points[at_index:])) + self.points = np.concatenate( + (self.points[0:at_index], np.array([point]), self.points[at_index:])) if startpoint is not None: - self.startpoints[at_index:at_index]=[startpoint] + self.startpoints[at_index:at_index] = [startpoint] if endpoint is not None: - self.endpoints[at_index:at_index]=[endpoint] + self.endpoints[at_index:at_index] = [endpoint] if rotation is not None: - self.rotations[at_index:at_index]=[rotation] + self.rotations[at_index:at_index] = [rotation] - def extend(self, points, startpoints=None, endpoints=None, rotations=None,at_index=None): - if len(points)==0: + def extend(self, points, startpoints=None, endpoints=None, rotations=None, at_index=None): + if len(points) == 0: return if at_index is None: - self.points=np.concatenate((self.points,np.array(points))) + self.points = np.concatenate((self.points, np.array(points))) if startpoints is not None: self.startpoints.extend(startpoints) if endpoints is not None: @@ -341,25 +340,25 @@ def extend(self, points, startpoints=None, endpoints=None, rotations=None,at_ind if rotations is not None: self.rotations.extend(rotations) else: - self.points=np.concatenate((self.points[0:at_index],np.array(points),self.points[at_index:])) + self.points = np.concatenate( + (self.points[0:at_index], np.array(points), self.points[at_index:])) if startpoints is not None: - self.startpoints[at_index:at_index]=startpoints + self.startpoints[at_index:at_index] = startpoints if endpoints is not None: - self.endpoints[at_index:at_index]=endpoints + self.endpoints[at_index:at_index] = endpoints if rotations is not None: - self.rotations[at_index:at_index]=rotations + self.rotations[at_index:at_index] = rotations - def clip_points(self,minx,maxx,miny,maxy): + def clip_points(self, minx, maxx, miny, maxy): """ remove any points outside this range """ - included_values= (self.points[:,0]>=minx) and ((self.points[:,0]<=maxx) - and (self.points[:,1]>=maxy) and (self.points[:,1]<=maxy)) - self.points=self.points[included_values] - + included_values = (self.points[:, 0] >= minx) and ((self.points[:, 0] <= maxx) + and (self.points[:, 1] >= maxy) and (self.points[:, 1] <= maxy)) + self.points = self.points[included_values] def rampContour(self, zstart, zend, o): stepdown = zstart - zend - chunk_points=[] + chunk_points = [] estlength = (zstart - zend) / tan(o.movement.ramp_in_angle) self.getLength() ramplength = estlength # min(ch.length,estlength) @@ -456,7 +455,7 @@ def rampContour(self, zstart, zend, o): def rampZigZag(self, zstart, zend, o): # TODO: convert to numpy properly - if zend==None: + if zend == None: zend = self.points[0][2] chunk_points = [] # print(zstart,zend) @@ -505,7 +504,8 @@ def rampZigZag(self, zstart, zend, o): ramppoints.extend(negramppoints[1:]) traveled = 0.0 - chunk_points.append((self.points[0][0], self.points[0][1], max(self.points[0][2], zstart))) + chunk_points.append( + (self.points[0][0], self.points[0][1], max(self.points[0][2], zstart))) for r in range(turns): for p in range(0, len(ramppoints)): p1 = chunk_points[-1] @@ -514,7 +514,8 @@ def rampZigZag(self, zstart, zend, o): traveled += d ratio = traveled / ramplength znew = zstart - stepdown * ratio - chunk_points.append((p2[0], p2[1], max(p2[2], znew))) # max value here is so that it doesn't go + # max value here is so that it doesn't go + chunk_points.append((p2[0], p2[1], max(p2[2], znew))) # below surface in the case of 3d paths # chunks = setChunksZ([ch],zend) @@ -545,7 +546,8 @@ def rampZigZag(self, zstart, zend, o): else: zigzagtraveled = 0.0 haspoints = False - ramppoints = [(self.points[-1][0], self.points[-1][1], self.points[-1][2])] + ramppoints = [(self.points[-1][0], self.points[-1] + [1], self.points[-1][2])] i = len(self.points) - 2 while not haspoints: # print(i,zigzaglength,zigzagtraveled) @@ -591,8 +593,7 @@ def changePathStart(self, o): newstart = o.profile_start chunkamt = len(self.points) newstart = newstart % chunkamt - self.points=np.concatenate((self.points[newstart:],self.points[:newstart])) - + self.points = np.concatenate((self.points[newstart:], self.points[:newstart])) def breakPathForLeadinLeadout(self, o): iradius = o.lead_in @@ -610,9 +611,12 @@ def breakPathForLeadinLeadout(self, o): if segmentLength > 2 * max(iradius, oradius): # Be certain there is enough room for the leadin and leadiout # add point on the line here - newpointx = (bpoint[0] + apoint[0]) / 2 # average of the two x points to find center - newpointy = (bpoint[1] + apoint[1]) / 2 # average of the two y points to find center - self.points=np.concatenate((self.points[:i+1],np.array([[newpointx, newpointy, apoint[2]]]),self.points[i+1:])) + # average of the two x points to find center + newpointx = (bpoint[0] + apoint[0]) / 2 + # average of the two y points to find center + newpointy = (bpoint[1] + apoint[1]) / 2 + self.points = np.concatenate( + (self.points[:i+1], np.array([[newpointx, newpointy, apoint[2]]]), self.points[i+1:])) def leadContour(self, o): perimeterDirection = 1 # 1 is clockwise, 0 is CCW @@ -689,7 +693,7 @@ def chunksCoherency(chunks): nchunk = camPathChunkBuilder() lastvec = vec if len(nchunk_points) > 4: # this is a testing threshold - nchunk.points=np.array(nchunk_points) + nchunk.points = np.array(nchunk_points) nchunks.append(nchunk) return nchunks @@ -704,13 +708,15 @@ def setChunksZ(chunks, z): # don't make this @jit parallel, because it sometimes gets called with small N # and the overhead of threading is too much. -@jit(nopython=True,fastmath=True,cache=True) -def _optimize_internal(points,keep_points,e,protect_vertical,protect_vertical_limit): + + +@jit(nopython=True, fastmath=True, cache=True) +def _optimize_internal(points, keep_points, e, protect_vertical, protect_vertical_limit): # inlined so that numba can optimize it nicely def _mag_sq(v1): return v1[0]**2 + v1[1]**2 + v1[2]**2 - def _dot_pr(v1,v2): + def _dot_pr(v1, v2): return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2] def _applyVerticalLimit(v1, v2, cos_limit): @@ -719,7 +725,7 @@ def _applyVerticalLimit(v1, v2, cos_limit): if z > 0: # don't use this vector because dot product of 0,0,1 is trivially just v2[2] # vec_up = np.array([0, 0, 1]) - vec_diff= v1-v2 + vec_diff = v1-v2 vec_diff2 = v2-v1 vec_diff_mag = np.sqrt(_mag_sq(vec_diff)) # dot product = cos(angle) * mag1 * mag2 @@ -734,34 +740,35 @@ def _applyVerticalLimit(v1, v2, cos_limit): v2[0] = v1[0] v2[1] = v1[1] - cos_limit = cos( protect_vertical_limit ) + cos_limit = cos(protect_vertical_limit) prev_i = 0 - for i in range(1,points.shape[0]-1): + for i in range(1, points.shape[0]-1): v1 = points[prev_i] - v2=points[i+1] - vmiddle=points[i] + v2 = points[i+1] + vmiddle = points[i] line_direction = v2-v1 line_length = sqrt(_mag_sq(line_direction)) - if line_length==0: + if line_length == 0: # don't keep duplicate points - keep_points[i]=False + keep_points[i] = False continue # normalize line direction - line_direction*= (1.0/line_length) # N in formula below + line_direction *= (1.0/line_length) # N in formula below # X = A + tN (line formula) Distance to point P # A = v1, N = line_direction, P = vmiddle # distance = || (P - A) - ((P-A).N)N || point_offset = vmiddle - v1 - distance_sq = _mag_sq(point_offset - (line_direction * _dot_pr(point_offset,line_direction))) + distance_sq = _mag_sq(point_offset - (line_direction * + _dot_pr(point_offset, line_direction))) # compare on squared distance to save a sqrt if distance_sq < e*e: - keep_points[i]=False + keep_points[i] = False else: - keep_points[i]=True + keep_points[i] = True if protect_vertical: _applyVerticalLimit(points[prev_i], points[i], cos_limit) - prev_i=i + prev_i = i def optimizeChunk(chunk, operation): @@ -774,21 +781,23 @@ def optimizeChunk(chunk, operation): naxispoints = True protect_vertical = operation.movement.protect_vertical and operation.machine_axes == '3' - keep_points=np.full(points.shape[0],True) + keep_points = np.full(points.shape[0], True) # shape points need to be on line, # but we need to protect vertical - which # means changing point values # bits of this are moved from simple.py so that # numba can optimize as a whole - _optimize_internal(points,keep_points,operation.optimisation.optimize_threshold * 0.000001,protect_vertical,operation.movement.protect_vertical_limit) + _optimize_internal(points, keep_points, operation.optimisation.optimize_threshold * + 0.000001, protect_vertical, operation.movement.protect_vertical_limit) # now do numpy select by boolean array - chunk.points=points[keep_points] + chunk.points = points[keep_points] if naxispoints: # list comprehension so we don't have to do tons of appends - chunk.startpoints=[chunk.startpoints[i] for i,b in enumerate(keep_points) if b==True] - chunk.endpoints=[chunk.endpoints[i] for i,b in enumerate(keep_points) if b==True] - chunk.rotations=[chunk.rotations[i] for i,b in enumerate(keep_points) if b==True] + chunk.startpoints = [chunk.startpoints[i] + for i, b in enumerate(keep_points) if b == True] + chunk.endpoints = [chunk.endpoints[i] for i, b in enumerate(keep_points) if b == True] + chunk.rotations = [chunk.rotations[i] for i, b in enumerate(keep_points) if b == True] return chunk @@ -809,12 +818,12 @@ def limitChunks(chunks, o, closed = False nchunks.append(nch.to_chunk()) if nch1 is None: - nch1=nchunks[-1] + nch1 = nchunks[-1] nch = camPathChunkBuilder() elif sampled: nch.points.append(s) prevsampled = sampled - if len(nch.points) > 2 and closed and ch.closed and np.array_equal(ch.points[0] ,ch.points[-1]): + if len(nch.points) > 2 and closed and ch.closed and np.array_equal(ch.points[0], ch.points[-1]): nch.closed = True elif ch.closed and nch1 is not None and len(nch.points) > 1 and np.array_equal(nch.points[-1], nch1.points[0]): # here adds beginning of closed chunk to the end, if the chunks were split during limiting @@ -833,10 +842,10 @@ def parentChildPoly(parents, children, o): # hierarchy works like this: - children get milled first. for parent in parents: - if parent.poly is None: + if parent.poly is None: parent.update_poly() for child in children: - if child.poly is None: + if child.poly is None: child.update_poly() if child != parent: # and len(child.poly)>0 if parent.poly.contains(sgeometry.Point(child.poly.boundary.coords[0])): @@ -854,12 +863,12 @@ def parentChildDist(parents, children, o, distance=None): dlim = dlim * 2 else: dlim = distance - + for child in children: for parent in parents: isrelation = False if parent != child: - if parent.xyDistanceWithin(child,cutoff=dlim): + if parent.xyDistanceWithin(child, cutoff=dlim): parent.children.append(child) child.parents.append(parent) @@ -880,7 +889,7 @@ def chunksToShapely(chunks): # this does more cleve chunks to Poly with hierarc for ch in chunks: # first convert chunk to poly if len(ch.points) > 2: # pchunk=[] - ch.poly = sgeometry.Polygon(ch.points[:,0:2]) + ch.poly = sgeometry.Polygon(ch.points[:, 0:2]) if not ch.poly.is_valid: ch.poly = sgeometry.Polygon() else: @@ -1041,28 +1050,30 @@ def meshFromCurveToChunk(object): chunk.points.append((mesh.vertices[lastvi].co + object.location).to_tuple()) # add first point to end#originally the z was mesh.vertices[lastvi].co.z+z lastvi = vi + 1 - chunk=chunk.to_chunk() + chunk = chunk.to_chunk() chunk.dedupePoints() - if chunk.count()>=1: - # dump single point chunks + if chunk.count() >= 1: + # dump single point chunks chunks.append(chunk) chunk = camPathChunkBuilder() progress('processing curve - FINISHED') vi = len(mesh.vertices) - 1 - chunk.points.append((mesh.vertices[vi].co.x + x, mesh.vertices[vi].co.y + y, mesh.vertices[vi].co.z + z)) + chunk.points.append((mesh.vertices[vi].co.x + x, + mesh.vertices[vi].co.y + y, mesh.vertices[vi].co.z + z)) if not (dk.isdisjoint([(vi, lastvi)])) or not (dk.isdisjoint([(lastvi, vi)])): chunk.closed = True chunk.points.append( (mesh.vertices[lastvi].co.x + x, mesh.vertices[lastvi].co.y + y, mesh.vertices[lastvi].co.z + z)) - chunk=chunk.to_chunk() + chunk = chunk.to_chunk() chunk.dedupePoints() - if chunk.count()>=1: - # dump single point chunks + if chunk.count() >= 1: + # dump single point chunks chunks.append(chunk) return chunks + def makeVisible(o): storage = [True, []] @@ -1096,10 +1107,11 @@ def meshFromCurve(o, use_modifiers=False): co = bpy.context.active_object - if co.type == 'FONT': # support for text objects is only and only here, just convert them to curves. + # support for text objects is only and only here, just convert them to curves. + if co.type == 'FONT': bpy.ops.object.convert(target='CURVE', keep_original=False) - elif co.type != 'CURVE': # curve must be a curve... - bpy.ops.object.delete() # delete temporary object + elif co.type != 'CURVE': # curve must be a curve... + bpy.ops.object.delete() # delete temporary object raise CamException("Source curve object must be of type CURVE") co.data.dimensions = '3D' co.data.bevel_depth = 0 diff --git a/scripts/addons/cam/collision.py b/scripts/addons/cam/collision.py index ddcd7741e..5fc5a5cd8 100644 --- a/scripts/addons/cam/collision.py +++ b/scripts/addons/cam/collision.py @@ -44,9 +44,9 @@ def getCutterBullet(o): type = o.cutter_type 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 = CUTTER_OFFSET, + 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=CUTTER_OFFSET, rotation=(0, 0, 0)) cutter = bpy.context.active_object cutter.scale *= BULLET_SCALE @@ -57,29 +57,29 @@ def getCutterBullet(o): cutter.rigid_body.collision_shape = 'CYLINDER' elif type == 'BALLNOSE': - # ballnose ending used mainly when projecting from sides. - # 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 = 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) - bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='BOUNDS') - bpy.ops.rigidbody.object_add(type='ACTIVE') - cutter = bpy.context.active_object - #cutter.dimensions.z = 0.2 * BULLET_SCALE # should be sufficient for now... 20 cm. - cutter.rigid_body.collision_shape = 'CAPSULE' - #bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) + # ballnose ending used mainly when projecting from sides. + # 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=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) + bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='BOUNDS') + bpy.ops.rigidbody.object_add(type='ACTIVE') + cutter = bpy.context.active_object + # cutter.dimensions.z = 0.2 * BULLET_SCALE # should be sufficient for now... 20 cm. + cutter.rigid_body.collision_shape = 'CAPSULE' + #bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) elif type == 'VCARVE': angle = o.cutter_tip_angle s = math.tan(math.pi * (90 - angle / 2) / 180) / 2 # angles in degrees 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 = CUTTER_OFFSET, + 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=CUTTER_OFFSET, rotation=(math.pi, 0, 0)) cutter = bpy.context.active_object cutter.scale *= BULLET_SCALE @@ -92,12 +92,12 @@ def getCutterBullet(o): angle = o.cutter_tip_angle s = math.tan(math.pi * (90 - angle / 2) / 180) / 2 # angles in degrees - cylcone_d =(o.cutter_diameter - o.cylcone_diameter) * s - bpy.ops.mesh.primitive_cone_add(vertices = 32, radius1 = o.cutter_diameter / 2, - radius2 = o.cylcone_diameter / 2, - depth = cylcone_d, end_fill_type='NGON', - align = 'WORLD', enter_editmode = False, - location = CUTTER_OFFSET,rotation = (math.pi, 0, 0)) + cylcone_d = (o.cutter_diameter - o.cylcone_diameter) * s + bpy.ops.mesh.primitive_cone_add(vertices=32, radius1=o.cutter_diameter / 2, + radius2=o.cylcone_diameter / 2, + depth=cylcone_d, end_fill_type='NGON', + align='WORLD', enter_editmode=False, + 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) @@ -112,23 +112,23 @@ def getCutterBullet(o): Ball_R = o.ball_radius/math.cos(angle) conedepth = (cutter_R - o.ball_radius)/math.tan(angle) bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), - Simple_Type='Point', use_cyclic_u=False) + Simple_Type='Point', use_cyclic_u=False) oy = Ball_R - for i in range(1 , 10): + for i in range(1, 10): ang = -i * (math.pi/2-angle) / 9 qx = math.sin(ang) * oy qy = oy - math.cos(ang) * oy - bpy.ops.curve.vertex_add(location=(qx , qy , 0)) + bpy.ops.curve.vertex_add(location=(qx, qy, 0)) conedepth += qy - bpy.ops.curve.vertex_add(location=(-cutter_R , conedepth , 0)) + bpy.ops.curve.vertex_add(location=(-cutter_R, conedepth, 0)) #bpy.ops.curve.vertex_add(location=(0 , conedepth , 0)) bpy.ops.object.editmode_toggle() bpy.ops.object.convert(target='MESH') - bpy.ops.transform.rotate(value = -math.pi / 2, orient_axis = 'X') + bpy.ops.transform.rotate(value=-math.pi / 2, orient_axis='X') bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) ob = bpy.context.active_object ob.name = "BallConeTool" - ob_scr = ob.modifiers.new(type='SCREW', name = 'scr') + ob_scr = ob.modifiers.new(type='SCREW', name='scr') ob_scr.angle = math.radians(-360) ob_scr.steps = 32 ob_scr.merge_threshold = 0 @@ -197,7 +197,8 @@ def subdivideLongEdges(ob, threshold): bpy.ops.mesh.subdivide(smoothness=0) if iter == 0: bpy.ops.mesh.select_all(action='SELECT') - bpy.ops.mesh.quads_convert_to_tris(quad_method='SHORTEST_DIAGONAL', ngon_method='BEAUTY') + bpy.ops.mesh.quads_convert_to_tris( + quad_method='SHORTEST_DIAGONAL', ngon_method='BEAUTY') bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.object.editmode_toggle() ob.update_from_editmode() @@ -266,7 +267,6 @@ def prepareBulletCollision(o): if active_collection in collisionob.users_collection: active_collection.objects.unlink(collisionob) - getCutterBullet(o) # machine objects scaling up to simulation scale diff --git a/scripts/addons/cam/constants.py b/scripts/addons/cam/constants.py index d93cb2f4c..595cab16f 100644 --- a/scripts/addons/cam/constants.py +++ b/scripts/addons/cam/constants.py @@ -6,6 +6,6 @@ CHIPLOAD_PRECISION = 10 -MAX_OPERATION_TIME = 3200000000 # seconds +MAX_OPERATION_TIME = 3200000000 # seconds G64_INCOMPATIBLE_MACHINES = ['GRBL'] diff --git a/scripts/addons/cam/curvecamcreate.py b/scripts/addons/cam/curvecamcreate.py index f80a44601..ef55b6a07 100644 --- a/scripts/addons/cam/curvecamcreate.py +++ b/scripts/addons/cam/curvecamcreate.py @@ -40,10 +40,14 @@ class CamCurveHatch(bpy.types.Operator): bl_label = "CrossHatch curve" bl_options = {'REGISTER', 'UNDO', 'PRESET'} - angle: bpy.props.FloatProperty(name="angle", default=0, min=-math.pi/2, max=math.pi/2, precision=4, subtype="ANGLE") - distance: bpy.props.FloatProperty(name="spacing", default=0.015, min=0, max=3.0, precision=4, unit="LENGTH") - offset: bpy.props.FloatProperty(name="Margin", default=0.001, min=-1.0, max=3.0, precision=4, unit="LENGTH") - height: bpy.props.FloatProperty(name="Height", default=0.000, min=-1.0, max=1.0, precision=4, unit="LENGTH") + angle: bpy.props.FloatProperty(name="angle", default=0, min=- + math.pi/2, max=math.pi/2, precision=4, subtype="ANGLE") + distance: bpy.props.FloatProperty(name="spacing", default=0.015, + min=0, max=3.0, precision=4, unit="LENGTH") + offset: bpy.props.FloatProperty(name="Margin", default=0.001, + min=-1.0, max=3.0, precision=4, unit="LENGTH") + height: bpy.props.FloatProperty(name="Height", default=0.000, + min=-1.0, max=1.0, precision=4, unit="LENGTH") amount: bpy.props.IntProperty(name="amount", default=10, min=1, max=10000) hull: bpy.props.BoolProperty(name="Convex Hull", default=False) contour: bpy.props.BoolProperty(name="Contour Curve", default=False) @@ -108,13 +112,15 @@ def execute(self, context): lines = MultiLineString(coords) # create a multilinestring shapely object rotated = affinity.rotate(lines, self.angle, use_radians=True) # rotate using shapely - translated = affinity.translate(rotated, xoff=centerx, yoff=centery) # move using shapely + translated = affinity.translate( + rotated, xoff=centerx, yoff=centery) # move using shapely simple.make_active('crosshatch_bound') bounds = simple.active_to_shapely_poly() if self.pocket_type == 'BOUNDS': - xing = translated.intersection(bounds) # Shapely detects intersections with the square bounds + # Shapely detects intersections with the square bounds + xing = translated.intersection(bounds) else: xing = translated.intersection(s.buffer(self.offset)) # Shapely detects intersections with the original curve or hull @@ -155,9 +161,12 @@ class CamCurvePlate(bpy.types.Operator): bl_label = "Sign plate" bl_options = {'REGISTER', 'UNDO', 'PRESET'} - radius: bpy.props.FloatProperty(name="Corner Radius", default=.025, min=0, max=0.1, precision=4, unit="LENGTH") - width: bpy.props.FloatProperty(name="Width of plate", default=0.3048, min=0, max=3.0, precision=4, unit="LENGTH") - height: bpy.props.FloatProperty(name="Height of plate", default=0.457, min=0, max=3.0, precision=4, unit="LENGTH") + radius: bpy.props.FloatProperty(name="Corner Radius", default=.025, + min=0, max=0.1, precision=4, unit="LENGTH") + width: bpy.props.FloatProperty(name="Width of plate", default=0.3048, + min=0, max=3.0, precision=4, unit="LENGTH") + height: bpy.props.FloatProperty(name="Height of plate", default=0.457, + min=0, max=3.0, precision=4, unit="LENGTH") hole_diameter: bpy.props.FloatProperty(name="Hole diameter", default=0.01, min=0, max=3.0, precision=4, unit="LENGTH") hole_tolerance: bpy.props.FloatProperty(name="Hole V Tolerance", default=0.005, min=0, max=3.0, precision=4, @@ -169,11 +178,11 @@ class CamCurvePlate(bpy.types.Operator): hole_hamount: bpy.props.IntProperty(name="Hole horiz amount", default=1, min=0, max=50) resolution: bpy.props.IntProperty(name="Spline resolution", default=50, min=3, max=150) plate_type: EnumProperty(name='Type plate', - items=(('ROUNDED', 'Rounded corner', 'Makes a rounded corner plate'), - ('COVE', 'Cove corner', 'Makes a plate with circles cut in each corner '), - ('BEVEL', 'Bevel corner', 'Makes a plate with beveled corners '), - ('OVAL', 'Elipse', 'Makes an oval plate')), - description='Type of Plate', default='ROUNDED') + items=(('ROUNDED', 'Rounded corner', 'Makes a rounded corner plate'), + ('COVE', 'Cove corner', 'Makes a plate with circles cut in each corner '), + ('BEVEL', 'Bevel corner', 'Makes a plate with beveled corners '), + ('OVAL', 'Elipse', 'Makes an oval plate')), + description='Type of Plate', default='ROUNDED') def draw(self, context): layout = self.layout @@ -190,7 +199,6 @@ def draw(self, context): if self.plate_type in ["ROUNDED", "COVE", "BEVEL"]: layout.prop(self, 'radius') - def execute(self, context): left = -self.width / 2 + self.radius bottom = -self.height / 2 + self.radius @@ -198,21 +206,21 @@ def execute(self, context): top = -bottom if self.plate_type == "ROUNDED": - # create base + # create base bpy.ops.curve.primitive_bezier_circle_add(radius=self.radius, enter_editmode=False, align='WORLD', - location=(left, bottom, 0), scale=(1, 1, 1)) + location=(left, bottom, 0), scale=(1, 1, 1)) simple.active_name("_circ_LB") bpy.context.object.data.resolution_u = self.resolution bpy.ops.curve.primitive_bezier_circle_add(radius=self.radius, enter_editmode=False, align='WORLD', - location=(right, bottom, 0), scale=(1, 1, 1)) + location=(right, bottom, 0), scale=(1, 1, 1)) simple.active_name("_circ_RB") bpy.context.object.data.resolution_u = self.resolution bpy.ops.curve.primitive_bezier_circle_add(radius=self.radius, enter_editmode=False, align='WORLD', - location=(left, top, 0), scale=(1, 1, 1)) + location=(left, top, 0), scale=(1, 1, 1)) simple.active_name("_circ_LT") bpy.context.object.data.resolution_u = self.resolution bpy.ops.curve.primitive_bezier_circle_add(radius=self.radius, enter_editmode=False, align='WORLD', - location=(right, top, 0), scale=(1, 1, 1)) + location=(right, top, 0), scale=(1, 1, 1)) simple.active_name("_circ_RT") bpy.context.object.data.resolution_u = self.resolution @@ -223,25 +231,25 @@ def execute(self, context): elif self.plate_type == "OVAL": bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), Simple_Type='Ellipse', - Simple_a=self.width/2,Simple_b=self.height/2, use_cyclic_u=True, edit_mode=False) + Simple_a=self.width/2, Simple_b=self.height/2, use_cyclic_u=True, edit_mode=False) bpy.context.object.data.resolution_u = self.resolution simple.active_name("plate_base") elif self.plate_type == 'COVE': bpy.ops.curve.primitive_bezier_circle_add(radius=self.radius, enter_editmode=False, align='WORLD', - location=(left-self.radius, bottom-self.radius, 0), scale=(1, 1, 1)) + location=(left-self.radius, bottom-self.radius, 0), scale=(1, 1, 1)) simple.active_name("_circ_LB") bpy.context.object.data.resolution_u = self.resolution bpy.ops.curve.primitive_bezier_circle_add(radius=self.radius, enter_editmode=False, align='WORLD', - location=(right+self.radius, bottom-self.radius, 0), scale=(1, 1, 1)) + location=(right+self.radius, bottom-self.radius, 0), scale=(1, 1, 1)) simple.active_name("_circ_RB") bpy.context.object.data.resolution_u = self.resolution bpy.ops.curve.primitive_bezier_circle_add(radius=self.radius, enter_editmode=False, align='WORLD', - location=(left-self.radius, top+self.radius, 0), scale=(1, 1, 1)) + location=(left-self.radius, top+self.radius, 0), scale=(1, 1, 1)) simple.active_name("_circ_LT") bpy.context.object.data.resolution_u = self.resolution bpy.ops.curve.primitive_bezier_circle_add(radius=self.radius, enter_editmode=False, align='WORLD', - location=(right+self.radius, top+self.radius, 0), scale=(1, 1, 1)) + location=(right+self.radius, top+self.radius, 0), scale=(1, 1, 1)) simple.active_name("_circ_RT") bpy.context.object.data.resolution_u = self.resolution @@ -252,27 +260,25 @@ def execute(self, context): edit_mode=False) simple.active_name("_base") - simple.difference("_","_base") - simple.rename("_base","plate_base") - - + simple.difference("_", "_base") + simple.rename("_base", "plate_base") elif self.plate_type == 'BEVEL': bpy.ops.curve.simple(align='WORLD', Simple_Type='Rectangle', Simple_width=self.radius*2, Simple_length=self.radius*2, location=(left-self.radius, bottom-self.radius, 0), - rotation=(0, 0, 0.785398),outputType='POLY', use_cyclic_u=True, edit_mode=False) + rotation=(0, 0, 0.785398), outputType='POLY', use_cyclic_u=True, edit_mode=False) simple.active_name("_bev_LB") bpy.context.object.data.resolution_u = self.resolution bpy.ops.curve.simple(align='WORLD', Simple_Type='Rectangle', Simple_width=self.radius*2, Simple_length=self.radius*2, location=(right+self.radius, bottom-self.radius, 0), - rotation=(0, 0, 0.785398),outputType='POLY', use_cyclic_u=True, edit_mode=False) + rotation=(0, 0, 0.785398), outputType='POLY', use_cyclic_u=True, edit_mode=False) simple.active_name("_bev_RB") bpy.context.object.data.resolution_u = self.resolution bpy.ops.curve.simple(align='WORLD', Simple_Type='Rectangle', Simple_width=self.radius*2, Simple_length=self.radius*2, location=(left-self.radius, top+self.radius, 0), - rotation=(0, 0, 0.785398),outputType='POLY', use_cyclic_u=True, edit_mode=False) + rotation=(0, 0, 0.785398), outputType='POLY', use_cyclic_u=True, edit_mode=False) simple.active_name("_bev_LT") bpy.context.object.data.resolution_u = self.resolution @@ -280,7 +286,7 @@ def execute(self, context): bpy.ops.curve.simple(align='WORLD', Simple_Type='Rectangle', Simple_width=self.radius*2, Simple_length=self.radius*2, location=(right+self.radius, top+self.radius, 0), - rotation=(0, 0, 0.785398),outputType='POLY', use_cyclic_u=True, edit_mode=False) + rotation=(0, 0, 0.785398), outputType='POLY', use_cyclic_u=True, edit_mode=False) simple.active_name("_bev_RT") bpy.context.object.data.resolution_u = self.resolution @@ -292,24 +298,24 @@ def execute(self, context): edit_mode=False) simple.active_name("_base") - simple.difference("_","_base") - simple.rename("_base","plate_base") - + simple.difference("_", "_base") + simple.rename("_base", "plate_base") if self.hole_diameter > 0 or self.hole_hamount > 0: bpy.ops.curve.primitive_bezier_circle_add(radius=self.hole_diameter / 2, enter_editmode=False, - align='WORLD', location=(0, self.hole_tolerance / 2, 0), - scale=(1, 1, 1)) + align='WORLD', location=(0, self.hole_tolerance / 2, 0), + scale=(1, 1, 1)) simple.active_name("_hole_Top") bpy.context.object.data.resolution_u = int(self.resolution / 4) if self.hole_tolerance > 0: bpy.ops.curve.primitive_bezier_circle_add(radius=self.hole_diameter / 2, enter_editmode=False, - align='WORLD', location=(0, -self.hole_tolerance / 2, 0), - scale=(1, 1, 1)) + align='WORLD', location=(0, -self.hole_tolerance / 2, 0), + scale=(1, 1, 1)) simple.active_name("_hole_Bottom") bpy.context.object.data.resolution_u = int(self.resolution / 4) - simple.select_multiple("_hole") # select everything starting with _hole and perform a convex hull on them + # select everything starting with _hole and perform a convex hull on them + simple.select_multiple("_hole") utils.polygonConvexHull(context) simple.active_name("plate_hole") simple.move(y=-self.hole_vdist / 2) @@ -344,7 +350,8 @@ def execute(self, context): simple.select_multiple("plate_") # select everything starting with plate_ - bpy.context.view_layer.objects.active = bpy.data.objects['plate_base'] # Make the plate base active + # Make the plate base active + bpy.context.view_layer.objects.active = bpy.data.objects['plate_base'] utils.polygonBoolean(context, "DIFFERENCE") # Remove holes from the base simple.remove_multiple("plate_") # Remove temporary base and holes simple.remove_multiple("_") @@ -355,18 +362,24 @@ def execute(self, context): return {'FINISHED'} + class CamCurveFlatCone(bpy.types.Operator): """perform generates rounded plate with mounting holes""" # by Alain Pelletier Sept 2021 bl_idname = "object.curve_flat_cone" bl_label = "Cone flat calculator" bl_options = {'REGISTER', 'UNDO', 'PRESET'} - small_d: bpy.props.FloatProperty(name="small diameter", default=.025, min=0, max=0.1, precision=4, unit="LENGTH") - large_d: bpy.props.FloatProperty(name="large diameter", default=0.3048, min=0, max=3.0, precision=4, unit="LENGTH") - height: bpy.props.FloatProperty(name="Height of cone", default=0.457, min=0, max=3.0, precision=4, unit="LENGTH") - tab: bpy.props.FloatProperty(name="tab witdh", default=0.01, min=0, max=0.100, precision=4, unit="LENGTH") - intake: bpy.props.FloatProperty(name="intake diameter", default=0, min=0, max=0.200, precision=4, unit="LENGTH") - intake_skew: bpy.props.FloatProperty(name="intake_skew", default=1, min=0.1, max=4 ) + small_d: bpy.props.FloatProperty( + name="small diameter", default=.025, min=0, max=0.1, precision=4, unit="LENGTH") + large_d: bpy.props.FloatProperty( + name="large diameter", default=0.3048, min=0, max=3.0, precision=4, unit="LENGTH") + height: bpy.props.FloatProperty(name="Height of cone", default=0.457, + min=0, max=3.0, precision=4, unit="LENGTH") + tab: bpy.props.FloatProperty(name="tab witdh", default=0.01, min=0, + max=0.100, precision=4, unit="LENGTH") + intake: bpy.props.FloatProperty(name="intake diameter", default=0, + min=0, max=0.200, precision=4, unit="LENGTH") + intake_skew: bpy.props.FloatProperty(name="intake_skew", default=1, min=0.1, max=4) resolution: bpy.props.IntProperty(name="Resolution", default=12, min=5, max=200) def execute(self, context): @@ -404,11 +417,9 @@ def execute(self, context): simple.active_name('flat_cone') - return {'FINISHED'} - class CamCurveMortise(bpy.types.Operator): """Generates mortise along a curve""" # by Alain Pelletier December 2021 bl_idname = "object.curve_mortise" @@ -425,8 +436,10 @@ class CamCurveMortise(bpy.types.Operator): unit="LENGTH") plate_thickness: bpy.props.FloatProperty(name="Drawer plate thickness", default=0.00477, min=0.001, max=3.0, unit="LENGTH") - side_height: bpy.props.FloatProperty(name="side height", default=0.05, min=0.001, max=3.0, unit="LENGTH") - flex_pocket: bpy.props.FloatProperty(name="Flex pocket", default=0.004, min=0.000, max=1.0, unit="LENGTH") + side_height: bpy.props.FloatProperty( + name="side height", default=0.05, min=0.001, max=3.0, unit="LENGTH") + flex_pocket: bpy.props.FloatProperty( + name="Flex pocket", default=0.004, min=0.000, max=1.0, unit="LENGTH") top_bottom: bpy.props.BoolProperty(name="Side Top & bottom fingers", default=True) opencurve: bpy.props.BoolProperty(name="OpenCurve", default=False) adaptive: bpy.props.FloatProperty(name="Adaptive angle threshold", default=0.0, min=0.000, max=2, subtype="ANGLE", @@ -479,15 +492,19 @@ def execute(self, context): locations = joinery.variable_finger(c, length, self.min_finger_size, self.finger_size, self.plate_thickness, self.finger_tolerance, self.adaptive, True, self.double_adaptive) - joinery.create_flex_side(loop_length, self.side_height, self.plate_thickness, self.top_bottom) + joinery.create_flex_side(loop_length, self.side_height, + self.plate_thickness, self.top_bottom) if self.flex_pocket > 0: joinery.make_variable_flex_pocket(self.side_height, self.plate_thickness, self.flex_pocket, locations) else: - joinery.fixed_finger(c, length, self.finger_size, self.plate_thickness, self.finger_tolerance) - joinery.fixed_finger(c, length, self.finger_size, self.plate_thickness, self.finger_tolerance, True) - joinery.create_flex_side(loop_length, self.side_height, self.plate_thickness, self.top_bottom) + joinery.fixed_finger(c, length, self.finger_size, + self.plate_thickness, self.finger_tolerance) + joinery.fixed_finger(c, length, self.finger_size, + self.plate_thickness, self.finger_tolerance, True) + joinery.create_flex_side(loop_length, self.side_height, + self.plate_thickness, self.top_bottom) if self.flex_pocket > 0: joinery.make_flex_pocket(length, self.side_height, self.plate_thickness, self.finger_size, self.flex_pocket) @@ -578,9 +595,12 @@ class CamCurveDrawer(bpy.types.Operator): bl_label = "Drawer" bl_options = {'REGISTER', 'UNDO', 'PRESET'} - depth: bpy.props.FloatProperty(name="Drawer Depth", default=0.2, min=0, max=1.0, precision=4, unit="LENGTH") - width: bpy.props.FloatProperty(name="Width of Drawer", default=0.125, min=0, max=3.0, precision=4, unit="LENGTH") - height: bpy.props.FloatProperty(name="Height of drawer", default=0.07, min=0, max=3.0, precision=4, unit="LENGTH") + depth: bpy.props.FloatProperty(name="Drawer Depth", default=0.2, + min=0, max=1.0, precision=4, unit="LENGTH") + width: bpy.props.FloatProperty(name="Width of Drawer", default=0.125, + min=0, max=3.0, precision=4, unit="LENGTH") + height: bpy.props.FloatProperty(name="Height of drawer", default=0.07, + min=0, max=3.0, precision=4, unit="LENGTH") finger_size: bpy.props.FloatProperty(name="Maximum Finger Size", default=0.015, min=0.005, max=3.0, precision=4, unit="LENGTH") finger_tolerance: bpy.props.FloatProperty(name="Finger play room", default=0.000045, min=0, max=0.003, precision=4, @@ -623,7 +643,8 @@ def execute(self, context): bpy.context.object.data.resolution_u = 64 bpy.context.scene.cursor.location = (0, 0, 0) - joinery.vertical_finger(height_finger, self.drawer_plate_thickness, self.finger_tolerance, height_finger_amt) + joinery.vertical_finger(height_finger, self.drawer_plate_thickness, + self.finger_tolerance, height_finger_amt) joinery.horizontal_finger(width_finger, self.drawer_plate_thickness, self.finger_tolerance, width_finger_amt * 2) @@ -632,7 +653,8 @@ def execute(self, context): bpy.ops.object.origin_set(type='ORIGIN_CURSOR', center='MEDIAN') # make drawer back - finger_pair = joinery.finger_pair("_vfa", self.width - self.drawer_plate_thickness - self.finger_inset * 2, 0) + finger_pair = joinery.finger_pair( + "_vfa", self.width - self.drawer_plate_thickness - self.finger_inset * 2, 0) simple.make_active('_wfa') fronth = bpy.context.active_object simple.make_active('_back') @@ -662,7 +684,8 @@ def execute(self, context): bpy.ops.transform.transform(mode='TRANSLATION', value=(0.0, 2 * self.height, 0.0, 0.0)) simple.make_active('drawer_back') - bpy.ops.transform.transform(mode='TRANSLATION', value=(self.width + 0.01, 2 * self.height, 0.0, 0.0)) + bpy.ops.transform.transform(mode='TRANSLATION', value=( + self.width + 0.01, 2 * self.height, 0.0, 0.0)) # make side finger_pair = joinery.finger_pair("_vfb", self.depth - self.drawer_plate_thickness, 0) @@ -686,7 +709,8 @@ def execute(self, context): simple.difference('_bot', '_bottom') simple.rotate(math.pi/2) - joinery.finger_pair("_wfb0", 0, self.width - self.drawer_plate_thickness - self.finger_inset * 2) + joinery.finger_pair("_wfb0", 0, self.width - + self.drawer_plate_thickness - self.finger_inset * 2) simple.active_name('_bot_fingers') simple.difference('_bot', '_bottom') @@ -773,19 +797,22 @@ class CamCurvePuzzle(bpy.types.Operator): twist_lock: bpy.props.BoolProperty(name="Add TwistLock", default=False) twist_thick: bpy.props.FloatProperty(name="Twist Thickness", default=0.0047, min=0.001, max=3.0, precision=4, unit="LENGTH") - twist_percent: bpy.props.FloatProperty(name="Twist neck", default=0.3, min=0.1, max=0.9, precision=4) + twist_percent: bpy.props.FloatProperty( + name="Twist neck", default=0.3, min=0.1, max=0.9, precision=4) twist_keep: bpy.props.BoolProperty(name="keep Twist holes", default=False) twist_line: bpy.props.BoolProperty(name="Add Twist to bar", default=False) twist_line_amount: bpy.props.IntProperty(name="amount of separators", default=2, min=1, max=600) twist_separator: bpy.props.BoolProperty(name="Add Twist separator", default=False) - twist_separator_amount: bpy.props.IntProperty(name="amount of separators", default=2, min=2, max=600) + twist_separator_amount: bpy.props.IntProperty( + name="amount of separators", default=2, min=2, max=600) twist_separator_spacing: bpy.props.FloatProperty(name="Separator spacing", default=0.025, min=-0.004, max=1.0, precision=4, unit="LENGTH") twist_separator_edge_distance: bpy.props.FloatProperty(name="Separator edge distance", default=0.01, min=0.0005, max=0.1, precision=4, unit="LENGTH") tile_x_amount: bpy.props.IntProperty(name="amount of x fingers", default=2, min=1, max=600) tile_y_amount: bpy.props.IntProperty(name="amount of y fingers", default=2, min=1, max=600) - interlock_amount: bpy.props.IntProperty(name="Interlock amount on curve", default=2, min=0, max=200) + interlock_amount: bpy.props.IntProperty( + name="Interlock amount on curve", default=2, min=0, max=200) overcut: bpy.props.BoolProperty(name="Add overcut", default=False) overcut_diameter: bpy.props.FloatProperty(name="Overcut toool Diameter", default=0.003175, min=-0.001, max=0.5, precision=4, unit="LENGTH") @@ -877,7 +904,8 @@ def execute(self, context): if self.interlock_type == 'JOINT': if self.finger_amount == 0: # cannot be 0 in joints self.finger_amount = 1 - puzzle_joinery.fingers(self.diameter, self.finger_tolerance, self.finger_amount, stem=self.stem_size) + puzzle_joinery.fingers(self.diameter, self.finger_tolerance, + self.finger_amount, stem=self.stem_size) if self.interlock_type == 'BAR': if not self.mitre: @@ -938,7 +966,7 @@ def execute(self, context): elif self.interlock_type == 'TILE': puzzle_joinery.tile(self.diameter, self.finger_tolerance, self.tile_x_amount, self.tile_y_amount, - stem=self.stem_size) + stem=self.stem_size) elif self.interlock_type == 'OPENCURVE' and curve_detected: puzzle_joinery.open_curve(line, self.height, self.diameter, self.finger_tolerance, self.finger_amount, @@ -978,7 +1006,7 @@ class CamCurveGear(bpy.types.Operator): rim_size: bpy.props.FloatProperty(name="Rim size", default=0.003175, min=0, max=3.0, precision=4, unit="LENGTH") hub_diameter: bpy.props.FloatProperty(name="Hub diameter", default=0.005, min=0, max=3.0, precision=4, - unit="LENGTH") + unit="LENGTH") pressure_angle: bpy.props.FloatProperty(name="Pressure Angle", default=math.radians(20), min=0.001, max=math.pi/2, precision=4, subtype="ANGLE", @@ -988,7 +1016,7 @@ class CamCurveGear(bpy.types.Operator): backlash: bpy.props.FloatProperty(name="Backlash", default=0.0, min=0.0, max=0.1, precision=4, unit="LENGTH") rack_height: bpy.props.FloatProperty(name="Rack Height", default=0.012, min=0.001, max=1, precision=4, - unit="LENGTH") + unit="LENGTH") rack_tooth_per_hole: bpy.props.IntProperty(name="teeth per mounting hole", default=7, min=2) gear_type: EnumProperty(name='Type of gear', items=(('PINION', 'Pinion', 'circular gear'), diff --git a/scripts/addons/cam/curvecamequation.py b/scripts/addons/cam/curvecamequation.py index 7ed90f90a..2c3935cb8 100644 --- a/scripts/addons/cam/curvecamequation.py +++ b/scripts/addons/cam/curvecamequation.py @@ -45,15 +45,21 @@ class CamSineCurve(bpy.types.Operator): ('triangle', 'Triangle Wave', 'triangle wave'), ('cycloid', 'Cycloid', 'Sine wave rectification'), ('invcycloid', 'Inverse Cycloid', 'Sine wave rectification')), default='sine') - amplitude: bpy.props.FloatProperty(name="Amplitude", default=.01, min=0, max=10, precision=4, unit="LENGTH") - period: bpy.props.FloatProperty(name="Period", default=.5, min=0.001, max=100, precision=4, unit="LENGTH") + amplitude: bpy.props.FloatProperty( + name="Amplitude", default=.01, min=0, max=10, precision=4, unit="LENGTH") + period: bpy.props.FloatProperty(name="Period", default=.5, + min=0.001, max=100, precision=4, unit="LENGTH") beatperiod: bpy.props.FloatProperty(name="Beat Period offset", default=0.0, min=0.0, max=100, precision=4, unit="LENGTH") - shift: bpy.props.FloatProperty(name="phase shift", default=0, min=-360, max=360, precision=4, unit="ROTATION") - offset: bpy.props.FloatProperty(name="offset", default=0, min=-1.0, max=1, precision=4, unit="LENGTH") + shift: bpy.props.FloatProperty(name="phase shift", default=0, + min=-360, max=360, precision=4, unit="ROTATION") + offset: bpy.props.FloatProperty(name="offset", default=0, min=- + 1.0, max=1, precision=4, unit="LENGTH") iteration: bpy.props.IntProperty(name="iteration", default=100, min=50, max=2000) - maxt: bpy.props.FloatProperty(name="Wave ends at x", default=0.5, min=-3.0, max=3, precision=4, unit="LENGTH") - mint: bpy.props.FloatProperty(name="Wave starts at x", default=0, min=-3.0, max=3, precision=4, unit="LENGTH") + maxt: bpy.props.FloatProperty(name="Wave ends at x", default=0.5, + min=-3.0, max=3, precision=4, unit="LENGTH") + mint: bpy.props.FloatProperty(name="Wave starts at x", default=0, + min=-3.0, max=3, precision=4, unit="LENGTH") wave_distance: bpy.props.FloatProperty(name="distance between multiple waves", default=0.0, min=0.0, max=100, precision=4, unit="LENGTH") wave_angle_offset: bpy.props.FloatProperty(name="angle offset for multiple waves", default=math.pi/2, @@ -64,18 +70,22 @@ def execute(self, context): # z=Asin(B(x+C))+D if self.wave == 'sine': - zstring = ssine(self.amplitude, self.period, dc_offset=self.offset, phase_shift=self.shift) + zstring = ssine(self.amplitude, self.period, + dc_offset=self.offset, phase_shift=self.shift) if self.beatperiod != 0: zstring += "+"+ssine(self.amplitude, self.period+self.beatperiod, dc_offset=self.offset, phase_shift=self.shift) elif self.wave == 'triangle': # build triangle wave from fourier series - zstring = str(round(self.offset, 6)) + "+(" + str(triangle(80, self.period, self.amplitude))+")" + zstring = str(round(self.offset, 6)) + \ + "+(" + str(triangle(80, self.period, self.amplitude))+")" if self.beatperiod != 0: zstring += '+' + str(triangle(80, self.period+self.beatperiod, self.amplitude)) elif self.wave == 'cycloid': - zstring = "abs("+ssine(self.amplitude, self.period, dc_offset=self.offset, phase_shift=self.shift)+")" + zstring = "abs("+ssine(self.amplitude, self.period, + dc_offset=self.offset, phase_shift=self.shift)+")" elif self.wave == 'invcycloid': - zstring = "-1*abs("+ssine(self.amplitude, self.period, dc_offset=self.offset, phase_shift=self.shift)+")" + zstring = "-1*abs("+ssine(self.amplitude, self.period, + dc_offset=self.offset, phase_shift=self.shift)+")" print(zstring) e = Expression(zstring, ["t"]) # make equation from string @@ -106,24 +116,33 @@ class CamLissajousCurve(bpy.types.Operator): bl_label = "Create Lissajous figure" bl_options = {'REGISTER', 'UNDO', 'PRESET'} - amplitude_A: bpy.props.FloatProperty(name="Amplitude A", default=.1, min=0, max=100, precision=4, unit="LENGTH") + amplitude_A: bpy.props.FloatProperty( + name="Amplitude A", default=.1, min=0, max=100, precision=4, unit="LENGTH") waveA: bpy.props.EnumProperty(name="Wave X", items=( ('sine', 'Sine Wave', 'Sine Wave'), ('triangle', 'Triangle Wave', 'triangle wave')), default='sine') - amplitude_B: bpy.props.FloatProperty(name="Amplitude B", default=.1, min=0, max=100, precision=4, unit="LENGTH") + amplitude_B: bpy.props.FloatProperty( + name="Amplitude B", default=.1, min=0, max=100, precision=4, unit="LENGTH") waveB: bpy.props.EnumProperty(name="Wave Y", items=( ('sine', 'Sine Wave', 'Sine Wave'), ('triangle', 'Triangle Wave', 'triangle wave')), default='sine') - period_A: bpy.props.FloatProperty(name="Period A", default=1.1, min=0.001, max=100, precision=4, unit="LENGTH") - period_B: bpy.props.FloatProperty(name="Period B", default=1.0, min=0.001, max=100, precision=4, unit="LENGTH") - period_Z: bpy.props.FloatProperty(name="Period Z", default=1.0, min=0.001, max=100, precision=4, unit="LENGTH") - amplitude_Z: bpy.props.FloatProperty(name="Amplitude Z", default=0.0, min=0, max=100, precision=4, unit="LENGTH") - shift: bpy.props.FloatProperty(name="phase shift", default=0, min=-360, max=360, precision=4, unit="ROTATION") + period_A: bpy.props.FloatProperty(name="Period A", default=1.1, + min=0.001, max=100, precision=4, unit="LENGTH") + period_B: bpy.props.FloatProperty(name="Period B", default=1.0, + min=0.001, max=100, precision=4, unit="LENGTH") + period_Z: bpy.props.FloatProperty(name="Period Z", default=1.0, + min=0.001, max=100, precision=4, unit="LENGTH") + amplitude_Z: bpy.props.FloatProperty( + name="Amplitude Z", default=0.0, min=0, max=100, precision=4, unit="LENGTH") + shift: bpy.props.FloatProperty(name="phase shift", default=0, + min=-360, max=360, precision=4, unit="ROTATION") iteration: bpy.props.IntProperty(name="iteration", default=500, min=50, max=10000) - maxt: bpy.props.FloatProperty(name="Wave ends at x", default=11, min=-3.0, max=1000000, precision=4, unit="LENGTH") - mint: bpy.props.FloatProperty(name="Wave starts at x", default=0, min=-10.0, max=3, precision=4, unit="LENGTH") + maxt: bpy.props.FloatProperty(name="Wave ends at x", default=11, + min=-3.0, max=1000000, precision=4, unit="LENGTH") + mint: bpy.props.FloatProperty(name="Wave starts at x", default=0, + min=-10.0, max=3, precision=4, unit="LENGTH") def execute(self, context): # x=Asin(at+delta ),y=Bsin(bt) @@ -166,12 +185,14 @@ class CamHypotrochoidCurve(bpy.types.Operator): typecurve: bpy.props.EnumProperty(name="type of curve", items=( ('hypo', 'Hypotrochoid', 'inside ring'), ('epi', 'Epicycloid', 'outside inner ring'))) - R: bpy.props.FloatProperty(name="Big circle radius", default=0.25, min=0.001, max=100, precision=4, unit="LENGTH") + R: bpy.props.FloatProperty(name="Big circle radius", default=0.25, + min=0.001, max=100, precision=4, unit="LENGTH") r: bpy.props.FloatProperty(name="Small circle radius", default=0.18, min=0.0001, max=100, precision=4, unit="LENGTH") d: bpy.props.FloatProperty(name="distance from center of interior circle", default=0.050, min=0, max=100, precision=4, unit="LENGTH") - dip: bpy.props.FloatProperty(name="variable depth from center", default=0.00, min=-100, max=100, precision=4) + dip: bpy.props.FloatProperty(name="variable depth from center", + default=0.00, min=-100, max=100, precision=4) def execute(self, context): r = round(self.r, 6) @@ -190,7 +211,8 @@ def execute(self, context): xstring = str(Rpr) + "*cos(t)-" + str(d) + "*cos(" + str(Rpror) + "*t)" ystring = str(Rpr) + "*sin(t)-" + str(d) + "*sin(" + str(Rpror) + "*t)" - zstring = '(' + str(round(self.dip, 6)) + '*(sqrt(((' + xstring + ')**2)+((' + ystring + ')**2))))' + zstring = '(' + str(round(self.dip, 6)) + \ + '*(sqrt(((' + xstring + ')**2)+((' + ystring + ')**2))))' print("x= " + str(xstring)) print("y= " + str(ystring)) @@ -210,7 +232,8 @@ def f(t, offset: float = 0.0): if iter > 10000: # do not calculate more than 10000 points print("limiting calculations to 10000 points") iter = 10000 - parametric.create_parametric_curve(f, offset=0.0, min=0, max=maxangle, use_cubic=True, iterations=iter) + parametric.create_parametric_curve( + f, offset=0.0, min=0, max=maxangle, use_cubic=True, iterations=iter) return {'FINISHED'} @@ -223,11 +246,14 @@ class CamCustomCurve(bpy.types.Operator): xstring: StringProperty(name="X equation", description="Equation x=F(t)", default="t") ystring: StringProperty(name="Y equation", description="Equation y=F(t)", default="0") - zstring: StringProperty(name="Z equation", description="Equation z=F(t)", default="0.05*sin(2*pi*4*t)") + zstring: StringProperty(name="Z equation", description="Equation z=F(t)", + default="0.05*sin(2*pi*4*t)") iteration: bpy.props.IntProperty(name="iteration", default=100, min=50, max=2000) - maxt: bpy.props.FloatProperty(name="Wave ends at x", default=0.5, min=-3.0, max=10, precision=4, unit="LENGTH") - mint: bpy.props.FloatProperty(name="Wave starts at x", default=0, min=-3.0, max=3, precision=4, unit="LENGTH") + maxt: bpy.props.FloatProperty(name="Wave ends at x", default=0.5, + min=-3.0, max=10, precision=4, unit="LENGTH") + mint: bpy.props.FloatProperty(name="Wave starts at x", default=0, + min=-3.0, max=3, precision=4, unit="LENGTH") def execute(self, context): print("x= " + self.xstring) diff --git a/scripts/addons/cam/curvecamtools.py b/scripts/addons/cam/curvecamtools.py index 05e0a3699..582b421dd 100644 --- a/scripts/addons/cam/curvecamtools.py +++ b/scripts/addons/cam/curvecamtools.py @@ -113,7 +113,8 @@ def execute(self, context): # Perimeter cut largen then intarsion pocket externally, optional - diam = self.diameter * 1.05 + self.backlight * 2 # make the diameter 5% larger and compensate for backlight + # make the diameter 5% larger and compensate for backlight + diam = self.diameter * 1.05 + self.backlight * 2 utils.silhoueteOffset(context, -diam / 2) o1 = bpy.context.active_object @@ -137,7 +138,8 @@ def execute(self, context): o3.select_set(True) context.view_layer.objects.active = o3 # intarsion profile is the inside piece of the intarsion - utils.silhoueteOffset(context, -self.tolerance / 2) # make smaller curve for material profile + # make smaller curve for material profile + utils.silhoueteOffset(context, -self.tolerance / 2) bpy.context.object.location[2] = self.intarsion_thickness o4 = bpy.context.active_object bpy.context.active_object.name = "intarsion_profil" @@ -146,7 +148,8 @@ def execute(self, context): if self.backlight > 0.0: # Make a smaller curve for backlighting purposes utils.silhoueteOffset(context, (-self.tolerance / 2) - self.backlight) bpy.context.active_object.name = "intarsion_backlight" - bpy.context.object.location[2] = -self.backlight_depth_from_top - self.intarsion_thickness + bpy.context.object.location[2] = - \ + self.backlight_depth_from_top - self.intarsion_thickness o4.select_set(True) o3.select_set(True) return {'FINISHED'} @@ -159,7 +162,8 @@ class CamCurveOvercuts(bpy.types.Operator): bl_label = "Add Overcuts" bl_options = {'REGISTER', 'UNDO'} - diameter: bpy.props.FloatProperty(name="diameter", default=.003175, min=0, max=100, precision=4, unit="LENGTH") + diameter: bpy.props.FloatProperty( + name="diameter", default=.003175, min=0, max=100, precision=4, unit="LENGTH") threshold: bpy.props.FloatProperty(name="threshold", default=math.pi / 2 * .99, min=-3.14, max=3.14, precision=4, subtype="ANGLE", unit="ROTATION") do_outer: bpy.props.BoolProperty(name="Outer polygons", default=True) @@ -349,7 +353,6 @@ def getCornerDelta(curidx, nextidx): else: loops = s.boundary - outercurve = self.do_outer or len(loops.geoms) == 1 for ci, c in enumerate(loops.geoms): if ci > 0 or outercurve: @@ -606,8 +609,10 @@ class CamOffsetSilhouete(bpy.types.Operator): bl_label = "Silhouete offset" bl_options = {'REGISTER', 'UNDO', 'PRESET'} - offset: bpy.props.FloatProperty(name="offset", default=.003, min=-100, max=100, precision=4, unit="LENGTH") - mitrelimit: bpy.props.FloatProperty(name="Mitre Limit", default=.003, min=0.0, max=20, precision=4, unit="LENGTH") + offset: bpy.props.FloatProperty(name="offset", default=.003, + min=-100, max=100, precision=4, unit="LENGTH") + mitrelimit: bpy.props.FloatProperty( + name="Mitre Limit", default=.003, min=0.0, max=20, precision=4, unit="LENGTH") style: bpy.props.EnumProperty(name="type of curve", items=( ('1', 'Round', ''), ('2', 'Mitre', ''), ('3', 'Bevel', ''))) opencurve: bpy.props.BoolProperty(name="Dialate open curve", default=False) @@ -615,8 +620,8 @@ class CamOffsetSilhouete(bpy.types.Operator): @classmethod def poll(cls, context): return context.active_object is not None and ( - context.active_object.type == 'CURVE' or context.active_object.type == 'FONT' or - context.active_object.type == 'MESH') + context.active_object.type == 'CURVE' or context.active_object.type == 'FONT' or + context.active_object.type == 'MESH') def execute(self, context): # this is almost same as getobjectoutline, just without the need of operation data bpy.ops.object.curve_remove_doubles() @@ -624,7 +629,8 @@ def execute(self, context): # this is almost same as getobjectoutline, just wit if self.opencurve and ob.type == 'CURVE': bpy.ops.object.duplicate() obj = context.active_object - bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) # apply all transforms + bpy.ops.object.transform_apply( + location=True, rotation=True, scale=True) # apply all transforms bpy.context.object.data.resolution_u = 60 bpy.ops.object.convert(target='MESH') bpy.context.active_object.name = "temp_mesh" @@ -659,8 +665,8 @@ def poll(cls, context): # return context.active_object is not None and (context.active_object.type == 'CURVE' # or context.active_object.type == 'FONT' or context.active_object.type == 'MESH') return context.active_object is not None and ( - context.active_object.type == 'FONT' or - context.active_object.type == 'MESH') + context.active_object.type == 'FONT' or + context.active_object.type == 'MESH') def execute(self, context): # this is almost same as getobjectoutline, just without the need of operation data ob = bpy.context.active_object diff --git a/scripts/addons/cam/exception.py b/scripts/addons/cam/exception.py index 71b3ace3d..8d46ffc8c 100644 --- a/scripts/addons/cam/exception.py +++ b/scripts/addons/cam/exception.py @@ -1,2 +1,2 @@ class CamException(Exception): - pass \ No newline at end of file + pass diff --git a/scripts/addons/cam/gcodeimportparser.py b/scripts/addons/cam/gcodeimportparser.py index 5e6131042..81dd9beca 100644 --- a/scripts/addons/cam/gcodeimportparser.py +++ b/scripts/addons/cam/gcodeimportparser.py @@ -18,7 +18,8 @@ # # ***** END GPL LICENCE BLOCK ***** -import bpy, bmesh +import bpy +import bmesh import math import re @@ -375,7 +376,7 @@ def classifySegments(self): # some horizontal movement, and positive extruder movement: extrusion if ( ((seg.coords["X"] != coords["X"]) or (seg.coords["Y"] != coords["Y"]) or ( - seg.coords["Z"] != coords["Z"]))): # != coords["E"] + seg.coords["Z"] != coords["Z"]))): # != coords["E"] style = "extrude" # #force extrude if there is some movement @@ -386,7 +387,8 @@ def classifySegments(self): currentLayerIdx += 1 seg.style = style seg.layerIdx = currentLayerIdx - self.layers.append(layer) # add layer to list of Layers, used to later draw single layer objects + # add layer to list of Layers, used to later draw single layer objects + self.layers.append(layer) break # positive extruder movement of next point in a different Z signals a layer change for this segment @@ -435,7 +437,8 @@ def subdivide(self, subd_threshold): P2 = seg.coords # interpolated points - interp_coords = np.linspace(list(P1.values()), list(P2.values()), num=subdivs, endpoint=True) + interp_coords = np.linspace(list(P1.values()), list( + P2.values()), num=subdivs, endpoint=True) for i in range(len(interp_coords)): # inteprolated points array back to segment object @@ -454,7 +457,8 @@ def subdivide(self, subd_threshold): # write segment only if movement changes, # avoid double coordinates due to same start and endpoint of linspace - new_seg = Segment(seg.type, new_coords, seg.color, seg.toolnumber, seg.lineNb, seg.line) + new_seg = Segment(seg.type, new_coords, seg.color, + seg.toolnumber, seg.lineNb, seg.line) new_seg.layerIdx = seg.layerIdx new_seg.style = seg.style subdivided_segs.append(new_seg) diff --git a/scripts/addons/cam/gcodepath.py b/scripts/addons/cam/gcodepath.py index b566c55fd..678bb37ac 100644 --- a/scripts/addons/cam/gcodepath.py +++ b/scripts/addons/cam/gcodepath.py @@ -60,6 +60,7 @@ from cam.opencamlib.opencamlib import * from cam.nc import iso + def pointonline(a, b, c, tolerence): b = b - a # convert to vector by subtracting origin c = c - a @@ -98,7 +99,8 @@ def exportGcodePath(filename, vertslist, operations): print('file will be separated into %i files' % filesnum) print('1') - basefilename = bpy.data.filepath[:-len(bpy.path.basename(bpy.data.filepath))] + safeFileName(filename) + basefilename = bpy.data.filepath[:- + len(bpy.path.basename(bpy.data.filepath))] + safeFileName(filename) extension = '.tap' if m.post_processor == 'ISO': @@ -158,7 +160,7 @@ def startNewFile(): if split: fileindex = '_' + str(findex) filename = basefilename + fileindex + extension - print("writing: ",filename) + print("writing: ", filename) c = postprocessor.Creator() # process user overrides for post processor settings @@ -323,7 +325,7 @@ def startNewFile(): # skip the first vertex if this is a chained operation # ie: outputting more than one operation # otherwise the machine gets sent back to 0,0 for each operation which is unecessary - shapes += 1 # Count amount of shapes + shapes += 1 # Count amount of shapes if i > 0 and vi == 0: continue v = vert.co @@ -435,9 +437,9 @@ def startNewFile(): c.rapid(x=vx, y=vy, z=vz) # this is to evaluate operation time and adds a feedrate for fast moves if vz is not None: - f = plungefeedrate * fadjustval * 0.35 # compensate for multiple fast move accelerations + f = plungefeedrate * fadjustval * 0.35 # compensate for multiple fast move accelerations if vx is not None or vy is not None: - f = freefeedrate * 0.8 # compensate for free feedrate acceleration + f = freefeedrate * 0.8 # compensate for free feedrate acceleration else: c.rapid(x=vx, y=vy, z=vz, a=ra, b=rb) @@ -451,7 +453,7 @@ def startNewFile(): c.feed(x=vx, y=vy, z=vz) else: c.feed(x=vx, y=vy, z=vz, a=ra, b=rb) - cut_distance+=vect.length * unitcorr + cut_distance += vect.length * unitcorr vector_duration = vect.length / f duration += vector_duration last = v @@ -494,7 +496,7 @@ def startNewFile(): c.write(aline + '\n') o.info.duration = duration * unitcorr - print("total time:",round(o.info.duration * 60),"seconds") + print("total time:", round(o.info.duration * 60), "seconds") if bpy.context.scene.unit_settings.system == 'METRIC': unit_distance = 'm' cut_distance /= 1000 @@ -502,7 +504,7 @@ def startNewFile(): unit_distance = 'feet' cut_distance /= 12 - print("cut distance:", round(cut_distance,3), unit_distance) + print("cut distance:", round(cut_distance, 3), unit_distance) if enable_dust: c.write(stop_dust + '\n') if enable_hold: @@ -542,14 +544,16 @@ async def getPath(context, operation): # should do all path calculations. print(operation.machine_axes) if operation.machine_axes == '3': - if USE_PROFILER == True: # profiler - import cProfile, pstats, io + if USE_PROFILER == True: # profiler + import cProfile + import pstats + import io pr = cProfile.Profile() pr.enable() await getPath3axis(context, operation) pr.disable() pr.dump_stats(time.strftime("blendercam_%Y%m%d_%H%M.prof")) - else: + else: await getPath3axis(context, operation) elif (operation.machine_axes == '5' and operation.strategy5axis == 'INDEXED') or ( @@ -680,7 +684,8 @@ async def getPath3axis(context, operation): chunks = chunksCoherency(chunks) print('coherency check') - if o.strategy in ['PARALLEL', 'CROSS', 'PENCIL', 'OUTLINEFILL']: # and not o.movement.parallel_step_back: + # and not o.movement.parallel_step_back: + if o.strategy in ['PARALLEL', 'CROSS', 'PENCIL', 'OUTLINEFILL']: print('sorting') chunks = await utils.sortChunks(chunks, o) if o.strategy == 'OUTLINEFILL': @@ -779,7 +784,8 @@ async def getPath3axis(context, operation): o.inverse and not poly.is_empty and slicesfilled == 1): # first slice fill restpoly = lastslice - restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.optimisation.circle_detail) + restpoly = restpoly.buffer(-o.dist_between_paths, + resolution=o.optimisation.circle_detail) fillz = z i = 0 @@ -796,7 +802,8 @@ async def getPath3axis(context, operation): parentChildDist(lastchunks, nchunks, o) lastchunks = nchunks # slicechunks.extend(polyToChunks(restpoly,z)) - restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.optimisation.circle_detail) + restpoly = restpoly.buffer(-o.dist_between_paths, + resolution=o.optimisation.circle_detail) i += 1 # print(i) @@ -814,10 +821,12 @@ async def getPath3axis(context, operation): if o.inverse and poly.is_empty and slicesfilled > 0: restpoly = bound_rectangle.difference(lastslice) - restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.optimisation.circle_detail) + restpoly = restpoly.buffer(-o.dist_between_paths, + resolution=o.optimisation.circle_detail) i = 0 - while not restpoly.is_empty: # 'GeometryCollection':#len(restpoly.boundary.coords)>0: + # 'GeometryCollection':#len(restpoly.boundary.coords)>0: + while not restpoly.is_empty: # print(i) nchunks = shapelyToChunks(restpoly, fillz) ######################### @@ -825,7 +834,8 @@ async def getPath3axis(context, operation): slicechunks.extend(nchunks) parentChildDist(lastchunks, nchunks, o) lastchunks = nchunks - restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.optimisation.circle_detail) + restpoly = restpoly.buffer(-o.dist_between_paths, + resolution=o.optimisation.circle_detail) i += 1 percent = int(h / nslices * 100) @@ -847,11 +857,11 @@ async def getPath3axis(context, operation): strategy.chunksToMesh(chunks, o) elif o.strategy == 'DRILL': - await strategy.drill(o) + await strategy.drill(o) elif o.strategy == 'MEDIAL_AXIS': await strategy.medial_axis(o) - await progress_async(f"Done",time.time() - tw,"s") + await progress_async(f"Done", time.time() - tw, "s") async def getPath4axis(context, operation): diff --git a/scripts/addons/cam/image_utils.py b/scripts/addons/cam/image_utils.py index e783716e4..85018cdab 100644 --- a/scripts/addons/cam/image_utils.py +++ b/scripts/addons/cam/image_utils.py @@ -36,11 +36,11 @@ from cam import simulation from cam.async_op import progress_async -from cam.numba_wrapper import jit,prange +from cam.numba_wrapper import jit, prange def getCircle(r, z): - car = numpy.full(shape=(r*2,r*2),fill_value=-10,dtype=numpy.double) + car = numpy.full(shape=(r*2, r*2), fill_value=-10, dtype=numpy.double) res = 2 * r m = r v = mathutils.Vector((0, 0, 0)) @@ -54,7 +54,7 @@ def getCircle(r, z): def getCircleBinary(r): - car = numpy.full(shape=(r*2,r*2),fill_value=False,dtype=bool) + car = numpy.full(shape=(r*2, r*2), fill_value=False, dtype=bool) res = 2 * r m = r v = mathutils.Vector((0, 0, 0)) @@ -118,7 +118,7 @@ def imagetonumpy(i): width = i.size[0] height = i.size[1] - na = numpy.full(shape=(width*height*4,),fill_value=-10,dtype=numpy.double) + na = numpy.full(shape=(width*height*4,), fill_value=-10, dtype=numpy.double) p = i.pixels[:] # these 2 lines are about 15% faster than na[:]=i.pixels[:].... whyyyyyyyy!!?!?!?!?! @@ -132,11 +132,12 @@ def imagetonumpy(i): return na -@jit(nopython=True,parallel=True,fastmath=False,cache=True) -def _offset_inner_loop(y1,y2,cutterArrayNan,cwidth,sourceArray,width,height,comparearea): - for y in prange(y1,y2): - for x in range(0,width-cwidth): - comparearea[x,y] = numpy.nanmax(sourceArray[x:x+cwidth,y:y+cwidth] + cutterArrayNan) +@jit(nopython=True, parallel=True, fastmath=False, cache=True) +def _offset_inner_loop(y1, y2, cutterArrayNan, cwidth, sourceArray, width, height, comparearea): + for y in prange(y1, y2): + for x in range(0, width-cwidth): + comparearea[x, y] = numpy.nanmax(sourceArray[x:x+cwidth, y:y+cwidth] + cutterArrayNan) + async def offsetArea(o, samples): """ offsets the whole image with the cutter + skin offsets """ @@ -151,7 +152,7 @@ async def offsetArea(o, samples): width = len(sourceArray) height = len(sourceArray[0]) cwidth = len(cutterArray) - o.offset_image= numpy.full(shape=(width,height),fill_value=-10.0,dtype=numpy.double) + o.offset_image = numpy.full(shape=(width, height), fill_value=-10.0, dtype=numpy.double) t = time.time() @@ -161,11 +162,13 @@ async def offsetArea(o, samples): sourceArray = -sourceArray + minz comparearea = o.offset_image[m: width - cwidth + m, m:height - cwidth + m] # i=0 - cutterArrayNan=np.where(cutterArray>-10,cutterArray,np.full(cutterArray.shape,np.nan)) - for y in range(0,10): + cutterArrayNan = np.where(cutterArray > -10, cutterArray, + np.full(cutterArray.shape, np.nan)) + for y in range(0, 10): y1 = (y * comparearea.shape[1])//10 y2 = ((y+1) * comparearea.shape[1])//10 - _offset_inner_loop(y1,y2,cutterArrayNan,cwidth,sourceArray,width,height,comparearea) + _offset_inner_loop(y1, y2, cutterArrayNan, cwidth, + sourceArray, width, height, comparearea) await progress_async('offset depth image', int((y2 * 100) / comparearea.shape[1])) o.offset_image[m: width - cwidth + m, m:height - cwidth + m] = comparearea @@ -202,7 +205,7 @@ def getOffsetImageCavities(o, i): # for pencil operation mainly # ##crop pixels that are on outer borders for chi in range(len(chunks) - 1, -1, -1): chunk = chunks[chi] - chunk.clip_points(o.min.x,o.max.x,o.min.y,o.max.y) + chunk.clip_points(o.min.x, o.max.x, o.min.y, o.max.y) # for si in range(len(chunk.points) - 1, -1, -1): # if not (o.min.x < chunk.points[si][0] < o.max.x and o.min.y < chunk.points[si][1] < o.max.y): # chunk.points.pop(si) @@ -347,8 +350,9 @@ async def crazyPath(o): resx = ceil(sx / o.optimisation.simulation_detail) + 2 * o.borderwidth resy = ceil(sy / o.optimisation.simulation_detail) + 2 * o.borderwidth - o.millimage = numpy.full(shape=(resx,resy),fill_value=0.,dtype=numpy.float) - o.cutterArray = -simulation.getCutterArray(o, o.optimisation.simulation_detail) # getting inverted cutter + o.millimage = numpy.full(shape=(resx, resy), fill_value=0., dtype=numpy.float) + # getting inverted cutter + o.cutterArray = -simulation.getCutterArray(o, o.optimisation.simulation_detail) def buildStroke(start, end, cutterArray): @@ -357,7 +361,7 @@ def buildStroke(start, end, cutterArray): size_y = abs(end[1] - start[1]) + cutterArray.size[0] r = cutterArray.size[0] / 2 - strokeArray = numpy.full(shape=(size_x,size_y),fill_value=-10.0,dtype=numpy.float) + strokeArray = numpy.full(shape=(size_x, size_y), fill_value=-10.0, dtype=numpy.float) samplesx = numpy.round(numpy.linspace(start[0], end[0], strokelength)) samplesy = numpy.round(numpy.linspace(start[1], end[1], strokelength)) samplesz = numpy.round(numpy.linspace(start[2], end[2], strokelength)) @@ -384,7 +388,8 @@ def crazyStrokeImage(o): # this surprisingly works, and can be used as a basis for something similar to adaptive milling strategy. minx, miny, minz, maxx, maxy, maxz = o.min.x, o.min.y, o.min.z, o.max.x, o.max.y, o.max.z - r = int((o.cutter_diameter / 2.0) / o.optimisation.pixsize) # ceil((o.cutter_diameter/12)/o.optimisation.pixsize) + # ceil((o.cutter_diameter/12)/o.optimisation.pixsize) + r = int((o.cutter_diameter / 2.0) / o.optimisation.pixsize) d = 2 * r coef = 0.75 @@ -396,7 +401,8 @@ def crazyStrokeImage(o): cutterArrayNegative = -cutterArray cutterimagepix = cutterArray.sum() - satisfypix = cutterimagepix * o.crazy_threshold1 # a threshold which says if it is valuable to cut in a direction + # a threshold which says if it is valuable to cut in a direction + satisfypix = cutterimagepix * o.crazy_threshold1 toomuchpix = cutterimagepix * o.crazy_threshold2 indices = ar.nonzero() # first get white pixels startpix = ar.sum() # @@ -411,7 +417,8 @@ def crazyStrokeImage(o): nchunk = camPathChunkBuilder([(xs, ys)]) # startposition print(indices) print(indices[0][0], indices[1][0]) - lastvect = Vector((r, 0, 0)) # vector is 3d, blender somehow doesn't rotate 2d vectors with angles. + # vector is 3d, blender somehow doesn't rotate 2d vectors with angles. + lastvect = Vector((r, 0, 0)) testvect = lastvect.normalized() * r / 2.0 # multiply *2 not to get values <1 pixel rot = Euler((0, 0, 1)) i = 0 @@ -422,7 +429,8 @@ def crazyStrokeImage(o): maxtotaltests = 1000000 print(xs, ys, indices[0][0], indices[1][0], r) - ar[xs - r:xs - r + d, ys - r:ys - r + d] = ar[xs - r:xs - r + d, ys - r:ys - r + d] * cutterArrayNegative + ar[xs - r:xs - r + d, ys - r:ys - r + d] = ar[xs - + r:xs - r + d, ys - r:ys - r + d] * cutterArrayNegative anglerange = [-pi, pi] # range for angle of toolpath vector versus material vector testangleinit = 0 angleincrement = 0.05 @@ -469,7 +477,8 @@ def crazyStrokeImage(o): if success: nchunk.points.append([xs, ys]) lastvect = testvect - ar[xs - r:xs - r + d, ys - r:ys - r + d] = ar[xs - r:xs - r + d, ys - r:ys - r + d] * (-cutterArray) + ar[xs - r:xs - r + d, ys - r:ys - r + d] = ar[xs - + r:xs - r + d, ys - r:ys - r + d] * (-cutterArray) totpix -= eatpix itests = 0 if 0: @@ -531,7 +540,7 @@ def crazyStrokeImage(o): ys = r nchunk = camPathChunkBuilder([(xs, ys)]) # startposition ar[xs - r:xs - r + d, ys - r:ys - r + d] = ar[xs - r:xs - r + d, - ys - r:ys - r + d] * cutterArrayNegative + ys - r:ys - r + d] * cutterArrayNegative r = random.random() * 2 * pi e = Euler((0, 0, r)) testvect = lastvect.normalized() * 4 # multiply *2 not to get values <1 pixel @@ -569,7 +578,8 @@ def crazyStrokeImageBinary(o, ar, avoidar): ar[:, :o.borderwidth] = 0 ar[:, -o.borderwidth:] = 0 - r = int((o.cutter_diameter / 2.0) / o.optimisation.pixsize) # ceil((o.cutter_diameter/12)/o.optimisation.pixsize) + # ceil((o.cutter_diameter/12)/o.optimisation.pixsize) + r = int((o.cutter_diameter / 2.0) / o.optimisation.pixsize) d = 2 * r coef = 0.75 maxarx = ar.shape[0] @@ -581,9 +591,11 @@ def crazyStrokeImageBinary(o, ar, avoidar): cutterimagepix = cutterArray.sum() anglelimit = o.crazy_threshold3 - satisfypix = cutterimagepix * o.crazy_threshold1 # a threshold which says if it is valuable to cut in a direction + # a threshold which says if it is valuable to cut in a direction + satisfypix = cutterimagepix * o.crazy_threshold1 toomuchpix = cutterimagepix * o.crazy_threshold2 # same, but upper limit - optimalpix = cutterimagepix * o.crazy_threshold5 # (satisfypix+toomuchpix)/2.0# the ideal eating ratio + # (satisfypix+toomuchpix)/2.0# the ideal eating ratio + optimalpix = cutterimagepix * o.crazy_threshold5 indices = ar.nonzero() # first get white pixels startpix = ar.sum() # @@ -602,7 +614,8 @@ def crazyStrokeImageBinary(o, ar, avoidar): nchunk = camPathChunkBuilder([(xs, ys)]) # startposition print(indices) print(indices[0][0], indices[1][0]) - lastvect = Vector((r, 0, 0)) # vector is 3d, blender somehow doesn't rotate 2d vectors with angles. + # vector is 3d, blender somehow doesn't rotate 2d vectors with angles. + lastvect = Vector((r, 0, 0)) testvect = lastvect.normalized() * r / 4.0 # multiply *2 not to get values <1 pixel rot = Euler((0, 0, 1)) i = 0 @@ -687,7 +700,8 @@ def crazyStrokeImageBinary(o, ar, avoidar): nchunk.points.append([xs, ys]) lastvect = testvect - ar[xs - r:xs + r, ys - r:ys + r] = ar[xs - r:xs + r, ys - r:ys + r] * cutterArrayNegative + ar[xs - r:xs + r, ys - r:ys + r] = ar[xs - + r:xs + r, ys - r:ys + r] * cutterArrayNegative totpix -= bestsolution[1] itests = 0 # if 0: @@ -781,7 +795,7 @@ def crazyStrokeImageBinary(o, ar, avoidar): nchunk = camPathChunk([(xs, ys)]) # startposition ar[xs - r:xs + r, ys - r:ys + r] = ar[xs - r:xs + r, - ys - r:ys + r] * cutterArrayNegative + ys - r:ys + r] * cutterArrayNegative # lastvect=Vector((r,0,0))#vector is 3d, # blender somehow doesn't rotate 2d vectors with angles. randomrot = random.random() * 2 * pi @@ -968,7 +982,8 @@ def imageToChunks(o, image, with_border=False): # print('directsimplify') reduxratio = 1.25 # was 1.25 - soptions = ['distance', 'distance', o.optimisation.pixsize * reduxratio, 5, o.optimisation.pixsize * reduxratio] + soptions = ['distance', 'distance', o.optimisation.pixsize * + reduxratio, 5, o.optimisation.pixsize * reduxratio] nchunks = [] for i, ch in enumerate(vecchunks): @@ -992,6 +1007,7 @@ def imageToShapely(o, i, with_border=False): return polys + def getSampleImage(s, sarray, minz): x = s[0] y = s[1] @@ -1002,10 +1018,10 @@ def getSampleImage(s, sarray, minz): maxx = minx + 1 miny = floor(y) maxy = miny + 1 - s1a=sarray[minx,miny] - s2a=sarray[maxx,miny] - s1b=sarray[minx,maxy] - s2b=sarray[maxx,maxy] + s1a = sarray[minx, miny] + s2a = sarray[maxx, miny] + s1b = sarray[minx, maxy] + s2b = sarray[maxx, maxy] # s1a = sarray.item(minx, miny) # most optimal access to array so far # s2a = sarray.item(maxx, miny) # s1b = sarray.item(minx, maxy) @@ -1029,28 +1045,30 @@ def getResolution(o): def _backup_render_settings(pairs): - properties=[] - for owner,struct_name in pairs: - obj = getattr(owner,struct_name) - if isinstance(obj,bpy.types.bpy_struct): + properties = [] + for owner, struct_name in pairs: + obj = getattr(owner, struct_name) + if isinstance(obj, bpy.types.bpy_struct): # structure, backup all properties - obj_value={} + obj_value = {} for k in dir(obj): if not k.startswith("_"): - obj_value[k]=getattr(obj,k) + obj_value[k] = getattr(obj, k) properties.append(obj_value) else: # simple value properties.append(obj) -def _restore_render_settings(pairs,properties): - for (owner,struct_name),obj_value in zip(pairs,properties): - obj = getattr(owner,struct_name) - if isinstance(obj,bpy.types.bpy_struct): - for k,v in obj_value.items(): - setattr(obj,k,v) + +def _restore_render_settings(pairs, properties): + for (owner, struct_name), obj_value in zip(pairs, properties): + obj = getattr(owner, struct_name) + if isinstance(obj, bpy.types.bpy_struct): + for k, v in obj_value.items(): + setattr(obj, k, v) else: - setattr(owner,struct_name,obj_value) + setattr(owner, struct_name, obj_value) + def renderSampleImage(o): t = time.time() @@ -1076,11 +1094,11 @@ def renderSampleImage(o): try: i = bpy.data.images.load(iname) if i.size[0] != resx or i.size[1] != resy: - print("Z buffer size changed:",i.size,resx,resy) + print("Z buffer size changed:", i.size, resx, resy) o.update_zbufferimage_tag = True - + except: - + o.update_zbufferimage_tag = True if o.update_zbufferimage_tag: s = bpy.context.scene @@ -1090,22 +1108,22 @@ def renderSampleImage(o): r = s.render SETTINGS_TO_BACKUP = [ - (s.render,"resolution_x"), - (s.render,"resolution_x"), - (s.cycles,"samples"), - (s,"camera"), - (vl,"samples"), - (vl.cycles,"use_denoising"), - (s.world,"mist_settings"), - (r,"resolution_x"), - (r,"resolution_y"), - (r,"resolution_percentage"), + (s.render, "resolution_x"), + (s.render, "resolution_x"), + (s.cycles, "samples"), + (s, "camera"), + (vl, "samples"), + (vl.cycles, "use_denoising"), + (s.world, "mist_settings"), + (r, "resolution_x"), + (r, "resolution_y"), + (r, "resolution_percentage"), ] for ob in s.objects: - SETTINGS_TO_BACKUP.append((ob,"hide_render")) - backup_settings=None + SETTINGS_TO_BACKUP.append((ob, "hide_render")) + backup_settings = None try: - backup_settings=_backup_render_settings(SETTINGS_TO_BACKUP) + backup_settings = _backup_render_settings(SETTINGS_TO_BACKUP) # prepare nodes first r.resolution_x = resx r.resolution_y = resy @@ -1113,19 +1131,19 @@ def renderSampleImage(o): # it renders okay on github actions r.engine = 'CYCLES' s.cycles.samples = 1 - vl.samples=1 - vl.cycles.use_denoising=False + vl.samples = 1 + vl.cycles.use_denoising = False n.links.clear() n.nodes.clear() - node_in = n.nodes.new('CompositorNodeRLayers') - s.view_layers[node_in.layer].use_pass_mist=True - mist_settings=s.world.mist_settings - s.world.mist_settings.depth=10.0 - s.world.mist_settings.start=0 - s.world.mist_settings.falloff="LINEAR" - s.world.mist_settings.height=0 - s.world.mist_settings.intensity=0 + node_in = n.nodes.new('CompositorNodeRLayers') + s.view_layers[node_in.layer].use_pass_mist = True + mist_settings = s.world.mist_settings + s.world.mist_settings.depth = 10.0 + s.world.mist_settings.start = 0 + s.world.mist_settings.falloff = "LINEAR" + s.world.mist_settings.height = 0 + s.world.mist_settings.intensity = 0 node_out = n.nodes.new("CompositorNodeOutputFile") node_out.base_path = os.path.dirname(iname) node_out.format.file_format = 'OPEN_EXR' @@ -1136,19 +1154,20 @@ def renderSampleImage(o): ################### # resize operation image - o.offset_image= numpy.full(shape=(resx,resy),fill_value=-10,dtype=numpy.double) + o.offset_image = numpy.full(shape=(resx, resy), fill_value=-10, dtype=numpy.double) # various settings for faster render r.resolution_percentage = 100 # add a new camera settings bpy.ops.object.camera_add(align='WORLD', enter_editmode=False, location=(0, 0, 0), - rotation=(0, 0, 0)) + rotation=(0, 0, 0)) camera = bpy.context.active_object bpy.context.scene.camera = camera camera.data.type = 'ORTHO' - camera.data.ortho_scale = max(resx * o.optimisation.pixsize, resy * o.optimisation.pixsize) + camera.data.ortho_scale = max( + resx * o.optimisation.pixsize, resy * o.optimisation.pixsize) camera.location = (o.min.x + sx / 2, o.min.y + sy / 2, 1) camera.rotation_euler = (0, 0, 0) camera.data.clip_end = 10.0 @@ -1169,21 +1188,19 @@ def renderSampleImage(o): camera.select_set(True) bpy.ops.object.delete() - os.replace(iname+"%04d.exr"%(s.frame_current),iname) + os.replace(iname+"%04d.exr" % (s.frame_current), iname) finally: if backup_settings is not None: - _restore_render_settings(SETTINGS_TO_BACKUP,backup_settings) + _restore_render_settings(SETTINGS_TO_BACKUP, backup_settings) else: print("Failed to backup scene settings") - i = bpy.data.images.load(iname) bpy.context.scene.render.engine = 'BLENDERCAM_RENDER' - a = imagetonumpy(i) a = 10.0 * a - a= 1.0 - a + a = 1.0 - a o.zbuffer_image = a o.update_zbufferimage_tag = False @@ -1210,9 +1227,11 @@ def renderSampleImage(o): mina = numpy.min(rawimage) neg = o.source_image_scale_z < 0 if o.strategy == 'WATERLINE': # waterline strategy needs image border to have ok ambient. - a = numpy.full(shape=(2 * o.borderwidth + i.size[0], 2 * o.borderwidth + i.size[1]),fill_value=1-neg,dtype=numpy.float) + a = numpy.full(shape=( + 2 * o.borderwidth + i.size[0], 2 * o.borderwidth + i.size[1]), fill_value=1-neg, dtype=numpy.float) else: # other operations like parallel need to reach the border - a = numpy.full(shape=(2 * o.borderwidth + i.size[0], 2 * o.borderwidth + i.size[1]),fill_value=neg,dtype=numpy.float) + a = numpy.full(shape=( + 2 * o.borderwidth + i.size[0], 2 * o.borderwidth + i.size[1]), fill_value=neg, dtype=numpy.float) # 2*o.borderwidth a[o.borderwidth:-o.borderwidth, o.borderwidth:-o.borderwidth] = rawimage a = a[sx:ex + o.borderwidth * 2, sy:ey + o.borderwidth * 2] diff --git a/scripts/addons/cam/involute_gear.py b/scripts/addons/cam/involute_gear.py index 925718c88..5f857455d 100644 --- a/scripts/addons/cam/involute_gear.py +++ b/scripts/addons/cam/involute_gear.py @@ -111,34 +111,35 @@ def gear(mm_per_tooth=0.003, number_of_teeth=5, hole_diameter=0.003175, b = p * math.cos(pressure_angle) # radius of base circle r = p-(c-p)-clearance # radius of root circle t = mm_per_tooth / 2 - backlash / 2 # tooth thickness at pitch circle - k = - gear_iang(b, p) - t / 2 / p # angle to where involute meets base circle on each side of tooth + # angle to where involute meets base circle on each side of tooth + k = - gear_iang(b, p) - t / 2 / p shapely_gear = Polygon([ - (0, 0), - gear_polar(r, k if r < b else -pi / number_of_teeth), - gear_q7(0, r, b, c, k, 1), - gear_q7(0.1, r, b, c, k, 1), - gear_q7(0.2, r, b, c, k, 1), - gear_q7(0.3, r, b, c, k, 1), - gear_q7(0.4, r, b, c, k, 1), - gear_q7(0.5, r, b, c, k, 1), - gear_q7(0.6, r, b, c, k, 1), - gear_q7(0.7, r, b, c, k, 1), - gear_q7(0.8, r, b, c, k, 1), - gear_q7(0.9, r, b, c, k, 1), - gear_q7(1.0, r, b, c, k, 1), - gear_q7(1.0, r, b, c, k, -1), - gear_q7(0.9, r, b, c, k, -1), - gear_q7(0.8, r, b, c, k, -1), - gear_q7(0.7, r, b, c, k, -1), - gear_q7(0.6, r, b, c, k, -1), - gear_q7(0.5, r, b, c, k, -1), - gear_q7(0.4, r, b, c, k, -1), - gear_q7(0.3, r, b, c, k, -1), - gear_q7(0.2, r, b, c, k, -1), - gear_q7(0.1, r, b, c, k, -1), - gear_q7(0.0, r, b, c, k, -1), - gear_polar(r, -k if r < b else pi / number_of_teeth) - ]) + (0, 0), + gear_polar(r, k if r < b else -pi / number_of_teeth), + gear_q7(0, r, b, c, k, 1), + gear_q7(0.1, r, b, c, k, 1), + gear_q7(0.2, r, b, c, k, 1), + gear_q7(0.3, r, b, c, k, 1), + gear_q7(0.4, r, b, c, k, 1), + gear_q7(0.5, r, b, c, k, 1), + gear_q7(0.6, r, b, c, k, 1), + gear_q7(0.7, r, b, c, k, 1), + gear_q7(0.8, r, b, c, k, 1), + gear_q7(0.9, r, b, c, k, 1), + gear_q7(1.0, r, b, c, k, 1), + gear_q7(1.0, r, b, c, k, -1), + gear_q7(0.9, r, b, c, k, -1), + gear_q7(0.8, r, b, c, k, -1), + gear_q7(0.7, r, b, c, k, -1), + gear_q7(0.6, r, b, c, k, -1), + gear_q7(0.5, r, b, c, k, -1), + gear_q7(0.4, r, b, c, k, -1), + gear_q7(0.3, r, b, c, k, -1), + gear_q7(0.2, r, b, c, k, -1), + gear_q7(0.1, r, b, c, k, -1), + gear_q7(0.0, r, b, c, k, -1), + gear_polar(r, -k if r < b else pi / number_of_teeth) + ]) utils.shapelyToCurve('tooth', shapely_gear, 0.0) i = number_of_teeth while i > 1: @@ -170,7 +171,8 @@ def gear(mm_per_tooth=0.003, number_of_teeth=5, hole_diameter=0.003175, simple.join_multiple('_') - simple.add_rectangle(r-rim_size-((hub_diameter-hole_diameter)/4 + hole_diameter/2), hub_diameter/2, center_x=False) + simple.add_rectangle(r-rim_size-((hub_diameter-hole_diameter)/4 + + hole_diameter/2), hub_diameter/2, center_x=False) simple.move(x=(hub_diameter-hole_diameter)/4 + hole_diameter/2) simple.active_name('_spoke') @@ -200,21 +202,22 @@ def rack(mm_per_tooth=0.01, number_of_teeth=11, height=0.012, pressure_angle=0.3 pi = math.pi mm_per_tooth *= 1000 a = mm_per_tooth / pi # addendum - t = (a * math.sin(pressure_angle)) # tooth side is tilted so top/bottom corners move this amount + # tooth side is tilted so top/bottom corners move this amount + t = (a * math.sin(pressure_angle)) a /= 1000 mm_per_tooth /= 1000 t /= 1000 shapely_gear = Polygon([ - (-mm_per_tooth * 2/4*1.001, a-height), - (-mm_per_tooth * 2/4*1.001 - backlash, -a), - (-mm_per_tooth * 1/4 + backlash - t, -a), - (-mm_per_tooth * 1/4 + backlash + t, a), - (mm_per_tooth * 1/4 - backlash - t, a), - (mm_per_tooth * 1/4 - backlash + t, -a), - (mm_per_tooth * 2/4*1.001 + backlash, -a), - (mm_per_tooth * 2/4*1.001, a-height) - ]) + (-mm_per_tooth * 2/4*1.001, a-height), + (-mm_per_tooth * 2/4*1.001 - backlash, -a), + (-mm_per_tooth * 1/4 + backlash - t, -a), + (-mm_per_tooth * 1/4 + backlash + t, a), + (mm_per_tooth * 1/4 - backlash - t, a), + (mm_per_tooth * 1/4 - backlash + t, -a), + (mm_per_tooth * 2/4*1.001 + backlash, -a), + (mm_per_tooth * 2/4*1.001, a-height) + ]) utils.shapelyToCurve('_tooth', shapely_gear, 0.0) i = number_of_teeth @@ -236,5 +239,3 @@ def rack(mm_per_tooth=0.01, number_of_teeth=11, height=0.012, pressure_angle=0.3 name = 'rack-' + str(round(mm_per_tooth * 1000, 1)) name += '-PA-' + str(round(math.degrees(pressure_angle), 1)) simple.active_name(name) - - diff --git a/scripts/addons/cam/joinery.py b/scripts/addons/cam/joinery.py index 5a50f498a..fe8c705b3 100644 --- a/scripts/addons/cam/joinery.py +++ b/scripts/addons/cam/joinery.py @@ -111,8 +111,8 @@ def twist_separator_slot(length, thickness, finger_play=0.00005, percentage=0.5) simple.mirrory() simple.join_multiple('simple_rectangle') simple.active_name('_separator_slot') - - + + def interlock_twist_separator(length, thickness, amount, spacing, edge_distance, finger_play=0.00005, percentage=0.5, start='rounded', end='rounded'): amount -= 1 @@ -171,7 +171,8 @@ def vertical_finger(length, thickness, finger_play, amount): # amount = amount of fingers for i in range(amount): - mortise(length, thickness, finger_play, 0, i * 2 * length + length / 2, rotation=math.pi / 2) + mortise(length, thickness, finger_play, 0, i * 2 * + length + length / 2, rotation=math.pi / 2) simple.active_name("_height_finger") simple.join_multiple("_height_finger") @@ -318,7 +319,7 @@ def fixed_finger(loop, loop_length, finger_size, finger_thick, finger_tolerance, mortise_angle = angle(oldp, p) mortise_angle_difference = abs(mortise_angle - old_mortise_angle) mad = (1 + 6 * min(mortise_angle_difference, math.pi / 4) / ( - math.pi / 4)) # factor for tolerance for the finger + math.pi / 4)) # factor for tolerance for the finger if base: mortise(finger_size, finger_thick, finger_tolerance * mad, distance, 0, 0) @@ -432,7 +433,7 @@ def variable_finger(loop, loop_length, min_finger, finger_size, finger_thick, fi mortise_angle = angle(oldp, p) mortise_angle_difference = abs(mortise_angle - old_mortise_angle) mad = (1 + 6 * min(mortise_angle_difference, math.pi / 4) / ( - math.pi / 4)) # factor for tolerance for the finger + math.pi / 4)) # factor for tolerance for the finger distance += mad * finger_tolerance # move finger by the factor mad greater with larger angle difference mortise_point = loop.interpolate(distance) if mad > 2 and double_adaptive: @@ -440,7 +441,8 @@ def variable_finger(loop, loop_length, min_finger, finger_size, finger_thick, fi hpos.append(distance + finger_sz) # saves the mortise center if base: - mortise(finger_sz, finger_thick, finger_tolerance * mad, distance + finger_sz, 0, 0) + mortise(finger_sz, finger_thick, finger_tolerance * + mad, distance + finger_sz, 0, 0) simple.active_name("_base") else: mortise(finger_sz, finger_thick, finger_tolerance * mad, mortise_point.x, mortise_point.y, @@ -450,7 +452,8 @@ def variable_finger(loop, loop_length, min_finger, finger_size, finger_thick, fi simple.remove_multiple("start_here") bpy.ops.mesh.primitive_cylinder_add(radius=finger_thick / 2, depth=0.025, enter_editmode=False, align='WORLD', - location=(mortise_point.x, mortise_point.y, 0), + location=(mortise_point.x, + mortise_point.y, 0), scale=(1, 1, 1)) simple.active_name("start_here_mortise") @@ -461,7 +464,7 @@ def variable_finger(loop, loop_length, min_finger, finger_size, finger_thick, fi # adaptive finger length start while finger_sz > min_finger and next_angle_difference > adaptive: -# while finger_sz > min_finger and next_angle_difference > adaptive: + # while finger_sz > min_finger and next_angle_difference > adaptive: finger_sz *= 0.95 # reduce the size of finger by a percentage... the closer to 1.0, the slower distance = old_distance + 3 * oldfinger_sz / 2 + finger_sz / 2 mortise_point = loop.interpolate(distance) # get the next mortise point @@ -487,7 +490,8 @@ def single_interlock(finger_depth, finger_thick, finger_tolerance, x, y, groove_ if type == "GROOVE": interlock_groove(finger_depth, finger_thick, finger_tolerance, x, y, groove_angle) elif type == "TWIST": - interlock_twist(finger_depth, finger_thick, finger_tolerance, x, y, groove_angle, percentage=twist_percentage) + interlock_twist(finger_depth, finger_thick, finger_tolerance, + x, y, groove_angle, percentage=twist_percentage) elif type == "PUZZLE": puzzle_joinery.fingers(finger_thick, finger_tolerance) @@ -535,7 +539,8 @@ def distributed_interlock(loop, loop_length, finger_depth, finger_thick, finger_ groove_point = loop.interpolate(distance) - print(j, "groove_angle", round(180 * groove_angle / math.pi), "distance", round(distance * 1000), "mm") + print(j, "groove_angle", round(180 * groove_angle / math.pi), + "distance", round(distance * 1000), "mm") single_interlock(finger_depth, finger_thick, finger_tolerance, groove_point.x, groove_point.y, groove_angle, type, twist_percentage=twist_percentage) diff --git a/scripts/addons/cam/nc/anilam_crusader_m.py b/scripts/addons/cam/nc/anilam_crusader_m.py index 89cee5532..994d64ed0 100644 --- a/scripts/addons/cam/nc/anilam_crusader_m.py +++ b/scripts/addons/cam/nc/anilam_crusader_m.py @@ -5,6 +5,7 @@ from . import nc from . import iso + class Creator(iso.Creator): def __init__(self): iso.Creator.__init__(self) @@ -16,10 +17,10 @@ def SPACE_STR(self): return(' ') # This version of COMMENT removes comments from the resultant GCode # Note: The Anilam hates comments when importing code. - def COMMENT(self,comment): return('') + def COMMENT(self, comment): return('') def program_begin(self, id, comment): - self.write('%\n'); # Start of file token that Anilam Crusader M likes + self.write('%\n') # Start of file token that Anilam Crusader M likes # No Comments for the Anilam crusaher M, please...... #self.write( ('(' + comment + ')' + '\n') ) @@ -28,41 +29,41 @@ def program_end(self): self.write('%\n') # EOF signal for Anilam Crusader M ############################################################################ - ## Settings + # Settings def imperial(self): - self.write( self.IMPERIAL() + '\n') + self.write(self.IMPERIAL() + '\n') self.fmt.number_of_decimal_places = 4 def metric(self): - self.write( self.METRIC() + '\n' ) + self.write(self.METRIC() + '\n') self.fmt.number_of_decimal_places = 3 def absolute(self): - self.write( self.ABSOLUTE() + '\n') + self.write(self.ABSOLUTE() + '\n') def incremental(self): - self.write( self.INCREMENTAL() + '\n' ) + self.write(self.INCREMENTAL() + '\n') def polar(self, on=True): - if (on) : - self.write(self.POLAR_ON() + '\n' ) - else : - self.write(self.POLAR_OFF() + '\n' ) + if (on): + self.write(self.POLAR_ON() + '\n') + else: + self.write(self.POLAR_OFF() + '\n') def set_plane(self, plane): - if (plane == 0) : + if (plane == 0): self.write('G17\n') - elif (plane == 1) : + elif (plane == 1): self.write('G18\n') - elif (plane == 2) : + elif (plane == 2): self.write('G19\n') def comment(self, text): - pass + pass ############################################################################ - ## Tools + # Tools def tool_change(self, id): self.write(('T%i' % id) + '\n') @@ -83,11 +84,13 @@ def tool_defn(self, id, name='', params=None): # These are selected by values from 1 to 9 inclusive. def workplane(self, id): if ((id >= 1) and (id <= 6)): - self.write( (self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + '\n') + self.write((self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + '\n') if ((id >= 7) and (id <= 9)): - self.write( ((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6))) + '\n') + self.write(((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6))) + '\n') + + def drill(self, x=None, y=None, dwell=None, depthparams=None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): + self.write( + '(Canned drill cycle ops are not yet supported here on this Anilam Crusader M postprocessor)') - def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance = None): - self.write('(Canned drill cycle ops are not yet supported here on this Anilam Crusader M postprocessor)') nc.creator = Creator() diff --git a/scripts/addons/cam/nc/anilam_crusader_m_read.py b/scripts/addons/cam/nc/anilam_crusader_m_read.py index 8e03a13c8..d8a688a5b 100644 --- a/scripts/addons/cam/nc/anilam_crusader_m_read.py +++ b/scripts/addons/cam/nc/anilam_crusader_m_read.py @@ -7,6 +7,7 @@ # Override some iso parser methods to interpret arc centers as relative to origin, not relative to start of arc. + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/attach.py b/scripts/addons/cam/nc/attach.py index 27c2d0c86..2da4f263a 100644 --- a/scripts/addons/cam/nc/attach.py +++ b/scripts/addons/cam/nc/attach.py @@ -16,6 +16,8 @@ units = 1.0 ################################################################################ + + class Creator(recreator.Redirector): def __init__(self, original): @@ -44,10 +46,11 @@ def z2(self, z): # use a line with no length path.append(ocl.Line(ocl.Point(self.x, self.y, self.z), ocl.Point(self.x, self.y, self.z))) self.setPdcfIfNotSet() - if (self.z>self.minz): - self.pdcf.setZ(self.z) # Adjust Z if we have gotten a higher limit (Fix pocketing loosing steps when using attach?) + if (self.z > self.minz): + # Adjust Z if we have gotten a higher limit (Fix pocketing loosing steps when using attach?) + self.pdcf.setZ(self.z) else: - self.pdcf.setZ(self.minz/units) # Else use minz + self.pdcf.setZ(self.minz/units) # Else use minz self.pdcf.setPath(path) self.pdcf.run() plist = self.pdcf.getCLPoints() @@ -55,13 +58,15 @@ def z2(self, z): return p.z + self.material_allowance/units def cut_path(self): - if self.path == None: return + if self.path == None: + return self.setPdcfIfNotSet() - if (self.z>self.minz): - self.pdcf.setZ(self.z) # Adjust Z if we have gotten a higher limit (Fix pocketing loosing steps when using attach?) + if (self.z > self.minz): + # Adjust Z if we have gotten a higher limit (Fix pocketing loosing steps when using attach?) + self.pdcf.setZ(self.z) else: - self.pdcf.setZ(self.minz/units) # Else use minz + self.pdcf.setZ(self.minz/units) # Else use minz # get the points on the surface self.pdcf.setPath(self.path) @@ -69,7 +74,7 @@ def cut_path(self): self.pdcf.run() plist = self.pdcf.getCLPoints() - #refine the points + # refine the points f = ocl.LineCLFilter() f.setTolerance(0.005) for p in plist: @@ -85,7 +90,7 @@ def cut_path(self): self.path = ocl.Path() - def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None ): + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): if z != None: if z < self.z: return @@ -102,24 +107,28 @@ def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): return # add a line to the path - if self.path == None: self.path = ocl.Path() + if self.path == None: + self.path = ocl.Path() self.path.append(ocl.Line(ocl.Point(px, py, pz), ocl.Point(self.x, self.y, self.z))) - def arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, ccw = True): + def arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, ccw=True): px = self.x py = self.y pz = self.z recreator.Redirector.arc(self, x, y, z, i, j, k, r, ccw) # add an arc to the path - if self.path == None: self.path = ocl.Path() - self.path.append(ocl.Arc(ocl.Point(px, py, pz), ocl.Point(self.x, self.y, self.z), ocl.Point(i, j, pz), ccw)) + if self.path == None: + self.path = ocl.Path() + self.path.append(ocl.Arc(ocl.Point(px, py, pz), ocl.Point( + self.x, self.y, self.z), ocl.Point(i, j, pz), ccw)) def set_ocl_cutter(self, cutter): self.cutter = cutter ################################################################################ + def attach_begin(): global attached if attached == True: @@ -130,6 +139,7 @@ def attach_begin(): nc.creator.pdcf = None nc.creator.path = None + def attach_end(): global attached nc.creator.cut_path() diff --git a/scripts/addons/cam/nc/cad_iso_read.py b/scripts/addons/cam/nc/cad_iso_read.py index 2ca104fec..b3a2511f6 100644 --- a/scripts/addons/cam/nc/cad_iso_read.py +++ b/scripts/addons/cam/nc/cad_iso_read.py @@ -16,17 +16,18 @@ import sys ################################################################################ -class Parser(nc.Parser): +class Parser(nc.Parser): def __init__(self, writer): nc.Parser.__init__(self, writer) - self.pattern_main = re.compile('([(!;].*|\s+|[a-zA-Z0-9_:](?:[+-])?\d*(?:\.\d*)?|\w\#\d+|\(.*?\)|\#\d+\=(?:[+-])?\d*(?:\.\d*)?)') + self.pattern_main = re.compile( + '([(!;].*|\s+|[a-zA-Z0-9_:](?:[+-])?\d*(?:\.\d*)?|\w\#\d+|\(.*?\)|\#\d+\=(?:[+-])?\d*(?:\.\d*)?)') - #if ( or ! or ; at least one space or a letter followed by some character or not followed by a +/- followed by decimal, with a possible decimal point - # followed by a possible deimcal, or a letter followed by # with a decimal . deimcal + # if ( or ! or ; at least one space or a letter followed by some character or not followed by a +/- followed by decimal, with a possible decimal point + # followed by a possible deimcal, or a letter followed by # with a decimal . deimcal # add your character here > [(!;] for comments char # then look for the 'comment' function towards the end of the file and add another elif @@ -81,7 +82,7 @@ def ParseWord(self, word): self.path_col = "feed" self.col = "feed" elif (word == 'G82' or word == 'g82'): - self.drill = True; + self.drill = True self.no_move = True self.path_col = "feed" self.col = "feed" @@ -94,7 +95,8 @@ def ParseWord(self, word): self.absolute() elif (word == 'G91' or word == 'g91'): self.incremental() - elif (word[0] == 'G') : col = "prep" + elif (word[0] == 'G'): + col = "prep" elif (word[0] == 'I' or word[0] == 'i'): self.col = "axis" self.i = eval(word[1:]) @@ -107,19 +109,22 @@ def ParseWord(self, word): self.col = "axis" self.k = eval(word[1:]) self.move = True - elif (word[0] == 'M') : self.col = "misc" - elif (word[0] == 'N') : self.col = "blocknum" - elif (word[0] == 'O') : self.col = "program" + elif (word[0] == 'M'): + self.col = "misc" + elif (word[0] == 'N'): + self.col = "blocknum" + elif (word[0] == 'O'): + self.col = "program" elif (word[0] == 'P' or word[0] == 'p'): - if (self.no_move != True): - self.col = "axis" - self.p = eval(word[1:]) - self.move = True + if (self.no_move != True): + self.col = "axis" + self.p = eval(word[1:]) + self.move = True elif (word[0] == 'Q' or word[0] == 'q'): - if (self.no_move != True): - self.col = "axis" - self.q = eval(word[1:]) - self.move = True + if (self.no_move != True): + self.col = "axis" + self.q = eval(word[1:]) + self.move = True elif (word[0] == 'R' or word[0] == 'r'): self.col = "axis" self.r = eval(word[1:]) @@ -128,9 +133,9 @@ def ParseWord(self, word): self.col = "axis" self.s = eval(word[1:]) self.move = True - elif (word[0] == 'T') : + elif (word[0] == 'T'): self.col = "tool" - self.set_tool( eval(word[1:]) ) + self.set_tool(eval(word[1:])) elif (word[0] == 'X' or word[0] == 'x'): self.col = "axis" self.x = eval(word[1:]) @@ -143,21 +148,27 @@ def ParseWord(self, word): self.col = "axis" self.z = eval(word[1:]) self.move = True - elif (word[0] == '(') : (self.col, self.cdata) = ("comment", True) - elif (word[0] == '!') : (self.col, self.cdata) = ("comment", True) - elif (word[0] == ';') : (self.col, self.cdata) = ("comment", True) - elif (word[0] == '#') : self.col = "variable" - elif (word[0] == ':') : self.col = "blocknum" - elif (ord(word[0]) <= 32) : self.cdata = True + elif (word[0] == '('): + (self.col, self.cdata) = ("comment", True) + elif (word[0] == '!'): + (self.col, self.cdata) = ("comment", True) + elif (word[0] == ';'): + (self.col, self.cdata) = ("comment", True) + elif (word[0] == '#'): + self.col = "variable" + elif (word[0] == ':'): + self.col = "blocknum" + elif (ord(word[0]) <= 32): + self.cdata = True def Parse(self, name, oname=None): - self.files_open(name,oname) + self.files_open(name, oname) - #self.begin_ncblock() - #self.begin_path(None) - #self.add_line(z=500) - #self.end_path() - #self.end_ncblock() + # self.begin_ncblock() + # self.begin_path(None) + # self.add_line(z=500) + # self.end_path() + # self.end_ncblock() self.path_col = None self.f = None @@ -179,7 +190,7 @@ def Parse(self, name, oname=None): self.y = None self.z = None - #self.begin_ncblock() + # self.begin_ncblock() self.move = False self.drill = False @@ -207,12 +218,15 @@ def Parse(self, name, oname=None): else: if (self.move and not self.no_move): self.begin_path(self.path_col) - if (self.arc==-1): - self.add_arc(self.x, self.y, self.z, self.i, self.j, self.k, self.r, self.arc) - elif (self.arc==1): - #self.add_arc(x, y, z, i, j, k, -r, arc) #if you want to use arcs with R values uncomment the first part of this line and comment the next one - self.add_arc(self.x, self.y, self.z, self.i, self.j, self.k, self.r, self.arc) - else : self.add_line(self.x, self.y, self.z, self.a, self.b, self.c) + if (self.arc == -1): + self.add_arc(self.x, self.y, self.z, self.i, + self.j, self.k, self.r, self.arc) + elif (self.arc == 1): + # self.add_arc(x, y, z, i, j, k, -r, arc) #if you want to use arcs with R values uncomment the first part of this line and comment the next one + self.add_arc(self.x, self.y, self.z, self.i, + self.j, self.k, self.r, self.arc) + else: + self.add_line(self.x, self.y, self.z, self.a, self.b, self.c) self.end_path() self.end_ncblock() @@ -221,9 +235,10 @@ def Parse(self, name, oname=None): ################################################################################ + if __name__ == '__main__': parser = ParserIso() - if len(sys.argv)>2: - parser.Parse(sys.argv[1],sys.argv[2]) + if len(sys.argv) > 2: + parser.Parse(sys.argv[1], sys.argv[2]) else: parser.Parse(sys.argv[1]) diff --git a/scripts/addons/cam/nc/cad_nc_read.py b/scripts/addons/cam/nc/cad_nc_read.py index 15ef23ece..4203fcdb3 100644 --- a/scripts/addons/cam/nc/cad_nc_read.py +++ b/scripts/addons/cam/nc/cad_nc_read.py @@ -12,14 +12,14 @@ def __init__(self): self.currentx = -1.0 self.currenty = 0.0 self.currentz = 0.0 - x,y,z = 0.0,0.0,0.0 + x, y, z = 0.0, 0.0, 0.0 self.absolute_flag = True ############################################################################ - ## Internals + # Internals def files_open(self, name, oname=None): - if (oname == None ): + if (oname == None): oname = (name+'.scr') self.file_in = open(name, 'r') self.file_out = open(oname, 'w') @@ -60,52 +60,61 @@ def begin_path(self, col=None): if (col != None): if col == 'rapid': self.file_out.write('-color Red\n') - #self.file_out.write('') + # self.file_out.write('') self.file_out.write('-linetype set dashed\n') self.file_out.write('\n') else: self.file_out.write('-color Green\n') - #self.file_out.write('') + # self.file_out.write('') self.file_out.write('-linetype set continuous\n') self.file_out.write('\n') - else : self.file_out.write('\n') + else: + self.file_out.write('\n') def end_path(self): self.file_out.write('\n') def add_line(self, x=None, y=None, z=None, a=None, b=None, c=None): - if (x == None and y == None and z == None and a == None and b == None and c == None) : return + if (x == None and y == None and z == None and a == None and b == None and c == None): + return #self.file_out.write('line %s,%s %s,%s' %(self.currentx,self.currenty,x,y)) - if (x == None) : x = self.currentx - if (y == None) : y = self.currenty - if (z == None) : z = self.currentz - self.file_out.write('line %s,%s,%s %s,%s,%s\n' %(self.currentx,self.currenty,self.currentz,x,y,z)) + if (x == None): + x = self.currentx + if (y == None): + y = self.currenty + if (z == None): + z = self.currentz + self.file_out.write('line %s,%s,%s %s,%s,%s\n' % + (self.currentx, self.currenty, self.currentz, x, y, z)) self.currentx = x self.currenty = y self.currentz = z def add_arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, d=None): - if (x == None and y == None and z == None and i == None and j == None and k == None and r == None and d == None) : return + if (x == None and y == None and z == None and i == None and j == None and k == None and r == None and d == None): + return z = self.currentz - if (x == None) : x = self.currentx - if (y == None) : y = self.currenty - if (z == None) : z = self.currentz + if (x == None): + x = self.currentx + if (y == None): + y = self.currenty + if (z == None): + z = self.currentz if (d == 1): - self.file_out.write('arc %s,%s,%s\n' %(self.currentx,self.currenty,self.currentz)) + self.file_out.write('arc %s,%s,%s\n' % (self.currentx, self.currenty, self.currentz)) self.file_out.write('c\n') - self.file_out.write('%s,%s,%s\n' %(self.currentx+i,self.currenty+j,self.currentz)) - self.file_out.write('%s,%s,%s' %(x,y,z)) + self.file_out.write('%s,%s,%s\n' % (self.currentx+i, self.currenty+j, self.currentz)) + self.file_out.write('%s,%s,%s' % (x, y, z)) else: - self.file_out.write('arc %s,%s,%s\n' %(x,y,z)) + self.file_out.write('arc %s,%s,%s\n' % (x, y, z)) self.file_out.write('c\n') - self.file_out.write('%s,%s,%s\n' %(self.currentx+i,self.currenty+j,self.currentz)) - self.file_out.write('%s,%s,%s' %(self.currentx,self.currenty,self.currentz)) + self.file_out.write('%s,%s,%s\n' % (self.currentx+i, self.currenty+j, self.currentz)) + self.file_out.write('%s,%s,%s' % (self.currentx, self.currenty, self.currentz)) self.currentx = x self.currenty = y self.currentz = z - def incremental(self): self.absolute_flag = False diff --git a/scripts/addons/cam/nc/cad_read.py b/scripts/addons/cam/nc/cad_read.py index 40efded39..faf31bbe8 100644 --- a/scripts/addons/cam/nc/cad_read.py +++ b/scripts/addons/cam/nc/cad_read.py @@ -12,12 +12,11 @@ import sys - # Override some iso parser methods to interpret arc centers as relative to origin, not relative to start of arc. -#def write_layer(name,number): - #FILE.write('-LAYER New %s%s \n' %(name,number)) - #FILE.write('-LAYER Set %s%s \n' %(name,number)) +# def write_layer(name,number): +#FILE.write('-LAYER New %s%s \n' %(name,number)) +#FILE.write('-LAYER Set %s%s \n' %(name,number)) class CAD_backplot(iso.Parser): @@ -26,13 +25,13 @@ def __init__(self): iso.Parser.__init__(self) def Parse(self, name, oname=None): - self.files_open(name,oname) + self.files_open(name, oname) - #self.begin_ncblock() - #self.begin_path(None) - #self.add_line(z=500) - #self.end_path() - #self.end_ncblock() + # self.begin_ncblock() + # self.begin_path(None) + # self.add_line(z=500) + # self.end_path() + # self.end_ncblock() path_col = None f = None @@ -45,7 +44,7 @@ def Parse(self, name, oname=None): oldz = 0.0 movelist = [] while (self.readline()): - # self.readline returns false if the line is empty - the parsing stops if the line is empty. + # self.readline returns false if the line is empty - the parsing stops if the line is empty. a = None b = None c = None @@ -64,7 +63,7 @@ def Parse(self, name, oname=None): jout = None kout = None tool = 0 - #self.begin_ncblock() + # self.begin_ncblock() move = False #arc = 0 @@ -129,7 +128,7 @@ def Parse(self, name, oname=None): path_col = "feed" col = "feed" elif (word == 'G82' or word == 'g82'): - drill = True; + drill = True no_move = True path_col = "feed" col = "feed" @@ -138,7 +137,8 @@ def Parse(self, name, oname=None): no_move = True path_col = "feed" col = "feed" - elif (word[0] == 'G') : col = "prep" + elif (word[0] == 'G'): + col = "prep" elif (word[0] == 'I' or word[0] == 'i'): col = "axis" i = eval(word[1:]) @@ -151,9 +151,12 @@ def Parse(self, name, oname=None): col = "axis" k = eval(word[1:]) move = True - elif (word[0] == 'M') : col = "misc" - elif (word[0] == 'N') : col = "blocknum" - elif (word[0] == 'O') : col = "program" + elif (word[0] == 'M'): + col = "misc" + elif (word[0] == 'N'): + col = "blocknum" + elif (word[0] == 'O'): + col = "program" elif (word[0] == 'P' or word[0] == 'p'): col = "axis" p = eval(word[1:]) @@ -170,10 +173,10 @@ def Parse(self, name, oname=None): col = "axis" s = eval(word[1:]) move = True - elif (word[0] == 'T') : + elif (word[0] == 'T'): col = "tool" - self.set_tool( eval(word[1:]) ) - tool = eval(word[1:]) + self.set_tool(eval(word[1:])) + tool = eval(word[1:]) elif (word[0] == 'X' or word[0] == 'x'): col = "axis" @@ -187,12 +190,18 @@ def Parse(self, name, oname=None): col = "axis" z = eval(word[1:]) move = True - elif (word[0] == '(') : (col, cdata) = ("comment", True) - elif (word[0] == '!') : (col, cdata) = ("comment", True) - elif (word[0] == ';') : (col, cdata) = ("comment", True) - elif (word[0] == '#') : col = "variable" - elif (word[0] == ':') : col = "blocknum" - elif (ord(word[0]) <= 32) : cdata = True + elif (word[0] == '('): + (col, cdata) = ("comment", True) + elif (word[0] == '!'): + (col, cdata) = ("comment", True) + elif (word[0] == ';'): + (col, cdata) = ("comment", True) + elif (word[0] == '#'): + col = "variable" + elif (word[0] == ':'): + col = "blocknum" + elif (ord(word[0]) <= 32): + cdata = True #self.add_text(word, col, cdata) if (drill): @@ -207,55 +216,63 @@ def Parse(self, name, oname=None): self.begin_path("feed") self.add_line(x, y, r) self.end_path() - #elif (tool): - #write_layer('T',tool) - + # elif (tool): + # write_layer('T',tool) else: if (move and not no_move): self.begin_path(path_col) - #use absolute arc centers for IJK params. + # use absolute arc centers for IJK params. # Subtract old XYZ off to get relative centers as expected: - #if path_col == 'rapid': - #FILE.write('-color Red\n') - #else: - #FILE.write('-color Green\n') + # if path_col == 'rapid': + #FILE.write('-color Red\n') + # else: + #FILE.write('-color Green\n') - if (arc) : + if (arc): z = oldz - if (x != None) and (oldx != None) and (i != None): iout = i - if (y != None) and (oldy != None) and (j != None): jout = j - if (z != None) and (oldz != None) and (k != None): kout = k + if (x != None) and (oldx != None) and (i != None): + iout = i + if (y != None) and (oldy != None) and (j != None): + jout = j + if (z != None) and (oldz != None) and (k != None): + kout = k self.add_arc(x, y, z, iout, jout, kout, r, arc) - #if (arc == -1): - ##FILE.write('arc %s,%s,%s\n' %(x,y,z)) - ##FILE.write('c\n') - ##FILE.write('%s,%s,%s\n' %(oldx+i,oldy+j,oldz)) - ##FILE.write('%s,%s,%s\n' %(oldx,oldy,z)) - - #else: - ##FILE.write('arc %s,%s,%s\n' %(oldx,oldy,z)) - ##FILE.write('c\n') - ##FILE.write('%s,%s,%s\n' %(oldx+i,oldy+j,oldz)) - ##FILE.write('%s,%s,%s\n' %(x,y,z)) + # if (arc == -1): + ##FILE.write('arc %s,%s,%s\n' %(x,y,z)) + # FILE.write('c\n') + ##FILE.write('%s,%s,%s\n' %(oldx+i,oldy+j,oldz)) + ##FILE.write('%s,%s,%s\n' %(oldx,oldy,z)) + + # else: + ##FILE.write('arc %s,%s,%s\n' %(oldx,oldy,z)) + # FILE.write('c\n') + ##FILE.write('%s,%s,%s\n' %(oldx+i,oldy+j,oldz)) + ##FILE.write('%s,%s,%s\n' %(x,y,z)) else: self.add_line(x, y, z, a, b, c) - if (x == None) : x = oldx - if (y == None) : y = oldy - if (z == None) : z = oldz - scr_line = ('line %s,%s,%s %s,%s,%s \n' %(oldx,oldy,oldz,x,y,z)) - #print scr_line + if (x == None): + x = oldx + if (y == None): + y = oldy + if (z == None): + z = oldz + scr_line = ('line %s,%s,%s %s,%s,%s \n' % (oldx, oldy, oldz, x, y, z)) + # print scr_line - ##FILE.write(scr_line) + # FILE.write(scr_line) self.end_path() - if (x != None) : oldx = x - if (y != None) : oldy = y - if (z != None) : oldz = z + if (x != None): + oldx = x + if (y != None): + oldy = y + if (z != None): + oldz = z #oldx = x #oldy = y @@ -263,13 +280,14 @@ def Parse(self, name, oname=None): self.end_ncblock() self.files_close() - #FILE.write('\n') - #FILE.close() + # FILE.write('\n') + # FILE.close() + ################################################################################ if __name__ == '__main__': parser = CAD_backplot() - if len(sys.argv)>2: - parser.Parse(sys.argv[1],sys.argv[2]) + if len(sys.argv) > 2: + parser.Parse(sys.argv[1], sys.argv[2]) else: parser.Parse(sys.argv[1]) diff --git a/scripts/addons/cam/nc/centroid1.py b/scripts/addons/cam/nc/centroid1.py index 214d01254..9fb899a22 100644 --- a/scripts/addons/cam/nc/centroid1.py +++ b/scripts/addons/cam/nc/centroid1.py @@ -15,7 +15,6 @@ now = datetime.datetime.now() - ################################################################################ class Creator(iso_modal.Creator): @@ -26,23 +25,25 @@ def __init__(self): self.useCrcCenterline = True self.absolute_flag = True self.prev_g91 = '' - self.safe_z =None + self.safe_z = None + def SPINDLE(self, format, speed): return(self.SPACE() + 'S' + (format % speed)) ################################################################################ -#cutter comp +# cutter comp - #def crc_on(self): + # def crc_on(self): # self.useCrc = True # self.useCrcCenterline = True - #def crc_off(self): + # def crc_off(self): # self.useCrc = False ################################################################################ # general def comment(self, text): - self.write(';' + text +'\n') + self.write(';' + text + '\n') + def write_blocknum(self): pass @@ -69,7 +70,6 @@ def program_begin(self, id, name=''): self.write(';time:'+str(now)+'\n') self.write('G17 G20 G80 G40 G90\n') - def program_end(self): self.write('M05\n') self.write('M25\n') @@ -79,9 +79,9 @@ def program_end(self): def program_stop(self, optional=False): self.write_blocknum() - if (optional) : + if (optional): self.write(self.STOP_OPTIONAL() + '\n') - else : + else: self.write('M05\n') self.write('M25\n') self.write(self.STOP() + '\n') @@ -94,16 +94,18 @@ def workplane(self, id): if ((id >= 1) and (id <= 6)): self.g_list.append(self.WORKPLANE() % (id + self.WORKPLANE_BASE())) if ((id >= 7) and (id <= 9)): - self.g_list.append(((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6)))) + self.g_list.append( + ((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6)))) self.prev_g0123 = '' ################################################################################ # clearance plane - def clearanceplane(self,z=None): + def clearanceplane(self, z=None): self.safe_z = z ################################################################################ # return to home + def rapid_home(self, x=None, y=None, z=None, a=None, b=None, c=None): """Rapid relative to home position""" self.write('M05\n') @@ -121,14 +123,13 @@ def tool_change(self, id): self.t = id self.write('M25\n') if self.safe_z == None: - self.write('G43 H'+ str(id) + ' Z') + self.write('G43 H' + str(id) + ' Z') self.write('1.0') - self.write ('\n') + self.write('\n') else: - self.write('G43 H'+ str(id) + ' Z') + self.write('G43 H' + str(id) + ' Z') self.write(str(self.safe_z)) - self.write ('\n') - + self.write('\n') def tool_defn(self, id, name='', params=None): #self.write('G43 \n') @@ -137,7 +138,6 @@ def tool_defn(self, id, name='', params=None): def write_spindle(self): pass - def spindle(self, s, clockwise): if s < 0: clockwise = not clockwise @@ -146,12 +146,12 @@ def spindle(self, s, clockwise): self.s = self.SPINDLE(self.FORMAT_ANG(), s) if clockwise: #self.s = self.SPINDLE_CW() + self.s - self.s = self.SPINDLE_CW() - self.write(self.s + '\n') + self.s = self.SPINDLE_CW() + self.write(self.s + '\n') self.write('G04 P2.0 \n') else: - self.s = self.SPINDLE_CCW() + self.s + self.s = self.SPINDLE_CCW() + self.s def end_canned_cycle(self): self.write_blocknum() @@ -165,4 +165,5 @@ def end_canned_cycle(self): self.write('M25\n') self.write('G00 X-1.0 Y1.0\n') + nc.creator = Creator() diff --git a/scripts/addons/cam/nc/centroid1_read.py b/scripts/addons/cam/nc/centroid1_read.py index a8040bec1..2ca0b6f50 100644 --- a/scripts/addons/cam/nc/centroid1_read.py +++ b/scripts/addons/cam/nc/centroid1_read.py @@ -3,6 +3,7 @@ # just use the iso reader + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/drag_knife.py b/scripts/addons/cam/nc/drag_knife.py index afc8da612..5288b8e3d 100644 --- a/scripts/addons/cam/nc/drag_knife.py +++ b/scripts/addons/cam/nc/drag_knife.py @@ -5,13 +5,15 @@ # # Dan Heeks 26th April 2012 +import area +import nc +from kurve_funcs import cut_curve as cut_curve from . import recreator dragging = False -from kurve_funcs import cut_curve as cut_curve -import nc -import area ################################################################################ + + class Creator(recreator.Redirector): def __init__(self, original, drag_distance): @@ -21,7 +23,8 @@ def __init__(self, original, drag_distance): self.path = None def cut_path(self): - if self.path == None: return + if self.path == None: + return print self.drag_distance self.path.OffsetForward(self.drag_distance, False) @@ -48,15 +51,19 @@ def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): return # add a line to the path - if self.path == None: self.path = area.Curve() + if self.path == None: + self.path = area.Curve() self.path.append(area.Point(self.x, self.y)) - def arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, ccw = True): + def arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, ccw=True): recreator.Redirector.arc(self, x, y, z, i, j, k, r, ccw) # add an arc to the path - if self.path == None: self.path = area.Curve() - self.path.append(area.Vertex(1 if ccw else -1, area.Point(self.x, self.y), area.Point(i, j))) + if self.path == None: + self.path = area.Curve() + self.path.append(area.Vertex(1 if ccw else -1, + area.Point(self.x, self.y), area.Point(i, j))) + def drag_begin(drag_distance): global dragging @@ -65,6 +72,7 @@ def drag_begin(drag_distance): nc.creator = Creator(nc.creator, drag_distance) dragging = True + def drag_end(): global dragging nc.creator.cut_path() diff --git a/scripts/addons/cam/nc/emc2.py b/scripts/addons/cam/nc/emc2.py index b9ca331c7..db3a311dc 100644 --- a/scripts/addons/cam/nc/emc2.py +++ b/scripts/addons/cam/nc/emc2.py @@ -2,230 +2,235 @@ from . import iso import math -class Creator(iso.Creator): - def __init__(self): - iso.Creator.__init__(self) - def SPACE(self): return('') - def TAP(self): return('G33.1') - def TAP_DEPTH(self, format, depth): return(self.SPACE() + 'K' + (format.string(depth))) - def BORE_FEED_OUT(self): return('G85') - def BORE_SPINDLE_STOP_RAPID_OUT(self): return('G86') - def BORE_DWELL_FEED_OUT(self, format, dwell): return('G89') + self.SPACE() + (format.string(dwell)) +class Creator(iso.Creator): + def __init__(self): + iso.Creator.__init__(self) - def program_begin(self, id, comment): - self.write( ('(' + comment + ')' + '\n') ) + def SPACE(self): return('') + def TAP(self): return('G33.1') + def TAP_DEPTH(self, format, depth): return(self.SPACE() + 'K' + (format.string(depth))) + def BORE_FEED_OUT(self): return('G85') + def BORE_SPINDLE_STOP_RAPID_OUT(self): return('G86') - ############################################################################ - ## Settings + def BORE_DWELL_FEED_OUT(self, format, dwell): return( + 'G89') + self.SPACE() + (format.string(dwell)) - def imperial(self): - self.write( self.IMPERIAL() + '\t (Imperial Values)\n') - self.fmt.number_of_decimal_places = 4 + def program_begin(self, id, comment): + self.write(('(' + comment + ')' + '\n')) - def metric(self): - self.fmt.number_of_decimal_places = 3 - self.write( self.METRIC() + '\t (Metric Values)\n' ) + ############################################################################ + # Settings - def absolute(self): - self.write( self.ABSOLUTE() + '\t (Absolute Coordinates)\n') + def imperial(self): + self.write(self.IMPERIAL() + '\t (Imperial Values)\n') + self.fmt.number_of_decimal_places = 4 - def incremental(self): - self.write( self.INCREMENTAL() + '\t (Incremental Coordinates)\n' ) + def metric(self): + self.fmt.number_of_decimal_places = 3 + self.write(self.METRIC() + '\t (Metric Values)\n') - def polar(self, on=True): - if (on) : - self.write(self.POLAR_ON() + '\t (Polar ON)\n' ) - else : - self.write(self.POLAR_OFF() + '\t (Polar OFF)\n' ) + def absolute(self): + self.write(self.ABSOLUTE() + '\t (Absolute Coordinates)\n') - def set_plane(self, plane): - if (plane == 0) : - self.write(self.PLANE_XY() + '\t (Select XY Plane)\n') - elif (plane == 1) : - self.write(self.PLANE_XZ() + '\t (Select XZ Plane)\n') - elif (plane == 2) : - self.write(self.PLANE_YZ() + '\t (Select YZ Plane)\n') + def incremental(self): + self.write(self.INCREMENTAL() + '\t (Incremental Coordinates)\n') + def polar(self, on=True): + if (on): + self.write(self.POLAR_ON() + '\t (Polar ON)\n') + else: + self.write(self.POLAR_OFF() + '\t (Polar OFF)\n') - # This is the coordinate system we're using. G54->G59, G59.1, G59.2, G59.3 - # These are selected by values from 1 to 9 inclusive. - def workplane(self, id): - if ((id >= 1) and (id <= 6)): - self.write( (self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + '\t (Select Relative Coordinate System)\n') - if ((id >= 7) and (id <= 9)): - self.write( ((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6))) + '\t (Select Relative Coordinate System)\n') + def set_plane(self, plane): + if (plane == 0): + self.write(self.PLANE_XY() + '\t (Select XY Plane)\n') + elif (plane == 1): + self.write(self.PLANE_XZ() + '\t (Select XZ Plane)\n') + elif (plane == 2): + self.write(self.PLANE_YZ() + '\t (Select YZ Plane)\n') - def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None ): - if (xml_file_name != None): - self.comment('Generate an XML document describing the probed coordinates found'); - self.write('(LOGOPEN,') - self.write(xml_file_name) - self.write(')\n') + # This is the coordinate system we're using. G54->G59, G59.1, G59.2, G59.3 + # These are selected by values from 1 to 9 inclusive. - self.write('(LOG,)\n') + def workplane(self, id): + if ((id >= 1) and (id <= 6)): + self.write((self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + + '\t (Select Relative Coordinate System)\n') + if ((id >= 7) and (id <= 9)): + self.write(((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % + (id - 6))) + '\t (Select Relative Coordinate System)\n') - if ((x1 != None) or (y1 != None) or (z1 != None)): - self.write('(LOG,)\n') + def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None): + if (xml_file_name != None): + self.comment('Generate an XML document describing the probed coordinates found') + self.write('(LOGOPEN,') + self.write(xml_file_name) + self.write(')\n') - if (x1 != None): - self.write('#<_value>=[' + x1 + ']\n') - self.write('(LOG,#<_value>)\n') + self.write('(LOG,)\n') - if (y1 != None): - self.write('#<_value>=[' + y1 + ']\n') - self.write('(LOG,#<_value>)\n') + if ((x1 != None) or (y1 != None) or (z1 != None)): + self.write('(LOG,)\n') - if (z1 != None): - self.write('#<_value>=[' + z1 + ']\n') - self.write('(LOG,#<_value>)\n') + if (x1 != None): + self.write('#<_value>=[' + x1 + ']\n') + self.write('(LOG,#<_value>)\n') - if ((x1 != None) or (y1 != None) or (z1 != None)): - self.write('(LOG,)\n') + if (y1 != None): + self.write('#<_value>=[' + y1 + ']\n') + self.write('(LOG,#<_value>)\n') - if ((x2 != None) or (y2 != None) or (z2 != None)): - self.write('(LOG,)\n') + if (z1 != None): + self.write('#<_value>=[' + z1 + ']\n') + self.write('(LOG,#<_value>)\n') - if (x2 != None): - self.write('#<_value>=[' + x2 + ']\n') - self.write('(LOG,#<_value>)\n') + if ((x1 != None) or (y1 != None) or (z1 != None)): + self.write('(LOG,)\n') - if (y2 != None): - self.write('#<_value>=[' + y2 + ']\n') - self.write('(LOG,#<_value>)\n') + if ((x2 != None) or (y2 != None) or (z2 != None)): + self.write('(LOG,)\n') - if (z2 != None): - self.write('#<_value>=[' + z2 + ']\n') - self.write('(LOG,#<_value>)\n') + if (x2 != None): + self.write('#<_value>=[' + x2 + ']\n') + self.write('(LOG,#<_value>)\n') - if ((x2 != None) or (y2 != None) or (z2 != None)): - self.write('(LOG,)\n') + if (y2 != None): + self.write('#<_value>=[' + y2 + ']\n') + self.write('(LOG,#<_value>)\n') - if ((x3 != None) or (y3 != None) or (z3 != None)): - self.write('(LOG,)\n') + if (z2 != None): + self.write('#<_value>=[' + z2 + ']\n') + self.write('(LOG,#<_value>)\n') - if (x3 != None): - self.write('#<_value>=[' + x3 + ']\n') - self.write('(LOG,#<_value>)\n') + if ((x2 != None) or (y2 != None) or (z2 != None)): + self.write('(LOG,)\n') - if (y3 != None): - self.write('#<_value>=[' + y3 + ']\n') - self.write('(LOG,#<_value>)\n') + if ((x3 != None) or (y3 != None) or (z3 != None)): + self.write('(LOG,)\n') - if (z3 != None): - self.write('#<_value>=[' + z3 + ']\n') - self.write('(LOG,#<_value>)\n') + if (x3 != None): + self.write('#<_value>=[' + x3 + ']\n') + self.write('(LOG,#<_value>)\n') - if ((x3 != None) or (y3 != None) or (z3 != None)): - self.write('(LOG,)\n') + if (y3 != None): + self.write('#<_value>=[' + y3 + ']\n') + self.write('(LOG,#<_value>)\n') - if ((x4 != None) or (y4 != None) or (z4 != None)): - self.write('(LOG,)\n') + if (z3 != None): + self.write('#<_value>=[' + z3 + ']\n') + self.write('(LOG,#<_value>)\n') - if (x4 != None): - self.write('#<_value>=[' + x4 + ']\n') - self.write('(LOG,#<_value>)\n') + if ((x3 != None) or (y3 != None) or (z3 != None)): + self.write('(LOG,)\n') - if (y4 != None): - self.write('#<_value>=[' + y4 + ']\n') - self.write('(LOG,#<_value>)\n') + if ((x4 != None) or (y4 != None) or (z4 != None)): + self.write('(LOG,)\n') - if (z4 != None): - self.write('#<_value>=[' + z4 + ']\n') - self.write('(LOG,#<_value>)\n') + if (x4 != None): + self.write('#<_value>=[' + x4 + ']\n') + self.write('(LOG,#<_value>)\n') - if ((x4 != None) or (y4 != None) or (z4 != None)): - self.write('(LOG,)\n') + if (y4 != None): + self.write('#<_value>=[' + y4 + ']\n') + self.write('(LOG,#<_value>)\n') - if ((x5 != None) or (y5 != None) or (z5 != None)): - self.write('(LOG,)\n') + if (z4 != None): + self.write('#<_value>=[' + z4 + ']\n') + self.write('(LOG,#<_value>)\n') - if (x5 != None): - self.write('#<_value>=[' + x5 + ']\n') - self.write('(LOG,#<_value>)\n') + if ((x4 != None) or (y4 != None) or (z4 != None)): + self.write('(LOG,)\n') - if (y5 != None): - self.write('#<_value>=[' + y5 + ']\n') - self.write('(LOG,#<_value>)\n') + if ((x5 != None) or (y5 != None) or (z5 != None)): + self.write('(LOG,)\n') - if (z5 != None): - self.write('#<_value>=[' + z5 + ']\n') - self.write('(LOG,#<_value>)\n') + if (x5 != None): + self.write('#<_value>=[' + x5 + ']\n') + self.write('(LOG,#<_value>)\n') - if ((x5 != None) or (y5 != None) or (z5 != None)): - self.write('(LOG,)\n') + if (y5 != None): + self.write('#<_value>=[' + y5 + ']\n') + self.write('(LOG,#<_value>)\n') - if ((x6 != None) or (y6 != None) or (z6 != None)): - self.write('(LOG,)\n') + if (z5 != None): + self.write('#<_value>=[' + z5 + ']\n') + self.write('(LOG,#<_value>)\n') - if (x6 != None): - self.write('#<_value>=[' + x6 + ']\n') - self.write('(LOG,#<_value>)\n') + if ((x5 != None) or (y5 != None) or (z5 != None)): + self.write('(LOG,)\n') - if (y6 != None): - self.write('#<_value>=[' + y6 + ']\n') - self.write('(LOG,#<_value>)\n') + if ((x6 != None) or (y6 != None) or (z6 != None)): + self.write('(LOG,)\n') - if (z6 != None): - self.write('#<_value>=[' + z6 + ']\n') - self.write('(LOG,#<_value>)\n') + if (x6 != None): + self.write('#<_value>=[' + x6 + ']\n') + self.write('(LOG,#<_value>)\n') - if ((x6 != None) or (y6 != None) or (z6 != None)): - self.write('(LOG,)\n') + if (y6 != None): + self.write('#<_value>=[' + y6 + ']\n') + self.write('(LOG,#<_value>)\n') - self.write('(LOG,)\n') + if (z6 != None): + self.write('#<_value>=[' + z6 + ']\n') + self.write('(LOG,#<_value>)\n') - if (xml_file_name != None): - self.write('(LOGCLOSE)\n') + if ((x6 != None) or (y6 != None) or (z6 != None)): + self.write('(LOG,)\n') - def open_log_file(self, xml_file_name=None ): - self.write('(LOGOPEN,') - self.write(xml_file_name) - self.write(')\n') + self.write('(LOG,)\n') - def close_log_file(self): - self.write('(LOGCLOSE)\n') + if (xml_file_name != None): + self.write('(LOGCLOSE)\n') - def log_coordinate(self, x=None, y=None, z=None): - if ((x != None) or (y != None) or (z != None)): - self.write('(LOG,)\n') + def open_log_file(self, xml_file_name=None): + self.write('(LOGOPEN,') + self.write(xml_file_name) + self.write(')\n') - if (x != None): - self.write('#<_value>=[' + x + ']\n') - self.write('(LOG,#<_value>)\n') + def close_log_file(self): + self.write('(LOGCLOSE)\n') - if (y != None): - self.write('#<_value>=[' + y + ']\n') - self.write('(LOG,#<_value>)\n') + def log_coordinate(self, x=None, y=None, z=None): + if ((x != None) or (y != None) or (z != None)): + self.write('(LOG,)\n') - if (z != None): - self.write('#<_value>=[' + z + ']\n') - self.write('(LOG,#<_value>)\n') + if (x != None): + self.write('#<_value>=[' + x + ']\n') + self.write('(LOG,#<_value>)\n') - if ((x != None) or (y != None) or (z != None)): - self.write('(LOG,)\n') + if (y != None): + self.write('#<_value>=[' + y + ']\n') + self.write('(LOG,#<_value>)\n') - def log_message(self, message=None ): - self.write('(LOG,' + message + ')\n') + if (z != None): + self.write('#<_value>=[' + z + ']\n') + self.write('(LOG,#<_value>)\n') - def start_CRC(self, left = True, radius = 0.0): - if self.t == None: - raise "No tool specified for start_CRC()" - if left: - self.write(('G41' + self.SPACE() + 'D%i') % self.t + '\t (start left cutter radius compensation)\n' ) - else: - self.write(('G42' + self.SPACE() + 'D%i') % self.t + '\t (start right cutter radius compensation)\n' ) + if ((x != None) or (y != None) or (z != None)): + self.write('(LOG,)\n') - def end_CRC(self): - self.g = 'G40' - self.write_preps() - self.write_misc() - self.write('\t (end cutter radius compensation)\n') + def log_message(self, message=None): + self.write('(LOG,' + message + ')\n') + def start_CRC(self, left=True, radius=0.0): + if self.t == None: + raise "No tool specified for start_CRC()" + if left: + self.write(('G41' + self.SPACE() + 'D%i') % + self.t + '\t (start left cutter radius compensation)\n') + else: + self.write(('G42' + self.SPACE() + 'D%i') % + self.t + '\t (start right cutter radius compensation)\n') + def end_CRC(self): + self.g = 'G40' + self.write_preps() + self.write_misc() + self.write('\t (end cutter radius compensation)\n') + def tool_defn(self, id, name='', params=None): + pass - def tool_defn(self, id, name='', params=None): - pass nc.creator = Creator() diff --git a/scripts/addons/cam/nc/emc2_read.py b/scripts/addons/cam/nc/emc2_read.py index a8040bec1..2ca0b6f50 100644 --- a/scripts/addons/cam/nc/emc2_read.py +++ b/scripts/addons/cam/nc/emc2_read.py @@ -3,6 +3,7 @@ # just use the iso reader + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/emc2b.py b/scripts/addons/cam/nc/emc2b.py index bd82c58f0..7e7b00b84 100644 --- a/scripts/addons/cam/nc/emc2b.py +++ b/scripts/addons/cam/nc/emc2b.py @@ -6,6 +6,7 @@ now = datetime.datetime.now() + class Creator(iso_modal.Creator): def __init__(self): iso_modal.Creator.__init__(self) @@ -21,24 +22,26 @@ def SPACE(self): return ' ' def PROGRAM(self): return None + def PROGRAM_END(self): if self.output_tool_change: - return( 'T0' + self.SPACE() + 'M06' + self.SPACE() + 'M02') + return('T0' + self.SPACE() + 'M06' + self.SPACE() + 'M02') else: return('M02') def dwell(self, t): - self.write('\n') - iso_modal.Creator.dwell(self, t) + self.write('\n') + iso_modal.Creator.dwell(self, t) ############################################################################ -## Begin Program - +# Begin Program def program_begin(self, id, comment): if (self.useCrc == False): - self.write( ('(Created with emc2b post processor ' + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n') ) + self.write(('(Created with emc2b post processor ' + + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n')) else: - self.write( ('(Created with emc2b Cutter Radius Compensation post processor ' + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n') ) + self.write(('(Created with emc2b Cutter Radius Compensation post processor ' + + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n')) iso_modal.Creator.program_begin(self, id, comment) diff --git a/scripts/addons/cam/nc/emc2b_crc.py b/scripts/addons/cam/nc/emc2b_crc.py index 5a5d03784..96fbebc2e 100644 --- a/scripts/addons/cam/nc/emc2b_crc.py +++ b/scripts/addons/cam/nc/emc2b_crc.py @@ -10,6 +10,8 @@ import math ################################################################################ + + class Creator(emc2b.Creator): def __init__(self): @@ -18,4 +20,5 @@ def __init__(self): ################################################################################ + nc.creator = Creator() diff --git a/scripts/addons/cam/nc/emc2b_crc_read.py b/scripts/addons/cam/nc/emc2b_crc_read.py index 075c24e09..2ca0b6f50 100644 --- a/scripts/addons/cam/nc/emc2b_crc_read.py +++ b/scripts/addons/cam/nc/emc2b_crc_read.py @@ -2,6 +2,8 @@ import sys # just use the iso reader + + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/emc2b_read.py b/scripts/addons/cam/nc/emc2b_read.py index 075c24e09..2ca0b6f50 100644 --- a/scripts/addons/cam/nc/emc2b_read.py +++ b/scripts/addons/cam/nc/emc2b_read.py @@ -2,6 +2,8 @@ import sys # just use the iso reader + + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/emc2tap.py b/scripts/addons/cam/nc/emc2tap.py index dd675fe70..0c8a2f1b2 100644 --- a/scripts/addons/cam/nc/emc2tap.py +++ b/scripts/addons/cam/nc/emc2tap.py @@ -3,16 +3,15 @@ from . import emc2 - class CodesEMC2(iso_codes.Codes): def SPACE(self): return(' ') def TAP(self): return('G33.1') def TAP_DEPTH(self, format, depth): return(self.SPACE() + 'K' + (format % depth)) - # This version of COMMENT removes comments from the resultant GCode #def COMMENT(self,comment): return('') + iso_codes.codes = CodesEMC2() @@ -20,9 +19,9 @@ class CreatorEMC2tap(emc2.CreatorEMC2): def init(self): iso.CreatorEMC2.init(self) - # G33.1 tapping with EMC for now # unsynchronized (chuck) taps NIY (tap_mode = 1) + def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None, tap_mode=None, direction=None): # mystery parameters: # zretract=None, dwell_bottom=None,pitch=None, stoppos=None, spin_in=None, spin_out=None): @@ -32,11 +31,11 @@ def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, # This is a bad thing. All the drilling cycles need a retraction (and starting) height. return if (z == None): - return # We need a Z value as well. This input parameter represents the top of the hole + return # We need a Z value as well. This input parameter represents the top of the hole if (pitch == None): - return # We need a pitch value. + return # We need a pitch value. if (direction == None): - return # We need a direction value. + return # We need a direction value. if (tap_mode != 0): self.comment('only rigid tapping currently supported') @@ -54,32 +53,31 @@ def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, # unsure if this is needed: if self.z != retract_height: - self.rapid(z = retract_height) + self.rapid(z=retract_height) # then continue to x,y if given if (x != None) or (y != None): - self.write_blocknum() - self.write(iso_codes.codes.RAPID() ) + self.write_blocknum() + self.write(iso_codes.codes.RAPID()) - if (x != None): - self.write(iso_codes.codes.X() + (self.fmt % x)) - self.x = x + if (x != None): + self.write(iso_codes.codes.X() + (self.fmt % x)) + self.x = x - if (y != None): - self.write(iso_codes.codes.Y() + (self.fmt % y)) - self.y = y - self.write('\n') + if (y != None): + self.write(iso_codes.codes.Y() + (self.fmt % y)) + self.y = y + self.write('\n') self.write_blocknum() - self.write( iso_codes.codes.TAP() ) - self.write( iso_codes.codes.TAP_DEPTH(self.ffmt,pitch) + iso_codes.codes.SPACE() ) - self.write(iso_codes.codes.Z() + (self.fmt % (z - depth))) # This is the 'z' value for the bottom of the tap. + self.write(iso_codes.codes.TAP()) + self.write(iso_codes.codes.TAP_DEPTH(self.ffmt, pitch) + iso_codes.codes.SPACE()) + # This is the 'z' value for the bottom of the tap. + self.write(iso_codes.codes.Z() + (self.fmt % (z - depth))) self.write_misc() self.write('\n') self.z = retract_height # this cycle returns to the start position, so remember that as z value - nc.creator = CreatorEMC2tap() - diff --git a/scripts/addons/cam/nc/emc2tap_read.py b/scripts/addons/cam/nc/emc2tap_read.py index 075c24e09..2ca0b6f50 100644 --- a/scripts/addons/cam/nc/emc2tap_read.py +++ b/scripts/addons/cam/nc/emc2tap_read.py @@ -2,6 +2,8 @@ import sys # just use the iso reader + + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/fadal.py b/scripts/addons/cam/nc/fadal.py index 2dd8a9eb9..2fc4608cc 100644 --- a/scripts/addons/cam/nc/fadal.py +++ b/scripts/addons/cam/nc/fadal.py @@ -11,54 +11,58 @@ from . import iso from .format import Format + class Creator(iso.Creator): - def __init__(self): - iso.Creator.__init__(self) - - # internal variables - - self.fmt = Format(add_trailing_zeros = True) - - ############################################################################ - ## Codes - - def SPACE_STR(self): return ' ' - - ############################################################################ - ## Programs - - def program_begin(self, id, name=''): - if self.use_this_program_id: - id = self.use_this_program_id - if self.PROGRAM() != None: - self.write('%') - self.write('\n') - self.writem([(self.PROGRAM() % id), self.SPACE(), (self.COMMENT(name))]) - self.write('\n') - self.program_id = id - self.program_name = name - - def program_end(self): - if self.z_for_g53 != None: - self.write(self.SPACE() + self.MACHINE_COORDINATES() + self.SPACE() + 'Z' + self.fmt.string(self.z_for_g53) + '\n') - self.write(self.SPACE() + self.PROGRAM_END() + '\n') - self.write('%') - - if self.temp_file_to_append_on_close != None: - f_in = open(self.temp_file_to_append_on_close, 'r') - while (True): - line = f_in.readline() - if (len(line) == 0) : break - self.write(line) - f_in.close() - - self.file_close() - - if self.output_block_numbers: - # number every line of the file afterwards - self.number_file(self.filename) - - for f in self.subroutine_files: - self.number_file(f) + def __init__(self): + iso.Creator.__init__(self) + + # internal variables + + self.fmt = Format(add_trailing_zeros=True) + + ############################################################################ + # Codes + + def SPACE_STR(self): return ' ' + + ############################################################################ + # Programs + + def program_begin(self, id, name=''): + if self.use_this_program_id: + id = self.use_this_program_id + if self.PROGRAM() != None: + self.write('%') + self.write('\n') + self.writem([(self.PROGRAM() % id), self.SPACE(), (self.COMMENT(name))]) + self.write('\n') + self.program_id = id + self.program_name = name + + def program_end(self): + if self.z_for_g53 != None: + self.write(self.SPACE() + self.MACHINE_COORDINATES() + + self.SPACE() + 'Z' + self.fmt.string(self.z_for_g53) + '\n') + self.write(self.SPACE() + self.PROGRAM_END() + '\n') + self.write('%') + + if self.temp_file_to_append_on_close != None: + f_in = open(self.temp_file_to_append_on_close, 'r') + while (True): + line = f_in.readline() + if (len(line) == 0): + break + self.write(line) + f_in.close() + + self.file_close() + + if self.output_block_numbers: + # number every line of the file afterwards + self.number_file(self.filename) + + for f in self.subroutine_files: + self.number_file(f) + nc.creator = Creator() diff --git a/scripts/addons/cam/nc/format.py b/scripts/addons/cam/nc/format.py index e93507f91..95d28bcee 100644 --- a/scripts/addons/cam/nc/format.py +++ b/scripts/addons/cam/nc/format.py @@ -1,10 +1,13 @@ import math + class Format: - def __init__(self, number_of_decimal_places = 3, add_leading_zeros = 1, add_trailing_zeros = False, dp_wanted = True, add_plus = False, no_minus = False, round_down = False): + def __init__(self, number_of_decimal_places=3, add_leading_zeros=1, add_trailing_zeros=False, dp_wanted=True, add_plus=False, no_minus=False, round_down=False): self.number_of_decimal_places = number_of_decimal_places - self.add_leading_zeros = add_leading_zeros # fill the start of the number with zeros, so there are at least this number of digits before the decimal point - self.add_trailing_zeros = add_trailing_zeros # fill the end of the number with zeros, as defined by "number_of_decimal_places" + # fill the start of the number with zeros, so there are at least this number of digits before the decimal point + self.add_leading_zeros = add_leading_zeros + # fill the end of the number with zeros, as defined by "number_of_decimal_places" + self.add_trailing_zeros = add_trailing_zeros self.dp_wanted = dp_wanted self.add_plus = add_plus self.no_minus = no_minus @@ -17,8 +20,10 @@ def string(self, number): s = format(f, 'f') if self.round_down == False: - if f < 0: f = f - .5 - else: f = f + .5 + if f < 0: + f = f - .5 + else: + f = f + .5 s = format(float(number), 'f') if math.fabs(f) < 1.0: @@ -52,13 +57,15 @@ def string(self, number): s += '+' s += before_dp if len(after_dp): - if self.dp_wanted: s += '.' + if self.dp_wanted: + s += '.' s += after_dp return s + class Address: - def __init__(self, text, fmt = Format(), modal = True): + def __init__(self, text, fmt=Format(), modal=True): self.text = text self.fmt = fmt self.modal = modal @@ -69,7 +76,8 @@ def set(self, number): self.str = self.text + self.fmt.string(number) def write(self, writer): - if self.str == None: return '' + if self.str == None: + return '' if self.modal: if self.str != self.previous: writer.write(writer.SPACE() + self.str) @@ -78,8 +86,9 @@ def write(self, writer): writer.write(writer.SPACE() + self.str) self.str = None + class AddressPlusMinus(Address): - def __init__(self, text, fmt = Format(), modal = True): + def __init__(self, text, fmt=Format(), modal=True): Address.__init__(self, text, fmt, modal) self.str2 = None self.previous2 = None @@ -93,7 +102,8 @@ def set(self, number, text_plus, text_minus): def write(self, writer): Address.write(self, writer) - if self.str2 == None: return '' + if self.str2 == None: + return '' if self.modal: if self.str2 != self.previous2: writer.write(writer.SPACE() + self.str2) diff --git a/scripts/addons/cam/nc/gantry_router.py b/scripts/addons/cam/nc/gantry_router.py index 80e5392db..86e1ac436 100644 --- a/scripts/addons/cam/nc/gantry_router.py +++ b/scripts/addons/cam/nc/gantry_router.py @@ -1,17 +1,19 @@ from . import nc from . import emc2 + class Creator(emc2.Creator): - def init(self): - emc2.Creator.init(self) + def init(self): + emc2.Creator.init(self) + + def program_begin(self, id, comment): + self.write(('(' + comment + ')' + '\n')) - def program_begin(self, id, comment): - self.write( ('(' + comment + ')' + '\n') ) + def tool_defn(self, id, name='', params=None): + pass - def tool_defn(self, id, name='', params=None): - pass + def spindle(self, s, clockwise): + pass - def spindle(self, s, clockwise): - pass nc.creator = Creator() diff --git a/scripts/addons/cam/nc/gantry_router_read.py b/scripts/addons/cam/nc/gantry_router_read.py index 075c24e09..2ca0b6f50 100644 --- a/scripts/addons/cam/nc/gantry_router_read.py +++ b/scripts/addons/cam/nc/gantry_router_read.py @@ -2,6 +2,8 @@ import sys # just use the iso reader + + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/gravos.py b/scripts/addons/cam/nc/gravos.py index 33a659f55..8dbf91f63 100644 --- a/scripts/addons/cam/nc/gravos.py +++ b/scripts/addons/cam/nc/gravos.py @@ -3,55 +3,55 @@ class Creator(iso.Creator): - def __init__(self): - iso.Creator.__init__(self) - - def SPACE_STR(self): return ' ' - def COMMENT(self, comment): return( (';%s' % comment ) ) - def PROGRAM(self): return(None) - - def program_begin(self, id, comment): - self.write( (';' + comment + '\n') ) - def TIME(self): return('X') - - def SPINDLE_OFF(self): return('M05') - # optimize - def RAPID(self): return('G0') - def FEED(self): return('G1') - - # def SPINDLE_DWELL(self,dwell): - # w='\n'+self.BLOCK() % self.n+ self.DWELL() % dwell - # return w - # - # def SPINDLE_CW(self,dwell): - # return('M03' + self.SPINDLE_DWELL(dwell) ) - # - # def SPINDLE_CCW(self,dwell): - # return('M04' + self.SPINDLE_DWELL(dwell)) - # - # def write_spindle(self): - # #self.write('\n') - # #self.write_blocknum() - # self.s.write(self) - - - def tool_change(self, id): - #print(self.SPACE()) - #print(self.TOOL()) - self.write(self.SPACE() + (self.TOOL() % id) + '\n') - #self.write('\n') - self.flush_nc() - self.t = id - - #def write_spindle(self): - # if self.s.str!=None: - # self.write(self.s.str) - # self.s.str = None - - def PROGRAM_END(self): return( 'M30') - - def program_end(self): - self.write(self.SPACE() + self.SPINDLE_OFF() + self.SPACE() + self.PROGRAM_END() + '\n') + def __init__(self): + iso.Creator.__init__(self) + + def SPACE_STR(self): return ' ' + def COMMENT(self, comment): return((';%s' % comment)) + def PROGRAM(self): return(None) + + def program_begin(self, id, comment): + self.write((';' + comment + '\n')) + + def TIME(self): return('X') + + def SPINDLE_OFF(self): return('M05') + # optimize + def RAPID(self): return('G0') + def FEED(self): return('G1') + + # def SPINDLE_DWELL(self,dwell): + # w='\n'+self.BLOCK() % self.n+ self.DWELL() % dwell + # return w + # + # def SPINDLE_CW(self,dwell): + # return('M03' + self.SPINDLE_DWELL(dwell) ) + # + # def SPINDLE_CCW(self,dwell): + # return('M04' + self.SPINDLE_DWELL(dwell)) + # + # def write_spindle(self): + # #self.write('\n') + # #self.write_blocknum() + # self.s.write(self) + + def tool_change(self, id): + # print(self.SPACE()) + # print(self.TOOL()) + self.write(self.SPACE() + (self.TOOL() % id) + '\n') + # self.write('\n') + self.flush_nc() + self.t = id + + # def write_spindle(self): + # if self.s.str!=None: + # self.write(self.s.str) + # self.s.str = None + + def PROGRAM_END(self): return('M30') + + def program_end(self): + self.write(self.SPACE() + self.SPINDLE_OFF() + self.SPACE() + self.PROGRAM_END() + '\n') nc.creator = Creator() diff --git a/scripts/addons/cam/nc/grbl.py b/scripts/addons/cam/nc/grbl.py index ff71bdd05..57d87c850 100644 --- a/scripts/addons/cam/nc/grbl.py +++ b/scripts/addons/cam/nc/grbl.py @@ -6,53 +6,57 @@ now = datetime.datetime.now() + class Creator(iso_modal.Creator): - def __init__(self): - iso_modal.Creator.__init__(self) - self.absolute_flag = True - self.prev_g91 = '' - self.useCrc = False - self.start_of_line = True - self.output_block_numbers = False - self.output_tool_definitions = False - - def PROGRAM_END(self): return ' ' - #optimize - def RAPID(self): return('G0') - def FEED(self): return('G1') + def __init__(self): + iso_modal.Creator.__init__(self) + self.absolute_flag = True + self.prev_g91 = '' + self.useCrc = False + self.start_of_line = True + self.output_block_numbers = False + self.output_tool_definitions = False + + def PROGRAM_END(self): return ' ' + # optimize + def RAPID(self): return('G0') + def FEED(self): return('G1') ############################################################################ -## Begin Program - - - def program_begin(self, id, comment): - if (self.useCrc == False): - self.write( ('(Created with grbl post processor ' + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n') ) - else: - self.write( ('(Created with grbl Cutter Radius Compensation post processor ' + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n') ) - +# Begin Program + def program_begin(self, id, comment): + if (self.useCrc == False): + self.write(('(Created with grbl post processor ' + + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n')) + else: + self.write(('(Created with grbl Cutter Radius Compensation post processor ' + + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n')) ############################################################################ -## Settings +# Settings - def tool_defn(self, id, name='', params=None): - pass + def tool_defn(self, id, name='', params=None): + pass - def tool_change(self, id): - pass + def tool_change(self, id): + pass # This is the coordinate system we're using. G54->G59, G59.1, G59.2, G59.3 # These are selected by values from 1 to 9 inclusive. - def workplane(self, id): - if ((id >= 1) and (id <= 6)): - self.write_blocknum() - self.write( (self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + '\t (Select Relative Coordinate System)\n') - if ((id >= 7) and (id <= 9)): - self.write_blocknum() - self.write( ((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6))) + '\t (Select Relative Coordinate System)\n') + + + def workplane(self, id): + if ((id >= 1) and (id <= 6)): + self.write_blocknum() + self.write((self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + + '\t (Select Relative Coordinate System)\n') + if ((id >= 7) and (id <= 9)): + self.write_blocknum() + self.write(((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % + (id - 6))) + '\t (Select Relative Coordinate System)\n') nc.creator = Creator() diff --git a/scripts/addons/cam/nc/heiden.py b/scripts/addons/cam/nc/heiden.py index bb9ff4a30..f75989f1a 100644 --- a/scripts/addons/cam/nc/heiden.py +++ b/scripts/addons/cam/nc/heiden.py @@ -1,1155 +1,1220 @@ # heiden.py, just copied from iso.py, to start with, but needs to be modified to make this sort of output -#1 BEGIN PGM 0011 MM -#2 BLK FORM 0.1 Z X-262.532 Y-262.55 Z-75.95 -#3 BLK FORM 0.2 X262.532 Y262.55 Z0.05 -#4 TOOL CALL 3 Z S3263 DL+0.0 DR+0.0 -#5 TOOL CALL 3 Z S3263 DL+0.0 DR+0.0 -#6 L X-80.644 Y-95.2 Z+100.0 R0 F237 M3 -#7 L Z-23.222 F333 -#8 L X-80.627 Y-95.208 Z-23.5 F326 -#49 L X-73.218 Y-88.104 Z-26.747 F229 -#50 L X-73.529 Y-87.795 Z-26.769 F227 -#51 L X-74.09 Y-87.326 Z-25.996 F279 -#52 M30 -#53 END PGM 0011 MM - - -from . import iso, nc,emc2 +# 1 BEGIN PGM 0011 MM +# 2 BLK FORM 0.1 Z X-262.532 Y-262.55 Z-75.95 +# 3 BLK FORM 0.2 X262.532 Y262.55 Z0.05 +# 4 TOOL CALL 3 Z S3263 DL+0.0 DR+0.0 +# 5 TOOL CALL 3 Z S3263 DL+0.0 DR+0.0 +# 6 L X-80.644 Y-95.2 Z+100.0 R0 F237 M3 +# 7 L Z-23.222 F333 +# 8 L X-80.627 Y-95.208 Z-23.5 F326 +# 49 L X-73.218 Y-88.104 Z-26.747 F229 +# 50 L X-73.529 Y-87.795 Z-26.769 F227 +# 51 L X-74.09 Y-87.326 Z-25.996 F279 +# 52 M30 +# 53 END PGM 0011 MM + + +from . import iso, nc, emc2 import math from .format import Format from .format import * ################################################################################ + + class Creator(nc.Creator): - def __init__(self): - nc.Creator.__init__(self) - - self.a = 0 - self.b = 0 - self.c = 0 - self.f = Address('F', fmt = Format(number_of_decimal_places = 2)) - self.fh = None - self.fv = None - self.fhv = False - self.g_plane = Address('G', fmt = Format(number_of_decimal_places = 0)) - self.g_list = [] - self.i = 0 - self.j = 0 - self.k = 0 - self.m = [] - self.n = 10 - self.r = 0 - self.s = AddressPlusMinus('S', fmt = Format(number_of_decimal_places = 2), modal = False) - self.t = None - self.x = 0 - self.y = 0 - self.z = 500 - self.g0123_modal = False - self.drill_modal = False - self.prev_f = '' - self.prev_g0123 = '' - self.prev_drill = '' - self.prev_retract = '' - self.prev_z = '' - self.useCrc = False - self.useCrcCenterline = False - self.gCRC = '' - self.fmt = Format() - self.absolute_flag = True - self.ffmt = Format(number_of_decimal_places = 2) - self.sfmt = Format(number_of_decimal_places = 1) - self.arc_centre_absolute = False - self.arc_centre_positive = False - self.in_quadrant_splitting = False - self.drillExpanded = False - self.can_do_helical_arcs = True - self.shift_x = 0.0 - self.shift_y = 0.0 - self.shift_z = 0.0 - ############################################################################ - ## Codes - - def SPACE(self): return('') - def FORMAT_FEEDRATE(self): return('%.2f') - def FEEDRATE(self): return((self.SPACE() + 'F')) - def FORMAT_ANG(self): return('%.1f') - def FORMAT_TIME(self): return('%.2f') - def FORMAT_DWELL(self): return('P%f') - - def BLOCK(self): return('%i') - def COMMENT(self,comment): return( ('(%s)' % comment ) ) - def VARIABLE(self): return( '#%i') - def VARIABLE_SET(self): return( '=%.3f') - - def SUBPROG_CALL(self): return( 'M98' + self.SPACE() + 'P%i') - def SUBPROG_END(self): return( 'M99') - - def STOP_OPTIONAL(self): return('M01') - def STOP(self): return('M00') - - def IMPERIAL(self): return('G20') - def METRIC(self): return('G21') - def ABSOLUTE(self): return('G90') - def INCREMENTAL(self): return('G91') - def SET_TEMPORARY_COORDINATE_SYSTEM(self): return('G92') - def REMOVE_TEMPORARY_COORDINATE_SYSTEM(self): return('G92.1') - def POLAR_ON(self): return('G16') - def POLAR_OFF(self): return('G15') - def PLANE_XY(self): return('17') - def PLANE_XZ(self): return('18') - def PLANE_YZ(self): return('19') - - def TOOL(self): return('T%i' + self.SPACE() + 'M06') - def TOOL_DEFINITION(self): return('G10' + self.SPACE() + 'L1') - - def WORKPLANE(self): return('G%i') - def WORKPLANE_BASE(self): return(53) - - def SPINDLE_CW(self): return('M03') - def SPINDLE_CCW(self): return('M04') - def COOLANT_OFF(self): return('M09') - def COOLANT_MIST(self): return('M07') - def COOLANT_FLOOD(self): return('M08') - def GEAR_OFF(self): return('?') - def GEAR(self): return('M%i') - def GEAR_BASE(self): return(37) - - def RAPID(self): return('G00') - def FEED(self): return('G01') - def ARC_CW(self): return('G02') - def ARC_CCW(self): return('G03') - def DWELL(self): return('G04') - def DRILL(self): return('G81') - def DRILL_WITH_DWELL(self, format, dwell): return('G82' + self.SPACE() + (format.string(dwell))) - def PECK_DRILL(self): return('G83') - def PECK_DEPTH(self, format, depth): return(self.SPACE() + 'Q' + (format.string(depth))) - def RETRACT(self, format, height): return(self.SPACE() + 'R' + (format.string(height))) - def END_CANNED_CYCLE(self): return('G80') - def TAP(self): return('G84') - def TAP_DEPTH(self, format, depth): return(self.SPACE() + 'K' + (format.string(depth))) - - def X(self): return('X') - def Y(self): return('Y') - def Z(self): return('Z') - def A(self): return('A') - def B(self): return('B') - def C(self): return('C') - def CENTRE_X(self): return('I') - def CENTRE_Y(self): return('J') - def CENTRE_Z(self): return('K') - def RADIUS(self): return('R') - def TIME(self): return('P') - - def PROBE_TOWARDS_WITH_SIGNAL(self): return('G38.2') - def PROBE_TOWARDS_WITHOUT_SIGNAL(self): return('G38.3') - def PROBE_AWAY_WITH_SIGNAL(self): return('G38.4') - def PROBE_AWAY_WITHOUT_SIGNAL(self): return('G38.5') - - def MACHINE_COORDINATES(self): return('G53') - - def EXACT_PATH_MODE(self): return('G61') - def EXACT_STOP_MODE(self): return('G61.1') - - ############################################################################ - ## Internals - - def write_feedrate(self): - self.f.write(self) - - def write_preps(self): - self.g_plane.write(self) - for g in self.g_list: - self.write(self.SPACE() + g) - self.g_list = [] - - def write_misc(self): - if (len(self.m)) : self.write(self.m.pop()) - - def write_blocknum(self): - self.write(self.BLOCK() % self.n) - self.n += 1 - - def write_spindle(self): - self.s.write(self) - - ############################################################################ - ## Programs - - def program_begin(self, id, name=''): - #1 BEGIN PGM 0011 MM - self.write_blocknum() - self.program_id = id - self.write(self.SPACE() + ('BEGIN PGM %i MM' % id)) - self.write('\n') - - def program_stop(self, optional=False): - self.write_blocknum() - if (optional) : - self.write(self.SPACE() + self.STOP_OPTIONAL() + '\n') - self.prev_g0123 = '' - else : - self.write(self.STOP() + '\n') - self.prev_g0123 = '' - - - def program_end(self): - self.write_blocknum() - self.write(self.SPACE() + ('END PGM %i MM' % self.program_id) + '\n') - - def flush_nc(self): - if len(self.g_list) == 0 and len(self.m) == 0: return - self.write_blocknum() - self.write_preps() - self.write_misc() - self.write('\n') - - ############################################################################ - ## Subprograms - - def sub_begin(self, id, name=''): - self.write((self.PROGRAM() % id) + self.SPACE() + (self.COMMENT(name))) - self.write('\n') - - def sub_call(self, id): - self.write_blocknum() - self.write(self.SPACE() + (self.SUBPROG_CALL() % id) + '\n') - - def sub_end(self): - self.write_blocknum() - self.write(self.SPACE() + self.SUBPROG_END() + '\n') - - ############################################################################ - ## Settings - - def imperial(self): - self.g_list.append(self.IMPERIAL()) - self.fmt.number_of_decimal_places = 4 - - def metric(self): - self.g_list.append(self.METRIC()) - self.fmt.number_of_decimal_places = 3 - - def absolute(self): - self.g_list.append(self.ABSOLUTE()) - self.absolute_flag = True - - def incremental(self): - self.g_list.append(self.INCREMENTAL()) - self.absolute_flag = False - - def polar(self, on=True): - if (on) : self.g_list.append(self.POLAR_ON()) - else : self.g_list.append(self.POLAR_OFF()) - - def set_plane(self, plane): - if (plane == 0) : self.g_plane.set(self.PLANE_XY()) - elif (plane == 1) : self.g_plane.set(self.PLANE_XZ()) - elif (plane == 2) : self.g_plane.set(self.PLANE_YZ()) - - def set_temporary_origin(self, x=None, y=None, z=None, a=None, b=None, c=None): - self.write_blocknum() - self.write(self.SPACE() + (self.SET_TEMPORARY_COORDINATE_SYSTEM())) - if (x != None): self.write( self.SPACE() + 'X ' + (self.fmt.string(x + self.shift_x)) ) - if (y != None): self.write( self.SPACE() + 'Y ' + (self.fmt.string(y + self.shift_y)) ) - if (z != None): self.write( self.SPACE() + 'Z ' + (self.fmt.string(z + self.shift_z)) ) - if (a != None): self.write( self.SPACE() + 'A ' + (self.fmt.string(a)) ) - if (b != None): self.write( self.SPACE() + 'B ' + (self.fmt.string(b)) ) - if (c != None): self.write( self.SPACE() + 'C ' + (self.fmt.string(c)) ) - self.write('\n') - - def remove_temporary_origin(self): - self.write_blocknum() - self.write(self.SPACE() + (self.REMOVE_TEMPORARY_COORDINATE_SYSTEM())) - self.write('\n') - ############################################################################ - ## new graphics origin- make a new coordinate system and snap it onto the geometry - ## the toolpath generated should be translated - def translate(self,x=None, y=None, z=None): - self.shift_x = -x - self.shift_y = -y - self.shift_z = -z - - ############################################################################ - ## Tools - - def tool_change(self, id): - self.write_blocknum() - self.write(self.SPACE() + (self.TOOL() % id) + '\n') - self.t = id - - def tool_defn(self, id, name='', params=None): - self.write_blocknum() - self.write(self.SPACE() + self.TOOL_DEFINITION()) - self.write(self.SPACE() + ('P%i' % id) + ' ') - - if (radius != None): - self.write(self.SPACE() + ('R%.3f' % (float(params['diameter'])/2))) - - if (length != None): - self.write(self.SPACE() + 'Z%.3f' % float(params['cutting edge height'])) - - self.write('\n') - - def offset_radius(self, id, radius=None): - pass - - def offset_length(self, id, length=None): - pass - - def current_tool(self): - return self.t - - ############################################################################ - ## Datums - - def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None): - pass - - def datum_set(self, x=None, y=None, z=None, a=None, b=None, c=None): - pass - - # This is the coordinate system we're using. G54->G59, G59.1, G59.2, G59.3 - # These are selected by values from 1 to 9 inclusive. - def workplane(self, id): - if ((id >= 1) and (id <= 6)): - self.g_list.append(self.WORKPLANE() % (id + self.WORKPLANE_BASE())) - if ((id >= 7) and (id <= 9)): - self.g_list.append(((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6)))) - - - ############################################################################ - ## Rates + Modes - - def feedrate(self, f): - self.f.set(f) - self.fhv = False - - def feedrate_hv(self, fh, fv): - self.fh = fh - self.fv = fv - self.fhv = True - - def calc_feedrate_hv(self, h, v): - if math.fabs(v) > math.fabs(h * 2): - # some horizontal, so it should be fine to use the horizontal feed rate - self.f.set(self.fv) - else: - # not much, if any horizontal component, so use the vertical feed rate - self.f.set(self.fh) - - def spindle(self, s, clockwise): - if clockwise == True: - self.s.set(s, self.SPACE() + self.SPINDLE_CW(), self.SPACE() + self.SPINDLE_CCW()) - else: - self.s.set(s, self.SPACE() + self.SPINDLE_CCW(), self.SPACE() + self.SPINDLE_CW()) - - def coolant(self, mode=0): - if (mode <= 0) : self.m.append(self.SPACE() + self.COOLANT_OFF()) - elif (mode == 1) : self.m.append(self.SPACE() + self.COOLANT_MIST()) - elif (mode == 2) : self.m.append(self.SPACE() + self.COOLANT_FLOOD()) - - def gearrange(self, gear=0): - if (gear <= 0) : self.m.append(self.SPACE() + self.GEAR_OFF()) - elif (gear <= 4) : self.m.append(self.SPACE() + self.GEAR() % (gear + GEAR_BASE())) - - ############################################################################ - ## Moves - - def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None ): - self.write_blocknum() - - if self.g0123_modal: - if self.prev_g0123 != self.RAPID(): - self.write(self.SPACE() + self.RAPID()) - self.prev_g0123 = self.RAPID() - else: - self.write(self.SPACE() + self.RAPID()) - self.write_preps() - if (x != None): - dx = x - self.x - if (self.absolute_flag ): - self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) - else: - self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) - self.x = x - if (y != None): - dy = y - self.y - if (self.absolute_flag ): - self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) - else: - self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) - - self.y = y - if (z != None): - dz = z - self.z - if (self.absolute_flag ): - self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) - else: - self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) - - self.z = z - - if (a != None): - da = a - self.a - if (self.absolute_flag ): - self.write(self.SPACE() + self.A() + (self.fmt.string(a))) - else: - self.write(self.SPACE() + self.A() + (self.fmt.string(da))) - self.a = a - - if (b != None): - db = b - self.b - if (self.absolute_flag ): - self.write(self.SPACE() + self.B() + (self.fmt.string(b))) - else: - self.write(self.SPACE() + self.B() + (self.fmt.string(db))) - self.b = b - - if (c != None): - dc = c - self.c - if (self.absolute_flag ): - self.write(self.SPACE() + self.C() + (self.fmt.string(c))) - else: - self.write(self.SPACE() + self.C() + (self.fmt.string(dc))) - self.c = c - self.write_spindle() - self.write_misc() - self.write('\n') - - def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): - if self.same_xyz(x, y, z): return - self.write_blocknum() - if self.g0123_modal: - if self.prev_g0123 != self.FEED(): - self.write(self.SPACE() + self.FEED()) - self.prev_g0123 = self.FEED() - else: - self.write(self.FEED()) - self.write_preps() - dx = dy = dz = 0 - if (x != None): - dx = x - self.x - if (self.absolute_flag ): - self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) - else: - self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) - self.x = x - if (y != None): - dy = y - self.y - if (self.absolute_flag ): - self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) - else: - self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) - - self.y = y - if (z != None): - dz = z - self.z - if (self.absolute_flag ): - self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) - else: - self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) - - self.z = z - if (self.fhv) : self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) - self.write_feedrate() - self.write_spindle() - self.write_misc() - self.write('\n') - - def same_xyz(self, x=None, y=None, z=None): - if (x != None): - if (self.fmt.string(x + self.shift_x)) != (self.fmt.string(self.x)): - return False - if (y != None): - if (self.fmt.string(y + self.shift_y)) != (self.fmt.string(self.y)): - return False - if (z != None): - if (self.fmt.string(z + self.shift_z)) != (self.fmt.string(self.z)): - return False - - return True - - - def get_quadrant(self, dx, dy): - if dx < 0: - if dy < 0: - return 2 - else: - return 1 - - else: - if dy < 0: - return 3 - else: - return 0 - - def quadrant_start(self, q, i, j, rad): - while q > 3: q = q - 4 - if q == 0: - return i + rad, j - if q == 1: - return i, j + rad - if q == 2: - return i - rad, j - return i, j - rad - - def quadrant_end(self, q, i, j, rad): - return self.quadrant_start(q + 1, i, j, rad) - - def get_arc_angle(self, sdx, sdy, edx, edy, cw): - angle_s = math.atan2(sdy, sdx); - angle_e = math.atan2(edy, edx); - if cw: - if angle_s < angle_e: angle_s = angle_s + 2 * math.pi - else: - if angle_e < angle_s: angle_e = angle_e + 2 * math.pi - return angle_e - angle_s - - def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): - if self.can_do_helical_arcs == False and self.in_quadrant_splitting == False and (z != None) and (math.fabs(z - self.z) > 0.000001) and (self.fmt.string(z) != self.fmt.string(self.z)): - # split the helical arc into little line feed moves - if x == None: x = self.x - if y == None: y = self.y - sdx = self.x - i - sdy = self.y - j - edx = x - i - edy = y - j - radius = math.sqrt(sdx*sdx + sdy*sdy) - arc_angle = self.get_arc_angle(sdx, sdy, edx, edy, cw) - angle_start = math.atan2(sdy, sdx); - tolerance = 0.02 - angle_step = 2.0 * math.atan( math.sqrt ( tolerance /(radius - tolerance) )) - segments = int(math.fabs(arc_angle / angle_step) + 1) - angle_step = arc_angle / segments - angle = angle_start - z_step = float(z - self.z)/segments - next_z = self.z - for p in range(0, segments): - angle = angle + angle_step - next_x = i + radius * math.cos(angle) - next_y = j + radius * math.sin(angle) - next_z = next_z + z_step - self.feed(next_x, next_y, next_z) - return - - if self.arc_centre_positive == True and self.in_quadrant_splitting == False: - # split in to quadrant arcs - self.in_quadrant_splitting = True - - if x == None: x = self.x - if y == None: y = self.y - sdx = self.x - i - sdy = self.y - j - edx = x - i - edy = y - j - - qs = self.get_quadrant(sdx, sdy) - qe = self.get_quadrant(edx, edy) - - if qs == qe: - arc_angle = math.fabs(self.get_arc_angle(sdx, sdy, edx, edy, cw)) - # arc_angle will be either less than pi/2 or greater than 3pi/2 - if arc_angle > 3.14: - if cw: - qs = qs + 4 - else: - qe = qe + 4 - - if qs == qe: - self.arc(cw, x, y, z, i, j, k, r) - else: - rad = math.sqrt(sdx * sdx + sdy * sdy) - if cw: - if qs < qe: qs = qs + 4 - else: - if qe < qs: qe = qe + 4 - - q = qs - while 1: - x1 = x - y1 = y - if q != qe: - if cw: - x1, y1 = self.quadrant_start(q, i, j, rad) - else: - x1, y1 = self.quadrant_end(q, i, j, rad) - - if ((math.fabs(x1 - self.x) > 0.000001) or (math.fabs(y1 - self.y) > 0.000001)) and ((self.fmt.string(x1) != self.fmt.string(self.x)) or (self.fmt.string(y1) != self.fmt.string(self.y))): - self.arc(cw, x1, y1, z, i, j, k, r) - if q == qe: - break - if cw: - q = q - 1 - else: - q = q + 1 - - self.in_quadrant_splitting = False - return - - #if self.same_xyz(x, y, z): return - self.write_blocknum() - arc_g_code = '' - if cw: arc_g_code = self.ARC_CW() - else: arc_g_code = self.ARC_CCW() - if self.g0123_modal: - if self.prev_g0123 != arc_g_code: - self.write(arc_g_code) - self.prev_g0123 = arc_g_code - else: - self.write(arc_g_code) - self.write_preps() - if (x != None): - dx = x - self.x - if (self.absolute_flag ): - self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) - else: - self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) - if (y != None): - dy = y - self.y - if (self.absolute_flag ): - self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) - else: - self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) - if (z != None): - dz = z - self.z - if (self.absolute_flag ): - self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) - else: - self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) - if (i != None): - if self.arc_centre_absolute == False: - i = i - self.x - s = self.fmt.string(i) - if self.arc_centre_positive == True: - if s[0] == '-': - s = s[1:] - self.write(self.SPACE() + self.CENTRE_X() + s) - if (j != None): - if self.arc_centre_absolute == False: - j = j - self.y - s = self.fmt.string(j) - if self.arc_centre_positive == True: - if s[0] == '-': - s = s[1:] - self.write(self.SPACE() + self.CENTRE_Y() + s) - if (k != None): - if self.arc_centre_absolute == False: - k = k - self.z - s = self.fmt.string(k) - if self.arc_centre_positive == True: - if s[0] == '-': - s = s[1:] - self.write(self.SPACE() + self.CENTRE_Z() + s) - if (r != None): - s = self.fmt.string(r) - if self.arc_centre_positive == True: - if s[0] == '-': - s = s[1:] - self.write(self.SPACE() + self.RADIUS() + s) + def __init__(self): + nc.Creator.__init__(self) + + self.a = 0 + self.b = 0 + self.c = 0 + self.f = Address('F', fmt=Format(number_of_decimal_places=2)) + self.fh = None + self.fv = None + self.fhv = False + self.g_plane = Address('G', fmt=Format(number_of_decimal_places=0)) + self.g_list = [] + self.i = 0 + self.j = 0 + self.k = 0 + self.m = [] + self.n = 10 + self.r = 0 + self.s = AddressPlusMinus('S', fmt=Format(number_of_decimal_places=2), modal=False) + self.t = None + self.x = 0 + self.y = 0 + self.z = 500 + self.g0123_modal = False + self.drill_modal = False + self.prev_f = '' + self.prev_g0123 = '' + self.prev_drill = '' + self.prev_retract = '' + self.prev_z = '' + self.useCrc = False + self.useCrcCenterline = False + self.gCRC = '' + self.fmt = Format() + self.absolute_flag = True + self.ffmt = Format(number_of_decimal_places=2) + self.sfmt = Format(number_of_decimal_places=1) + self.arc_centre_absolute = False + self.arc_centre_positive = False + self.in_quadrant_splitting = False + self.drillExpanded = False + self.can_do_helical_arcs = True + self.shift_x = 0.0 + self.shift_y = 0.0 + self.shift_z = 0.0 + ############################################################################ + # Codes + + def SPACE(self): return('') + def FORMAT_FEEDRATE(self): return('%.2f') + def FEEDRATE(self): return((self.SPACE() + 'F')) + def FORMAT_ANG(self): return('%.1f') + def FORMAT_TIME(self): return('%.2f') + def FORMAT_DWELL(self): return('P%f') + + def BLOCK(self): return('%i') + def COMMENT(self, comment): return(('(%s)' % comment)) + def VARIABLE(self): return('#%i') + def VARIABLE_SET(self): return('=%.3f') + + def SUBPROG_CALL(self): return('M98' + self.SPACE() + 'P%i') + def SUBPROG_END(self): return('M99') + + def STOP_OPTIONAL(self): return('M01') + def STOP(self): return('M00') + + def IMPERIAL(self): return('G20') + def METRIC(self): return('G21') + def ABSOLUTE(self): return('G90') + def INCREMENTAL(self): return('G91') + def SET_TEMPORARY_COORDINATE_SYSTEM(self): return('G92') + def REMOVE_TEMPORARY_COORDINATE_SYSTEM(self): return('G92.1') + def POLAR_ON(self): return('G16') + def POLAR_OFF(self): return('G15') + def PLANE_XY(self): return('17') + def PLANE_XZ(self): return('18') + def PLANE_YZ(self): return('19') + + def TOOL(self): return('T%i' + self.SPACE() + 'M06') + def TOOL_DEFINITION(self): return('G10' + self.SPACE() + 'L1') + + def WORKPLANE(self): return('G%i') + def WORKPLANE_BASE(self): return(53) + + def SPINDLE_CW(self): return('M03') + def SPINDLE_CCW(self): return('M04') + def COOLANT_OFF(self): return('M09') + def COOLANT_MIST(self): return('M07') + def COOLANT_FLOOD(self): return('M08') + def GEAR_OFF(self): return('?') + def GEAR(self): return('M%i') + def GEAR_BASE(self): return(37) + + def RAPID(self): return('G00') + def FEED(self): return('G01') + def ARC_CW(self): return('G02') + def ARC_CCW(self): return('G03') + def DWELL(self): return('G04') + def DRILL(self): return('G81') + def DRILL_WITH_DWELL(self, format, dwell): return('G82' + self.SPACE() + (format.string(dwell))) + def PECK_DRILL(self): return('G83') + def PECK_DEPTH(self, format, depth): return(self.SPACE() + 'Q' + (format.string(depth))) + def RETRACT(self, format, height): return(self.SPACE() + 'R' + (format.string(height))) + def END_CANNED_CYCLE(self): return('G80') + def TAP(self): return('G84') + def TAP_DEPTH(self, format, depth): return(self.SPACE() + 'K' + (format.string(depth))) + + def X(self): return('X') + def Y(self): return('Y') + def Z(self): return('Z') + def A(self): return('A') + def B(self): return('B') + def C(self): return('C') + def CENTRE_X(self): return('I') + def CENTRE_Y(self): return('J') + def CENTRE_Z(self): return('K') + def RADIUS(self): return('R') + def TIME(self): return('P') + + def PROBE_TOWARDS_WITH_SIGNAL(self): return('G38.2') + def PROBE_TOWARDS_WITHOUT_SIGNAL(self): return('G38.3') + def PROBE_AWAY_WITH_SIGNAL(self): return('G38.4') + def PROBE_AWAY_WITHOUT_SIGNAL(self): return('G38.5') + + def MACHINE_COORDINATES(self): return('G53') + + def EXACT_PATH_MODE(self): return('G61') + def EXACT_STOP_MODE(self): return('G61.1') + + ############################################################################ + # Internals + + def write_feedrate(self): + self.f.write(self) + + def write_preps(self): + self.g_plane.write(self) + for g in self.g_list: + self.write(self.SPACE() + g) + self.g_list = [] + + def write_misc(self): + if (len(self.m)): + self.write(self.m.pop()) + + def write_blocknum(self): + self.write(self.BLOCK() % self.n) + self.n += 1 + + def write_spindle(self): + self.s.write(self) + + ############################################################################ + # Programs + + def program_begin(self, id, name=''): + # 1 BEGIN PGM 0011 MM + self.write_blocknum() + self.program_id = id + self.write(self.SPACE() + ('BEGIN PGM %i MM' % id)) + self.write('\n') + + def program_stop(self, optional=False): + self.write_blocknum() + if (optional): + self.write(self.SPACE() + self.STOP_OPTIONAL() + '\n') + self.prev_g0123 = '' + else: + self.write(self.STOP() + '\n') + self.prev_g0123 = '' + + def program_end(self): + self.write_blocknum() + self.write(self.SPACE() + ('END PGM %i MM' % self.program_id) + '\n') + + def flush_nc(self): + if len(self.g_list) == 0 and len(self.m) == 0: + return + self.write_blocknum() + self.write_preps() + self.write_misc() + self.write('\n') + + ############################################################################ + # Subprograms + + def sub_begin(self, id, name=''): + self.write((self.PROGRAM() % id) + self.SPACE() + (self.COMMENT(name))) + self.write('\n') + + def sub_call(self, id): + self.write_blocknum() + self.write(self.SPACE() + (self.SUBPROG_CALL() % id) + '\n') + + def sub_end(self): + self.write_blocknum() + self.write(self.SPACE() + self.SUBPROG_END() + '\n') + + ############################################################################ + # Settings + + def imperial(self): + self.g_list.append(self.IMPERIAL()) + self.fmt.number_of_decimal_places = 4 + + def metric(self): + self.g_list.append(self.METRIC()) + self.fmt.number_of_decimal_places = 3 + + def absolute(self): + self.g_list.append(self.ABSOLUTE()) + self.absolute_flag = True + + def incremental(self): + self.g_list.append(self.INCREMENTAL()) + self.absolute_flag = False + + def polar(self, on=True): + if (on): + self.g_list.append(self.POLAR_ON()) + else: + self.g_list.append(self.POLAR_OFF()) + + def set_plane(self, plane): + if (plane == 0): + self.g_plane.set(self.PLANE_XY()) + elif (plane == 1): + self.g_plane.set(self.PLANE_XZ()) + elif (plane == 2): + self.g_plane.set(self.PLANE_YZ()) + + def set_temporary_origin(self, x=None, y=None, z=None, a=None, b=None, c=None): + self.write_blocknum() + self.write(self.SPACE() + (self.SET_TEMPORARY_COORDINATE_SYSTEM())) + if (x != None): + self.write(self.SPACE() + 'X ' + (self.fmt.string(x + self.shift_x))) + if (y != None): + self.write(self.SPACE() + 'Y ' + (self.fmt.string(y + self.shift_y))) + if (z != None): + self.write(self.SPACE() + 'Z ' + (self.fmt.string(z + self.shift_z))) + if (a != None): + self.write(self.SPACE() + 'A ' + (self.fmt.string(a))) + if (b != None): + self.write(self.SPACE() + 'B ' + (self.fmt.string(b))) + if (c != None): + self.write(self.SPACE() + 'C ' + (self.fmt.string(c))) + self.write('\n') + + def remove_temporary_origin(self): + self.write_blocknum() + self.write(self.SPACE() + (self.REMOVE_TEMPORARY_COORDINATE_SYSTEM())) + self.write('\n') + ############################################################################ + # new graphics origin- make a new coordinate system and snap it onto the geometry + # the toolpath generated should be translated + + def translate(self, x=None, y=None, z=None): + self.shift_x = -x + self.shift_y = -y + self.shift_z = -z + + ############################################################################ + # Tools + + def tool_change(self, id): + self.write_blocknum() + self.write(self.SPACE() + (self.TOOL() % id) + '\n') + self.t = id + + def tool_defn(self, id, name='', params=None): + self.write_blocknum() + self.write(self.SPACE() + self.TOOL_DEFINITION()) + self.write(self.SPACE() + ('P%i' % id) + ' ') + + if (radius != None): + self.write(self.SPACE() + ('R%.3f' % (float(params['diameter'])/2))) + + if (length != None): + self.write(self.SPACE() + 'Z%.3f' % float(params['cutting edge height'])) + + self.write('\n') + + def offset_radius(self, id, radius=None): + pass + + def offset_length(self, id, length=None): + pass + + def current_tool(self): + return self.t + + ############################################################################ + # Datums + + def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + def datum_set(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + # This is the coordinate system we're using. G54->G59, G59.1, G59.2, G59.3 + # These are selected by values from 1 to 9 inclusive. + def workplane(self, id): + if ((id >= 1) and (id <= 6)): + self.g_list.append(self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + if ((id >= 7) and (id <= 9)): + self.g_list.append( + ((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6)))) + + ############################################################################ + ## Rates + Modes + + def feedrate(self, f): + self.f.set(f) + self.fhv = False + + def feedrate_hv(self, fh, fv): + self.fh = fh + self.fv = fv + self.fhv = True + + def calc_feedrate_hv(self, h, v): + if math.fabs(v) > math.fabs(h * 2): + # some horizontal, so it should be fine to use the horizontal feed rate + self.f.set(self.fv) + else: + # not much, if any horizontal component, so use the vertical feed rate + self.f.set(self.fh) + + def spindle(self, s, clockwise): + if clockwise == True: + self.s.set(s, self.SPACE() + self.SPINDLE_CW(), self.SPACE() + self.SPINDLE_CCW()) + else: + self.s.set(s, self.SPACE() + self.SPINDLE_CCW(), self.SPACE() + self.SPINDLE_CW()) + + def coolant(self, mode=0): + if (mode <= 0): + self.m.append(self.SPACE() + self.COOLANT_OFF()) + elif (mode == 1): + self.m.append(self.SPACE() + self.COOLANT_MIST()) + elif (mode == 2): + self.m.append(self.SPACE() + self.COOLANT_FLOOD()) + + def gearrange(self, gear=0): + if (gear <= 0): + self.m.append(self.SPACE() + self.GEAR_OFF()) + elif (gear <= 4): + self.m.append(self.SPACE() + self.GEAR() % (gear + GEAR_BASE())) + + ############################################################################ + # Moves + + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): + self.write_blocknum() + + if self.g0123_modal: + if self.prev_g0123 != self.RAPID(): + self.write(self.SPACE() + self.RAPID()) + self.prev_g0123 = self.RAPID() + else: + self.write(self.SPACE() + self.RAPID()) + self.write_preps() + if (x != None): + dx = x - self.x + if (self.absolute_flag): + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + else: + self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) + self.x = x + if (y != None): + dy = y - self.y + if (self.absolute_flag): + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + else: + self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) + + self.y = y + if (z != None): + dz = z - self.z + if (self.absolute_flag): + self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) + else: + self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) + + self.z = z + + if (a != None): + da = a - self.a + if (self.absolute_flag): + self.write(self.SPACE() + self.A() + (self.fmt.string(a))) + else: + self.write(self.SPACE() + self.A() + (self.fmt.string(da))) + self.a = a + + if (b != None): + db = b - self.b + if (self.absolute_flag): + self.write(self.SPACE() + self.B() + (self.fmt.string(b))) + else: + self.write(self.SPACE() + self.B() + (self.fmt.string(db))) + self.b = b + + if (c != None): + dc = c - self.c + if (self.absolute_flag): + self.write(self.SPACE() + self.C() + (self.fmt.string(c))) + else: + self.write(self.SPACE() + self.C() + (self.fmt.string(dc))) + self.c = c + self.write_spindle() + self.write_misc() + self.write('\n') + + def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): + if self.same_xyz(x, y, z): + return + self.write_blocknum() + if self.g0123_modal: + if self.prev_g0123 != self.FEED(): + self.write(self.SPACE() + self.FEED()) + self.prev_g0123 = self.FEED() + else: + self.write(self.FEED()) + self.write_preps() + dx = dy = dz = 0 + if (x != None): + dx = x - self.x + if (self.absolute_flag): + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + else: + self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) + self.x = x + if (y != None): + dy = y - self.y + if (self.absolute_flag): + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + else: + self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) + + self.y = y + if (z != None): + dz = z - self.z + if (self.absolute_flag): + self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) + else: + self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) + + self.z = z + if (self.fhv): + self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) + self.write_feedrate() + self.write_spindle() + self.write_misc() + self.write('\n') + + def same_xyz(self, x=None, y=None, z=None): + if (x != None): + if (self.fmt.string(x + self.shift_x)) != (self.fmt.string(self.x)): + return False + if (y != None): + if (self.fmt.string(y + self.shift_y)) != (self.fmt.string(self.y)): + return False + if (z != None): + if (self.fmt.string(z + self.shift_z)) != (self.fmt.string(self.z)): + return False + + return True + + def get_quadrant(self, dx, dy): + if dx < 0: + if dy < 0: + return 2 + else: + return 1 + + else: + if dy < 0: + return 3 + else: + return 0 + + def quadrant_start(self, q, i, j, rad): + while q > 3: + q = q - 4 + if q == 0: + return i + rad, j + if q == 1: + return i, j + rad + if q == 2: + return i - rad, j + return i, j - rad + + def quadrant_end(self, q, i, j, rad): + return self.quadrant_start(q + 1, i, j, rad) + + def get_arc_angle(self, sdx, sdy, edx, edy, cw): + angle_s = math.atan2(sdy, sdx) + angle_e = math.atan2(edy, edx) + if cw: + if angle_s < angle_e: + angle_s = angle_s + 2 * math.pi + else: + if angle_e < angle_s: + angle_e = angle_e + 2 * math.pi + return angle_e - angle_s + + def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + if self.can_do_helical_arcs == False and self.in_quadrant_splitting == False and (z != None) and (math.fabs(z - self.z) > 0.000001) and (self.fmt.string(z) != self.fmt.string(self.z)): + # split the helical arc into little line feed moves + if x == None: + x = self.x + if y == None: + y = self.y + sdx = self.x - i + sdy = self.y - j + edx = x - i + edy = y - j + radius = math.sqrt(sdx*sdx + sdy*sdy) + arc_angle = self.get_arc_angle(sdx, sdy, edx, edy, cw) + angle_start = math.atan2(sdy, sdx) + tolerance = 0.02 + angle_step = 2.0 * math.atan(math.sqrt(tolerance / (radius - tolerance))) + segments = int(math.fabs(arc_angle / angle_step) + 1) + angle_step = arc_angle / segments + angle = angle_start + z_step = float(z - self.z)/segments + next_z = self.z + for p in range(0, segments): + angle = angle + angle_step + next_x = i + radius * math.cos(angle) + next_y = j + radius * math.sin(angle) + next_z = next_z + z_step + self.feed(next_x, next_y, next_z) + return + + if self.arc_centre_positive == True and self.in_quadrant_splitting == False: + # split in to quadrant arcs + self.in_quadrant_splitting = True + + if x == None: + x = self.x + if y == None: + y = self.y + sdx = self.x - i + sdy = self.y - j + edx = x - i + edy = y - j + + qs = self.get_quadrant(sdx, sdy) + qe = self.get_quadrant(edx, edy) + + if qs == qe: + arc_angle = math.fabs(self.get_arc_angle(sdx, sdy, edx, edy, cw)) + # arc_angle will be either less than pi/2 or greater than 3pi/2 + if arc_angle > 3.14: + if cw: + qs = qs + 4 + else: + qe = qe + 4 + + if qs == qe: + self.arc(cw, x, y, z, i, j, k, r) + else: + rad = math.sqrt(sdx * sdx + sdy * sdy) + if cw: + if qs < qe: + qs = qs + 4 + else: + if qe < qs: + qe = qe + 4 + + q = qs + while 1: + x1 = x + y1 = y + if q != qe: + if cw: + x1, y1 = self.quadrant_start(q, i, j, rad) + else: + x1, y1 = self.quadrant_end(q, i, j, rad) + + if ((math.fabs(x1 - self.x) > 0.000001) or (math.fabs(y1 - self.y) > 0.000001)) and ((self.fmt.string(x1) != self.fmt.string(self.x)) or (self.fmt.string(y1) != self.fmt.string(self.y))): + self.arc(cw, x1, y1, z, i, j, k, r) + if q == qe: + break + if cw: + q = q - 1 + else: + q = q + 1 + + self.in_quadrant_splitting = False + return + + # if self.same_xyz(x, y, z): return + self.write_blocknum() + arc_g_code = '' + if cw: + arc_g_code = self.ARC_CW() + else: + arc_g_code = self.ARC_CCW() + if self.g0123_modal: + if self.prev_g0123 != arc_g_code: + self.write(arc_g_code) + self.prev_g0123 = arc_g_code + else: + self.write(arc_g_code) + self.write_preps() + if (x != None): + dx = x - self.x + if (self.absolute_flag): + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + else: + self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) + if (y != None): + dy = y - self.y + if (self.absolute_flag): + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + else: + self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) + if (z != None): + dz = z - self.z + if (self.absolute_flag): + self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) + else: + self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) + if (i != None): + if self.arc_centre_absolute == False: + i = i - self.x + s = self.fmt.string(i) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.CENTRE_X() + s) + if (j != None): + if self.arc_centre_absolute == False: + j = j - self.y + s = self.fmt.string(j) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.CENTRE_Y() + s) + if (k != None): + if self.arc_centre_absolute == False: + k = k - self.z + s = self.fmt.string(k) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.CENTRE_Z() + s) + if (r != None): + s = self.fmt.string(r) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.RADIUS() + s) # use horizontal feed rate - if (self.fhv) : self.calc_feedrate_hv(1, 0) - self.write_feedrate() - self.write_spindle() - self.write_misc() - self.write('\n') - if (x != None): - self.x = x - if (y != None): - self.y = y - if (z != None): - self.z = z - - def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): - self.arc(True, x, y, z, i, j, k, r) - - def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): - self.arc(False, x, y, z, i, j, k, r) - - def dwell(self, t): - self.write_blocknum() - self.write_preps() - self.write(self.FORMAT_DWELL() % t) - self.write_misc() - self.write('\n') - - def rapid_home(self, x=None, y=None, z=None, a=None, b=None, c=None): - pass - - def rapid_unhome(self): - pass - - def set_machine_coordinates(self): - self.write(self.SPACE() + self.MACHINE_COORDINATES()) - self.prev_g0123 = '' - - ############################################################################ - ## CRC - - def use_CRC(self): - return self.useCrc - - def CRC_nominal_path(self): - return self.useCrcCenterline - - def start_CRC(self, left = True, radius = 0.0): - # set up prep code, to be output on next line - if self.t == None: - raise "No tool specified for start_CRC()" - self.write_blocknum() - if left: - self.write(self.SPACE() + 'G41') - else: - self.write(self.SPACE() + 'G42') - self.write((self.SPACE() + 'D%i\n') % self.t) - - def end_CRC(self): - self.write_blocknum() - self.write(self.SPACE() + 'G40\n') - - ############################################################################ - ## Cycles - - def pattern(self): - pass - - def pocket(self): - pass - - def profile(self): - pass - - # The drill routine supports drilling (G81), drilling with dwell (G82) and peck drilling (G83). - # The x,y,z values are INITIAL locations (above the hole to be made. This is in contrast to - # the Z value used in the G8[1-3] cycles where the Z value is that of the BOTTOM of the hole. - # Instead, this routine combines the Z value and the depth value to determine the bottom of - # the hole. - # - # The standoff value is the distance up from the 'z' value (normally just above the surface) where the bit retracts - # to in order to clear the swarf. This combines with 'z' to form the 'R' value in the G8[1-3] cycles. - # - # The peck_depth value is the incremental depth (Q value) that tells the peck drilling - # cycle how deep to go on each peck until the full depth is achieved. - # - # NOTE: This routine forces the mode to absolute mode so that the values passed into - # the G8[1-3] cycles make sense. I don't know how to find the mode to revert it so I won't - # revert it. I must set the mode so that I can be sure the values I'm passing in make - # sense to the end-machine. - # - def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance = None): - if (standoff == None): - # This is a bad thing. All the drilling cycles need a retraction (and starting) height. - return - - if (z == None): - return # We need a Z value as well. This input parameter represents the top of the hole - - if self.drillExpanded: - # for machines which don't understand G81, G82 etc. - if peck_depth == None: - peck_depth = depth - current_z = z - self.rapid(x, y) - - first = True - - while True: - next_z = current_z - peck_depth - if next_z < z - depth: - next_z = z - depth - if next_z >= current_z: - break; - if first: - self.rapid(z = z + standoff) - else: - self.rapid(z = current_z) - self.feed(z = next_z) - self.rapid(z = z + standoff) - current_z = next_z - if dwell: - self.dwell(dwell) - first = False - - # we should pass clearance height into here, but my machine is on and I'm in a hurry... 22nd June 2011 danheeks - self.rapid(z = z + 5.0) - - return - - self.write_preps() - self.write_blocknum() - - if (peck_depth != 0): - # We're pecking. Let's find a tree. - if self.drill_modal: - if self.PECK_DRILL() + self.PECK_DEPTH(self.fmt, peck_depth) != self.prev_drill: - self.write(self.SPACE() + self.PECK_DRILL() + self.SPACE() + self.PECK_DEPTH(self.fmt, peck_depth)) - self.prev_drill = self.PECK_DRILL() + self.PECK_DEPTH(self.fmt, peck_depth) - else: - self.write(self.PECK_DRILL() + self.PECK_DEPTH(self.fmt, peck_depth)) - - else: - # We're either just drilling or drilling with dwell. - if (dwell == 0): - # We're just drilling. - if self.drill_modal: - if self.DRILL() != self.prev_drill: - self.write(self.SPACE() + self.DRILL()) - self.prev_drill = self.DRILL() - else: - self.write(self.SPACE() + self.DRILL()) - - else: - # We're drilling with dwell. - - if self.drill_modal: - if self.DRILL_WITH_DWELL(self.FORMAT_DWELL(),dwell) != self.prev_drill: - self.write(self.SPACE() + self.DRILL_WITH_DWELL(self.FORMAT_DWELL(),dwell)) - self.prev_drill = self.DRILL_WITH_DWELL(self.FORMAT_DWELL(),dwell) - else: - self.write(self.SPACE() + self.DRILL_WITH_DWELL(self.FORMAT_DWELL(),dwell)) - - - #self.write(self.DRILL_WITH_DWELL(self.FORMAT_DWELL(),dwell)) - - # Set the retraction point to the 'standoff' distance above the starting z height. - retract_height = z + standoff - if (x != None): - dx = x - self.x - self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) - self.x = x - - if (y != None): - dy = y - self.y - self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) - self.y = y - - dz = (z + standoff) - self.z # In the end, we will be standoff distance above the z value passed in. - - if self.drill_modal: - if z != self.prev_z: - self.write(self.SPACE() + self.Z() + (self.fmt.string(z - depth))) - self.prev_z=z - else: - self.write(self.SPACE() + self.Z() + (self.fmt.string(z - depth))) # This is the 'z' value for the bottom of the hole. - self.z = (z + standoff) # We want to remember where z is at the end (at the top of the hole) - - if self.drill_modal: - if self.prev_retract != self.RETRACT(self.fmt, retract_height) : - self.write(self.SPACE() + self.RETRACT(self.fmt, retract_height)) - self.prev_retract = self.RETRACT(self.fmt, retract_height) - else: - self.write(self.SPACE() + self.RETRACT(self.fmt, retract_height)) - - if (self.fhv) : - self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) - - self.write_feedrate() - self.write_spindle() - self.write_misc() - self.write('\n') - - # G33.1 tapping with EMC for now - # unsynchronized (chuck) taps NIY (tap_mode = 1) - - def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None, tap_mode=None, direction=None): - # mystery parameters: - # zretract=None, dwell_bottom=None,pitch=None, stoppos=None, spin_in=None, spin_out=None): - # I dont see how to map these to EMC Gcode - - if (standoff == None): - # This is a bad thing. All the drilling cycles need a retraction (and starting) height. - return - if (z == None): - return # We need a Z value as well. This input parameter represents the top of the hole - if (pitch == None): - return # We need a pitch value. - if (direction == None): - return # We need a direction value. - - if (tap_mode != 0): - raise "only rigid tapping currently supported" - - self.write_preps() - self.write_blocknum() - self.write_spindle() - self.write('\n') - - # rapid to starting point; z first, then x,y iff given - - # Set the retraction point to the 'standoff' distance above the starting z height. - retract_height = z + standoff - - # unsure if this is needed: - if self.z != retract_height: - self.rapid(z = retract_height) - - # then continue to x,y if given - if (x != None) or (y != None): - self.write_blocknum() - self.write(self.RAPID() ) - - if (x != None): - self.write(self.X() + self.fmt.string(x + self.shift_x)) - self.x = x - - if (y != None): - self.write(self.Y() + self.fmt.string(y + self.shift_y)) - self.y = y - self.write('\n') - - self.write_blocknum() - self.write( self.TAP() ) - self.write( self.TAP_DEPTH(self.ffmt,pitch) + self.SPACE() ) - self.write(self.Z() + self.fmt.string(z - depth))# This is the 'z' value for the bottom of the tap. - self.write_misc() - self.write('\n') - - self.z = retract_height # this cycle returns to the start position, so remember that as z value - - def bore(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, feed_in=None, feed_out=None, stoppos=None, shift_back=None, shift_right=None, backbore=False, stop=False): - pass - - def end_canned_cycle(self): - if self.drillExpanded: - return - self.write_blocknum() - self.write(self.SPACE() + self.END_CANNED_CYCLE() + '\n') - self.prev_drill = '' - self.prev_g0123 = '' - self.prev_z = '' - self.prev_f = '' - self.prev_retract = '' - ############################################################################ - ## Misc - - def comment(self, text): - self.write((self.COMMENT(text) + '\n')) - - def insert(self, text): - pass - - def block_delete(self, on=False): - pass - - def variable(self, id): - return (self.VARIABLE() % id) - - def variable_set(self, id, value): - self.write_blocknum() - self.write(self.SPACE() + (self.VARIABLE() % id) + self.SPACE() + (self.VARIABLE_SET() % value) + '\n') - - # This routine uses the G92 coordinate system offsets to establish a temporary coordinate - # system at the machine's current position. It can then use absolute coordinates relative - # to this position which makes coding easy. It then moves to the 'point along edge' which - # should be above the workpiece but still on one edge. It then backs off from the edge - # to the 'retracted point'. It then plunges down by the depth value specified. It then - # probes back towards the 'destination point'. The probed X,Y location are stored - # into the 'intersection variable' variables. Finally the machine moves back to the - # original location. This is important so that the results of multiple calls to this - # routine may be compared meaningfully. - def probe_single_point(self, point_along_edge_x=None, point_along_edge_y=None, depth=None, retracted_point_x=None, retracted_point_y=None, destination_point_x=None, destination_point_y=None, intersection_variable_x=None, intersection_variable_y=None, probe_offset_x_component=None, probe_offset_y_component=None ): - self.write_blocknum() - self.write(self.SPACE() + (self.SET_TEMPORARY_COORDINATE_SYSTEM() + (' X 0 Y 0 Z 0') + ('\t(Temporarily make this the origin)\n'))) - - if (self.fhv) : self.calc_feedrate_hv(1, 0) - self.write_blocknum() - self.write_feedrate() - self.write('\t(Set the feed rate for probing)\n') - - self.rapid(point_along_edge_x,point_along_edge_y) - self.rapid(retracted_point_x,retracted_point_y) - self.feed(z=depth) - - self.write_blocknum() - self.write((self.PROBE_TOWARDS_WITH_SIGNAL() + (' X ' + (self.fmt.string(destination_point_x)) + ' Y ' + (self.fmt.string(destination_point_y)) ) + ('\t(Probe towards our destination point)\n'))) - - self.comment('Back off the workpiece and re-probe more slowly') - self.write_blocknum() - self.write(self.SPACE() + ('#' + intersection_variable_x + '= [#5061 - [ 0.5 * ' + probe_offset_x_component + ']]\n')) - self.write_blocknum() - self.write(self.SPACE() + ('#' + intersection_variable_y + '= [#5062 - [ 0.5 * ' + probe_offset_y_component + ']]\n')) - self.write_blocknum(); - self.write(self.RAPID()) - self.write(self.SPACE() + ' X #' + intersection_variable_x + ' Y #' + intersection_variable_y + '\n') - - self.write_blocknum() - self.write(self.SPACE() + self.FEEDRATE() + self.ffmt.string(self.fh / 2.0) + '\n') - - self.write_blocknum() - self.write((self.SPACE() + self.PROBE_TOWARDS_WITH_SIGNAL() + (' X ' + (self.fmt.string(destination_point_x)) + ' Y ' + (self.fmt.string(destination_point_y)) ) + ('\t(Probe towards our destination point)\n'))) - - self.comment('Store the probed location somewhere we can get it again later') - self.write_blocknum() - self.write(('#' + intersection_variable_x + '=' + probe_offset_x_component + ' (Portion of probe radius that contributes to the X coordinate)\n')) - self.write_blocknum() - self.write(('#' + intersection_variable_x + '=[#' + intersection_variable_x + ' + #5061]\n')) - self.write_blocknum() - self.write(('#' + intersection_variable_y + '=' + probe_offset_y_component + ' (Portion of probe radius that contributes to the Y coordinate)\n')) - self.write_blocknum() - self.write(('#' + intersection_variable_y + '=[#' + intersection_variable_y + ' + #5062]\n')) - - self.comment('Now move back to the original location') - self.rapid(retracted_point_x,retracted_point_y) - self.rapid(z=0) - self.rapid(point_along_edge_x,point_along_edge_y) - self.rapid(x=0, y=0) - - self.write_blocknum() - self.write((self.REMOVE_TEMPORARY_COORDINATE_SYSTEM() + ('\t(Restore the previous coordinate system)\n'))) - - def probe_downward_point(self, x=None, y=None, depth=None, intersection_variable_z=None): - self.write_blocknum() - self.write((self.SET_TEMPORARY_COORDINATE_SYSTEM() + (' X 0 Y 0 Z 0') + ('\t(Temporarily make this the origin)\n'))) - if (self.fhv) : self.calc_feedrate_hv(1, 0) - self.write_blocknum() - self.write(self.FEEDRATE() + ' [' + self.ffmt.string(self.fh) + ' / 5.0 ]') - self.write('\t(Set the feed rate for probing)\n') - - if x != None and y != None: - self.write_blocknum(); - self.write(self.RAPID()) - self.write(' X ' + x + ' Y ' + y + '\n') - - self.write_blocknum() - self.write((self.PROBE_TOWARDS_WITH_SIGNAL() + ' Z ' + (self.fmt.string(depth)) + ('\t(Probe towards our destination point)\n'))) - - self.comment('Store the probed location somewhere we can get it again later') - self.write_blocknum() - self.write(('#' + intersection_variable_z + '= #5063\n')) - - self.comment('Now move back to the original location') - self.rapid(z=0) - self.rapid(x=0, y=0) - - self.write_blocknum() - self.write((self.REMOVE_TEMPORARY_COORDINATE_SYSTEM() + ('\t(Restore the previous coordinate system)\n'))) - - - def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None ): - pass - - def open_log_file(self, xml_file_name=None ): - pass - - def log_coordinate(self, x=None, y=None, z=None): - pass - - def log_message(self, message=None): - pass - - def close_log_file(self): - pass - - # Rapid movement to the midpoint between the two points specified. - # NOTE: The points are specified either as strings representing numbers or as strings - # representing variable names. This allows the HeeksCNC module to determine which - # variable names are used in these various routines. - def rapid_to_midpoint(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None): - self.write_blocknum() - self.write(self.RAPID()) - if ((x1 != None) and (x2 != None)): - self.write((' X ' + '[[[' + x1 + ' - ' + x2 + '] / 2.0] + ' + x2 + ']')) - - if ((y1 != None) and (y2 != None)): - self.write((' Y ' + '[[[' + y1 + ' - ' + y2 + '] / 2.0] + ' + y2 + ']')) - - if ((z1 != None) and (z2 != None)): - self.write((' Z ' + '[[[' + z1 + ' - ' + z2 + '] / 2.0] + ' + z2 + ']')) - - self.write('\n') - - # Rapid movement to the intersection of two lines (in the XY plane only). This routine - # is based on information found in http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ - # written by Paul Bourke. The ua_numerator, ua_denominator, ua and ub parameters - # represent variable names (with the preceding '#' included in them) for use as temporary - # variables. They're specified here simply so that HeeksCNC can manage which variables - # are used in which GCode calculations. - # - # As per the notes on the web page, the ua_denominator and ub_denominator formulae are - # the same so we don't repeat this. If the two lines are coincident or parallel then - # no movement occurs. - # - # NOTE: The points are specified either as strings representing numbers or as strings - # representing variable names. This allows the HeeksCNC module to determine which - # variable names are used in these various routines. - def rapid_to_intersection(self, x1, y1, x2, y2, x3, y3, x4, y4, intersection_x, intersection_y, ua_numerator, ua_denominator, ua, ub_numerator, ub): - self.comment('Find the intersection of the two lines made up by the four probed points') - self.write_blocknum(); - self.write(ua_numerator + '=[[[' + x4 + ' - ' + x3 + '] * [' + y1 + ' - ' + y3 + ']] - [[' + y4 + ' - ' + y3 + '] * [' + x1 + ' - ' + x3 + ']]]\n') - self.write_blocknum(); - self.write(ua_denominator + '=[[[' + y4 + ' - ' + y3 + '] * [' + x2 + ' - ' + x1 + ']] - [[' + x4 + ' - ' + x3 + '] * [' + y2 + ' - ' + y1 + ']]]\n') - self.write_blocknum(); - self.write(ub_numerator + '=[[[' + x2 + ' - ' + x1 + '] * [' + y1 + ' - ' + y3 + ']] - [[' + y2 + ' - ' + y1 + '] * [' + x1 + ' - ' + x3 + ']]]\n') - - self.comment('If they are not parallel') - self.write('O900 IF [' + ua_denominator + ' NE 0]\n') - self.comment('And if they are not coincident') - self.write('O901 IF [' + ua_numerator + ' NE 0 ]\n') - - self.write_blocknum(); - self.write(' ' + ua + '=[' + ua_numerator + ' / ' + ua_denominator + ']\n') - self.write_blocknum(); - self.write(' ' + ub + '=[' + ub_numerator + ' / ' + ua_denominator + ']\n') # NOTE: ub denominator is the same as ua denominator - self.write_blocknum(); - self.write(' ' + intersection_x + '=[' + x1 + ' + [[' + ua + ' * [' + x2 + ' - ' + x1 + ']]]]\n') - self.write_blocknum(); - self.write(' ' + intersection_y + '=[' + y1 + ' + [[' + ua + ' * [' + y2 + ' - ' + y1 + ']]]]\n') - self.write_blocknum(); - self.write(' ' + self.RAPID()) - self.write(' X ' + intersection_x + ' Y ' + intersection_y + '\n') - - self.write('O901 ENDIF\n') - self.write('O900 ENDIF\n') - - # We need to calculate the rotation angle based on the line formed by the - # x1,y1 and x2,y2 coordinate pair. With that angle, we need to move - # x_offset and y_offset distance from the current (0,0,0) position. - # - # The x1,y1,x2 and y2 parameters are all variable names that contain the actual - # values. - # The x_offset and y_offset are both numeric (floating point) values - def rapid_to_rotated_coordinate(self, x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final): - self.comment('Rapid to rotated coordinate') - self.write_blocknum(); - self.write( '#1 = [atan[' + y2 + ' - ' + y1 + ']/[' + x2 +' - ' + x1 + ']] (nominal_angle)\n') - self.write_blocknum(); - self.write( '#2 = [atan[' + ref_y + ']/[' + ref_x + ']] (reference angle)\n') - self.write_blocknum(); - self.write( '#3 = [#1 - #2] (angle)\n' ) - self.write_blocknum(); - self.write( '#4 = [[[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(x_current)) + '] * COS[ #3 ]] - [[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(y_current)) + '] * SIN[ #3 ]]]\n' ) - self.write_blocknum(); - self.write( '#5 = [[[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(x_current)) + '] * SIN[ #3 ]] + [[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(y_current)) + '] * COS[ #3 ]]]\n' ) - - self.write_blocknum(); - self.write( '#6 = [[' + (self.fmt.string(x_final)) + ' * COS[ #3 ]] - [' + (self.fmt.string(y_final)) + ' * SIN[ #3 ]]]\n' ) - self.write_blocknum(); - self.write( '#7 = [[' + (self.fmt.string(y_final)) + ' * SIN[ #3 ]] + [' + (self.fmt.string(y_final)) + ' * COS[ #3 ]]]\n' ) - - self.write_blocknum(); - self.write( self.RAPID() + ' X [ #4 + #6 ] Y [ #5 + #7 ]\n' ) - - def BEST_POSSIBLE_SPEED(self, motion_blending_tolerance, naive_cam_tolerance): - statement = 'G64' - - if (motion_blending_tolerance > 0): - statement += ' P ' + str(motion_blending_tolerance) - - if (naive_cam_tolerance > 0): - statement += ' Q ' + str(naive_cam_tolerance) - - return(statement) - - def set_path_control_mode(self, mode, motion_blending_tolerance, naive_cam_tolerance ): - self.write_blocknum() - if (mode == 0): - self.write( self.EXACT_PATH_MODE() + '\n' ) - if (mode == 1): - self.write( self.EXACT_STOP_MODE() + '\n' ) - if (mode == 2): - self.write( self.BEST_POSSIBLE_SPEED( motion_blending_tolerance, naive_cam_tolerance ) + '\n' ) + if (self.fhv): + self.calc_feedrate_hv(1, 0) + self.write_feedrate() + self.write_spindle() + self.write_misc() + self.write('\n') + if (x != None): + self.x = x + if (y != None): + self.y = y + if (z != None): + self.z = z + + def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(True, x, y, z, i, j, k, r) + + def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(False, x, y, z, i, j, k, r) + + def dwell(self, t): + self.write_blocknum() + self.write_preps() + self.write(self.FORMAT_DWELL() % t) + self.write_misc() + self.write('\n') + + def rapid_home(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + def rapid_unhome(self): + pass + + def set_machine_coordinates(self): + self.write(self.SPACE() + self.MACHINE_COORDINATES()) + self.prev_g0123 = '' + + ############################################################################ + # CRC + + def use_CRC(self): + return self.useCrc + + def CRC_nominal_path(self): + return self.useCrcCenterline + + def start_CRC(self, left=True, radius=0.0): + # set up prep code, to be output on next line + if self.t == None: + raise "No tool specified for start_CRC()" + self.write_blocknum() + if left: + self.write(self.SPACE() + 'G41') + else: + self.write(self.SPACE() + 'G42') + self.write((self.SPACE() + 'D%i\n') % self.t) + + def end_CRC(self): + self.write_blocknum() + self.write(self.SPACE() + 'G40\n') + + ############################################################################ + # Cycles + + def pattern(self): + pass + + def pocket(self): + pass + + def profile(self): + pass + + # The drill routine supports drilling (G81), drilling with dwell (G82) and peck drilling (G83). + # The x,y,z values are INITIAL locations (above the hole to be made. This is in contrast to + # the Z value used in the G8[1-3] cycles where the Z value is that of the BOTTOM of the hole. + # Instead, this routine combines the Z value and the depth value to determine the bottom of + # the hole. + # + # The standoff value is the distance up from the 'z' value (normally just above the surface) where the bit retracts + # to in order to clear the swarf. This combines with 'z' to form the 'R' value in the G8[1-3] cycles. + # + # The peck_depth value is the incremental depth (Q value) that tells the peck drilling + # cycle how deep to go on each peck until the full depth is achieved. + # + # NOTE: This routine forces the mode to absolute mode so that the values passed into + # the G8[1-3] cycles make sense. I don't know how to find the mode to revert it so I won't + # revert it. I must set the mode so that I can be sure the values I'm passing in make + # sense to the end-machine. + # + def drill(self, x=None, y=None, dwell=None, depthparams=None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): + if (standoff == None): + # This is a bad thing. All the drilling cycles need a retraction (and starting) height. + return + + if (z == None): + return # We need a Z value as well. This input parameter represents the top of the hole + + if self.drillExpanded: + # for machines which don't understand G81, G82 etc. + if peck_depth == None: + peck_depth = depth + current_z = z + self.rapid(x, y) + + first = True + + while True: + next_z = current_z - peck_depth + if next_z < z - depth: + next_z = z - depth + if next_z >= current_z: + break + if first: + self.rapid(z=z + standoff) + else: + self.rapid(z=current_z) + self.feed(z=next_z) + self.rapid(z=z + standoff) + current_z = next_z + if dwell: + self.dwell(dwell) + first = False + + # we should pass clearance height into here, but my machine is on and I'm in a hurry... 22nd June 2011 danheeks + self.rapid(z=z + 5.0) + + return + + self.write_preps() + self.write_blocknum() + + if (peck_depth != 0): + # We're pecking. Let's find a tree. + if self.drill_modal: + if self.PECK_DRILL() + self.PECK_DEPTH(self.fmt, peck_depth) != self.prev_drill: + self.write(self.SPACE() + self.PECK_DRILL() + self.SPACE() + + self.PECK_DEPTH(self.fmt, peck_depth)) + self.prev_drill = self.PECK_DRILL() + self.PECK_DEPTH(self.fmt, peck_depth) + else: + self.write(self.PECK_DRILL() + self.PECK_DEPTH(self.fmt, peck_depth)) + + else: + # We're either just drilling or drilling with dwell. + if (dwell == 0): + # We're just drilling. + if self.drill_modal: + if self.DRILL() != self.prev_drill: + self.write(self.SPACE() + self.DRILL()) + self.prev_drill = self.DRILL() + else: + self.write(self.SPACE() + self.DRILL()) + + else: + # We're drilling with dwell. + + if self.drill_modal: + if self.DRILL_WITH_DWELL(self.FORMAT_DWELL(), dwell) != self.prev_drill: + self.write(self.SPACE() + self.DRILL_WITH_DWELL(self.FORMAT_DWELL(), dwell)) + self.prev_drill = self.DRILL_WITH_DWELL(self.FORMAT_DWELL(), dwell) + else: + self.write(self.SPACE() + self.DRILL_WITH_DWELL(self.FORMAT_DWELL(), dwell)) + + # self.write(self.DRILL_WITH_DWELL(self.FORMAT_DWELL(),dwell)) + + # Set the retraction point to the 'standoff' distance above the starting z height. + retract_height = z + standoff + if (x != None): + dx = x - self.x + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + self.x = x + + if (y != None): + dy = y - self.y + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + self.y = y + + # In the end, we will be standoff distance above the z value passed in. + dz = (z + standoff) - self.z + + if self.drill_modal: + if z != self.prev_z: + self.write(self.SPACE() + self.Z() + (self.fmt.string(z - depth))) + self.prev_z = z + else: + # This is the 'z' value for the bottom of the hole. + self.write(self.SPACE() + self.Z() + (self.fmt.string(z - depth))) + # We want to remember where z is at the end (at the top of the hole) + self.z = (z + standoff) + + if self.drill_modal: + if self.prev_retract != self.RETRACT(self.fmt, retract_height): + self.write(self.SPACE() + self.RETRACT(self.fmt, retract_height)) + self.prev_retract = self.RETRACT(self.fmt, retract_height) + else: + self.write(self.SPACE() + self.RETRACT(self.fmt, retract_height)) + + if (self.fhv): + self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) + + self.write_feedrate() + self.write_spindle() + self.write_misc() + self.write('\n') + + # G33.1 tapping with EMC for now + # unsynchronized (chuck) taps NIY (tap_mode = 1) + + def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None, tap_mode=None, direction=None): + # mystery parameters: + # zretract=None, dwell_bottom=None,pitch=None, stoppos=None, spin_in=None, spin_out=None): + # I dont see how to map these to EMC Gcode + + if (standoff == None): + # This is a bad thing. All the drilling cycles need a retraction (and starting) height. + return + if (z == None): + return # We need a Z value as well. This input parameter represents the top of the hole + if (pitch == None): + return # We need a pitch value. + if (direction == None): + return # We need a direction value. + + if (tap_mode != 0): + raise "only rigid tapping currently supported" + + self.write_preps() + self.write_blocknum() + self.write_spindle() + self.write('\n') + + # rapid to starting point; z first, then x,y iff given + + # Set the retraction point to the 'standoff' distance above the starting z height. + retract_height = z + standoff + + # unsure if this is needed: + if self.z != retract_height: + self.rapid(z=retract_height) + + # then continue to x,y if given + if (x != None) or (y != None): + self.write_blocknum() + self.write(self.RAPID()) + + if (x != None): + self.write(self.X() + self.fmt.string(x + self.shift_x)) + self.x = x + + if (y != None): + self.write(self.Y() + self.fmt.string(y + self.shift_y)) + self.y = y + self.write('\n') + + self.write_blocknum() + self.write(self.TAP()) + self.write(self.TAP_DEPTH(self.ffmt, pitch) + self.SPACE()) + # This is the 'z' value for the bottom of the tap. + self.write(self.Z() + self.fmt.string(z - depth)) + self.write_misc() + self.write('\n') + + self.z = retract_height # this cycle returns to the start position, so remember that as z value + + def bore(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, feed_in=None, feed_out=None, stoppos=None, shift_back=None, shift_right=None, backbore=False, stop=False): + pass + + def end_canned_cycle(self): + if self.drillExpanded: + return + self.write_blocknum() + self.write(self.SPACE() + self.END_CANNED_CYCLE() + '\n') + self.prev_drill = '' + self.prev_g0123 = '' + self.prev_z = '' + self.prev_f = '' + self.prev_retract = '' + ############################################################################ + # Misc + + def comment(self, text): + self.write((self.COMMENT(text) + '\n')) + + def insert(self, text): + pass + + def block_delete(self, on=False): + pass + + def variable(self, id): + return (self.VARIABLE() % id) + + def variable_set(self, id, value): + self.write_blocknum() + self.write(self.SPACE() + (self.VARIABLE() % id) + + self.SPACE() + (self.VARIABLE_SET() % value) + '\n') + + # This routine uses the G92 coordinate system offsets to establish a temporary coordinate + # system at the machine's current position. It can then use absolute coordinates relative + # to this position which makes coding easy. It then moves to the 'point along edge' which + # should be above the workpiece but still on one edge. It then backs off from the edge + # to the 'retracted point'. It then plunges down by the depth value specified. It then + # probes back towards the 'destination point'. The probed X,Y location are stored + # into the 'intersection variable' variables. Finally the machine moves back to the + # original location. This is important so that the results of multiple calls to this + # routine may be compared meaningfully. + def probe_single_point(self, point_along_edge_x=None, point_along_edge_y=None, depth=None, retracted_point_x=None, retracted_point_y=None, destination_point_x=None, destination_point_y=None, intersection_variable_x=None, intersection_variable_y=None, probe_offset_x_component=None, probe_offset_y_component=None): + self.write_blocknum() + self.write(self.SPACE() + (self.SET_TEMPORARY_COORDINATE_SYSTEM() + + (' X 0 Y 0 Z 0') + ('\t(Temporarily make this the origin)\n'))) + + if (self.fhv): + self.calc_feedrate_hv(1, 0) + self.write_blocknum() + self.write_feedrate() + self.write('\t(Set the feed rate for probing)\n') + + self.rapid(point_along_edge_x, point_along_edge_y) + self.rapid(retracted_point_x, retracted_point_y) + self.feed(z=depth) + + self.write_blocknum() + self.write((self.PROBE_TOWARDS_WITH_SIGNAL() + (' X ' + (self.fmt.string(destination_point_x)) + + ' Y ' + (self.fmt.string(destination_point_y))) + ('\t(Probe towards our destination point)\n'))) + + self.comment('Back off the workpiece and re-probe more slowly') + self.write_blocknum() + self.write(self.SPACE() + ('#' + intersection_variable_x + + '= [#5061 - [ 0.5 * ' + probe_offset_x_component + ']]\n')) + self.write_blocknum() + self.write(self.SPACE() + ('#' + intersection_variable_y + + '= [#5062 - [ 0.5 * ' + probe_offset_y_component + ']]\n')) + self.write_blocknum() + self.write(self.RAPID()) + self.write(self.SPACE() + ' X #' + intersection_variable_x + + ' Y #' + intersection_variable_y + '\n') + + self.write_blocknum() + self.write(self.SPACE() + self.FEEDRATE() + self.ffmt.string(self.fh / 2.0) + '\n') + + self.write_blocknum() + self.write((self.SPACE() + self.PROBE_TOWARDS_WITH_SIGNAL() + (' X ' + (self.fmt.string(destination_point_x) + ) + ' Y ' + (self.fmt.string(destination_point_y))) + ('\t(Probe towards our destination point)\n'))) + + self.comment('Store the probed location somewhere we can get it again later') + self.write_blocknum() + self.write(('#' + intersection_variable_x + '=' + probe_offset_x_component + + ' (Portion of probe radius that contributes to the X coordinate)\n')) + self.write_blocknum() + self.write(('#' + intersection_variable_x + + '=[#' + intersection_variable_x + ' + #5061]\n')) + self.write_blocknum() + self.write(('#' + intersection_variable_y + '=' + probe_offset_y_component + + ' (Portion of probe radius that contributes to the Y coordinate)\n')) + self.write_blocknum() + self.write(('#' + intersection_variable_y + + '=[#' + intersection_variable_y + ' + #5062]\n')) + + self.comment('Now move back to the original location') + self.rapid(retracted_point_x, retracted_point_y) + self.rapid(z=0) + self.rapid(point_along_edge_x, point_along_edge_y) + self.rapid(x=0, y=0) + + self.write_blocknum() + self.write((self.REMOVE_TEMPORARY_COORDINATE_SYSTEM() + + ('\t(Restore the previous coordinate system)\n'))) + + def probe_downward_point(self, x=None, y=None, depth=None, intersection_variable_z=None): + self.write_blocknum() + self.write((self.SET_TEMPORARY_COORDINATE_SYSTEM() + (' X 0 Y 0 Z 0') + + ('\t(Temporarily make this the origin)\n'))) + if (self.fhv): + self.calc_feedrate_hv(1, 0) + self.write_blocknum() + self.write(self.FEEDRATE() + ' [' + self.ffmt.string(self.fh) + ' / 5.0 ]') + self.write('\t(Set the feed rate for probing)\n') + + if x != None and y != None: + self.write_blocknum() + self.write(self.RAPID()) + self.write(' X ' + x + ' Y ' + y + '\n') + + self.write_blocknum() + self.write((self.PROBE_TOWARDS_WITH_SIGNAL() + ' Z ' + + (self.fmt.string(depth)) + ('\t(Probe towards our destination point)\n'))) + + self.comment('Store the probed location somewhere we can get it again later') + self.write_blocknum() + self.write(('#' + intersection_variable_z + '= #5063\n')) + + self.comment('Now move back to the original location') + self.rapid(z=0) + self.rapid(x=0, y=0) + + self.write_blocknum() + self.write((self.REMOVE_TEMPORARY_COORDINATE_SYSTEM() + + ('\t(Restore the previous coordinate system)\n'))) + + def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None): + pass + + def open_log_file(self, xml_file_name=None): + pass + + def log_coordinate(self, x=None, y=None, z=None): + pass + + def log_message(self, message=None): + pass + + def close_log_file(self): + pass + + # Rapid movement to the midpoint between the two points specified. + # NOTE: The points are specified either as strings representing numbers or as strings + # representing variable names. This allows the HeeksCNC module to determine which + # variable names are used in these various routines. + def rapid_to_midpoint(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None): + self.write_blocknum() + self.write(self.RAPID()) + if ((x1 != None) and (x2 != None)): + self.write((' X ' + '[[[' + x1 + ' - ' + x2 + '] / 2.0] + ' + x2 + ']')) + + if ((y1 != None) and (y2 != None)): + self.write((' Y ' + '[[[' + y1 + ' - ' + y2 + '] / 2.0] + ' + y2 + ']')) + + if ((z1 != None) and (z2 != None)): + self.write((' Z ' + '[[[' + z1 + ' - ' + z2 + '] / 2.0] + ' + z2 + ']')) + + self.write('\n') + + # Rapid movement to the intersection of two lines (in the XY plane only). This routine + # is based on information found in http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ + # written by Paul Bourke. The ua_numerator, ua_denominator, ua and ub parameters + # represent variable names (with the preceding '#' included in them) for use as temporary + # variables. They're specified here simply so that HeeksCNC can manage which variables + # are used in which GCode calculations. + # + # As per the notes on the web page, the ua_denominator and ub_denominator formulae are + # the same so we don't repeat this. If the two lines are coincident or parallel then + # no movement occurs. + # + # NOTE: The points are specified either as strings representing numbers or as strings + # representing variable names. This allows the HeeksCNC module to determine which + # variable names are used in these various routines. + def rapid_to_intersection(self, x1, y1, x2, y2, x3, y3, x4, y4, intersection_x, intersection_y, ua_numerator, ua_denominator, ua, ub_numerator, ub): + self.comment('Find the intersection of the two lines made up by the four probed points') + self.write_blocknum() + self.write(ua_numerator + '=[[[' + x4 + ' - ' + x3 + '] * [' + y1 + ' - ' + + y3 + ']] - [[' + y4 + ' - ' + y3 + '] * [' + x1 + ' - ' + x3 + ']]]\n') + self.write_blocknum() + self.write(ua_denominator + '=[[[' + y4 + ' - ' + y3 + '] * [' + x2 + ' - ' + + x1 + ']] - [[' + x4 + ' - ' + x3 + '] * [' + y2 + ' - ' + y1 + ']]]\n') + self.write_blocknum() + self.write(ub_numerator + '=[[[' + x2 + ' - ' + x1 + '] * [' + y1 + ' - ' + + y3 + ']] - [[' + y2 + ' - ' + y1 + '] * [' + x1 + ' - ' + x3 + ']]]\n') + + self.comment('If they are not parallel') + self.write('O900 IF [' + ua_denominator + ' NE 0]\n') + self.comment('And if they are not coincident') + self.write('O901 IF [' + ua_numerator + ' NE 0 ]\n') + + self.write_blocknum() + self.write(' ' + ua + '=[' + ua_numerator + ' / ' + ua_denominator + ']\n') + self.write_blocknum() + # NOTE: ub denominator is the same as ua denominator + self.write(' ' + ub + '=[' + ub_numerator + ' / ' + ua_denominator + ']\n') + self.write_blocknum() + self.write(' ' + intersection_x + '=[' + x1 + + ' + [[' + ua + ' * [' + x2 + ' - ' + x1 + ']]]]\n') + self.write_blocknum() + self.write(' ' + intersection_y + '=[' + y1 + + ' + [[' + ua + ' * [' + y2 + ' - ' + y1 + ']]]]\n') + self.write_blocknum() + self.write(' ' + self.RAPID()) + self.write(' X ' + intersection_x + ' Y ' + intersection_y + '\n') + + self.write('O901 ENDIF\n') + self.write('O900 ENDIF\n') + + # We need to calculate the rotation angle based on the line formed by the + # x1,y1 and x2,y2 coordinate pair. With that angle, we need to move + # x_offset and y_offset distance from the current (0,0,0) position. + # + # The x1,y1,x2 and y2 parameters are all variable names that contain the actual + # values. + # The x_offset and y_offset are both numeric (floating point) values + def rapid_to_rotated_coordinate(self, x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final): + self.comment('Rapid to rotated coordinate') + self.write_blocknum() + self.write('#1 = [atan[' + y2 + ' - ' + y1 + ']/[' + + x2 + ' - ' + x1 + ']] (nominal_angle)\n') + self.write_blocknum() + self.write('#2 = [atan[' + ref_y + ']/[' + ref_x + ']] (reference angle)\n') + self.write_blocknum() + self.write('#3 = [#1 - #2] (angle)\n') + self.write_blocknum() + self.write('#4 = [[[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(x_current)) + + '] * COS[ #3 ]] - [[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(y_current)) + '] * SIN[ #3 ]]]\n') + self.write_blocknum() + self.write('#5 = [[[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(x_current)) + + '] * SIN[ #3 ]] + [[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(y_current)) + '] * COS[ #3 ]]]\n') + + self.write_blocknum() + self.write('#6 = [[' + (self.fmt.string(x_final)) + ' * COS[ #3 ]] - [' + + (self.fmt.string(y_final)) + ' * SIN[ #3 ]]]\n') + self.write_blocknum() + self.write('#7 = [[' + (self.fmt.string(y_final)) + ' * SIN[ #3 ]] + [' + + (self.fmt.string(y_final)) + ' * COS[ #3 ]]]\n') + + self.write_blocknum() + self.write(self.RAPID() + ' X [ #4 + #6 ] Y [ #5 + #7 ]\n') + + def BEST_POSSIBLE_SPEED(self, motion_blending_tolerance, naive_cam_tolerance): + statement = 'G64' + + if (motion_blending_tolerance > 0): + statement += ' P ' + str(motion_blending_tolerance) + + if (naive_cam_tolerance > 0): + statement += ' Q ' + str(naive_cam_tolerance) + + return(statement) + + def set_path_control_mode(self, mode, motion_blending_tolerance, naive_cam_tolerance): + self.write_blocknum() + if (mode == 0): + self.write(self.EXACT_PATH_MODE() + '\n') + if (mode == 1): + self.write(self.EXACT_STOP_MODE() + '\n') + if (mode == 2): + self.write(self.BEST_POSSIBLE_SPEED( + motion_blending_tolerance, naive_cam_tolerance) + '\n') ################################################################################ diff --git a/scripts/addons/cam/nc/heiden530.py b/scripts/addons/cam/nc/heiden530.py index d7473f871..1a5e4061b 100644 --- a/scripts/addons/cam/nc/heiden530.py +++ b/scripts/addons/cam/nc/heiden530.py @@ -13,6 +13,7 @@ from .format import Format from .format import * + class Creator(iso.Creator): def __init__(self): iso.Creator.__init__(self) @@ -34,15 +35,14 @@ def __init__(self): self.shift_y = 0.0 self.shift_z = 0.0 - ############################################################################ - ## Codes + # Codes def SPACE(self): return(' ') def NEW_LINE(self): return('\n') def BLOCK(self): return('%i') - def COMMENT(self,comment): return( (';%s' % comment ) ) + def COMMENT(self, comment): return((';%s' % comment)) def BEGIN_PGM(self): return('BEGIN PGM %i') def END_PGM(self): return('END PGM %i') @@ -65,7 +65,7 @@ def Y(self): return('Y') def Z(self): return('Z') ############################################################################ - ## Internals + # Internals def write_blocknum(self): self.write(self.BLOCK() % self.n) @@ -76,7 +76,7 @@ def write_spindle(self): self.write(self.NEW_LINE()) ############################################################################ - ## Programs + # Programs def program_begin(self, id, name=''): self.program_id = id @@ -99,7 +99,7 @@ def program_end(self): self.write(self.NEW_LINE()) ############################################################################ - ## Settings + # Settings def absolute(self): pass @@ -111,7 +111,7 @@ def set_plane(self, plane): pass ############################################################################ - ## Tools + # Tools def tool_change(self, id): self.t = id @@ -122,9 +122,9 @@ def tool_change(self, id): self.write(self.TOOL() % self.t) ############################################################################ - ## Moves + # Moves - def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None ): + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): self.write_blocknum() @@ -135,14 +135,14 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None ): if (x != None): dx = x - self.x - if (self.absolute_flag ): + if (self.absolute_flag): self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) else: self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) self.x = x if (y != None): dy = y - self.y - if (self.absolute_flag ): + if (self.absolute_flag): self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) else: self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) @@ -150,7 +150,7 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None ): self.y = y if (z != None): dz = z - self.z - if (self.absolute_flag ): + if (self.absolute_flag): self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) else: self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) @@ -162,8 +162,8 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None ): def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): - (x, y, z, a, b, c,axis_count)=self.filter_xyz(x, y, z) - if axis_count==0: return + (x, y, z, a, b, c, axis_count) = self.filter_xyz(x, y, z) + if axis_count == 0: return self.write_blocknum() @@ -201,7 +201,7 @@ def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): self.write_misc() ############################################################################ - ## Misc + # Misc def comment(self, text): self.write((self.COMMENT(text) + '\n')) diff --git a/scripts/addons/cam/nc/heiden_read.py b/scripts/addons/cam/nc/heiden_read.py index bcd14760d..e2f81fe44 100644 --- a/scripts/addons/cam/nc/heiden_read.py +++ b/scripts/addons/cam/nc/heiden_read.py @@ -10,6 +10,8 @@ import math ################################################################################ + + class Parser(nc.Parser): def __init__(self, writer): @@ -97,7 +99,7 @@ def ParseWord(self, word): def Parsey(self, name): self.files_open(name) - for_full_machine_sim = True # to do, make derived class to do this + for_full_machine_sim = True # to do, make derived class to do this #for_full_machine_sim = False self.f = None @@ -135,16 +137,22 @@ def Parsey(self, name): self.ParseWord(word) if (self.move and not self.no_move): - if (self.arc==0): + if (self.arc == 0): self.add_line(self.x, self.y, self.z, self.a, self.b, self.rapid) else: self.add_arc(self.x, self.y, self.z, self.i, self.j, self.k, self.r, self.arc) - if self.x != None: self.oldx = self.x - if self.y != None: self.oldy = self.y - if self.z != None: self.oldz = self.z - if self.a != None: self.olda = self.a - if self.b != None: self.oldb = self.b - if self.c != None: self.oldc = self.c + if self.x != None: + self.oldx = self.x + if self.y != None: + self.oldy = self.y + if self.z != None: + self.oldz = self.z + if self.a != None: + self.olda = self.a + if self.b != None: + self.oldb = self.b + if self.c != None: + self.oldc = self.c elif (self.t): self.change_tool(self.t) diff --git a/scripts/addons/cam/nc/hm50.py b/scripts/addons/cam/nc/hm50.py index 2e0b065ba..2282edad1 100644 --- a/scripts/addons/cam/nc/hm50.py +++ b/scripts/addons/cam/nc/hm50.py @@ -1,19 +1,20 @@ from . import nc from . import emc2 + class Creator(emc2.Creator): - def __init__(self): - emc2.Creator.__init__(self) + def __init__(self): + emc2.Creator.__init__(self) - def program_begin(self, id, comment): - self.write( ('(' + comment + ')' + '\n') ) + def program_begin(self, id, comment): + self.write(('(' + comment + ')' + '\n')) - def tool_change(self, id): - self.write('G53 G00 Z30\n') - self.write((self.TOOL() % id) + '\n') - self.write('G01 Z100.000 F800.000\n') - self.write('M0\n') - self.write('G01 Z10.000 F300.000\n') + def tool_change(self, id): + self.write('G53 G00 Z30\n') + self.write((self.TOOL() % id) + '\n') + self.write('G01 Z100.000 F800.000\n') + self.write('M0\n') + self.write('G01 Z10.000 F300.000\n') nc.creator = Creator() diff --git a/scripts/addons/cam/nc/hm50_read.py b/scripts/addons/cam/nc/hm50_read.py index 25f8cf320..3df5337c7 100644 --- a/scripts/addons/cam/nc/hm50_read.py +++ b/scripts/addons/cam/nc/hm50_read.py @@ -2,6 +2,8 @@ import sys # just use the iso reader + + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/hpgl2d.py b/scripts/addons/cam/nc/hpgl2d.py index 7b6642c18..fa7b8a9db 100644 --- a/scripts/addons/cam/nc/hpgl2d.py +++ b/scripts/addons/cam/nc/hpgl2d.py @@ -7,18 +7,19 @@ from . import nc import math + class Creator(nc.Creator): def __init__(self): nc.Creator.__init__(self) self.x = int(0) - self.y = int(0) # these are in machine units, like 0.01mm or maybe 0.25mm - self.metric() # set self.units_to_mc_units + self.y = int(0) # these are in machine units, like 0.01mm or maybe 0.25mm + self.metric() # set self.units_to_mc_units def imperial(self): - self.units_to_mc_units = 2540 # multiplier from inches to machine units + self.units_to_mc_units = 2540 # multiplier from inches to machine units def metric(self): - self.units_to_mc_units = 100 # multiplier from mm to machine units + self.units_to_mc_units = 100 # multiplier from mm to machine units def program_begin(self, id, name=''): self.write('IN;\n') @@ -89,12 +90,15 @@ def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): start_angle = math.atan2(sdy, sdx) end_angle = math.atan2(edy, edx) if cw: - if start_angle < end_angle: start_angle += 2 * math.pi + if start_angle < end_angle: + start_angle += 2 * math.pi else: - if end_angle < start_angle: end_angle += 2 * math.pi + if end_angle < start_angle: + end_angle += 2 * math.pi a = math.fabs(end_angle - start_angle) - if cw: a = -a + if cw: + a = -a mcx, mcy = self.get_machine_x_y(cx, cy) @@ -106,4 +110,5 @@ def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): self.arc(False, x, y, z, i, j, k, r) + nc.creator = Creator() diff --git a/scripts/addons/cam/nc/hpgl2d_read.py b/scripts/addons/cam/nc/hpgl2d_read.py index c1825bca2..4a49c5fd5 100644 --- a/scripts/addons/cam/nc/hpgl2d_read.py +++ b/scripts/addons/cam/nc/hpgl2d_read.py @@ -2,6 +2,7 @@ import sys import math + class Parser(num_reader.NumReader): def __init__(self, writer): @@ -21,12 +22,16 @@ def ParsePuOrPd(self, up): if len(x) > 0: y = self.get_number() if len(y) > 0: - if up: color = "rapid" - else: color = "feed" + if up: + color = "rapid" + else: + color = "feed" self.add_word(color) self.begin_path(color) - if up: z = self.up_z - else: z = self.down_z + if up: + z = self.up_z + else: + z = self.down_z if self.up != up: self.add_line(self.x * self.units_to_mm, self.y * self.units_to_mm, z) self.add_line(int(x) * self.units_to_mm, int(y) * self.units_to_mm, z) @@ -61,10 +66,13 @@ def ParseAA(self): ex = int(cx) + radius * math.cos(end_angle) ey = int(cy) + radius * math.sin(end_angle) - if int(a) > 0: d = 1 - else: d = -1 + if int(a) > 0: + d = 1 + else: + d = -1 - self.add_arc(ex * self.units_to_mm, ey * self.units_to_mm, i = int(-sdx) * self.units_to_mm, j = int(-sdy) * self.units_to_mm, d = d) + self.add_arc(ex * self.units_to_mm, ey * self.units_to_mm, i=int(-sdx) + * self.units_to_mm, j=int(-sdy) * self.units_to_mm, d=d) self.end_path() self.up = False self.x = int(ex) @@ -76,15 +84,14 @@ def ParseFromFirstLetter(self, c): if self.line_index < self.line_length: c1 = self.line[self.line_index] self.parse_word += c1 - if c1 == 'U': # PU + if c1 == 'U': # PU self.ParsePuOrPd(True) - elif c1 == 'D': # PD + elif c1 == 'D': # PD self.ParsePuOrPd(False) elif c == 'A': self.line_index = self.line_index + 1 if self.line_index < self.line_length: c1 = self.line[self.line_index] self.parse_word += c1 - if c1 == 'A': # AA, arc absolute + if c1 == 'A': # AA, arc absolute self.ParseAA() - diff --git a/scripts/addons/cam/nc/hpgl2dv.py b/scripts/addons/cam/nc/hpgl2dv.py index 717d9f970..1f1be7942 100644 --- a/scripts/addons/cam/nc/hpgl2dv.py +++ b/scripts/addons/cam/nc/hpgl2dv.py @@ -9,14 +9,16 @@ from . import nc from . import hpgl2d + class Creator(hpgl2d.Creator): def __init__(self): hpgl2d.Creator.__init__(self) def imperial(self): - self.units_to_mc_units = 101.6 # multiplier from inches to machine units + self.units_to_mc_units = 101.6 # multiplier from inches to machine units def metric(self): - self.units_to_mc_units = 4 # multiplier from mm to machine units + self.units_to_mc_units = 4 # multiplier from mm to machine units + nc.creator = Creator() diff --git a/scripts/addons/cam/nc/hpgl2dv_read.py b/scripts/addons/cam/nc/hpgl2dv_read.py index def61e0ff..a4b8d13b3 100644 --- a/scripts/addons/cam/nc/hpgl2dv_read.py +++ b/scripts/addons/cam/nc/hpgl2dv_read.py @@ -3,6 +3,7 @@ # same as hpgl2d, but with 0.25mm units, instead of 0.01mm + class Parser(hpgl.Parser): def __init__(self, writer): hpgl.ParserHgpl2d.__init__(self, writer) diff --git a/scripts/addons/cam/nc/hpgl3d.py b/scripts/addons/cam/nc/hpgl3d.py index 42948bc72..8f8f8564f 100644 --- a/scripts/addons/cam/nc/hpgl3d.py +++ b/scripts/addons/cam/nc/hpgl3d.py @@ -8,11 +8,12 @@ from . import hpgl2d import math + class Creator(hpgl2d.Creator): def __init__(self): hpgl2d.Creator.__init__(self) self.z = int(0) - self.metric() # set self.units_to_mc_units + self.metric() # set self.units_to_mc_units self.doing_rapid = True def program_begin(self, id, name=''): @@ -42,7 +43,8 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): # for now, do all rapid moves at V50 ( 50 mm/s ) mx, my, mz = self.get_machine_xyz(x, y, z) if mx != self.x or my != self.y or mz != self.z: - if self.doing_rapid == False: self.write('V50.0;') + if self.doing_rapid == False: + self.write('V50.0;') self.write(('Z%i' % mx) + (',%i' % my) + (',%i;\n' % mz)) self.x = mx self.y = my @@ -54,11 +56,13 @@ def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): # for now, do all feed moves at V10 ( 10 mm/s ) mx, my, mz = self.get_machine_xyz(x, y, z) if mx != self.x or my != self.y or mz != self.z: - if self.doing_rapid == True: self.write('V10.0;') + if self.doing_rapid == True: + self.write('V10.0;') self.write(('Z%i' % mx) + (',%i' % my) + (',%i;\n' % mz)) self.x = mx self.y = my self.z = mz self.doing_rapid = False + nc.creator = Creator() diff --git a/scripts/addons/cam/nc/hpgl3d_read.py b/scripts/addons/cam/nc/hpgl3d_read.py index 646fb883b..732689462 100644 --- a/scripts/addons/cam/nc/hpgl3d_read.py +++ b/scripts/addons/cam/nc/hpgl3d_read.py @@ -2,6 +2,7 @@ import sys import math + class Parser(num_reader.NumReader): def __init__(self, writer): @@ -27,11 +28,14 @@ def ParseZ(self): if len(y) > 0: z = self.get_number() if len(z) > 0: - if self.f > 40: color = "rapid" - else: color = "feed" + if self.f > 40: + color = "rapid" + else: + color = "feed" self.add_word(color) self.begin_path(color) - self.add_line(int(x) * self.units_to_mm, int(y) * self.units_to_mm, int(z) * self.units_to_mm) + self.add_line(int(x) * self.units_to_mm, int(y) * + self.units_to_mm, int(z) * self.units_to_mm) self.end_path() self.x = int(x) self.y = int(y) @@ -42,4 +46,3 @@ def ParseFromFirstLetter(self, c): self.ParseZ() elif c == 'V': self.ParseV() - diff --git a/scripts/addons/cam/nc/hxml_writer.py b/scripts/addons/cam/nc/hxml_writer.py index 455f82c21..39ad2fb97 100644 --- a/scripts/addons/cam/nc/hxml_writer.py +++ b/scripts/addons/cam/nc/hxml_writer.py @@ -1,5 +1,6 @@ import tempfile + class HxmlWriter: def __init__(self): self.file_out = open(tempfile.gettempdir()+'/backplot.xml', 'w') @@ -26,25 +27,32 @@ def add_text(self, s, col, cdata): s.replace('"', '"') s.replace('<', '<') s.replace('>', '>') - if (cdata) : (cd1, cd2) = ('') - else : (cd1, cd2) = ('', '') - if (col != None) : self.file_out.write('\t\t'+cd1+s+cd2+'\n') - else : self.file_out.write('\t\t'+cd1+s+cd2+'\n') + if (cdata): + (cd1, cd2) = ('') + else: + (cd1, cd2) = ('', '') + if (col != None): + self.file_out.write('\t\t'+cd1+s+cd2+'\n') + else: + self.file_out.write('\t\t'+cd1+s+cd2+'\n') def set_mode(self, units): self.file_out.write('\t\t\n') def metric(self): - self.set_mode(units = 1.0) + self.set_mode(units=1.0) def imperial(self): - self.set_mode(units = 25.4) + self.set_mode(units=25.4) def begin_path(self, col): - if (col != None) : self.file_out.write('\t\t\n') - else : self.file_out.write('\t\t\n') + if (col != None): + self.file_out.write('\t\t\n') + else: + self.file_out.write('\t\t\n') def end_path(self): self.file_out.write('\t\t\n') @@ -71,7 +79,7 @@ def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): def tool_change(self, id): self.file_out.write('\t\t\n') @@ -81,36 +89,50 @@ def spindle(self, s, clockwise): def feedrate(self, f): pass - def add_line(self, x, y, z, a = None, b = None, c = None): + def add_line(self, x, y, z, a=None, b=None, c=None): self.file_out.write('\t\t\t\n') - if x != None: self.oldx = x - if y != None: self.oldy = y - if z != None: self.oldz = z - - def add_arc(self, x, y, z, i, j, k, r = None, d = None): + if x != None: + self.oldx = x + if y != None: + self.oldy = y + if z != None: + self.oldz = z + + def add_arc(self, x, y, z, i, j, k, r=None, d=None): self.file_out.write('\t\t\t\n') - if x != None: self.oldx = x - if y != None: self.oldy = y - if z != None: self.oldz = z + if x != None: + self.oldx = x + if y != None: + self.oldy = y + if z != None: + self.oldz = z diff --git a/scripts/addons/cam/nc/iso.py b/scripts/addons/cam/nc/iso.py index 32701e1f0..5b115b93f 100644 --- a/scripts/addons/cam/nc/iso.py +++ b/scripts/addons/cam/nc/iso.py @@ -11,1328 +11,1404 @@ from .format import * ################################################################################ + + class Creator(nc.Creator): - def __init__(self): - nc.Creator.__init__(self) - - # internal variables - self.a = 0 - self.b = 0 - self.c = 0 - self.f = Address('F', fmt = Format(number_of_decimal_places = 2)) - self.fh = None - self.fv = None - self.fhv = False - self.g_plane = Address('G', fmt = Format(number_of_decimal_places = 0)) - self.g_list = [] - self.i = 0 - self.j = 0 - self.k = 0 - self.m = [] - self.r = 0 - self.s = AddressPlusMinus('S', fmt = Format(number_of_decimal_places = 2), modal = False) - self.t = None - self.x = None - self.y = None - self.z = None - self.g0123_modal = False - self.drill_modal = False - self.prev_f = '' - self.prev_g0123 = '' - self.prev_drill = '' - self.prev_retract = '' - self.prev_z = '' - self.useCrc = False - self.useCrcCenterline = False - self.gCRC = '' - self.fmt = Format() - self.absolute_flag = True - self.ffmt = Format(number_of_decimal_places = 2) - self.sfmt = Format(number_of_decimal_places = 1) - self.in_quadrant_splitting = False - self.in_canned_cycle = False - self.first_drill_pos = True - self.shift_x = 0.0 - self.shift_y = 0.0 - self.shift_z = 0.0 - self.start_of_line = False - self.internal_coolant_on = None - self.g98_not_g99 = None # True for G98 ouput, False for G99 output - self.current_fixture = None - self.fixture_wanted = '54' - self.move_done_since_tool_change = False - self.tool_defn_params = {} - self.program_id = None - self.current_sub_id = None - self.subroutine_files = [] - self.program_name = None - self.temp_file_to_append_on_close = None - self.fixture_order = ['54', '55', '56', '57', '58', '59'] - for i in range(1, 50): - self.fixture_order.append('54.' + str(i)) - self.output_disabled = False - self.z_for_g43 = None - - # optional settings - self.arc_centre_absolute = False - self.arc_centre_positive = False - self.drillExpanded = False - self.dwell_allowed_in_G83 = False - self.can_do_helical_arcs = True - self.z_for_g53 = None # set this to a value to output G53 Zvalue in tool change and at program end - self.output_h_and_d_at_tool_change = False - self.output_block_numbers = True - self.start_block_number = 10 - self.block_number_increment = 10 - self.block_number_restart_after = None - self.output_tool_definitions = True - self.output_tool_change = True - self.output_g43_on_tool_change_line = False - self.output_internal_coolant_commands = False - self.output_g98_and_g99 = True - self.output_g43_z_before_drilling_if_g98 = False - self.output_cutviewer_comments = False - self.output_fixtures = False - self.use_this_program_id = None - self.subroutines_in_own_files = False - self.pattern_done_with_subroutine = False - self.output_comment_before_tool_change = True - self.output_arcs_as_lines = False - self.m_codes_on_their_own_line = False - - ############################################################################ - ## Codes - - def SPACE_STR(self): return '' - def SPACE(self): - if self.start_of_line == True: - self.start_of_line = False - return '' - else: - return self.SPACE_STR() - - def FORMAT_FEEDRATE(self): return('%.2f') - def FORMAT_ANG(self): return('%.1f') - def FORMAT_TIME(self): return self.fmt - - def BLOCK(self): return('N%i') - def COMMENT(self,comment): return( ('(%s)' % comment ) ) - def VARIABLE(self): return( '#%i') - def VARIABLE_SET(self): return( '=%.3f') - - def PROGRAM(self): return( 'O%i') - def PROGRAM_END(self): return( 'M02') - - def SUBPROG_CALL(self): return( 'M98' + self.SPACE() + 'P%i') - def SUBPROG_END(self): return( 'M99') - - def STOP_OPTIONAL(self): return('M01') - def STOP(self): return('M00') - - def IMPERIAL(self): return('G20') - def METRIC(self): return('G21') - def ABSOLUTE(self): return('G90') - def INCREMENTAL(self): return('G91') - def SET_TEMPORARY_COORDINATE_SYSTEM(self): return('G92') - def REMOVE_TEMPORARY_COORDINATE_SYSTEM(self): return('G92.1') - def POLAR_ON(self): return('G16') - def POLAR_OFF(self): return('G15') - def PLANE_XY(self): return('17') - def PLANE_XZ(self): return('18') - def PLANE_YZ(self): return('19') - - def TOOL(self): return('T%i' + self.SPACE() + 'M06') - def TOOL_DEFINITION(self): return('G10' + self.SPACE() + 'L1') - - def WORKPLANE(self): return('G%i') - def WORKPLANE_BASE(self): return(53) - - def SPINDLE_CW(self): return('M03') - def SPINDLE_CCW(self): return('M04') - def COOLANT_OFF(self): return('M09') - def COOLANT_MIST(self): return('M07') - def COOLANT_FLOOD(self): return('M08') - def GEAR_OFF(self): return('?') - def GEAR(self): return('M%i') - def GEAR_BASE(self): return(37) - - def RAPID(self): return('G00') - def FEED(self): return('G01') - def ARC_CW(self): return('G02') - def ARC_CCW(self): return('G03') - def DWELL(self, dwell): return('G04' + self.SPACE() + self.TIME() + (self.FORMAT_TIME().string(dwell))) - def DRILL(self): return('G81') - def DRILL_WITH_DWELL(self, dwell): return('G82' + self.SPACE() + self.TIME() + (self.FORMAT_TIME().string(dwell))) - def PECK_DRILL(self): return('G83') - def PECK_DEPTH(self, depth): return('Q' + (self.fmt.string(depth))) - def RETRACT(self, height): return('R' + (self.fmt.string(height))) - def END_CANNED_CYCLE(self): return('G80') - def TAP(self): return('G84') - def TAP_DEPTH(self, depth): return('K' + (self.fmt.string(depth))) - def INTERNAL_COOLANT_ON(self): return('M18') - def INTERNAL_COOLANT_OFF(self): return('M9') - - def X(self): return('X') - def Y(self): return('Y') - def Z(self): return('Z') - def A(self): return('A') - def B(self): return('B') - def C(self): return('C') - def CENTRE_X(self): return('I') - def CENTRE_Y(self): return('J') - def CENTRE_Z(self): return('K') - def RADIUS(self): return('R') - def TIME(self): return('P') - - def PROBE_TOWARDS_WITH_SIGNAL(self): return('G38.2') - def PROBE_TOWARDS_WITHOUT_SIGNAL(self): return('G38.3') - def PROBE_AWAY_WITH_SIGNAL(self): return('G38.4') - def PROBE_AWAY_WITHOUT_SIGNAL(self): return('G38.5') - - def MACHINE_COORDINATES(self): return('G53') - - def EXACT_PATH_MODE(self): return('G61') - def EXACT_STOP_MODE(self): return('G61.1') - - def RETRACT_TO_CLEARANCE(self): return('G98') - def RETRACT_TO_STANDOFF(self): return('G99') - - ############################################################################ - ## Internals - def write(self, s): - if self.output_disabled == False: - nc.Creator.write(self, s) - if '\n' in s: - self.start_of_line = s[-1] == '\n' - - def write_feedrate(self): - self.f.write(self) - - def write_preps(self): - i = 0 - if self.g_plane.str: - i += 1 - self.g_plane.write(self) - for g in self.g_list: - if i > 0: - self.write('\n' if self.m_codes_on_their_own_line else self.SPACE()) - self.write(g) - i += 1 - self.g_list = [] - - def write_misc(self): - if (len(self.m)): - self.write('\n' if self.m_codes_on_their_own_line else self.SPACE()) - self.write(self.m.pop()) - - def write_spindle(self): - if self.s.str: - self.write('\n' if self.m_codes_on_their_own_line else '') - self.s.write(self) - - def output_fixture(self): - if self.current_fixture != self.fixture_wanted: - self.current_fixture = self.fixture_wanted - self.g_list.append('G' + str(self.current_fixture)) - - def increment_fixture(self): - for i in range(0, len(self.fixture_order) - 1): - if self.fixture_order[i] == self.fixture_wanted: - self.fixture_wanted = self.fixture_order[i+1] - return - raise 'too many fixtures wanted!' - - def get_fixture(self): - return self.fixture_wanted - - def set_fixture(self, fixture): - self.fixture_wanted = fixture - - ############################################################################ - ## Programs - - def program_begin(self, id, name=''): - if self.use_this_program_id: - id = self.use_this_program_id - if self.PROGRAM() != None: - self.writem([(self.PROGRAM() % id), self.SPACE(), (self.COMMENT(name))]) - self.write('\n') - self.program_id = id - self.program_name = name - - def add_stock(self, type_name, params): - if self.output_cutviewer_comments: - self.write("(STOCK/" + type_name) - for param in params: - self.write(",") - self.write(str(param)) - self.write(")\n") - - def program_stop(self, optional=False): - if (optional) : - self.write(self.SPACE() + self.STOP_OPTIONAL() + '\n') - self.prev_g0123 = '' - else : - self.write(self.STOP() + '\n') - self.prev_g0123 = '' - - def number_file(self, filename): - import tempfile - temp_filename = tempfile.gettempdir()+'/renumbering.txt' - - # make a copy of file - f_in = open(filename, 'r') - f_out = open(temp_filename, 'w') - while (True): - line = f_in.readline() - if (len(line) == 0) : break - f_out.write(line) - f_in.close() - f_out.close() - - # read copy - f_in = open(temp_filename, 'r') - f_out = open(filename, 'w') - n = self.start_block_number - while (True): - line = f_in.readline() - - if (len(line) == 0) : break - f_out.write(self.BLOCK() % n + self.SPACE() + line) - n += self.block_number_increment - if self.block_number_restart_after != None: - if n >= self.block_number_restart_after: - n = self.start_block_number - f_in.close() - f_out.close() - - def program_end(self): - if self.z_for_g53 != None: - self.write(self.SPACE() + self.MACHINE_COORDINATES() + self.SPACE() + 'Z' + self.fmt.string(self.z_for_g53) + '\n') - self.write(self.SPACE() + self.PROGRAM_END() + '\n') - - if self.temp_file_to_append_on_close != None: - f_in = open(self.temp_file_to_append_on_close, 'r') - while (True): - line = f_in.readline() - if (len(line) == 0) : break - self.write(line) - f_in.close() - - self.file_close() - - if self.output_block_numbers: - # number every line of the file afterwards - self.number_file(self.filename) - - for f in self.subroutine_files: - self.number_file(f) - - def flush_nc(self): - if len(self.g_list) == 0 and len(self.m) == 0: return - self.write_preps() - self.write_misc() - self.write('\n') - - ############################################################################ - ## Subprograms - - def make_subroutine_name(self, id): - s = self.filename - for i in reversed(range(0, len(s))): - if s[i] == '.': - return s[0:i] + 'sub' + str(id) + s[i:] - - # '.' not found - return s + 'sub' + str(id) - - def sub_begin(self, id, name=None): - if id == None: - if self.current_sub_id == None: - self.current_sub_id = self.program_id - self.current_sub_id += 1 - id = self.current_sub_id - - if name == None: - name = self.program_name + ' subroutine ' + str(id) - - self.save_file = self.file - if self.subroutines_in_own_files: - new_name = self.make_subroutine_name(id) - self.file = open(new_name, 'w') - self.subroutine_files.append(new_name) - else: - ## use temporary file - import tempfile - temp_filename = tempfile.gettempdir()+'/subroutines.txt' - if self.temp_file_to_append_on_close == None: - self.temp_file_to_append_on_close = temp_filename - self.file = open(temp_filename, 'w') - else: - self.file = open(temp_filename, 'a') - - if self.PROGRAM() != None: - self.write((self.PROGRAM() % id) + self.SPACE() + (self.COMMENT(name))) - self.write('\n') - - def sub_call(self, id): - if id == None: - id = self.current_sub_id - self.write(self.SPACE() + (self.SUBPROG_CALL() % id) + '\n') - - def sub_end(self): - self.write(self.SPACE() + self.SUBPROG_END() + '\n') - - self.file.close() - self.file = self.save_file - - def disable_output(self): - self.output_disabled = True - - def enable_output(self): - self.output_disabled = False - - ############################################################################ - ## Settings - - def imperial(self): - self.g_list.append(self.IMPERIAL()) - self.fmt.number_of_decimal_places = 4 - - def metric(self): - self.g_list.append(self.METRIC()) - self.fmt.number_of_decimal_places = 3 - - def absolute(self): - self.g_list.append(self.ABSOLUTE()) - self.absolute_flag = True - - def incremental(self): - self.g_list.append(self.INCREMENTAL()) - self.absolute_flag = False - - def polar(self, on=True): - if (on) : self.g_list.append(self.POLAR_ON()) - else : self.g_list.append(self.POLAR_OFF()) - - def set_plane(self, plane): - if (plane == 0) : self.g_plane.set(self.PLANE_XY()) - elif (plane == 1) : self.g_plane.set(self.PLANE_XZ()) - elif (plane == 2) : self.g_plane.set(self.PLANE_YZ()) - - def set_temporary_origin(self, x=None, y=None, z=None, a=None, b=None, c=None): - self.write(self.SPACE() + (self.SET_TEMPORARY_COORDINATE_SYSTEM())) - if (x != None): self.write( self.SPACE() + 'X ' + (self.fmt.string(x + self.shift_x)) ) - if (y != None): self.write( self.SPACE() + 'Y ' + (self.fmt.string(y + self.shift_y)) ) - if (z != None): self.write( self.SPACE() + 'Z ' + (self.fmt.string(z + self.shift_z)) ) - if (a != None): self.write( self.SPACE() + 'A ' + (self.fmt.string(a)) ) - if (b != None): self.write( self.SPACE() + 'B ' + (self.fmt.string(b)) ) - if (c != None): self.write( self.SPACE() + 'C ' + (self.fmt.string(c)) ) - self.write('\n') - - def remove_temporary_origin(self): - self.write(self.SPACE() + (self.REMOVE_TEMPORARY_COORDINATE_SYSTEM())) - self.write('\n') - ############################################################################ - ## new graphics origin- make a new coordinate system and snap it onto the geometry - ## the toolpath generated should be translated - def translate(self,x=None, y=None, z=None): - self.shift_x = -x - self.shift_y = -y - self.shift_z = -z - - ############################################################################ - ## Tools - - def tool_change(self, id): - if self.output_comment_before_tool_change: - if id in self.tool_defn_params: - self.comment('tool change to ' + self.tool_defn_params[id]['name']); - - if self.output_cutviewer_comments: - import cutviewer - if id in self.tool_defn_params: - cutviewer.tool_defn(self, id, self.tool_defn_params[id]) - if (self.t != None) and (self.z_for_g53 != None): - self.write('G53 Z' + str(self.z_for_g53) + '\n') - self.write(self.SPACE() + (self.TOOL() % id)) - if self.output_g43_on_tool_change_line == True: - self.write(self.SPACE() + 'G43') - self.write('\n') - if self.output_h_and_d_at_tool_change == True: - if self.output_g43_on_tool_change_line == False: - self.write(self.SPACE() + 'G43') - self.write(self.SPACE() + 'D' + str(id) + self.SPACE() + 'H' + str(id) + '\n') - self.t = id - self.move_done_since_tool_change = False - - def tool_defn(self, id, name='',params=None): - self.tool_defn_params[id] = params - if self.output_tool_definitions: - self.write(self.SPACE() + self.TOOL_DEFINITION()) - self.write(self.SPACE() + ('P%i' % id) + ' ') - - if (params['diameter'] != None): - self.write(self.SPACE() + ('R%.3f' % (float(params['diameter'])/2))) - - if (params['cutting edge height'] != None): - self.write(self.SPACE() + 'Z%.3f' % float(params['cutting edge height'])) - - self.write('\n') - - def offset_radius(self, id, radius=None): - pass - - def offset_length(self, id, length=None): - pass - - def current_tool(self): - return self.t - - - ############################################################################ - ## Datums - - def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None): - pass - - def datum_set(self, x=None, y=None, z=None, a=None, b=None, c=None): - pass - - # This is the coordinate system we're using. G54->G59, G59.1, G59.2, G59.3 - # These are selected by values from 1 to 9 inclusive. - def workplane(self, id): - if ((id >= 1) and (id <= 6)): - self.g_list.append(self.WORKPLANE() % (id + self.WORKPLANE_BASE())) - if ((id >= 7) and (id <= 9)): - self.g_list.append(((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6)))) - - - ############################################################################ - ## Rates + Modes - - def feedrate(self, f): - self.f.set(f) - self.fhv = False - - def feedrate_hv(self, fh, fv): - self.fh = fh - self.fv = fv - self.fhv = True - - def calc_feedrate_hv(self, h, v): - if math.fabs(v) > math.fabs(h * 2): - # some horizontal, so it should be fine to use the horizontal feed rate - self.f.set(self.fv) - else: - # not much, if any horizontal component, so use the vertical feed rate - self.f.set(self.fh) - - def spindle(self, s, clockwise): - if clockwise == True: - self.s.set(s, self.SPINDLE_CW(), self.SPINDLE_CCW()) - else: - self.s.set(s, self.SPINDLE_CCW(), self.SPINDLE_CW()) - - def coolant(self, mode=0): - if (mode <= 0) : self.m.append(self.COOLANT_OFF()) - elif (mode == 1) : self.m.append(self.COOLANT_MIST()) - elif (mode == 2) : self.m.append(self.COOLANT_FLOOD()) - - def gearrange(self, gear=0): - if (gear <= 0) : self.m.append(self.GEAR_OFF()) - elif (gear <= 4) : self.m.append(self.GEAR() % (gear + GEAR_BASE())) - - ############################################################################ - ## Moves - - def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None ): - (x, y, z, a, b, c,axis_count)=self.filter_xyz(x, y, z, a, b, c) - if axis_count==0: return - self.on_move() - - if self.g0123_modal: - if self.prev_g0123 != self.RAPID(): - self.write(self.SPACE() + self.RAPID()) - self.prev_g0123 = self.RAPID() - else: - self.write(self.SPACE() + self.RAPID()) - self.write_preps() - if (x != None): - if (self.absolute_flag ): - self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) - else: - dx = x - self.x - self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) - self.x = x - if (y != None): - if (self.absolute_flag ): - self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) - else: - dy = y - self.y - self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) - - self.y = y - if (z != None): - if (self.absolute_flag ): - self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) - else: - dz = z - self.z - self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) - - self.z = z - - if (a != None): - if (self.absolute_flag ): - self.write(self.SPACE() + self.A() + (self.fmt.string(a))) - else: - da = a - self.a - self.write(self.SPACE() + self.A() + (self.fmt.string(da))) - self.a = a - - if (b != None): - if (self.absolute_flag ): - self.write(self.SPACE() + self.B() + (self.fmt.string(b))) - else: - db = b - self.b - self.write(self.SPACE() + self.B() + (self.fmt.string(db))) - self.b = b - - if (c != None): - if (self.absolute_flag ): - self.write(self.SPACE() + self.C() + (self.fmt.string(c))) - else: - dc = c - self.c - self.write(self.SPACE() + self.C() + (self.fmt.string(dc))) - self.c = c - self.write_spindle() - self.write_misc() - self.write('\n') - - def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): - (x, y, z, a, b, c,axis_count)=self.filter_xyz(x, y, z, a, b, c) - if axis_count==0: return - self.on_move() - if self.g0123_modal: - if self.prev_g0123 != self.FEED(): - self.writem([self.SPACE() , self.FEED()]) - self.prev_g0123 = self.FEED() - else: - self.write(self.SPACE() + self.FEED()) - self.write_preps() - dx = dy = dz = 0 - if (x != None): - dx = x - self.x - if (self.absolute_flag ): - self.writem([self.SPACE() , self.X() , (self.fmt.string(x + self.shift_x))]) - else: - self.writem([self.SPACE() , self.X() , (self.fmt.string(dx))]) - self.x = x - if (y != None): - dy = y - self.y - if (self.absolute_flag ): - self.writem([self.SPACE() , self.Y() , (self.fmt.string(y + self.shift_y))]) - else: - self.writem([self.SPACE() , self.Y() , (self.fmt.string(dy))]) - - self.y = y - if (z != None): - dz = z - self.z - if (self.absolute_flag ): - self.writem([self.SPACE() , self.Z() , (self.fmt.string(z + self.shift_z))]) - else: - self.writem([self.SPACE() , self.Z() , (self.fmt.string(dz))]) - - self.z = z - - if (a != None): - da = a - self.a - if (self.absolute_flag ): - self.writem([self.SPACE() , self.A() , (self.fmt.string(a))]) - else: - self.writem([self.SPACE() , self.A() , (self.fmt.string(da))]) - self.a = a - - if (b != None): - db = b - self.b - if (self.absolute_flag ): - self.writem([self.SPACE() , self.B() , (self.fmt.string(b))]) - else: - self.writem([self.SPACE() , self.B() , (self.fmt.string(db))]) - self.b = b - - if (c != None): - dc = c - self.c - if (self.absolute_flag ): - self.writem([self.SPACE() , self.C() , (self.fmt.string(c))]) - else: - self.writem([self.SPACE() , self.C() , (self.fmt.string(dc))]) - self.c = c - - if (self.fhv) : self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) - self.write_feedrate() - self.write_spindle() - self.write_misc() - self.write('\n') - - def filter_xyz(self, x=None, y=None, z=None, a=None, b=None, c=None): - """ Check if x,y,z,a,b,c are the same and set them to None if they are - return value = (x,y,z,a,b,c,count) where count is the number of - axis moves left. - """ - rv = [x,y,z,a,b,c,0] - comparisons = ((x,self.shift_x,self.x),(y,self.shift_y,self.y),(z,self.shift_z,self.z), - (a,0,self.a),(b,0,self.b),(c,0,self.c)) - - for i,(new_val,shift,current_val) in enumerate(comparisons): - if new_val is not None: - if self.fmt.string(new_val+shift) == self.fmt.string(current_val): - rv[i]=None - else: - rv[6]+=1 - return rv - - - def get_quadrant(self, dx, dy): - if dx < 0: - if dy < 0: - return 2 - else: - return 1 - - else: - if dy < 0: - return 3 - else: - return 0 - - def quadrant_start(self, q, i, j, rad): - while q > 3: q = q - 4 - if q == 0: - return i + rad, j - if q == 1: - return i, j + rad - if q == 2: - return i - rad, j - return i, j - rad - - def quadrant_end(self, q, i, j, rad): - return self.quadrant_start(q + 1, i, j, rad) - - def get_arc_angle(self, sdx, sdy, edx, edy, cw): - angle_s = math.atan2(sdy, sdx); - angle_e = math.atan2(edy, edx); - if cw: - if angle_s < angle_e: angle_s = angle_s + 2 * math.pi - else: - if angle_e < angle_s: angle_e = angle_e + 2 * math.pi - return angle_e - angle_s - - def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): - (x,y,z,_,_,_,axis_count) = self.filter_xyz(x,y,z) - if axis_count==0: return - - if self.output_arcs_as_lines or (self.can_do_helical_arcs == False and self.in_quadrant_splitting == False and (z != None) and (math.fabs(z - self.z) > 0.000001) and (self.fmt.string(z) != self.fmt.string(self.z))): - # split the helical arc into little line feed moves - - if x == None: x = self.x - if y == None: y = self.y - sdx = self.x - i - sdy = self.y - j - edx = x - i - edy = y - j - radius = math.sqrt(sdx*sdx + sdy*sdy) - arc_angle = self.get_arc_angle(sdx, sdy, edx, edy, cw) - angle_start = math.atan2(sdy, sdx); - tolerance = 0.02 - angle_step = 2.0 * math.atan( math.sqrt ( tolerance /(radius - tolerance) )) - segments = int(math.fabs(arc_angle / angle_step) + 1) - angle_step = arc_angle / segments - angle = angle_start - if z != None: - z_step = float(z - self.z)/segments - next_z = self.z - - for p in range(0, segments): - angle = angle + angle_step - next_x = i + radius * math.cos(angle) - next_y = j + radius * math.sin(angle) - if z == None: - next_z = None - else: - next_z = next_z + z_step - self.feed(next_x, next_y, next_z) - return - - if self.arc_centre_positive == True and self.in_quadrant_splitting == False: - # split in to quadrant arcs - self.in_quadrant_splitting = True - - if x == None: x = self.x - if y == None: y = self.y - sdx = self.x - i - sdy = self.y - j - edx = x - i - edy = y - j - - qs = self.get_quadrant(sdx, sdy) - qe = self.get_quadrant(edx, edy) - - if qs == qe: - arc_angle = math.fabs(self.get_arc_angle(sdx, sdy, edx, edy, cw)) - # arc_angle will be either less than pi/2 or greater than 3pi/2 - if arc_angle > 3.14: - if cw: - qs = qs + 4 - else: - qe = qe + 4 - - if qs == qe: - self.arc(cw, x, y, z, i, j, k, r) - else: - rad = math.sqrt(sdx * sdx + sdy * sdy) - if cw: - if qs < qe: qs = qs + 4 - else: - if qe < qs: qe = qe + 4 - - q = qs - while 1: - x1 = x - y1 = y - if q != qe: - if cw: - x1, y1 = self.quadrant_start(q, i, j, rad) - else: - x1, y1 = self.quadrant_end(q, i, j, rad) - - if (self.fmt.string(x1) != self.fmt.string(self.x)) or (self.fmt.string(y1) != self.fmt.string(self.y)): - if (math.fabs(x1 - self.x) > 0.01) or (math.fabs(y1 - self.y) > 0.01): - self.arc(cw, x1, y1, z, i, j, k, r) - else: - self.feed(x1, y1, z) - if q == qe: - break - if cw: - q = q - 1 - else: - q = q + 1 - - self.in_quadrant_splitting = False - return - - self.on_move() - arc_g_code = '' - if cw: arc_g_code = self.ARC_CW() - else: arc_g_code = self.ARC_CCW() - if self.g0123_modal: - if self.prev_g0123 != arc_g_code: - self.write(self.SPACE() + arc_g_code) - self.prev_g0123 = arc_g_code - else: - self.write(self.SPACE() + arc_g_code) - self.write_preps() - if (x != None): - dx = x - self.x - if (self.absolute_flag ): - self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) - else: - self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) - if (y != None): - dy = y - self.y - if (self.absolute_flag ): - self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) - else: - self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) - if (z != None): - dz = z - self.z - if (self.absolute_flag ): - self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) - else: - self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) - if (i != None): - if self.arc_centre_absolute == False: - i = i - self.x - s = self.fmt.string(i) - if self.arc_centre_positive == True: - if s[0] == '-': - s = s[1:] - self.write(self.SPACE() + self.CENTRE_X() + s) - if (j != None): - if self.arc_centre_absolute == False: - j = j - self.y - s = self.fmt.string(j) - if self.arc_centre_positive == True: - if s[0] == '-': - s = s[1:] - self.write(self.SPACE() + self.CENTRE_Y() + s) - if (k != None): - if self.arc_centre_absolute == False: - k = k - self.z - s = self.fmt.string(k) - if self.arc_centre_positive == True: - if s[0] == '-': - s = s[1:] - self.write(self.SPACE() + self.CENTRE_Z() + s) - if (r != None): - s = self.fmt.string(r) - if self.arc_centre_positive == True: - if s[0] == '-': - s = s[1:] - self.write(self.SPACE() + self.RADIUS() + s) + def __init__(self): + nc.Creator.__init__(self) + + # internal variables + self.a = 0 + self.b = 0 + self.c = 0 + self.f = Address('F', fmt=Format(number_of_decimal_places=2)) + self.fh = None + self.fv = None + self.fhv = False + self.g_plane = Address('G', fmt=Format(number_of_decimal_places=0)) + self.g_list = [] + self.i = 0 + self.j = 0 + self.k = 0 + self.m = [] + self.r = 0 + self.s = AddressPlusMinus('S', fmt=Format(number_of_decimal_places=2), modal=False) + self.t = None + self.x = None + self.y = None + self.z = None + self.g0123_modal = False + self.drill_modal = False + self.prev_f = '' + self.prev_g0123 = '' + self.prev_drill = '' + self.prev_retract = '' + self.prev_z = '' + self.useCrc = False + self.useCrcCenterline = False + self.gCRC = '' + self.fmt = Format() + self.absolute_flag = True + self.ffmt = Format(number_of_decimal_places=2) + self.sfmt = Format(number_of_decimal_places=1) + self.in_quadrant_splitting = False + self.in_canned_cycle = False + self.first_drill_pos = True + self.shift_x = 0.0 + self.shift_y = 0.0 + self.shift_z = 0.0 + self.start_of_line = False + self.internal_coolant_on = None + self.g98_not_g99 = None # True for G98 ouput, False for G99 output + self.current_fixture = None + self.fixture_wanted = '54' + self.move_done_since_tool_change = False + self.tool_defn_params = {} + self.program_id = None + self.current_sub_id = None + self.subroutine_files = [] + self.program_name = None + self.temp_file_to_append_on_close = None + self.fixture_order = ['54', '55', '56', '57', '58', '59'] + for i in range(1, 50): + self.fixture_order.append('54.' + str(i)) + self.output_disabled = False + self.z_for_g43 = None + + # optional settings + self.arc_centre_absolute = False + self.arc_centre_positive = False + self.drillExpanded = False + self.dwell_allowed_in_G83 = False + self.can_do_helical_arcs = True + self.z_for_g53 = None # set this to a value to output G53 Zvalue in tool change and at program end + self.output_h_and_d_at_tool_change = False + self.output_block_numbers = True + self.start_block_number = 10 + self.block_number_increment = 10 + self.block_number_restart_after = None + self.output_tool_definitions = True + self.output_tool_change = True + self.output_g43_on_tool_change_line = False + self.output_internal_coolant_commands = False + self.output_g98_and_g99 = True + self.output_g43_z_before_drilling_if_g98 = False + self.output_cutviewer_comments = False + self.output_fixtures = False + self.use_this_program_id = None + self.subroutines_in_own_files = False + self.pattern_done_with_subroutine = False + self.output_comment_before_tool_change = True + self.output_arcs_as_lines = False + self.m_codes_on_their_own_line = False + + ############################################################################ + # Codes + + def SPACE_STR(self): return '' + + def SPACE(self): + if self.start_of_line == True: + self.start_of_line = False + return '' + else: + return self.SPACE_STR() + + def FORMAT_FEEDRATE(self): return('%.2f') + def FORMAT_ANG(self): return('%.1f') + def FORMAT_TIME(self): return self.fmt + + def BLOCK(self): return('N%i') + def COMMENT(self, comment): return(('(%s)' % comment)) + def VARIABLE(self): return('#%i') + def VARIABLE_SET(self): return('=%.3f') + + def PROGRAM(self): return('O%i') + def PROGRAM_END(self): return('M02') + + def SUBPROG_CALL(self): return('M98' + self.SPACE() + 'P%i') + def SUBPROG_END(self): return('M99') + + def STOP_OPTIONAL(self): return('M01') + def STOP(self): return('M00') + + def IMPERIAL(self): return('G20') + def METRIC(self): return('G21') + def ABSOLUTE(self): return('G90') + def INCREMENTAL(self): return('G91') + def SET_TEMPORARY_COORDINATE_SYSTEM(self): return('G92') + def REMOVE_TEMPORARY_COORDINATE_SYSTEM(self): return('G92.1') + def POLAR_ON(self): return('G16') + def POLAR_OFF(self): return('G15') + def PLANE_XY(self): return('17') + def PLANE_XZ(self): return('18') + def PLANE_YZ(self): return('19') + + def TOOL(self): return('T%i' + self.SPACE() + 'M06') + def TOOL_DEFINITION(self): return('G10' + self.SPACE() + 'L1') + + def WORKPLANE(self): return('G%i') + def WORKPLANE_BASE(self): return(53) + + def SPINDLE_CW(self): return('M03') + def SPINDLE_CCW(self): return('M04') + def COOLANT_OFF(self): return('M09') + def COOLANT_MIST(self): return('M07') + def COOLANT_FLOOD(self): return('M08') + def GEAR_OFF(self): return('?') + def GEAR(self): return('M%i') + def GEAR_BASE(self): return(37) + + def RAPID(self): return('G00') + def FEED(self): return('G01') + def ARC_CW(self): return('G02') + def ARC_CCW(self): return('G03') + def DWELL(self, dwell): return('G04' + self.SPACE() + + self.TIME() + (self.FORMAT_TIME().string(dwell))) + + def DRILL(self): return('G81') + def DRILL_WITH_DWELL(self, dwell): return('G82' + self.SPACE() + + self.TIME() + (self.FORMAT_TIME().string(dwell))) + + def PECK_DRILL(self): return('G83') + def PECK_DEPTH(self, depth): return('Q' + (self.fmt.string(depth))) + def RETRACT(self, height): return('R' + (self.fmt.string(height))) + def END_CANNED_CYCLE(self): return('G80') + def TAP(self): return('G84') + def TAP_DEPTH(self, depth): return('K' + (self.fmt.string(depth))) + def INTERNAL_COOLANT_ON(self): return('M18') + def INTERNAL_COOLANT_OFF(self): return('M9') + + def X(self): return('X') + def Y(self): return('Y') + def Z(self): return('Z') + def A(self): return('A') + def B(self): return('B') + def C(self): return('C') + def CENTRE_X(self): return('I') + def CENTRE_Y(self): return('J') + def CENTRE_Z(self): return('K') + def RADIUS(self): return('R') + def TIME(self): return('P') + + def PROBE_TOWARDS_WITH_SIGNAL(self): return('G38.2') + def PROBE_TOWARDS_WITHOUT_SIGNAL(self): return('G38.3') + def PROBE_AWAY_WITH_SIGNAL(self): return('G38.4') + def PROBE_AWAY_WITHOUT_SIGNAL(self): return('G38.5') + + def MACHINE_COORDINATES(self): return('G53') + + def EXACT_PATH_MODE(self): return('G61') + def EXACT_STOP_MODE(self): return('G61.1') + + def RETRACT_TO_CLEARANCE(self): return('G98') + def RETRACT_TO_STANDOFF(self): return('G99') + + ############################################################################ + # Internals + def write(self, s): + if self.output_disabled == False: + nc.Creator.write(self, s) + if '\n' in s: + self.start_of_line = s[-1] == '\n' + + def write_feedrate(self): + self.f.write(self) + + def write_preps(self): + i = 0 + if self.g_plane.str: + i += 1 + self.g_plane.write(self) + for g in self.g_list: + if i > 0: + self.write('\n' if self.m_codes_on_their_own_line else self.SPACE()) + self.write(g) + i += 1 + self.g_list = [] + + def write_misc(self): + if (len(self.m)): + self.write('\n' if self.m_codes_on_their_own_line else self.SPACE()) + self.write(self.m.pop()) + + def write_spindle(self): + if self.s.str: + self.write('\n' if self.m_codes_on_their_own_line else '') + self.s.write(self) + + def output_fixture(self): + if self.current_fixture != self.fixture_wanted: + self.current_fixture = self.fixture_wanted + self.g_list.append('G' + str(self.current_fixture)) + + def increment_fixture(self): + for i in range(0, len(self.fixture_order) - 1): + if self.fixture_order[i] == self.fixture_wanted: + self.fixture_wanted = self.fixture_order[i+1] + return + raise 'too many fixtures wanted!' + + def get_fixture(self): + return self.fixture_wanted + + def set_fixture(self, fixture): + self.fixture_wanted = fixture + + ############################################################################ + # Programs + + def program_begin(self, id, name=''): + if self.use_this_program_id: + id = self.use_this_program_id + if self.PROGRAM() != None: + self.writem([(self.PROGRAM() % id), self.SPACE(), (self.COMMENT(name))]) + self.write('\n') + self.program_id = id + self.program_name = name + + def add_stock(self, type_name, params): + if self.output_cutviewer_comments: + self.write("(STOCK/" + type_name) + for param in params: + self.write(",") + self.write(str(param)) + self.write(")\n") + + def program_stop(self, optional=False): + if (optional): + self.write(self.SPACE() + self.STOP_OPTIONAL() + '\n') + self.prev_g0123 = '' + else: + self.write(self.STOP() + '\n') + self.prev_g0123 = '' + + def number_file(self, filename): + import tempfile + temp_filename = tempfile.gettempdir()+'/renumbering.txt' + + # make a copy of file + f_in = open(filename, 'r') + f_out = open(temp_filename, 'w') + while (True): + line = f_in.readline() + if (len(line) == 0): + break + f_out.write(line) + f_in.close() + f_out.close() + + # read copy + f_in = open(temp_filename, 'r') + f_out = open(filename, 'w') + n = self.start_block_number + while (True): + line = f_in.readline() + + if (len(line) == 0): + break + f_out.write(self.BLOCK() % n + self.SPACE() + line) + n += self.block_number_increment + if self.block_number_restart_after != None: + if n >= self.block_number_restart_after: + n = self.start_block_number + f_in.close() + f_out.close() + + def program_end(self): + if self.z_for_g53 != None: + self.write(self.SPACE() + self.MACHINE_COORDINATES() + + self.SPACE() + 'Z' + self.fmt.string(self.z_for_g53) + '\n') + self.write(self.SPACE() + self.PROGRAM_END() + '\n') + + if self.temp_file_to_append_on_close != None: + f_in = open(self.temp_file_to_append_on_close, 'r') + while (True): + line = f_in.readline() + if (len(line) == 0): + break + self.write(line) + f_in.close() + + self.file_close() + + if self.output_block_numbers: + # number every line of the file afterwards + self.number_file(self.filename) + + for f in self.subroutine_files: + self.number_file(f) + + def flush_nc(self): + if len(self.g_list) == 0 and len(self.m) == 0: + return + self.write_preps() + self.write_misc() + self.write('\n') + + ############################################################################ + # Subprograms + + def make_subroutine_name(self, id): + s = self.filename + for i in reversed(range(0, len(s))): + if s[i] == '.': + return s[0:i] + 'sub' + str(id) + s[i:] + + # '.' not found + return s + 'sub' + str(id) + + def sub_begin(self, id, name=None): + if id == None: + if self.current_sub_id == None: + self.current_sub_id = self.program_id + self.current_sub_id += 1 + id = self.current_sub_id + + if name == None: + name = self.program_name + ' subroutine ' + str(id) + + self.save_file = self.file + if self.subroutines_in_own_files: + new_name = self.make_subroutine_name(id) + self.file = open(new_name, 'w') + self.subroutine_files.append(new_name) + else: + # use temporary file + import tempfile + temp_filename = tempfile.gettempdir()+'/subroutines.txt' + if self.temp_file_to_append_on_close == None: + self.temp_file_to_append_on_close = temp_filename + self.file = open(temp_filename, 'w') + else: + self.file = open(temp_filename, 'a') + + if self.PROGRAM() != None: + self.write((self.PROGRAM() % id) + self.SPACE() + (self.COMMENT(name))) + self.write('\n') + + def sub_call(self, id): + if id == None: + id = self.current_sub_id + self.write(self.SPACE() + (self.SUBPROG_CALL() % id) + '\n') + + def sub_end(self): + self.write(self.SPACE() + self.SUBPROG_END() + '\n') + + self.file.close() + self.file = self.save_file + + def disable_output(self): + self.output_disabled = True + + def enable_output(self): + self.output_disabled = False + + ############################################################################ + # Settings + + def imperial(self): + self.g_list.append(self.IMPERIAL()) + self.fmt.number_of_decimal_places = 4 + + def metric(self): + self.g_list.append(self.METRIC()) + self.fmt.number_of_decimal_places = 3 + + def absolute(self): + self.g_list.append(self.ABSOLUTE()) + self.absolute_flag = True + + def incremental(self): + self.g_list.append(self.INCREMENTAL()) + self.absolute_flag = False + + def polar(self, on=True): + if (on): + self.g_list.append(self.POLAR_ON()) + else: + self.g_list.append(self.POLAR_OFF()) + + def set_plane(self, plane): + if (plane == 0): + self.g_plane.set(self.PLANE_XY()) + elif (plane == 1): + self.g_plane.set(self.PLANE_XZ()) + elif (plane == 2): + self.g_plane.set(self.PLANE_YZ()) + + def set_temporary_origin(self, x=None, y=None, z=None, a=None, b=None, c=None): + self.write(self.SPACE() + (self.SET_TEMPORARY_COORDINATE_SYSTEM())) + if (x != None): + self.write(self.SPACE() + 'X ' + (self.fmt.string(x + self.shift_x))) + if (y != None): + self.write(self.SPACE() + 'Y ' + (self.fmt.string(y + self.shift_y))) + if (z != None): + self.write(self.SPACE() + 'Z ' + (self.fmt.string(z + self.shift_z))) + if (a != None): + self.write(self.SPACE() + 'A ' + (self.fmt.string(a))) + if (b != None): + self.write(self.SPACE() + 'B ' + (self.fmt.string(b))) + if (c != None): + self.write(self.SPACE() + 'C ' + (self.fmt.string(c))) + self.write('\n') + + def remove_temporary_origin(self): + self.write(self.SPACE() + (self.REMOVE_TEMPORARY_COORDINATE_SYSTEM())) + self.write('\n') + ############################################################################ + # new graphics origin- make a new coordinate system and snap it onto the geometry + # the toolpath generated should be translated + + def translate(self, x=None, y=None, z=None): + self.shift_x = -x + self.shift_y = -y + self.shift_z = -z + + ############################################################################ + # Tools + + def tool_change(self, id): + if self.output_comment_before_tool_change: + if id in self.tool_defn_params: + self.comment('tool change to ' + self.tool_defn_params[id]['name']) + + if self.output_cutviewer_comments: + import cutviewer + if id in self.tool_defn_params: + cutviewer.tool_defn(self, id, self.tool_defn_params[id]) + if (self.t != None) and (self.z_for_g53 != None): + self.write('G53 Z' + str(self.z_for_g53) + '\n') + self.write(self.SPACE() + (self.TOOL() % id)) + if self.output_g43_on_tool_change_line == True: + self.write(self.SPACE() + 'G43') + self.write('\n') + if self.output_h_and_d_at_tool_change == True: + if self.output_g43_on_tool_change_line == False: + self.write(self.SPACE() + 'G43') + self.write(self.SPACE() + 'D' + str(id) + self.SPACE() + 'H' + str(id) + '\n') + self.t = id + self.move_done_since_tool_change = False + + def tool_defn(self, id, name='', params=None): + self.tool_defn_params[id] = params + if self.output_tool_definitions: + self.write(self.SPACE() + self.TOOL_DEFINITION()) + self.write(self.SPACE() + ('P%i' % id) + ' ') + + if (params['diameter'] != None): + self.write(self.SPACE() + ('R%.3f' % (float(params['diameter'])/2))) + + if (params['cutting edge height'] != None): + self.write(self.SPACE() + 'Z%.3f' % float(params['cutting edge height'])) + + self.write('\n') + + def offset_radius(self, id, radius=None): + pass + + def offset_length(self, id, length=None): + pass + + def current_tool(self): + return self.t + + ############################################################################ + # Datums + + def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + def datum_set(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + # This is the coordinate system we're using. G54->G59, G59.1, G59.2, G59.3 + # These are selected by values from 1 to 9 inclusive. + def workplane(self, id): + if ((id >= 1) and (id <= 6)): + self.g_list.append(self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + if ((id >= 7) and (id <= 9)): + self.g_list.append( + ((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6)))) + + ############################################################################ + ## Rates + Modes + + def feedrate(self, f): + self.f.set(f) + self.fhv = False + + def feedrate_hv(self, fh, fv): + self.fh = fh + self.fv = fv + self.fhv = True + + def calc_feedrate_hv(self, h, v): + if math.fabs(v) > math.fabs(h * 2): + # some horizontal, so it should be fine to use the horizontal feed rate + self.f.set(self.fv) + else: + # not much, if any horizontal component, so use the vertical feed rate + self.f.set(self.fh) + + def spindle(self, s, clockwise): + if clockwise == True: + self.s.set(s, self.SPINDLE_CW(), self.SPINDLE_CCW()) + else: + self.s.set(s, self.SPINDLE_CCW(), self.SPINDLE_CW()) + + def coolant(self, mode=0): + if (mode <= 0): + self.m.append(self.COOLANT_OFF()) + elif (mode == 1): + self.m.append(self.COOLANT_MIST()) + elif (mode == 2): + self.m.append(self.COOLANT_FLOOD()) + + def gearrange(self, gear=0): + if (gear <= 0): + self.m.append(self.GEAR_OFF()) + elif (gear <= 4): + self.m.append(self.GEAR() % (gear + GEAR_BASE())) + + ############################################################################ + # Moves + + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): + (x, y, z, a, b, c, axis_count) = self.filter_xyz(x, y, z, a, b, c) + if axis_count == 0: + return + self.on_move() + + if self.g0123_modal: + if self.prev_g0123 != self.RAPID(): + self.write(self.SPACE() + self.RAPID()) + self.prev_g0123 = self.RAPID() + else: + self.write(self.SPACE() + self.RAPID()) + self.write_preps() + if (x != None): + if (self.absolute_flag): + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + else: + dx = x - self.x + self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) + self.x = x + if (y != None): + if (self.absolute_flag): + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + else: + dy = y - self.y + self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) + + self.y = y + if (z != None): + if (self.absolute_flag): + self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) + else: + dz = z - self.z + self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) + + self.z = z + + if (a != None): + if (self.absolute_flag): + self.write(self.SPACE() + self.A() + (self.fmt.string(a))) + else: + da = a - self.a + self.write(self.SPACE() + self.A() + (self.fmt.string(da))) + self.a = a + + if (b != None): + if (self.absolute_flag): + self.write(self.SPACE() + self.B() + (self.fmt.string(b))) + else: + db = b - self.b + self.write(self.SPACE() + self.B() + (self.fmt.string(db))) + self.b = b + + if (c != None): + if (self.absolute_flag): + self.write(self.SPACE() + self.C() + (self.fmt.string(c))) + else: + dc = c - self.c + self.write(self.SPACE() + self.C() + (self.fmt.string(dc))) + self.c = c + self.write_spindle() + self.write_misc() + self.write('\n') + + def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): + (x, y, z, a, b, c, axis_count) = self.filter_xyz(x, y, z, a, b, c) + if axis_count == 0: + return + self.on_move() + if self.g0123_modal: + if self.prev_g0123 != self.FEED(): + self.writem([self.SPACE(), self.FEED()]) + self.prev_g0123 = self.FEED() + else: + self.write(self.SPACE() + self.FEED()) + self.write_preps() + dx = dy = dz = 0 + if (x != None): + dx = x - self.x + if (self.absolute_flag): + self.writem([self.SPACE(), self.X(), (self.fmt.string(x + self.shift_x))]) + else: + self.writem([self.SPACE(), self.X(), (self.fmt.string(dx))]) + self.x = x + if (y != None): + dy = y - self.y + if (self.absolute_flag): + self.writem([self.SPACE(), self.Y(), (self.fmt.string(y + self.shift_y))]) + else: + self.writem([self.SPACE(), self.Y(), (self.fmt.string(dy))]) + + self.y = y + if (z != None): + dz = z - self.z + if (self.absolute_flag): + self.writem([self.SPACE(), self.Z(), (self.fmt.string(z + self.shift_z))]) + else: + self.writem([self.SPACE(), self.Z(), (self.fmt.string(dz))]) + + self.z = z + + if (a != None): + da = a - self.a + if (self.absolute_flag): + self.writem([self.SPACE(), self.A(), (self.fmt.string(a))]) + else: + self.writem([self.SPACE(), self.A(), (self.fmt.string(da))]) + self.a = a + + if (b != None): + db = b - self.b + if (self.absolute_flag): + self.writem([self.SPACE(), self.B(), (self.fmt.string(b))]) + else: + self.writem([self.SPACE(), self.B(), (self.fmt.string(db))]) + self.b = b + + if (c != None): + dc = c - self.c + if (self.absolute_flag): + self.writem([self.SPACE(), self.C(), (self.fmt.string(c))]) + else: + self.writem([self.SPACE(), self.C(), (self.fmt.string(dc))]) + self.c = c + + if (self.fhv): + self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) + self.write_feedrate() + self.write_spindle() + self.write_misc() + self.write('\n') + + def filter_xyz(self, x=None, y=None, z=None, a=None, b=None, c=None): + """ Check if x,y,z,a,b,c are the same and set them to None if they are + return value = (x,y,z,a,b,c,count) where count is the number of + axis moves left. + """ + rv = [x, y, z, a, b, c, 0] + comparisons = ((x, self.shift_x, self.x), (y, self.shift_y, self.y), (z, self.shift_z, self.z), + (a, 0, self.a), (b, 0, self.b), (c, 0, self.c)) + + for i, (new_val, shift, current_val) in enumerate(comparisons): + if new_val is not None: + if self.fmt.string(new_val+shift) == self.fmt.string(current_val): + rv[i] = None + else: + rv[6] += 1 + return rv + + def get_quadrant(self, dx, dy): + if dx < 0: + if dy < 0: + return 2 + else: + return 1 + + else: + if dy < 0: + return 3 + else: + return 0 + + def quadrant_start(self, q, i, j, rad): + while q > 3: + q = q - 4 + if q == 0: + return i + rad, j + if q == 1: + return i, j + rad + if q == 2: + return i - rad, j + return i, j - rad + + def quadrant_end(self, q, i, j, rad): + return self.quadrant_start(q + 1, i, j, rad) + + def get_arc_angle(self, sdx, sdy, edx, edy, cw): + angle_s = math.atan2(sdy, sdx) + angle_e = math.atan2(edy, edx) + if cw: + if angle_s < angle_e: + angle_s = angle_s + 2 * math.pi + else: + if angle_e < angle_s: + angle_e = angle_e + 2 * math.pi + return angle_e - angle_s + + def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + (x, y, z, _, _, _, axis_count) = self.filter_xyz(x, y, z) + if axis_count == 0: + return + + if self.output_arcs_as_lines or (self.can_do_helical_arcs == False and self.in_quadrant_splitting == False and (z != None) and (math.fabs(z - self.z) > 0.000001) and (self.fmt.string(z) != self.fmt.string(self.z))): + # split the helical arc into little line feed moves + + if x == None: + x = self.x + if y == None: + y = self.y + sdx = self.x - i + sdy = self.y - j + edx = x - i + edy = y - j + radius = math.sqrt(sdx*sdx + sdy*sdy) + arc_angle = self.get_arc_angle(sdx, sdy, edx, edy, cw) + angle_start = math.atan2(sdy, sdx) + tolerance = 0.02 + angle_step = 2.0 * math.atan(math.sqrt(tolerance / (radius - tolerance))) + segments = int(math.fabs(arc_angle / angle_step) + 1) + angle_step = arc_angle / segments + angle = angle_start + if z != None: + z_step = float(z - self.z)/segments + next_z = self.z + + for p in range(0, segments): + angle = angle + angle_step + next_x = i + radius * math.cos(angle) + next_y = j + radius * math.sin(angle) + if z == None: + next_z = None + else: + next_z = next_z + z_step + self.feed(next_x, next_y, next_z) + return + + if self.arc_centre_positive == True and self.in_quadrant_splitting == False: + # split in to quadrant arcs + self.in_quadrant_splitting = True + + if x == None: + x = self.x + if y == None: + y = self.y + sdx = self.x - i + sdy = self.y - j + edx = x - i + edy = y - j + + qs = self.get_quadrant(sdx, sdy) + qe = self.get_quadrant(edx, edy) + + if qs == qe: + arc_angle = math.fabs(self.get_arc_angle(sdx, sdy, edx, edy, cw)) + # arc_angle will be either less than pi/2 or greater than 3pi/2 + if arc_angle > 3.14: + if cw: + qs = qs + 4 + else: + qe = qe + 4 + + if qs == qe: + self.arc(cw, x, y, z, i, j, k, r) + else: + rad = math.sqrt(sdx * sdx + sdy * sdy) + if cw: + if qs < qe: + qs = qs + 4 + else: + if qe < qs: + qe = qe + 4 + + q = qs + while 1: + x1 = x + y1 = y + if q != qe: + if cw: + x1, y1 = self.quadrant_start(q, i, j, rad) + else: + x1, y1 = self.quadrant_end(q, i, j, rad) + + if (self.fmt.string(x1) != self.fmt.string(self.x)) or (self.fmt.string(y1) != self.fmt.string(self.y)): + if (math.fabs(x1 - self.x) > 0.01) or (math.fabs(y1 - self.y) > 0.01): + self.arc(cw, x1, y1, z, i, j, k, r) + else: + self.feed(x1, y1, z) + if q == qe: + break + if cw: + q = q - 1 + else: + q = q + 1 + + self.in_quadrant_splitting = False + return + + self.on_move() + arc_g_code = '' + if cw: + arc_g_code = self.ARC_CW() + else: + arc_g_code = self.ARC_CCW() + if self.g0123_modal: + if self.prev_g0123 != arc_g_code: + self.write(self.SPACE() + arc_g_code) + self.prev_g0123 = arc_g_code + else: + self.write(self.SPACE() + arc_g_code) + self.write_preps() + if (x != None): + dx = x - self.x + if (self.absolute_flag): + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + else: + self.write(self.SPACE() + self.X() + (self.fmt.string(dx))) + if (y != None): + dy = y - self.y + if (self.absolute_flag): + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + else: + self.write(self.SPACE() + self.Y() + (self.fmt.string(dy))) + if (z != None): + dz = z - self.z + if (self.absolute_flag): + self.write(self.SPACE() + self.Z() + (self.fmt.string(z + self.shift_z))) + else: + self.write(self.SPACE() + self.Z() + (self.fmt.string(dz))) + if (i != None): + if self.arc_centre_absolute == False: + i = i - self.x + s = self.fmt.string(i) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.CENTRE_X() + s) + if (j != None): + if self.arc_centre_absolute == False: + j = j - self.y + s = self.fmt.string(j) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.CENTRE_Y() + s) + if (k != None): + if self.arc_centre_absolute == False: + k = k - self.z + s = self.fmt.string(k) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.CENTRE_Z() + s) + if (r != None): + s = self.fmt.string(r) + if self.arc_centre_positive == True: + if s[0] == '-': + s = s[1:] + self.write(self.SPACE() + self.RADIUS() + s) # use horizontal feed rate - if (self.fhv) : self.calc_feedrate_hv(1, 0) - self.write_feedrate() - self.write_spindle() - self.write_misc() - self.write('\n') - if (x != None): - self.x = x - if (y != None): - self.y = y - if (z != None): - self.z = z - - def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): - self.arc(True, x, y, z, i, j, k, r) - - def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): - self.arc(False, x, y, z, i, j, k, r) - - def dwell(self, t): - self.write_preps() - self.write(self.SPACE() + self.DWELL(t)) - self.write_misc() - self.write('\n') - - def on_move(self): - if self.output_fixtures: - self.output_fixture() - self.move_done_since_tool_change = True - - def rapid_home(self, x=None, y=None, z=None, a=None, b=None, c=None): - pass - - def rapid_unhome(self): - pass - - def set_machine_coordinates(self): - self.write(self.SPACE() + self.MACHINE_COORDINATES()) - self.prev_g0123 = '' - - ############################################################################ - ## CRC - - def use_CRC(self): - return self.useCrc - - def CRC_nominal_path(self): - return self.useCrcCenterline - - def start_CRC(self, left = True, radius = 0.0): - # set up prep code, to be output on next line - if self.t == None: - raise "No tool specified for start_CRC()" - if left: - self.write(self.SPACE() + 'G41') - else: - self.write(self.SPACE() + 'G42') - self.write((self.SPACE() + 'D%i\n') % self.t) - - def end_CRC(self): - self.write(self.SPACE() + 'G40\n') - - ############################################################################ - ## Cycles - - def pattern(self): - pass - - def pattern_uses_subroutine(self): - return self.pattern_done_with_subroutine - - def pocket(self): - pass - - def profile(self): - pass - - def write_internal_coolant_commands(self, internal_coolant_on): - if (internal_coolant_on != None) and (self.output_internal_coolant_commands == True): - if internal_coolant_on == True: - if self.internal_coolant_on != True: - self.write(self.SPACE()) - self.write(self.INTERNAL_COOLANT_ON() + '\n') - self.internal_coolant_on = True - else: - if self.internal_coolant_on != False: - self.write(self.SPACE()) - self.write(self.INTERNAL_COOLANT_OFF() + '\n') - self.internal_coolant_on = False - - # The drill routine supports drilling (G81), drilling with dwell (G82) and peck drilling (G83). - # The x,y,z values are INITIAL locations (above the hole to be made. This is in contrast to - # the Z value used in the G8[1-3] cycles where the Z value is that of the BOTTOM of the hole. - # Instead, this routine combines the Z value and the depth value to determine the bottom of - # the hole. - # - # The standoff value is the distance up from the 'z' value (normally just above the surface) where the bit retracts - # to in order to clear the swarf. This combines with 'z' to form the 'R' value in the G8[1-3] cycles. - # - # The peck_depth value is the incremental depth (Q value) that tells the peck drilling - # cycle how deep to go on each peck until the full depth is achieved. - # - # NOTE: This routine forces the mode to absolute mode so that the values passed into - # the G8[1-3] cycles make sense. I don't know how to find the mode to revert it so I won't - # revert it. I must set the mode so that I can be sure the values I'm passing in make - # sense to the end-machine. - # - def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): - if (depthparams.clearance_height == None): - self.first_drill_pos = False - return - - self.write_internal_coolant_commands(internal_coolant_on) - - drillExpanded = self.drillExpanded - if (depthparams.step_down != 0) and (dwell != 0): - # pecking and dwell together - if self.dwell_allowed_in_G83 != True: - drillExpanded = True - - if drillExpanded: - # for machines which don't understand G81, G82 etc. - peck_depth = depthparams.step_down - if peck_depth == None: - peck_depth = depthparams.final_depth - current_z = depthparams.start_depth - self.rapid(x, y) - - first = True - last_cut = False - - while True: - next_z = current_z - peck_depth - if next_z < (depthparams.final_depth + 0.001): - next_z = depthparams.final_depth - last_cut = True - if next_z >= current_z: - break; - if first: - self.rapid(z = depthparams.start_depth + depthparams.rapid_safety_space) - else: - self.rapid(z = current_z) - self.feed(z = next_z) - if dwell != 0 and last_cut: - self.dwell(dwell) - if last_cut:self.rapid(z = depthparams.clearance_height) - else: - if rapid_to_clearance: - self.rapid(z = depthparams.clearance_height) - else: - self.rapid(z = depthparams.start_depth + depthparams.rapid_safety_space) - current_z = next_z - first = False - - self.first_drill_pos = False - return - - if self.output_g98_and_g99 == True: - if rapid_to_clearance == True: - if self.output_g43_z_before_drilling_if_g98: - if self.fmt.string(depthparams.clearance_height) != self.z_for_g43: - self.z_for_g43 = self.fmt.string(depthparams.clearance_height) - self.write(self.SPACE() + 'G43' + self.SPACE() + 'Z' + self.z_for_g43 + '\n') - - if self.first_drill_pos ==True and rapid_to_clearance == True: - self.rapid(x, y) - self.rapid(z = depthparams.clearance_height) - - self.in_canned_cycle = True - self.write_preps() - - if (depthparams.step_down != 0): - # G83 peck drilling - if self.drill_modal: - if self.PECK_DRILL() + self.PECK_DEPTH(depthparams.step_down) != self.prev_drill: - self.write(self.SPACE() + self.PECK_DRILL() + self.SPACE() + self.PECK_DEPTH(depthparams.step_down)) - self.prev_drill = self.PECK_DRILL() + self.PECK_DEPTH(depthparams.step_down) - else: - self.write(self.PECK_DRILL() + self.PECK_DEPTH(depthparams.step_down)) - - if (self.dwell != 0) and self.dwell_allowed_in_G83: - self.write(self.SPACE() + self.TIME() + (self.FORMAT_TIME().string(dwell))) - - else: - # We're either just drilling or drilling with dwell. - if (dwell == 0): - # We're just drilling. - if self.drill_modal: - if self.DRILL() != self.prev_drill: - self.write(self.SPACE() + self.DRILL()) - self.prev_drill = self.DRILL() - else: - self.write(self.SPACE() + self.DRILL()) - - else: - # We're drilling with dwell. - - if self.drill_modal: - if self.DRILL_WITH_DWELL(dwell) != self.prev_drill: - self.write(self.SPACE() + self.DRILL_WITH_DWELL(dwell)) - self.prev_drill = self.DRILL_WITH_DWELL(dwell) - else: - self.write(self.SPACE() + self.DRILL_WITH_DWELL(dwell)) - - if self.output_g98_and_g99 == True: - if rapid_to_clearance == True: - if self.g98_not_g99 != True: - self.write(self.SPACE() + self.RETRACT_TO_CLEARANCE()) - self.g98_not_g99 = True - else: - if self.g98_not_g99 != False: - self.write(self.SPACE() + self.RETRACT_TO_STANDOFF()) - self.g98_not_g99 = False - - # Set the retraction point to the 'standoff' distance above the starting z height. - retract_height = depthparams.start_depth + depthparams.rapid_safety_space - if (x != None): - self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) - self.x = x - - if (y != None): - self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) - self.y = y - - if self.drill_modal: - if depthparams.start_depth != self.prev_z: - self.write(self.SPACE() + self.Z() + (self.fmt.string(depthparams.final_depth))) - self.prev_z=depthparams.start_depth - else: - self.write(self.SPACE() + self.Z() + (self.fmt.string(depthparams.final_depth))) # This is the 'z' value for the bottom of the hole. - self.z = (depthparams.start_depth + depthparams.rapid_safety_space) # We want to remember where z is at the end (at the top of the hole) - - if self.drill_modal: - if self.prev_retract != self.RETRACT(retract_height) : - self.write(self.SPACE() + self.RETRACT(retract_height)) - self.prev_retract = self.RETRACT(retract_height) - else: - self.write(self.SPACE() + self.RETRACT(retract_height)) - - if (self.fv) : - self.f.set(self.fv) - - self.write_feedrate() - self.write_spindle() - self.write_misc() - self.write('\n') - self.first_drill_pos = False - - def end_canned_cycle(self): - if self.in_canned_cycle == False: - return - self.write(self.SPACE() + self.END_CANNED_CYCLE() + '\n') - self.write_internal_coolant_commands(0) - self.prev_drill = '' - self.prev_g0123 = '' - self.prev_z = '' - self.prev_f = '' - self.prev_retract = '' - self.in_canned_cycle = False - self.first_drill_pos = True - - ############################################################################ - ## Misc - - def comment(self, text): - self.write((self.COMMENT(text) + '\n')) - - def insert(self, text): - pass - - def block_delete(self, on=False): - pass - - def variable(self, id): - return (self.VARIABLE() % id) - - def variable_set(self, id, value): - self.write(self.SPACE() + (self.VARIABLE() % id) + self.SPACE() + (self.VARIABLE_SET() % value) + '\n') - - # This routine uses the G92 coordinate system offsets to establish a temporary coordinate - # system at the machine's current position. It can then use absolute coordinates relative - # to this position which makes coding easy. It then moves to the 'point along edge' which - # should be above the workpiece but still on one edge. It then backs off from the edge - # to the 'retracted point'. It then plunges down by the depth value specified. It then - # probes back towards the 'destination point'. The probed X,Y location are stored - # into the 'intersection variable' variables. Finally the machine moves back to the - # original location. This is important so that the results of multiple calls to this - # routine may be compared meaningfully. - def probe_single_point(self, point_along_edge_x=None, point_along_edge_y=None, depth=None, retracted_point_x=None, retracted_point_y=None, destination_point_x=None, destination_point_y=None, intersection_variable_x=None, intersection_variable_y=None, probe_offset_x_component=None, probe_offset_y_component=None ): - self.write(self.SPACE() + (self.SET_TEMPORARY_COORDINATE_SYSTEM() + (' X 0 Y 0 Z 0') + ('\t(Temporarily make this the origin)\n'))) - - if (self.fhv) : self.calc_feedrate_hv(1, 0) - self.write_feedrate() - self.write('\t(Set the feed rate for probing)\n') - - self.rapid(point_along_edge_x,point_along_edge_y) - self.rapid(retracted_point_x,retracted_point_y) - self.feed(z=depth) - - self.write((self.PROBE_TOWARDS_WITH_SIGNAL() + (' X ' + (self.fmt.string(destination_point_x)) + ' Y ' + (self.fmt.string(destination_point_y)) ) + ('\t(Probe towards our destination point)\n'))) - - self.comment('Back off the workpiece and re-probe more slowly') - self.write(self.SPACE() + ('#' + intersection_variable_x + '= [#5061 - [ 0.5 * ' + probe_offset_x_component + ']]\n')) - self.write(self.SPACE() + ('#' + intersection_variable_y + '= [#5062 - [ 0.5 * ' + probe_offset_y_component + ']]\n')) - self.write(self.RAPID()) - self.write(self.SPACE() + ' X #' + intersection_variable_x + ' Y #' + intersection_variable_y + '\n') - - self.write(self.SPACE() + self.FEEDRATE() + self.ffmt.string(self.fh / 2.0) + '\n') - - self.write((self.SPACE() + self.PROBE_TOWARDS_WITH_SIGNAL() + (' X ' + (self.fmt.string(destination_point_x)) + ' Y ' + (self.fmt.string(destination_point_y)) ) + ('\t(Probe towards our destination point)\n'))) - - self.comment('Store the probed location somewhere we can get it again later') - self.write(('#' + intersection_variable_x + '=' + probe_offset_x_component + ' (Portion of probe radius that contributes to the X coordinate)\n')) - self.write(('#' + intersection_variable_x + '=[#' + intersection_variable_x + ' + #5061]\n')) - self.write(('#' + intersection_variable_y + '=' + probe_offset_y_component + ' (Portion of probe radius that contributes to the Y coordinate)\n')) - self.write(('#' + intersection_variable_y + '=[#' + intersection_variable_y + ' + #5062]\n')) - - self.comment('Now move back to the original location') - self.rapid(retracted_point_x,retracted_point_y) - self.rapid(z=0) - self.rapid(point_along_edge_x,point_along_edge_y) - self.rapid(x=0, y=0) - - self.write((self.REMOVE_TEMPORARY_COORDINATE_SYSTEM() + ('\t(Restore the previous coordinate system)\n'))) - - def probe_downward_point(self, x=None, y=None, depth=None, intersection_variable_z=None): - self.write((self.SET_TEMPORARY_COORDINATE_SYSTEM() + (' X 0 Y 0 Z 0') + ('\t(Temporarily make this the origin)\n'))) - if (self.fhv) : self.calc_feedrate_hv(1, 0) - self.write(self.FEEDRATE() + ' [' + self.ffmt.string(self.fh) + ' / 5.0 ]') - self.write('\t(Set the feed rate for probing)\n') - - if x != None and y != None: - self.write(self.RAPID()) - self.write(' X ' + x + ' Y ' + y + '\n') - - self.write((self.PROBE_TOWARDS_WITH_SIGNAL() + ' Z ' + (self.fmt.string(depth)) + ('\t(Probe towards our destination point)\n'))) - - self.comment('Store the probed location somewhere we can get it again later') - self.write(('#' + intersection_variable_z + '= #5063\n')) - - self.comment('Now move back to the original location') - self.rapid(z=0) - self.rapid(x=0, y=0) - - self.write((self.REMOVE_TEMPORARY_COORDINATE_SYSTEM() + ('\t(Restore the previous coordinate system)\n'))) - - - def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None ): - pass - - def open_log_file(self, xml_file_name=None ): - pass - - def log_coordinate(self, x=None, y=None, z=None): - pass - - def log_message(self, message=None): - pass - - def close_log_file(self): - pass - - # Rapid movement to the midpoint between the two points specified. - # NOTE: The points are specified either as strings representing numbers or as strings - # representing variable names. This allows the HeeksCNC module to determine which - # variable names are used in these various routines. - def rapid_to_midpoint(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None): - self.write(self.RAPID()) - if ((x1 != None) and (x2 != None)): - self.write((' X ' + '[[[' + x1 + ' - ' + x2 + '] / 2.0] + ' + x2 + ']')) - - if ((y1 != None) and (y2 != None)): - self.write((' Y ' + '[[[' + y1 + ' - ' + y2 + '] / 2.0] + ' + y2 + ']')) - - if ((z1 != None) and (z2 != None)): - self.write((' Z ' + '[[[' + z1 + ' - ' + z2 + '] / 2.0] + ' + z2 + ']')) - - self.write('\n') - - # Rapid movement to the intersection of two lines (in the XY plane only). This routine - # is based on information found in http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ - # written by Paul Bourke. The ua_numerator, ua_denominator, ua and ub parameters - # represent variable names (with the preceding '#' included in them) for use as temporary - # variables. They're specified here simply so that HeeksCNC can manage which variables - # are used in which GCode calculations. - # - # As per the notes on the web page, the ua_denominator and ub_denominator formulae are - # the same so we don't repeat this. If the two lines are coincident or parallel then - # no movement occurs. - # - # NOTE: The points are specified either as strings representing numbers or as strings - # representing variable names. This allows the HeeksCNC module to determine which - # variable names are used in these various routines. - def rapid_to_intersection(self, x1, y1, x2, y2, x3, y3, x4, y4, intersection_x, intersection_y, ua_numerator, ua_denominator, ua, ub_numerator, ub): - self.comment('Find the intersection of the two lines made up by the four probed points') - self.write(ua_numerator + '=[[[' + x4 + ' - ' + x3 + '] * [' + y1 + ' - ' + y3 + ']] - [[' + y4 + ' - ' + y3 + '] * [' + x1 + ' - ' + x3 + ']]]\n') - self.write(ua_denominator + '=[[[' + y4 + ' - ' + y3 + '] * [' + x2 + ' - ' + x1 + ']] - [[' + x4 + ' - ' + x3 + '] * [' + y2 + ' - ' + y1 + ']]]\n') - self.write(ub_numerator + '=[[[' + x2 + ' - ' + x1 + '] * [' + y1 + ' - ' + y3 + ']] - [[' + y2 + ' - ' + y1 + '] * [' + x1 + ' - ' + x3 + ']]]\n') - - self.comment('If they are not parallel') - self.write('O900 IF [' + ua_denominator + ' NE 0]\n') - self.comment('And if they are not coincident') - self.write('O901 IF [' + ua_numerator + ' NE 0 ]\n') - - self.write(' ' + ua + '=[' + ua_numerator + ' / ' + ua_denominator + ']\n') - self.write(' ' + ub + '=[' + ub_numerator + ' / ' + ua_denominator + ']\n') # NOTE: ub denominator is the same as ua denominator - self.write(' ' + intersection_x + '=[' + x1 + ' + [[' + ua + ' * [' + x2 + ' - ' + x1 + ']]]]\n') - self.write(' ' + intersection_y + '=[' + y1 + ' + [[' + ua + ' * [' + y2 + ' - ' + y1 + ']]]]\n') - self.write(' ' + self.RAPID()) - self.write(' X ' + intersection_x + ' Y ' + intersection_y + '\n') - - self.write('O901 ENDIF\n') - self.write('O900 ENDIF\n') - - # We need to calculate the rotation angle based on the line formed by the - # x1,y1 and x2,y2 coordinate pair. With that angle, we need to move - # x_offset and y_offset distance from the current (0,0,0) position. - # - # The x1,y1,x2 and y2 parameters are all variable names that contain the actual - # values. - # The x_offset and y_offset are both numeric (floating point) values - def rapid_to_rotated_coordinate(self, x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final): - self.comment('Rapid to rotated coordinate') - self.write( '#1 = [atan[' + y2 + ' - ' + y1 + ']/[' + x2 +' - ' + x1 + ']] (nominal_angle)\n') - self.write( '#2 = [atan[' + ref_y + ']/[' + ref_x + ']] (reference angle)\n') - self.write( '#3 = [#1 - #2] (angle)\n' ) - self.write( '#4 = [[[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(x_current)) + '] * COS[ #3 ]] - [[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(y_current)) + '] * SIN[ #3 ]]]\n' ) - self.write( '#5 = [[[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(x_current)) + '] * SIN[ #3 ]] + [[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(y_current)) + '] * COS[ #3 ]]]\n' ) - - self.write( '#6 = [[' + (self.fmt.string(x_final)) + ' * COS[ #3 ]] - [' + (self.fmt.string(y_final)) + ' * SIN[ #3 ]]]\n' ) - self.write( '#7 = [[' + (self.fmt.string(y_final)) + ' * SIN[ #3 ]] + [' + (self.fmt.string(y_final)) + ' * COS[ #3 ]]]\n' ) - - self.write( self.RAPID() + ' X [ #4 + #6 ] Y [ #5 + #7 ]\n' ) - - def BEST_POSSIBLE_SPEED(self, motion_blending_tolerance, naive_cam_tolerance): - statement = 'G64' - - if (motion_blending_tolerance > 0): - statement += ' P ' + str(motion_blending_tolerance) - - if (naive_cam_tolerance > 0): - statement += ' Q ' + str(naive_cam_tolerance) - - return(statement) - - def set_path_control_mode(self, mode, motion_blending_tolerance, naive_cam_tolerance ): - if (mode == 0): - self.write( self.EXACT_PATH_MODE() + '\n' ) - if (mode == 1): - self.write( self.EXACT_STOP_MODE() + '\n' ) - if (mode == 2): - self.write( self.BEST_POSSIBLE_SPEED( motion_blending_tolerance, naive_cam_tolerance ) + '\n' ) + if (self.fhv): + self.calc_feedrate_hv(1, 0) + self.write_feedrate() + self.write_spindle() + self.write_misc() + self.write('\n') + if (x != None): + self.x = x + if (y != None): + self.y = y + if (z != None): + self.z = z + + def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(True, x, y, z, i, j, k, r) + + def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + self.arc(False, x, y, z, i, j, k, r) + + def dwell(self, t): + self.write_preps() + self.write(self.SPACE() + self.DWELL(t)) + self.write_misc() + self.write('\n') + + def on_move(self): + if self.output_fixtures: + self.output_fixture() + self.move_done_since_tool_change = True + + def rapid_home(self, x=None, y=None, z=None, a=None, b=None, c=None): + pass + + def rapid_unhome(self): + pass + + def set_machine_coordinates(self): + self.write(self.SPACE() + self.MACHINE_COORDINATES()) + self.prev_g0123 = '' + + ############################################################################ + # CRC + + def use_CRC(self): + return self.useCrc + + def CRC_nominal_path(self): + return self.useCrcCenterline + + def start_CRC(self, left=True, radius=0.0): + # set up prep code, to be output on next line + if self.t == None: + raise "No tool specified for start_CRC()" + if left: + self.write(self.SPACE() + 'G41') + else: + self.write(self.SPACE() + 'G42') + self.write((self.SPACE() + 'D%i\n') % self.t) + + def end_CRC(self): + self.write(self.SPACE() + 'G40\n') + + ############################################################################ + # Cycles + + def pattern(self): + pass + + def pattern_uses_subroutine(self): + return self.pattern_done_with_subroutine + + def pocket(self): + pass + + def profile(self): + pass + + def write_internal_coolant_commands(self, internal_coolant_on): + if (internal_coolant_on != None) and (self.output_internal_coolant_commands == True): + if internal_coolant_on == True: + if self.internal_coolant_on != True: + self.write(self.SPACE()) + self.write(self.INTERNAL_COOLANT_ON() + '\n') + self.internal_coolant_on = True + else: + if self.internal_coolant_on != False: + self.write(self.SPACE()) + self.write(self.INTERNAL_COOLANT_OFF() + '\n') + self.internal_coolant_on = False + + # The drill routine supports drilling (G81), drilling with dwell (G82) and peck drilling (G83). + # The x,y,z values are INITIAL locations (above the hole to be made. This is in contrast to + # the Z value used in the G8[1-3] cycles where the Z value is that of the BOTTOM of the hole. + # Instead, this routine combines the Z value and the depth value to determine the bottom of + # the hole. + # + # The standoff value is the distance up from the 'z' value (normally just above the surface) where the bit retracts + # to in order to clear the swarf. This combines with 'z' to form the 'R' value in the G8[1-3] cycles. + # + # The peck_depth value is the incremental depth (Q value) that tells the peck drilling + # cycle how deep to go on each peck until the full depth is achieved. + # + # NOTE: This routine forces the mode to absolute mode so that the values passed into + # the G8[1-3] cycles make sense. I don't know how to find the mode to revert it so I won't + # revert it. I must set the mode so that I can be sure the values I'm passing in make + # sense to the end-machine. + # + def drill(self, x=None, y=None, dwell=None, depthparams=None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): + if (depthparams.clearance_height == None): + self.first_drill_pos = False + return + + self.write_internal_coolant_commands(internal_coolant_on) + + drillExpanded = self.drillExpanded + if (depthparams.step_down != 0) and (dwell != 0): + # pecking and dwell together + if self.dwell_allowed_in_G83 != True: + drillExpanded = True + + if drillExpanded: + # for machines which don't understand G81, G82 etc. + peck_depth = depthparams.step_down + if peck_depth == None: + peck_depth = depthparams.final_depth + current_z = depthparams.start_depth + self.rapid(x, y) + + first = True + last_cut = False + + while True: + next_z = current_z - peck_depth + if next_z < (depthparams.final_depth + 0.001): + next_z = depthparams.final_depth + last_cut = True + if next_z >= current_z: + break + if first: + self.rapid(z=depthparams.start_depth + depthparams.rapid_safety_space) + else: + self.rapid(z=current_z) + self.feed(z=next_z) + if dwell != 0 and last_cut: + self.dwell(dwell) + if last_cut: + self.rapid(z=depthparams.clearance_height) + else: + if rapid_to_clearance: + self.rapid(z=depthparams.clearance_height) + else: + self.rapid(z=depthparams.start_depth + depthparams.rapid_safety_space) + current_z = next_z + first = False + + self.first_drill_pos = False + return + + if self.output_g98_and_g99 == True: + if rapid_to_clearance == True: + if self.output_g43_z_before_drilling_if_g98: + if self.fmt.string(depthparams.clearance_height) != self.z_for_g43: + self.z_for_g43 = self.fmt.string(depthparams.clearance_height) + self.write(self.SPACE() + 'G43' + self.SPACE() + + 'Z' + self.z_for_g43 + '\n') + + if self.first_drill_pos == True and rapid_to_clearance == True: + self.rapid(x, y) + self.rapid(z=depthparams.clearance_height) + + self.in_canned_cycle = True + self.write_preps() + + if (depthparams.step_down != 0): + # G83 peck drilling + if self.drill_modal: + if self.PECK_DRILL() + self.PECK_DEPTH(depthparams.step_down) != self.prev_drill: + self.write(self.SPACE() + self.PECK_DRILL() + self.SPACE() + + self.PECK_DEPTH(depthparams.step_down)) + self.prev_drill = self.PECK_DRILL() + self.PECK_DEPTH(depthparams.step_down) + else: + self.write(self.PECK_DRILL() + self.PECK_DEPTH(depthparams.step_down)) + + if (self.dwell != 0) and self.dwell_allowed_in_G83: + self.write(self.SPACE() + self.TIME() + (self.FORMAT_TIME().string(dwell))) + + else: + # We're either just drilling or drilling with dwell. + if (dwell == 0): + # We're just drilling. + if self.drill_modal: + if self.DRILL() != self.prev_drill: + self.write(self.SPACE() + self.DRILL()) + self.prev_drill = self.DRILL() + else: + self.write(self.SPACE() + self.DRILL()) + + else: + # We're drilling with dwell. + + if self.drill_modal: + if self.DRILL_WITH_DWELL(dwell) != self.prev_drill: + self.write(self.SPACE() + self.DRILL_WITH_DWELL(dwell)) + self.prev_drill = self.DRILL_WITH_DWELL(dwell) + else: + self.write(self.SPACE() + self.DRILL_WITH_DWELL(dwell)) + + if self.output_g98_and_g99 == True: + if rapid_to_clearance == True: + if self.g98_not_g99 != True: + self.write(self.SPACE() + self.RETRACT_TO_CLEARANCE()) + self.g98_not_g99 = True + else: + if self.g98_not_g99 != False: + self.write(self.SPACE() + self.RETRACT_TO_STANDOFF()) + self.g98_not_g99 = False + + # Set the retraction point to the 'standoff' distance above the starting z height. + retract_height = depthparams.start_depth + depthparams.rapid_safety_space + if (x != None): + self.write(self.SPACE() + self.X() + (self.fmt.string(x + self.shift_x))) + self.x = x + + if (y != None): + self.write(self.SPACE() + self.Y() + (self.fmt.string(y + self.shift_y))) + self.y = y + + if self.drill_modal: + if depthparams.start_depth != self.prev_z: + self.write(self.SPACE() + self.Z() + (self.fmt.string(depthparams.final_depth))) + self.prev_z = depthparams.start_depth + else: + # This is the 'z' value for the bottom of the hole. + self.write(self.SPACE() + self.Z() + (self.fmt.string(depthparams.final_depth))) + # We want to remember where z is at the end (at the top of the hole) + self.z = (depthparams.start_depth + depthparams.rapid_safety_space) + + if self.drill_modal: + if self.prev_retract != self.RETRACT(retract_height): + self.write(self.SPACE() + self.RETRACT(retract_height)) + self.prev_retract = self.RETRACT(retract_height) + else: + self.write(self.SPACE() + self.RETRACT(retract_height)) + + if (self.fv): + self.f.set(self.fv) + + self.write_feedrate() + self.write_spindle() + self.write_misc() + self.write('\n') + self.first_drill_pos = False + + def end_canned_cycle(self): + if self.in_canned_cycle == False: + return + self.write(self.SPACE() + self.END_CANNED_CYCLE() + '\n') + self.write_internal_coolant_commands(0) + self.prev_drill = '' + self.prev_g0123 = '' + self.prev_z = '' + self.prev_f = '' + self.prev_retract = '' + self.in_canned_cycle = False + self.first_drill_pos = True + + ############################################################################ + # Misc + + def comment(self, text): + self.write((self.COMMENT(text) + '\n')) + + def insert(self, text): + pass + + def block_delete(self, on=False): + pass + + def variable(self, id): + return (self.VARIABLE() % id) + + def variable_set(self, id, value): + self.write(self.SPACE() + (self.VARIABLE() % id) + + self.SPACE() + (self.VARIABLE_SET() % value) + '\n') + + # This routine uses the G92 coordinate system offsets to establish a temporary coordinate + # system at the machine's current position. It can then use absolute coordinates relative + # to this position which makes coding easy. It then moves to the 'point along edge' which + # should be above the workpiece but still on one edge. It then backs off from the edge + # to the 'retracted point'. It then plunges down by the depth value specified. It then + # probes back towards the 'destination point'. The probed X,Y location are stored + # into the 'intersection variable' variables. Finally the machine moves back to the + # original location. This is important so that the results of multiple calls to this + # routine may be compared meaningfully. + def probe_single_point(self, point_along_edge_x=None, point_along_edge_y=None, depth=None, retracted_point_x=None, retracted_point_y=None, destination_point_x=None, destination_point_y=None, intersection_variable_x=None, intersection_variable_y=None, probe_offset_x_component=None, probe_offset_y_component=None): + self.write(self.SPACE() + (self.SET_TEMPORARY_COORDINATE_SYSTEM() + + (' X 0 Y 0 Z 0') + ('\t(Temporarily make this the origin)\n'))) + + if (self.fhv): + self.calc_feedrate_hv(1, 0) + self.write_feedrate() + self.write('\t(Set the feed rate for probing)\n') + + self.rapid(point_along_edge_x, point_along_edge_y) + self.rapid(retracted_point_x, retracted_point_y) + self.feed(z=depth) + + self.write((self.PROBE_TOWARDS_WITH_SIGNAL() + (' X ' + (self.fmt.string(destination_point_x)) + + ' Y ' + (self.fmt.string(destination_point_y))) + ('\t(Probe towards our destination point)\n'))) + + self.comment('Back off the workpiece and re-probe more slowly') + self.write(self.SPACE() + ('#' + intersection_variable_x + + '= [#5061 - [ 0.5 * ' + probe_offset_x_component + ']]\n')) + self.write(self.SPACE() + ('#' + intersection_variable_y + + '= [#5062 - [ 0.5 * ' + probe_offset_y_component + ']]\n')) + self.write(self.RAPID()) + self.write(self.SPACE() + ' X #' + intersection_variable_x + + ' Y #' + intersection_variable_y + '\n') + + self.write(self.SPACE() + self.FEEDRATE() + self.ffmt.string(self.fh / 2.0) + '\n') + + self.write((self.SPACE() + self.PROBE_TOWARDS_WITH_SIGNAL() + (' X ' + (self.fmt.string(destination_point_x) + ) + ' Y ' + (self.fmt.string(destination_point_y))) + ('\t(Probe towards our destination point)\n'))) + + self.comment('Store the probed location somewhere we can get it again later') + self.write(('#' + intersection_variable_x + '=' + probe_offset_x_component + + ' (Portion of probe radius that contributes to the X coordinate)\n')) + self.write(('#' + intersection_variable_x + + '=[#' + intersection_variable_x + ' + #5061]\n')) + self.write(('#' + intersection_variable_y + '=' + probe_offset_y_component + + ' (Portion of probe radius that contributes to the Y coordinate)\n')) + self.write(('#' + intersection_variable_y + + '=[#' + intersection_variable_y + ' + #5062]\n')) + + self.comment('Now move back to the original location') + self.rapid(retracted_point_x, retracted_point_y) + self.rapid(z=0) + self.rapid(point_along_edge_x, point_along_edge_y) + self.rapid(x=0, y=0) + + self.write((self.REMOVE_TEMPORARY_COORDINATE_SYSTEM() + + ('\t(Restore the previous coordinate system)\n'))) + + def probe_downward_point(self, x=None, y=None, depth=None, intersection_variable_z=None): + self.write((self.SET_TEMPORARY_COORDINATE_SYSTEM() + (' X 0 Y 0 Z 0') + + ('\t(Temporarily make this the origin)\n'))) + if (self.fhv): + self.calc_feedrate_hv(1, 0) + self.write(self.FEEDRATE() + ' [' + self.ffmt.string(self.fh) + ' / 5.0 ]') + self.write('\t(Set the feed rate for probing)\n') + + if x != None and y != None: + self.write(self.RAPID()) + self.write(' X ' + x + ' Y ' + y + '\n') + + self.write((self.PROBE_TOWARDS_WITH_SIGNAL() + ' Z ' + + (self.fmt.string(depth)) + ('\t(Probe towards our destination point)\n'))) + + self.comment('Store the probed location somewhere we can get it again later') + self.write(('#' + intersection_variable_z + '= #5063\n')) + + self.comment('Now move back to the original location') + self.rapid(z=0) + self.rapid(x=0, y=0) + + self.write((self.REMOVE_TEMPORARY_COORDINATE_SYSTEM() + + ('\t(Restore the previous coordinate system)\n'))) + + def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None): + pass + + def open_log_file(self, xml_file_name=None): + pass + + def log_coordinate(self, x=None, y=None, z=None): + pass + + def log_message(self, message=None): + pass + + def close_log_file(self): + pass + + # Rapid movement to the midpoint between the two points specified. + # NOTE: The points are specified either as strings representing numbers or as strings + # representing variable names. This allows the HeeksCNC module to determine which + # variable names are used in these various routines. + def rapid_to_midpoint(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None): + self.write(self.RAPID()) + if ((x1 != None) and (x2 != None)): + self.write((' X ' + '[[[' + x1 + ' - ' + x2 + '] / 2.0] + ' + x2 + ']')) + + if ((y1 != None) and (y2 != None)): + self.write((' Y ' + '[[[' + y1 + ' - ' + y2 + '] / 2.0] + ' + y2 + ']')) + + if ((z1 != None) and (z2 != None)): + self.write((' Z ' + '[[[' + z1 + ' - ' + z2 + '] / 2.0] + ' + z2 + ']')) + + self.write('\n') + + # Rapid movement to the intersection of two lines (in the XY plane only). This routine + # is based on information found in http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ + # written by Paul Bourke. The ua_numerator, ua_denominator, ua and ub parameters + # represent variable names (with the preceding '#' included in them) for use as temporary + # variables. They're specified here simply so that HeeksCNC can manage which variables + # are used in which GCode calculations. + # + # As per the notes on the web page, the ua_denominator and ub_denominator formulae are + # the same so we don't repeat this. If the two lines are coincident or parallel then + # no movement occurs. + # + # NOTE: The points are specified either as strings representing numbers or as strings + # representing variable names. This allows the HeeksCNC module to determine which + # variable names are used in these various routines. + def rapid_to_intersection(self, x1, y1, x2, y2, x3, y3, x4, y4, intersection_x, intersection_y, ua_numerator, ua_denominator, ua, ub_numerator, ub): + self.comment('Find the intersection of the two lines made up by the four probed points') + self.write(ua_numerator + '=[[[' + x4 + ' - ' + x3 + '] * [' + y1 + ' - ' + + y3 + ']] - [[' + y4 + ' - ' + y3 + '] * [' + x1 + ' - ' + x3 + ']]]\n') + self.write(ua_denominator + '=[[[' + y4 + ' - ' + y3 + '] * [' + x2 + ' - ' + + x1 + ']] - [[' + x4 + ' - ' + x3 + '] * [' + y2 + ' - ' + y1 + ']]]\n') + self.write(ub_numerator + '=[[[' + x2 + ' - ' + x1 + '] * [' + y1 + ' - ' + + y3 + ']] - [[' + y2 + ' - ' + y1 + '] * [' + x1 + ' - ' + x3 + ']]]\n') + + self.comment('If they are not parallel') + self.write('O900 IF [' + ua_denominator + ' NE 0]\n') + self.comment('And if they are not coincident') + self.write('O901 IF [' + ua_numerator + ' NE 0 ]\n') + + self.write(' ' + ua + '=[' + ua_numerator + ' / ' + ua_denominator + ']\n') + # NOTE: ub denominator is the same as ua denominator + self.write(' ' + ub + '=[' + ub_numerator + ' / ' + ua_denominator + ']\n') + self.write(' ' + intersection_x + + '=[' + x1 + ' + [[' + ua + ' * [' + x2 + ' - ' + x1 + ']]]]\n') + self.write(' ' + intersection_y + + '=[' + y1 + ' + [[' + ua + ' * [' + y2 + ' - ' + y1 + ']]]]\n') + self.write(' ' + self.RAPID()) + self.write(' X ' + intersection_x + ' Y ' + intersection_y + '\n') + + self.write('O901 ENDIF\n') + self.write('O900 ENDIF\n') + + # We need to calculate the rotation angle based on the line formed by the + # x1,y1 and x2,y2 coordinate pair. With that angle, we need to move + # x_offset and y_offset distance from the current (0,0,0) position. + # + # The x1,y1,x2 and y2 parameters are all variable names that contain the actual + # values. + # The x_offset and y_offset are both numeric (floating point) values + def rapid_to_rotated_coordinate(self, x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final): + self.comment('Rapid to rotated coordinate') + self.write('#1 = [atan[' + y2 + ' - ' + y1 + ']/[' + + x2 + ' - ' + x1 + ']] (nominal_angle)\n') + self.write('#2 = [atan[' + ref_y + ']/[' + ref_x + ']] (reference angle)\n') + self.write('#3 = [#1 - #2] (angle)\n') + self.write('#4 = [[[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(x_current)) + + '] * COS[ #3 ]] - [[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(y_current)) + '] * SIN[ #3 ]]]\n') + self.write('#5 = [[[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(x_current)) + + '] * SIN[ #3 ]] + [[' + (self.fmt.string(0)) + ' - ' + (self.fmt.string(y_current)) + '] * COS[ #3 ]]]\n') + + self.write('#6 = [[' + (self.fmt.string(x_final)) + ' * COS[ #3 ]] - [' + + (self.fmt.string(y_final)) + ' * SIN[ #3 ]]]\n') + self.write('#7 = [[' + (self.fmt.string(y_final)) + ' * SIN[ #3 ]] + [' + + (self.fmt.string(y_final)) + ' * COS[ #3 ]]]\n') + + self.write(self.RAPID() + ' X [ #4 + #6 ] Y [ #5 + #7 ]\n') + + def BEST_POSSIBLE_SPEED(self, motion_blending_tolerance, naive_cam_tolerance): + statement = 'G64' + + if (motion_blending_tolerance > 0): + statement += ' P ' + str(motion_blending_tolerance) + + if (naive_cam_tolerance > 0): + statement += ' Q ' + str(naive_cam_tolerance) + + return(statement) + + def set_path_control_mode(self, mode, motion_blending_tolerance, naive_cam_tolerance): + if (mode == 0): + self.write(self.EXACT_PATH_MODE() + '\n') + if (mode == 1): + self.write(self.EXACT_STOP_MODE() + '\n') + if (mode == 2): + self.write(self.BEST_POSSIBLE_SPEED( + motion_blending_tolerance, naive_cam_tolerance) + '\n') ################################################################################ diff --git a/scripts/addons/cam/nc/iso_codes.py b/scripts/addons/cam/nc/iso_codes.py index 3c17f37a1..1cba92e10 100644 --- a/scripts/addons/cam/nc/iso_codes.py +++ b/scripts/addons/cam/nc/iso_codes.py @@ -1,4 +1,5 @@ class Codes(): - pass + pass + codes = Codes() diff --git a/scripts/addons/cam/nc/iso_crc.py b/scripts/addons/cam/nc/iso_crc.py index 312b00e11..61144d3ec 100644 --- a/scripts/addons/cam/nc/iso_crc.py +++ b/scripts/addons/cam/nc/iso_crc.py @@ -10,6 +10,8 @@ import math ################################################################################ + + class Creator(iso.Creator): def __init__(self): @@ -18,4 +20,5 @@ def __init__(self): ################################################################################ + nc.creator = Creator() diff --git a/scripts/addons/cam/nc/iso_crc_read.py b/scripts/addons/cam/nc/iso_crc_read.py index 075c24e09..2ca0b6f50 100644 --- a/scripts/addons/cam/nc/iso_crc_read.py +++ b/scripts/addons/cam/nc/iso_crc_read.py @@ -2,6 +2,8 @@ import sys # just use the iso reader + + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/iso_modal.py b/scripts/addons/cam/nc/iso_modal.py index 98508791f..ffdde3a97 100644 --- a/scripts/addons/cam/nc/iso_modal.py +++ b/scripts/addons/cam/nc/iso_modal.py @@ -10,6 +10,8 @@ import math ################################################################################ + + class Creator(iso.Creator): def __init__(self): @@ -19,4 +21,5 @@ def __init__(self): self.drill_modal = True ################################################################################ + nc.creator = Creator() diff --git a/scripts/addons/cam/nc/iso_modal_read.py b/scripts/addons/cam/nc/iso_modal_read.py index 075c24e09..2ca0b6f50 100644 --- a/scripts/addons/cam/nc/iso_modal_read.py +++ b/scripts/addons/cam/nc/iso_modal_read.py @@ -2,6 +2,8 @@ import sys # just use the iso reader + + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/iso_read.py b/scripts/addons/cam/nc/iso_read.py index a923bcd88..9e729a657 100644 --- a/scripts/addons/cam/nc/iso_read.py +++ b/scripts/addons/cam/nc/iso_read.py @@ -10,20 +10,23 @@ import sys ################################################################################ + + class Parser(nc.Parser): def __init__(self, writer): nc.Parser.__init__(self, writer) - self.pattern_main = re.compile('([(!;].*|\s+|[a-zA-Z0-9_:](?:[+-])?\d*(?:\.\d*)?|\w\#\d+|\(.*?\)|\#\d+\=(?:[+-])?\d*(?:\.\d*)?)') + self.pattern_main = re.compile( + '([(!;].*|\s+|[a-zA-Z0-9_:](?:[+-])?\d*(?:\.\d*)?|\w\#\d+|\(.*?\)|\#\d+\=(?:[+-])?\d*(?:\.\d*)?)') self.arc_centre_absolute = False self.arc_centre_positive = False self.oldx = None self.oldy = None self.oldz = None - #if ( or ! or ; at least one space or a letter followed by some character or not followed by a +/- followed by decimal, with a possible decimal point - # followed by a possible deimcal, or a letter followed by # with a decimal . deimcal + # if ( or ! or ; at least one space or a letter followed by some character or not followed by a +/- followed by decimal, with a possible decimal point + # followed by a possible deimcal, or a letter followed by # with a decimal . deimcal # add your character here > [(!;] for comments char # then look for the 'comment' function towards the end of the file and add another elif @@ -86,7 +89,7 @@ def ParseWord(self, word): self.path_col = "feed" self.col = "feed" elif (word == 'G82' or word == 'g82'): - self.drill = True; + self.drill = True self.no_move = True self.path_col = "feed" self.col = "feed" @@ -99,7 +102,8 @@ def ParseWord(self, word): self.absolute() elif (word == 'G91' or word == 'g91'): self.incremental() - elif (word[0] == 'G') : col = "prep" + elif (word[0] == 'G'): + col = "prep" elif (word[0] == 'I' or word[0] == 'i'): self.col = "axis" self.i = eval(word[1:]) @@ -112,19 +116,22 @@ def ParseWord(self, word): self.col = "axis" self.k = eval(word[1:]) self.move = True - elif (word[0] == 'M') : self.col = "misc" - elif (word[0] == 'N') : self.col = "blocknum" - elif (word[0] == 'O') : self.col = "program" + elif (word[0] == 'M'): + self.col = "misc" + elif (word[0] == 'N'): + self.col = "blocknum" + elif (word[0] == 'O'): + self.col = "program" elif (word[0] == 'P' or word[0] == 'p'): - if (self.no_move != True): - self.col = "axis" - self.p = eval(word[1:]) - self.move = True + if (self.no_move != True): + self.col = "axis" + self.p = eval(word[1:]) + self.move = True elif (word[0] == 'Q' or word[0] == 'q'): - if (self.no_move != True): - self.col = "axis" - self.q = eval(word[1:]) - self.move = True + if (self.no_move != True): + self.col = "axis" + self.q = eval(word[1:]) + self.move = True elif (word[0] == 'R' or word[0] == 'r'): self.col = "axis" self.r = eval(word[1:]) @@ -132,9 +139,9 @@ def ParseWord(self, word): elif (word[0] == 'S' or word[0] == 's'): self.col = "axis" self.writer.spindle(word[1:], (float(word[1:]) >= 0.0)) - elif (word[0] == 'T') : + elif (word[0] == 'T'): self.col = "tool" - self.writer.tool_change( eval(word[1:]) ) + self.writer.tool_change(eval(word[1:])) elif (word[0] == 'X' or word[0] == 'x'): self.col = "axis" self.x = eval(word[1:]) @@ -147,9 +154,15 @@ def ParseWord(self, word): self.col = "axis" self.z = eval(word[1:]) self.move = True - elif (word[0] == '(') : (self.col, self.cdata) = ("comment", True) - elif (word[0] == '!') : (self.col, self.cdata) = ("comment", True) - elif (word[0] == ';') : (self.col, self.cdata) = ("comment", True) - elif (word[0] == '#') : self.col = "variable" - elif (word[0] == ':') : self.col = "blocknum" - elif (ord(word[0]) <= 32) : self.cdata = True + elif (word[0] == '('): + (self.col, self.cdata) = ("comment", True) + elif (word[0] == '!'): + (self.col, self.cdata) = ("comment", True) + elif (word[0] == ';'): + (self.col, self.cdata) = ("comment", True) + elif (word[0] == '#'): + self.col = "variable" + elif (word[0] == ':'): + self.col = "blocknum" + elif (ord(word[0]) <= 32): + self.cdata = True diff --git a/scripts/addons/cam/nc/lathe1.py b/scripts/addons/cam/nc/lathe1.py index adec62097..2d3b0e2c8 100644 --- a/scripts/addons/cam/nc/lathe1.py +++ b/scripts/addons/cam/nc/lathe1.py @@ -10,6 +10,8 @@ import math ################################################################################ + + class CreatorIso(nc.Creator): def __init__(self): @@ -48,7 +50,7 @@ def __init__(self): self.absolute_flag = True self.ffmt = iso.codes.FORMAT_FEEDRATE() ############################################################################ - ## Internals + # Internals def write_feedrate(self): if self.f_modal: @@ -64,39 +66,40 @@ def write_preps(self): self.g = '' def write_misc(self): - if (len(self.m)) : self.write(self.m.pop()) + if (len(self.m)): + self.write(self.m.pop()) def write_spindle(self): self.write(self.s) self.s = '' ############################################################################ - ## Programs + # Programs def program_begin(self, id, name=''): self.write((iso.codes.PROGRAM() % id) + iso.codes.SPACE() + (iso.codes.COMMENT(name))) self.write('\n') def program_stop(self, optional=False): - if (optional) : + if (optional): self.write(iso.codes.STOP_OPTIONAL() + '\n') self.prev_g0123 = '' - else : + else: self.write(iso.codes.STOP() + '\n') self.prev_g0123 = '' - def program_end(self): self.write(iso.codes.PROGRAM_END() + '\n') def flush_nc(self): - if len(self.g) == 0 and len(self.m) == 0: return + if len(self.g) == 0 and len(self.m) == 0: + return self.write_preps() self.write_misc() self.write('\n') ############################################################################ - ## Subprograms + # Subprograms def sub_begin(self, id, name=''): self.write((iso.codes.PROGRAM() % id) + iso.codes.SPACE() + (iso.codes.COMMENT(name))) @@ -109,7 +112,7 @@ def sub_end(self): self.write(iso.codes.SUBPROG_END() + '\n') ############################################################################ - ## Settings + # Settings def imperial(self): self.g += iso.codes.IMPERIAL() @@ -128,22 +131,33 @@ def incremental(self): self.absolute_flag = False def polar(self, on=True): - if (on) : self.g += iso.codes.POLAR_ON() - else : self.g += iso.codes.POLAR_OFF() + if (on): + self.g += iso.codes.POLAR_ON() + else: + self.g += iso.codes.POLAR_OFF() def set_plane(self, plane): - if (plane == 0) : self.g += iso.codes.PLANE_XY() - elif (plane == 1) : self.g += iso.codes.PLANE_XZ() - elif (plane == 2) : self.g += iso.codes.PLANE_YZ() + if (plane == 0): + self.g += iso.codes.PLANE_XY() + elif (plane == 1): + self.g += iso.codes.PLANE_XZ() + elif (plane == 2): + self.g += iso.codes.PLANE_YZ() def set_temporary_origin(self, x=None, y=None, z=None, a=None, b=None, c=None): self.write((iso.codes.SET_TEMPORARY_COORDINATE_SYSTEM())) - if (x != None): self.write( iso.codes.SPACE() + 'X ' + (self.fmt % x) ) - if (y != None): self.write( iso.codes.SPACE() + 'Y ' + (self.fmt % y) ) - if (z != None): self.write( iso.codes.SPACE() + 'Z ' + (self.fmt % z) ) - if (a != None): self.write( iso.codes.SPACE() + 'A ' + (self.fmt % a) ) - if (b != None): self.write( iso.codes.SPACE() + 'B ' + (self.fmt % b) ) - if (c != None): self.write( iso.codes.SPACE() + 'C ' + (self.fmt % c) ) + if (x != None): + self.write(iso.codes.SPACE() + 'X ' + (self.fmt % x)) + if (y != None): + self.write(iso.codes.SPACE() + 'Y ' + (self.fmt % y)) + if (z != None): + self.write(iso.codes.SPACE() + 'Z ' + (self.fmt % z)) + if (a != None): + self.write(iso.codes.SPACE() + 'A ' + (self.fmt % a)) + if (b != None): + self.write(iso.codes.SPACE() + 'B ' + (self.fmt % b)) + if (c != None): + self.write(iso.codes.SPACE() + 'C ' + (self.fmt % c)) self.write('\n') def remove_temporary_origin(self): @@ -151,7 +165,7 @@ def remove_temporary_origin(self): self.write('\n') ############################################################################ - ## Tools + # Tools def tool_change(self, id): self.write((iso.codes.TOOL() % id) + '\n') @@ -167,7 +181,7 @@ def offset_length(self, id, length=None): pass ############################################################################ - ## Datums + # Datums def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None): pass @@ -181,8 +195,8 @@ def workplane(self, id): if ((id >= 1) and (id <= 6)): self.g += iso.codes.WORKPLANE() % (id + iso.codes.WORKPLANE_BASE()) if ((id >= 7) and (id <= 9)): - self.g += ((iso.codes.WORKPLANE() % (6 + iso.codes.WORKPLANE_BASE())) + ('.%i' % (id - 6))) - + self.g += ((iso.codes.WORKPLANE() % + (6 + iso.codes.WORKPLANE_BASE())) + ('.%i' % (id - 6))) ############################################################################ ## Rates + Modes @@ -205,7 +219,8 @@ def calc_feedrate_hv(self, h, v): self.f = iso.codes.FEEDRATE() + (self.ffmt % self.fh) def spindle(self, s, clockwise): - if s < 0: clockwise = not clockwise + if s < 0: + clockwise = not clockwise s = abs(s) self.s = iso.codes.SPINDLE(iso.codes.FORMAT_ANG(), s) if clockwise: @@ -214,16 +229,21 @@ def spindle(self, s, clockwise): self.s = self.s + iso.codes.SPINDLE_CCW() def coolant(self, mode=0): - if (mode <= 0) : self.m.append(iso.codes.COOLANT_OFF()) - elif (mode == 1) : self.m.append(iso.codes.COOLANT_MIST()) - elif (mode == 2) : self.m.append(iso.codes.COOLANT_FLOOD()) + if (mode <= 0): + self.m.append(iso.codes.COOLANT_OFF()) + elif (mode == 1): + self.m.append(iso.codes.COOLANT_MIST()) + elif (mode == 2): + self.m.append(iso.codes.COOLANT_FLOOD()) def gearrange(self, gear=0): - if (gear <= 0) : self.m.append(iso.codes.GEAR_OFF()) - elif (gear <= 4) : self.m.append(iso.codes.GEAR() % (gear + GEAR_BASE())) + if (gear <= 0): + self.m.append(iso.codes.GEAR_OFF()) + elif (gear <= 4): + self.m.append(iso.codes.GEAR() % (gear + GEAR_BASE())) ############################################################################ - ## Moves + # Moves def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): self.write_blocknum() @@ -237,7 +257,7 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): if (y != None): dy = y - self.y - if (self.absolute_flag ): + if (self.absolute_flag): self.write(iso.codes.X() + (self.fmt % (y*2))) else: self.write(iso.codes.X() + (self.fmt % (dy*2))) @@ -246,16 +266,15 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): if (x != None): dx = x - self.x - if (self.absolute_flag ): + if (self.absolute_flag): self.write(iso.codes.Z() + (self.fmt % x)) else: self.write(iso.codes.Z() + (self.fmt % dx)) self.x = x - if (z != None): dz = z - self.z - if (self.absolute_flag ): + if (self.absolute_flag): pass #self.write(iso.codes.Z() + (self.fmt % z)) else: @@ -266,7 +285,7 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): if (a != None): da = a - self.a - if (self.absolute_flag ): + if (self.absolute_flag): self.write(iso.codes.A() + (self.fmt % a)) else: self.write(iso.codes.A() + (self.fmt % da)) @@ -274,7 +293,7 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): if (b != None): db = b - self.b - if (self.absolute_flag ): + if (self.absolute_flag): self.write(iso.codes.B() + (self.fmt % b)) else: self.write(iso.codes.B() + (self.fmt % db)) @@ -282,7 +301,7 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): if (c != None): dc = c - self.c - if (self.absolute_flag ): + if (self.absolute_flag): self.write(iso.codes.C() + (self.fmt % c)) else: self.write(iso.codes.C() + (self.fmt % dc)) @@ -292,7 +311,8 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): self.write('\n') def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): - if self.same_xyz(x, y, z): return + if self.same_xyz(x, y, z): + return self.write_blocknum() if self.g0123_modal: if self.prev_g0123 != iso.codes.FEED(): @@ -305,7 +325,7 @@ def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): if (y != None): dy = y - self.y - if (self.absolute_flag ): + if (self.absolute_flag): self.write(iso.codes.X() + (self.fmt % (y*2))) else: self.write(iso.codes.X() + (self.fmt % (dy*2))) @@ -314,7 +334,7 @@ def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): if (x != None): dx = x - self.x - if (self.absolute_flag ): + if (self.absolute_flag): self.write(iso.codes.Z() + (self.fmt % x)) else: self.write(iso.codes.Z() + (self.fmt % dx)) @@ -322,7 +342,7 @@ def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): if (z != None): dz = z - self.z - if (self.absolute_flag ): + if (self.absolute_flag): pass #self.write(iso.codes.Z() + (self.fmt % z)) else: @@ -330,7 +350,8 @@ def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): #self.write(iso.codes.Z() + (self.fmt % dz)) self.z = z - if (self.fhv) : self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) + if (self.fhv): + self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) self.write_feedrate() self.write_spindle() self.write_misc() @@ -350,11 +371,14 @@ def same_xyz(self, x=None, y=None, z=None): return True def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): - if self.same_xyz(x, y, z): return + if self.same_xyz(x, y, z): + return self.write_blocknum() arc_g_code = '' - if cw: arc_g_code = iso.codes.ARC_CW() - else: arc_g_code = iso.codes.ARC_CCW() + if cw: + arc_g_code = iso.codes.ARC_CW() + else: + arc_g_code = iso.codes.ARC_CCW() if self.g0123_modal: if self.prev_g0123 != arc_g_code: self.write(arc_g_code) @@ -365,23 +389,23 @@ def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): # make X take y values and multiply by 2 for diameter values for lathe if (y != None): dy = y - self.y - if (self.absolute_flag ): + if (self.absolute_flag): self.write(iso.codes.X() + (self.fmt % (y*2))) else: self.write(iso.codes.X() + (self.fmt % (dy*2))) self.y = y -#make Z take x values for lathe +# make Z take x values for lathe if (x != None): dx = x - self.x - if (self.absolute_flag ): + if (self.absolute_flag): self.write(iso.codes.Z() + (self.fmt % x)) else: self.write(iso.codes.Z() + (self.fmt % dx)) self.x = x if (z != None): dz = z - self.z - if (self.absolute_flag ): + if (self.absolute_flag): pass #self.write(iso.codes.X() + (self.fmt % z)) else: @@ -389,12 +413,17 @@ def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): #self.write(iso.codes.X() + (self.fmt % dz)) self.z = z - if (j != None) : self.write(iso.codes.CENTRE_X() + (self.fmt % j)) #change the order - if (i != None) : self.write(iso.codes.CENTRE_Z() + (self.fmt % i)) #and reversed i and j - if (k != None) :pass # self.write(iso.codes.CENTRE_Z() + (self.fmt % k)) - if (r != None) : self.write(iso.codes.RADIUS() + (self.fmt % r)) + if (j != None): + self.write(iso.codes.CENTRE_X() + (self.fmt % j)) # change the order + if (i != None): + self.write(iso.codes.CENTRE_Z() + (self.fmt % i)) # and reversed i and j + if (k != None): + pass # self.write(iso.codes.CENTRE_Z() + (self.fmt % k)) + if (r != None): + self.write(iso.codes.RADIUS() + (self.fmt % r)) # use horizontal feed rate - if (self.fhv) : self.calc_feedrate_hv(1, 0) + if (self.fhv): + self.calc_feedrate_hv(1, 0) self.write_feedrate() self.write_spindle() self.write_misc() @@ -424,12 +453,12 @@ def set_machine_coordinates(self): self.prev_g0123 = '' ############################################################################ - ## CRC + # CRC def use_CRC(self): return self.useCrc - def start_CRC(self, left = True, radius = 0.0): + def start_CRC(self, left=True, radius=0.0): # set up prep code, to be output on next line if self.t == None: raise "No tool specified for start_CRC()" @@ -443,12 +472,11 @@ def end_CRC(self): self.write('\n') ############################################################################ - ## Cycles + # Cycles def pattern(self): pass - def profile(self): pass @@ -461,7 +489,7 @@ def end_canned_cycle(self): self.prev_f = '' self.prev_retract = '' ############################################################################ - ## Misc + # Misc def comment(self, text): self.write((iso.codes.COMMENT(text) + '\n')) @@ -481,4 +509,5 @@ def variable_set(self, id, value): ################################################################################ + nc.creator = CreatorIso() diff --git a/scripts/addons/cam/nc/lathe1_read.py b/scripts/addons/cam/nc/lathe1_read.py index 6d7892f77..8297825b0 100644 --- a/scripts/addons/cam/nc/lathe1_read.py +++ b/scripts/addons/cam/nc/lathe1_read.py @@ -10,31 +10,34 @@ import sys ################################################################################ + + class Parser(nc.Parser): def __init__(self, writer): nc.Parser.__init__(self, writer) - self.pattern_main = re.compile('([(!;].*|\s+|[a-zA-Z0-9_:](?:[+-])?\d*(?:\.\d*)?|\w\#\d+|\(.*?\)|\#\d+\=(?:[+-])?\d*(?:\.\d*)?)') + self.pattern_main = re.compile( + '([(!;].*|\s+|[a-zA-Z0-9_:](?:[+-])?\d*(?:\.\d*)?|\w\#\d+|\(.*?\)|\#\d+\=(?:[+-])?\d*(?:\.\d*)?)') - #if ( or ! or ; at least one space or a letter followed by some character or not followed by a +/- followed by decimal, with a possible decimal point - # followed by a possible deimcal, or a letter followed by # with a decimal . deimcal + # if ( or ! or ; at least one space or a letter followed by some character or not followed by a +/- followed by decimal, with a possible decimal point + # followed by a possible deimcal, or a letter followed by # with a decimal . deimcal # add your character here > [(!;] for comments char # then look for the 'comment' function towards the end of the file and add another elif def Parse(self, name, oname=None): - self.files_open(name,oname) + self.files_open(name, oname) - #self.begin_ncblock() - #self.begin_path(None) - #self.add_line(z=500) - #self.end_path() - #self.end_ncblock() + # self.begin_ncblock() + # self.begin_path(None) + # self.add_line(z=500) + # self.end_path() + # self.end_ncblock() path_col = None f = None arc = 0 - uw = 0 + uw = 0 while (self.readline()): a = None @@ -113,7 +116,7 @@ def Parse(self, name, oname=None): path_col = "feed" col = "feed" elif (word == 'G82' or word == 'g82'): - drill = True; + drill = True no_move = True path_col = "feed" col = "feed" @@ -126,7 +129,8 @@ def Parse(self, name, oname=None): self.absolute() elif (word == 'G91' or word == 'g91'): self.incremental() - elif (word[0] == 'G') : col = "prep" + elif (word[0] == 'G'): + col = "prep" elif (word[0] == 'I' or word[0] == 'i'): col = "axis" i = eval(word[1:]) @@ -139,9 +143,12 @@ def Parse(self, name, oname=None): col = "axis" k = eval(word[1:]) move = True - elif (word[0] == 'M') : col = "misc" - elif (word[0] == 'N') : col = "blocknum" - elif (word[0] == 'O') : col = "program" + elif (word[0] == 'M'): + col = "misc" + elif (word[0] == 'N'): + col = "blocknum" + elif (word[0] == 'O'): + col = "program" elif (word[0] == 'P' or word[0] == 'p'): col = "axis" p = eval(word[1:]) @@ -158,9 +165,9 @@ def Parse(self, name, oname=None): col = "axis" s = eval(word[1:]) move = True - elif (word[0] == 'T') : + elif (word[0] == 'T'): col = "tool" - self.set_tool( eval(word[1:]) ) + self.set_tool(eval(word[1:])) elif (word[0] == 'X' or word[0] == 'x'): col = "axis" x = eval(word[1:]) @@ -173,24 +180,30 @@ def Parse(self, name, oname=None): col = "axis" u = eval(word[1:]) move = True - uw=1 + uw = 1 elif (word[0] == 'W' or word[0] == 'w'): col = "axis" w = eval(word[1:]) move = True - uw=1 + uw = 1 elif (word[0] == 'Z' or word[0] == 'z'): col = "axis" z = eval(word[1:]) move = True - elif (word[0] == '(') : (col, cdata) = ("comment", True) - elif (word[0] == '!') : (col, cdata) = ("comment", True) - elif (word[0] == ';') : (col, cdata) = ("comment", True) - elif (word[0] == '#') : col = "variable" - elif (word[0] == ':') : col = "blocknum" - elif (ord(word[0]) <= 32) : cdata = True + elif (word[0] == '('): + (col, cdata) = ("comment", True) + elif (word[0] == '!'): + (col, cdata) = ("comment", True) + elif (word[0] == ';'): + (col, cdata) = ("comment", True) + elif (word[0] == '#'): + col = "variable" + elif (word[0] == ':'): + col = "blocknum" + elif (ord(word[0]) <= 32): + cdata = True self.add_text(word, col, cdata) if (drill): @@ -208,17 +221,18 @@ def Parse(self, name, oname=None): else: if (move and not no_move): self.begin_path(path_col) - if (arc==-1): + if (arc == -1): self.add_arc(x, y, z, i, j, k, r, arc) - elif (arc==1): - #self.add_arc(x, y, z, i, j, k, -r, arc) #if you want to use arcs with R values uncomment the first part of this line and comment the next one + elif (arc == 1): + # self.add_arc(x, y, z, i, j, k, -r, arc) #if you want to use arcs with R values uncomment the first part of this line and comment the next one self.add_arc(x, y, z, i, j, k, r, arc) - #else : self.add_line(x, y, z, a, b, c) - elif(uw==1): - self.add_lathe_increment_line(u,w) + # else : self.add_line(x, y, z, a, b, c) + elif(uw == 1): + self.add_lathe_increment_line(u, w) - else : self.add_line(x, y, z, a, b, c) - self.end_path() + else: + self.add_line(x, y, z, a, b, c) + self.end_path() self.end_ncblock() diff --git a/scripts/addons/cam/nc/lynx_otter_o.py b/scripts/addons/cam/nc/lynx_otter_o.py index bf4301f02..4680ef18c 100644 --- a/scripts/addons/cam/nc/lynx_otter_o.py +++ b/scripts/addons/cam/nc/lynx_otter_o.py @@ -3,49 +3,50 @@ class Creator(iso.Creator): - def __init__(self): - iso.Creator.__init__(self) + def __init__(self): + iso.Creator.__init__(self) - def SPACE_STR(self): return(' ') + def SPACE_STR(self): return(' ') - def COMMENT(self, comment): return('') + def COMMENT(self, comment): return('') - def PROGRAM(self): return(None) + def PROGRAM(self): return(None) - def FORMAT_DWELL(self): return( self.SPACE() + self.DWELL() + ' X%f') - def SPINDLE_OFF(self): return('M05\n') - #optimize - def RAPID(self): return('G00') - def FEED(self): return('G01') + def FORMAT_DWELL(self): return(self.SPACE() + self.DWELL() + ' X%f') + def SPINDLE_OFF(self): return('M05\n') + # optimize + def RAPID(self): return('G00') + def FEED(self): return('G01') - # def IMPERIAL(self): return('G20\n') - # def METRIC(self): return('G21\n') - # def ABSOLUTE(self): return('G90\n') - # def INCREMENTAL(self): return('G91\n') - # def PLANE_XY(self): return('17\n') - # def PLANE_XZ(self): return('18\n') - # def PLANE_YZ(self): return('19\n') + # def IMPERIAL(self): return('G20\n') + # def METRIC(self): return('G21\n') + # def ABSOLUTE(self): return('G90\n') + # def INCREMENTAL(self): return('G91\n') + # def PLANE_XY(self): return('17\n') + # def PLANE_XZ(self): return('18\n') + # def PLANE_YZ(self): return('19\n') - def dwell(self, t): - pass - """ + def dwell(self, t): + pass + """ self.write_blocknum() self.write_preps() self.write(self.FORMAT_DWELL() % t) self.write_misc() self.write('\n') """ - def tool_change(self, id): - pass - # self.write_blocknum() - # self.write(self.SPACE() + (self.TOOL() % id) + '\n') - # self.write_blocknum() - # self.write(self.SPACE() + self.s.str) - # self.write('\n') - # self.flush_nc() - # self.t = id - - def PROGRAM_END(self): return( self.SPACE() + self.SPINDLE_OFF() + self.SPACE() + 'M30') + + def tool_change(self, id): + pass + # self.write_blocknum() + # self.write(self.SPACE() + (self.TOOL() % id) + '\n') + # self.write_blocknum() + # self.write(self.SPACE() + self.s.str) + # self.write('\n') + # self.flush_nc() + # self.t = id + + def PROGRAM_END(self): return(self.SPACE() + self.SPINDLE_OFF() + self.SPACE() + 'M30') nc.creator = Creator() diff --git a/scripts/addons/cam/nc/mach3.py b/scripts/addons/cam/nc/mach3.py index 25d608586..f2fc8f3b7 100644 --- a/scripts/addons/cam/nc/mach3.py +++ b/scripts/addons/cam/nc/mach3.py @@ -1,19 +1,21 @@ from . import nc from . import iso + class Creator(iso.Creator): - def __init__(self): - iso.Creator.__init__(self) + def __init__(self): + iso.Creator.__init__(self) + + def SPACE_STR(self): return(' ') - def SPACE_STR(self): return(' ') + def program_begin(self, id, comment): + self.write(('(' + 'GCode created using the HeeksCNC Mach3 post processor' + ')' + '\n')) + self.write(('(' + comment + ')' + '\n')) - def program_begin(self, id, comment): - self.write( ('(' + 'GCode created using the HeeksCNC Mach3 post processor' + ')' + '\n') ) - self.write( ('(' + comment + ')' + '\n') ) + def tool_change(self, id): + self.write('G43H%i' % id + '\n') + self.write((self.TOOL() % id) + '\n') + self.t = id - def tool_change(self, id): - self.write('G43H%i'% id +'\n') - self.write((self.TOOL() % id) + '\n') - self.t = id nc.creator = Creator() diff --git a/scripts/addons/cam/nc/mach3_read.py b/scripts/addons/cam/nc/mach3_read.py index 3a830598d..00575a3c3 100644 --- a/scripts/addons/cam/nc/mach3_read.py +++ b/scripts/addons/cam/nc/mach3_read.py @@ -2,6 +2,8 @@ from . import sys # just use the iso reader + + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/makerbotHBP.py b/scripts/addons/cam/nc/makerbotHBP.py index 0a5ad904a..49db7118d 100644 --- a/scripts/addons/cam/nc/makerbotHBP.py +++ b/scripts/addons/cam/nc/makerbotHBP.py @@ -8,6 +8,8 @@ now = datetime.datetime.now() ################################################################################ + + class CreatorMakerbotHBP(iso_modal.CreatorIsoModal): def __init__(self): iso_modal.CreatorIsoModal.__init__(self) @@ -19,33 +21,35 @@ def __init__(self): ################################################################################ # program begin and end + def program_begin(self, id, name=''): self.write((maker.codes.COMMENT(now))) - self.write((maker.codes.EXTRUDER_TEMP('220')) + (maker.codes.COMMENT('Extruder Temp')) ) - self.write((maker.codes.BUILD_BED_TEMP('110'))+ (maker.codes.COMMENT('Build Bed Temp')) ) - self.write((maker.codes.FAN_OFF()) + (maker.codes.COMMENT('Fan Off')) ) - self.write((maker.codes.METRIC()) + (maker.codes.COMMENT('Metric units')) ) - self.write((maker.codes.ABSOLUTE()) + (maker.codes.COMMENT('Absolute units')) ) + self.write((maker.codes.EXTRUDER_TEMP('220')) + (maker.codes.COMMENT('Extruder Temp'))) + self.write((maker.codes.BUILD_BED_TEMP('110')) + (maker.codes.COMMENT('Build Bed Temp'))) + self.write((maker.codes.FAN_OFF()) + (maker.codes.COMMENT('Fan Off'))) + self.write((maker.codes.METRIC()) + (maker.codes.COMMENT('Metric units'))) + self.write((maker.codes.ABSOLUTE()) + (maker.codes.COMMENT('Absolute units'))) self.write('G92 X0 Y0 Z0 (You are now at 0,0,0)\n') self.write('G0 Z15 (Move up for warmup)\n') - self.write((maker.codes.EXTRUDER_SPEED_PWM('255')) + (maker.codes.COMMENT('Extruder Speed')) ) + self.write((maker.codes.EXTRUDER_SPEED_PWM('255')) + + (maker.codes.COMMENT('Extruder Speed'))) self.write('M6 T0 (Wait for tool to heat up)\n') self.write('G04 P5000 (Wait 5 seconds)\n') - self.write((maker.codes.EXTRUDER_ON_FWD()) + (maker.codes.COMMENT('Extruder On')) ) + self.write((maker.codes.EXTRUDER_ON_FWD()) + (maker.codes.COMMENT('Extruder On'))) self.write('G04 P5000 (Wait 5 seconds)\n') - self.write((maker.codes.EXTRUDER_OFF()) + (maker.codes.COMMENT('Extruder Off')) ) + self.write((maker.codes.EXTRUDER_OFF()) + (maker.codes.COMMENT('Extruder Off'))) self.write('M01 (The heated build platform is heating up. Wait until after the lights have turned off for the first time, clear the test extrusion, and click yes.)\n') self.write('G0 Z0 (Go back to zero.)\n') def program_end(self): self.write((maker.codes.COMMENT('End of the file. Begin cool-down'))) - self.write((maker.codes.EXTRUDER_TEMP('0')) + (maker.codes.COMMENT('Extruder Temp')) ) - self.write((maker.codes.BUILD_BED_TEMP('0')) + (maker.codes.COMMENT('Build Bed Temp')) ) - self.write((maker.codes.FAN_ON()) + (maker.codes.COMMENT('Fan On')) ) + self.write((maker.codes.EXTRUDER_TEMP('0')) + (maker.codes.COMMENT('Extruder Temp'))) + self.write((maker.codes.BUILD_BED_TEMP('0')) + (maker.codes.COMMENT('Build Bed Temp'))) + self.write((maker.codes.FAN_ON()) + (maker.codes.COMMENT('Fan On'))) self.write('G92 Z0 (zero our z axis - hack b/c skeinforge mangles gcodes in end.txt)\n') self.write('G1 Z10 (go up 10 b/c it was zeroed earlier.)\n') self.write('G1 X0 Y0 Z10 (go to 0,0,z)\n') - self.write((maker.codes.STEPPERS_OFF()) + (maker.codes.COMMENT('Steppers Off')) ) + self.write((maker.codes.STEPPERS_OFF()) + (maker.codes.COMMENT('Steppers Off'))) def program_stop(self): self.write((maker.codes.EXTRUDER_TEMP('0'))) @@ -58,7 +62,7 @@ def write_blocknum(self): pass def set_plane(self, plane): - pass + pass def workplane(self, id): pass @@ -69,32 +73,32 @@ def spindle(self, s, clockwise): # Extruder Control def extruder_on(self): - self.write((maker.codes.EXTRUDER_ON()) + ('\n')) + self.write((maker.codes.EXTRUDER_ON()) + ('\n')) def extruder_off(self): - self.write((maker.codes.EXTRUDER_OFF()) + ('\n')) + self.write((maker.codes.EXTRUDER_OFF()) + ('\n')) def set_extruder_flowrate(self, flowrate): - self.write((maker.codes.EXTRUDER_SPEED_PWM(flowrate)) + ('\n')) + self.write((maker.codes.EXTRUDER_SPEED_PWM(flowrate)) + ('\n')) def extruder_temp(self, temp): - self.write((maker.codes.EXTRUDER_TEMP(temp)) + ('\n')) + self.write((maker.codes.EXTRUDER_TEMP(temp)) + ('\n')) ################################################################################ # Build Environment Control def build_bed_temp(self, temp): - self.write((maker.codes.BUILD_BED_TEMP(temp)) + ('\n')) + self.write((maker.codes.BUILD_BED_TEMP(temp)) + ('\n')) def chamber_temp(self, temp): - self.write((maker.codes.CHAMBER_TEMP(temp)) + ('\n')) + self.write((maker.codes.CHAMBER_TEMP(temp)) + ('\n')) ################################################################################ # Fan Control def fan_on(self): - self.write((maker.codes.FAN_ON()) + ('\n')) + self.write((maker.codes.FAN_ON()) + ('\n')) def fan_off(self): - self.write((maker.codes.FAN_OFF()) + ('\n')) + self.write((maker.codes.FAN_OFF()) + ('\n')) ################################################################################ # Custom routines @@ -111,15 +115,15 @@ def insert(self, text): ################################################################################ # tool info def tool_change(self, id): - pass + pass # self.write_blocknum() # self.write((maker.codes.TOOL() % id) + '\n') # self.t = id def tool_defn(self, id, name='', params=None): - pass + pass ############################################################################ -## Moves +# Moves def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): self.write_blocknum() @@ -132,14 +136,14 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): self.write_preps() if (x != None): dx = x - self.x - if (self.absolute_flag ): + if (self.absolute_flag): self.write(maker.codes.X() + (self.fmt % x)) else: self.write(maker.codes.X() + (self.fmt % dx)) self.x = x if (y != None): dy = y - self.y - if (self.absolute_flag ): + if (self.absolute_flag): self.write(maker.codes.Y() + (self.fmt % y)) else: self.write(maker.codes.Y() + (self.fmt % dy)) @@ -147,7 +151,7 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): self.y = y if (z != None): dz = z - self.z - if (self.absolute_flag ): + if (self.absolute_flag): self.write(maker.codes.Z() + (self.fmt % z)) else: self.write(maker.codes.Z() + (self.fmt % dz)) @@ -156,7 +160,7 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): if (a != None): da = a - self.a - if (self.absolute_flag ): + if (self.absolute_flag): self.write(maker.codes.A() + (self.fmt % a)) else: self.write(maker.codes.A() + (self.fmt % da)) @@ -164,7 +168,7 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): if (b != None): db = b - self.b - if (self.absolute_flag ): + if (self.absolute_flag): self.write(maker.codes.B() + (self.fmt % b)) else: self.write(maker.codes.B() + (self.fmt % db)) @@ -172,7 +176,7 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): if (c != None): dc = c - self.c - if (self.absolute_flag ): + if (self.absolute_flag): self.write(maker.codes.C() + (self.fmt % c)) else: self.write(maker.codes.C() + (self.fmt % dc)) @@ -182,7 +186,8 @@ def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): self.write('\n') def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): - if self.same_xyz(x, y, z): return + if self.same_xyz(x, y, z): + return self.write_blocknum() if self.g0123_modal: if self.prev_g0123 != maker.codes.FEED(): @@ -194,14 +199,14 @@ def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): dx = dy = dz = 0 if (x != None): dx = x - self.x - if (self.absolute_flag ): + if (self.absolute_flag): self.write(maker.codes.X() + (self.fmt % x)) else: self.write(maker.codes.X() + (self.fmt % dx)) self.x = x if (y != None): dy = y - self.y - if (self.absolute_flag ): + if (self.absolute_flag): self.write(maker.codes.Y() + (self.fmt % y)) else: self.write(maker.codes.Y() + (self.fmt % dy)) @@ -209,13 +214,14 @@ def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): self.y = y if (z != None): dz = z - self.z - if (self.absolute_flag ): + if (self.absolute_flag): self.write(maker.codes.Z() + (self.fmt % z)) else: self.write(maker.codes.Z() + (self.fmt % dz)) self.z = z - if (self.fhv) : self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) + if (self.fhv): + self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) self.write_feedrate() self.write_spindle() self.write_misc() @@ -235,11 +241,14 @@ def same_xyz(self, x=None, y=None, z=None): return True def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): - if self.same_xyz(x, y, z): return + if self.same_xyz(x, y, z): + return self.write_blocknum() arc_g_code = '' - if cw: arc_g_code = maker.codes.ARC_CW() - else: arc_g_code = maker.codes.ARC_CCW() + if cw: + arc_g_code = maker.codes.ARC_CW() + else: + arc_g_code = maker.codes.ARC_CCW() if self.g0123_modal: if self.prev_g0123 != arc_g_code: self.write(arc_g_code) @@ -249,31 +258,36 @@ def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): self.write_preps() if (x != None): dx = x - self.x - if (self.absolute_flag ): + if (self.absolute_flag): self.write(maker.codes.X() + (self.fmt % x)) else: self.write(maker.codes.X() + (self.fmt % dx)) self.x = x if (y != None): dy = y - self.y - if (self.absolute_flag ): + if (self.absolute_flag): self.write(maker.codes.Y() + (self.fmt % y)) else: self.write(maker.codes.Y() + (self.fmt % dy)) self.y = y if (z != None): dz = z - self.z - if (self.absolute_flag ): + if (self.absolute_flag): self.write(maker.codes.Z() + (self.fmt % z)) else: self.write(maker.codes.Z() + (self.fmt % dz)) self.z = z - if (i != None) : self.write(maker.codes.CENTRE_X() + (self.fmt % i)) - if (j != None) : self.write(maker.codes.CENTRE_Y() + (self.fmt % j)) - if (k != None) : self.write(maker.codes.CENTRE_Z() + (self.fmt % k)) - if (r != None) : self.write(maker.codes.RADIUS() + (self.fmt % r)) + if (i != None): + self.write(maker.codes.CENTRE_X() + (self.fmt % i)) + if (j != None): + self.write(maker.codes.CENTRE_Y() + (self.fmt % j)) + if (k != None): + self.write(maker.codes.CENTRE_Z() + (self.fmt % k)) + if (r != None): + self.write(maker.codes.RADIUS() + (self.fmt % r)) # use horizontal feed rate - if (self.fhv) : self.calc_feedrate_hv(1, 0) + if (self.fhv): + self.calc_feedrate_hv(1, 0) self.write_feedrate() self.write_spindle() self.write_misc() @@ -302,4 +316,5 @@ def set_machine_coordinates(self): self.write(maker.codes.MACHINE_COORDINATES()) self.prev_g0123 = '' + nc.creator = CreatorMakerbotHBP() diff --git a/scripts/addons/cam/nc/makerbotHBP_read.py b/scripts/addons/cam/nc/makerbotHBP_read.py index 075c24e09..2ca0b6f50 100644 --- a/scripts/addons/cam/nc/makerbotHBP_read.py +++ b/scripts/addons/cam/nc/makerbotHBP_read.py @@ -2,6 +2,8 @@ import sys # just use the iso reader + + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/makerbot_codes.py b/scripts/addons/cam/nc/makerbot_codes.py index 14b8b3366..814f1a6f3 100644 --- a/scripts/addons/cam/nc/makerbot_codes.py +++ b/scripts/addons/cam/nc/makerbot_codes.py @@ -8,121 +8,122 @@ # Many of these codes have nothing to do with reprap/additive machining but are left here in anticipation of future hybrid machines. class Codes(): - def SPACE(self): return(' ') - def FORMAT_FEEDRATE(self): return('%.2f') - def FORMAT_IN(self): return('%.5f') - def FORMAT_MM(self): return('%.3f') - def FORMAT_ANG(self): return('%.1f') - def FORMAT_TIME(self): return('%.2f') - def FORMAT_DWELL(self): return('P%f') - - def BLOCK(self): return('N%i' + self.SPACE()) - def COMMENT(self,comment): return( (' (%s)\n' % comment ) ) - def VARIABLE(self): return( '#%i') - def VARIABLE_SET(self): return( '=%.3f') - - def PROGRAM(self): return( 'O%i') - def PROGRAM_END(self): return( 'M02') - - def SUBPROG_CALL(self): return( 'M98' + self.SPACE() + 'P%i') - def SUBPROG_END(self): return( 'M99') - - def STOP_OPTIONAL(self): return('M01') - def STOP(self): return('M00') - - def IMPERIAL(self): return(self.SPACE() + 'G20') - def METRIC(self): return(self.SPACE() + 'G21' + self.SPACE()) - def ABSOLUTE(self): return(self.SPACE() + 'G90' + self.SPACE()) - def INCREMENTAL(self): return(self.SPACE() + 'G91') - def SET_TEMPORARY_COORDINATE_SYSTEM(self): return('G92' + self.SPACE()) - def REMOVE_TEMPORARY_COORDINATE_SYSTEM(self): return('G92.1' + self.SPACE()) - def POLAR_ON(self): return(self.SPACE() + 'G16') - def POLAR_OFF(self): return(self.SPACE() + 'G15') - def PLANE_XY(self): return(self.SPACE() + 'G17') - def PLANE_XZ(self): return(self.SPACE() + 'G18') - def PLANE_YZ(self): return(self.SPACE() + 'G19') - - def TOOL(self): return(self.SPACE() +'T%i') - def TOOL_DEFINITION(self): return('G10' + self.SPACE() + 'L1' + self.SPACE()) - - def WORKPLANE(self): return('G%i') - def WORKPLANE_BASE(self): return(53) - - def FEEDRATE(self): return((self.SPACE() + ' F')) - def SPINDLE(self, format, speed): return(self.SPACE() + 'S' + (format % speed)) - def SPINDLE_CW(self): return(self.SPACE() + 'M03') - def SPINDLE_CCW(self): return(self.SPACE() + 'M04') - def COOLANT_OFF(self): return(self.SPACE() + 'M09') - def COOLANT_MIST(self): return(self.SPACE() + 'M07') - def COOLANT_FLOOD(self): return(self.SPACE() + 'M08') - def GEAR_OFF(self): return(self.SPACE() + '?') - def GEAR(self): return('M%i') - def GEAR_BASE(self): return(37) - - def RAPID(self): return('G0') - def FEED(self): return('G1') - def ARC_CW(self): return('G2') - def ARC_CCW(self): return('G3') - def DWELL(self): return('G04') - def DRILL(self): return(self.SPACE() + 'G81') - def DRILL_WITH_DWELL(self, format, dwell): return(self.SPACE() + 'G82' + (format % dwell)) - def PECK_DRILL(self): return(self.SPACE() + 'G83') - def PECK_DEPTH(self, format, depth): return(self.SPACE() + 'Q' + (format % depth)) - def RETRACT(self, format, height): return(self.SPACE() + 'R' + (format % height)) - def END_CANNED_CYCLE(self): return(self.SPACE() + 'G80') - - def X(self): return(self.SPACE() + 'X') - def Y(self): return(self.SPACE() + 'Y') - def Z(self): return(self.SPACE() + 'Z') - def A(self): return(self.SPACE() + 'A') - def B(self): return(self.SPACE() + 'B') - def C(self): return(self.SPACE() + 'C') - def CENTRE_X(self): return(self.SPACE() + 'I') - def CENTRE_Y(self): return(self.SPACE() + 'J') - def CENTRE_Z(self): return(self.SPACE() + 'K') - def RADIUS(self): return(self.SPACE() + 'R') - def TIME(self): return(self.SPACE() + 'P') - - def PROBE_TOWARDS_WITH_SIGNAL(self): return('G38.2' + self.SPACE()) - def PROBE_TOWARDS_WITHOUT_SIGNAL(self): return('G38.3' + self.SPACE()) - def PROBE_AWAY_WITH_SIGNAL(self): return('G38.4' + self.SPACE()) - def PROBE_AWAY_WITHOUT_SIGNAL(self): return('G38.5' + self.SPACE()) - - def MACHINE_COORDINATES(self): return('G53' + self.SPACE()) - - def EXTRUDER_ON (self): return('M101') #deprecated - def EXTRUDER_OFF (self): return('M103') - def EXTRUDER_TEMP (self, degree_celsius): return('M104 S' + '%s' % degree_celsius) - def EXTRUDER_TEMP_WAIT (self, degree_celsius): return('M109 S' + '%s' % degree_celsius) - def READ_EXTRUDER_TEMP (self): return('M105') - def EXTRUDER_SPEED_PWM (self, speed_in_PWM): return('M108 S' + '%s' % speed_in_PWM) #deprecated - def EXTRUDER_SPEED_RPM (self, speed_in_RPM): return('M108 P' + '%s' % speed_in_RPM) #deprecated - - def STEPPERS_OFF(self): return(self.SPACE() + 'M118') - - def ALL_WAIT (self): return(self.SPACE() + 'M116') # Wait for all temperature and slow-changing variables to reach set values - - def FAN_ON (self): return(self.SPACE() + 'M106') - def FAN_OFF (self): return(self.SPACE() + 'M107') - - def VALVE_OPEN (self, delay): return(self.SPACE() + ('M126 P' + '%' % delay) ) - def VALVE_CLOSE (self, delay): return(self.SPACE() + ('M127 P' + '%' % delay) ) - - def BUILD_BED_TEMP (self, degree_celsius): return('M140 S' + '%s' % degree_celsius) - def BED_HOLDING_PRESSURE (self, pressure): return('M142 S' + '%s' % pressure) - - def CHAMBER_TEMP (self, degree_celsius): return('M141 S' + '%s' % degree_celsius) - -#The following codes are listed on the reprap wiki page at http://reprap.org/wiki/Mendel_User_Manual:_RepRapGCodes but require more study. + def SPACE(self): return(' ') + def FORMAT_FEEDRATE(self): return('%.2f') + def FORMAT_IN(self): return('%.5f') + def FORMAT_MM(self): return('%.3f') + def FORMAT_ANG(self): return('%.1f') + def FORMAT_TIME(self): return('%.2f') + def FORMAT_DWELL(self): return('P%f') + + def BLOCK(self): return('N%i' + self.SPACE()) + def COMMENT(self, comment): return((' (%s)\n' % comment)) + def VARIABLE(self): return('#%i') + def VARIABLE_SET(self): return('=%.3f') + + def PROGRAM(self): return('O%i') + def PROGRAM_END(self): return('M02') + + def SUBPROG_CALL(self): return('M98' + self.SPACE() + 'P%i') + def SUBPROG_END(self): return('M99') + + def STOP_OPTIONAL(self): return('M01') + def STOP(self): return('M00') + + def IMPERIAL(self): return(self.SPACE() + 'G20') + def METRIC(self): return(self.SPACE() + 'G21' + self.SPACE()) + def ABSOLUTE(self): return(self.SPACE() + 'G90' + self.SPACE()) + def INCREMENTAL(self): return(self.SPACE() + 'G91') + def SET_TEMPORARY_COORDINATE_SYSTEM(self): return('G92' + self.SPACE()) + def REMOVE_TEMPORARY_COORDINATE_SYSTEM(self): return('G92.1' + self.SPACE()) + def POLAR_ON(self): return(self.SPACE() + 'G16') + def POLAR_OFF(self): return(self.SPACE() + 'G15') + def PLANE_XY(self): return(self.SPACE() + 'G17') + def PLANE_XZ(self): return(self.SPACE() + 'G18') + def PLANE_YZ(self): return(self.SPACE() + 'G19') + + def TOOL(self): return(self.SPACE() + 'T%i') + def TOOL_DEFINITION(self): return('G10' + self.SPACE() + 'L1' + self.SPACE()) + + def WORKPLANE(self): return('G%i') + def WORKPLANE_BASE(self): return(53) + + def FEEDRATE(self): return((self.SPACE() + ' F')) + def SPINDLE(self, format, speed): return(self.SPACE() + 'S' + (format % speed)) + def SPINDLE_CW(self): return(self.SPACE() + 'M03') + def SPINDLE_CCW(self): return(self.SPACE() + 'M04') + def COOLANT_OFF(self): return(self.SPACE() + 'M09') + def COOLANT_MIST(self): return(self.SPACE() + 'M07') + def COOLANT_FLOOD(self): return(self.SPACE() + 'M08') + def GEAR_OFF(self): return(self.SPACE() + '?') + def GEAR(self): return('M%i') + def GEAR_BASE(self): return(37) + + def RAPID(self): return('G0') + def FEED(self): return('G1') + def ARC_CW(self): return('G2') + def ARC_CCW(self): return('G3') + def DWELL(self): return('G04') + def DRILL(self): return(self.SPACE() + 'G81') + def DRILL_WITH_DWELL(self, format, dwell): return(self.SPACE() + 'G82' + (format % dwell)) + def PECK_DRILL(self): return(self.SPACE() + 'G83') + def PECK_DEPTH(self, format, depth): return(self.SPACE() + 'Q' + (format % depth)) + def RETRACT(self, format, height): return(self.SPACE() + 'R' + (format % height)) + def END_CANNED_CYCLE(self): return(self.SPACE() + 'G80') + + def X(self): return(self.SPACE() + 'X') + def Y(self): return(self.SPACE() + 'Y') + def Z(self): return(self.SPACE() + 'Z') + def A(self): return(self.SPACE() + 'A') + def B(self): return(self.SPACE() + 'B') + def C(self): return(self.SPACE() + 'C') + def CENTRE_X(self): return(self.SPACE() + 'I') + def CENTRE_Y(self): return(self.SPACE() + 'J') + def CENTRE_Z(self): return(self.SPACE() + 'K') + def RADIUS(self): return(self.SPACE() + 'R') + def TIME(self): return(self.SPACE() + 'P') + + def PROBE_TOWARDS_WITH_SIGNAL(self): return('G38.2' + self.SPACE()) + def PROBE_TOWARDS_WITHOUT_SIGNAL(self): return('G38.3' + self.SPACE()) + def PROBE_AWAY_WITH_SIGNAL(self): return('G38.4' + self.SPACE()) + def PROBE_AWAY_WITHOUT_SIGNAL(self): return('G38.5' + self.SPACE()) + + def MACHINE_COORDINATES(self): return('G53' + self.SPACE()) + + def EXTRUDER_ON(self): return('M101') # deprecated + def EXTRUDER_OFF(self): return('M103') + def EXTRUDER_TEMP(self, degree_celsius): return('M104 S' + '%s' % degree_celsius) + def EXTRUDER_TEMP_WAIT(self, degree_celsius): return('M109 S' + '%s' % degree_celsius) + def READ_EXTRUDER_TEMP(self): return('M105') + def EXTRUDER_SPEED_PWM(self, speed_in_PWM): return('M108 S' + '%s' % speed_in_PWM) # deprecated + def EXTRUDER_SPEED_RPM(self, speed_in_RPM): return('M108 P' + '%s' % speed_in_RPM) # deprecated + + def STEPPERS_OFF(self): return(self.SPACE() + 'M118') + + # Wait for all temperature and slow-changing variables to reach set values + def ALL_WAIT(self): return(self.SPACE() + 'M116') + + def FAN_ON(self): return(self.SPACE() + 'M106') + def FAN_OFF(self): return(self.SPACE() + 'M107') + + def VALVE_OPEN(self, delay): return(self.SPACE() + ('M126 P' + '%' % delay)) + def VALVE_CLOSE(self, delay): return(self.SPACE() + ('M127 P' + '%' % delay)) + + def BUILD_BED_TEMP(self, degree_celsius): return('M140 S' + '%s' % degree_celsius) + def BED_HOLDING_PRESSURE(self, pressure): return('M142 S' + '%s' % pressure) + + def CHAMBER_TEMP(self, degree_celsius): return('M141 S' + '%s' % degree_celsius) + +# The following codes are listed on the reprap wiki page at http://reprap.org/wiki/Mendel_User_Manual:_RepRapGCodes but require more study. # -#G28 G Y Xnnn Ynnn Znnn Move to origin (on specified axes only, if X/Y/Z parameters are present) -#M105 M N none Request current extruder and base temperatures (in Celsius) -#M110 M N none Set current line number to Nxxx value preceeding command -#M111 M N Snnn Set debug level bitfield to value of parameter (default 6) -#M112 M N none Emergency stop (stop immediately, discarding any buffered commands) -#M113 M N Snnn Set Extruder PWM (to value defined by pot, or to parameter value if present) -#M114 M N none Get Current Position (return current X, Y, Z and E values) -#M117 M N none Get Zero Position (return X, Y, Z and E values of endstop hits) +# G28 G Y Xnnn Ynnn Znnn Move to origin (on specified axes only, if X/Y/Z parameters are present) +# M105 M N none Request current extruder and base temperatures (in Celsius) +# M110 M N none Set current line number to Nxxx value preceeding command +# M111 M N Snnn Set debug level bitfield to value of parameter (default 6) +# M112 M N none Emergency stop (stop immediately, discarding any buffered commands) +# M113 M N Snnn Set Extruder PWM (to value defined by pot, or to parameter value if present) +# M114 M N none Get Current Position (return current X, Y, Z and E values) +# M117 M N none Get Zero Position (return X, Y, Z and E values of endstop hits) codes = Codes() diff --git a/scripts/addons/cam/nc/nc.py b/scripts/addons/cam/nc/nc.py index c4632fa82..1860ad516 100644 --- a/scripts/addons/cam/nc/nc.py +++ b/scripts/addons/cam/nc/nc.py @@ -21,688 +21,785 @@ ncFLOOD = 2 ################################################################################ + + class Creator: - def __init__(self): - pass + def __init__(self): + pass + + ############################################################################ + # Internals + + def file_open(self, name): + # self.buffer=[] + self.file = open(name, 'w') + self.filename = name + + def file_close(self): + # self.file.write(''.join(self.buffer)) + # self.buffer=[] + self.file.close() + + def write(self, s): + self.file.write(s) + # self.buffer.append(s) + # if len(self.buffer)>100000: + # self.file.write(''.join(self.buffer)) + # self.buffer=[] + + def writem(self, a): + self.file.write(''.join(a)) + # self.buffer.extend(a) + ############################################################################ + # Programs + + def program_begin(self, id, name=''): + """Begin a program""" + pass + + def add_stock(self, type_name, params): + pass + + def program_stop(self, optional=False): + """Stop the machine""" + pass + + def program_end(self): + """End the program""" + pass + + def flush_nc(self): + """Flush all pending codes""" + pass + + ############################################################################ + # Subprograms + + def sub_begin(self, id, name=''): + """Begin a subprogram""" + pass + + def sub_call(self, id): + """Call a subprogram""" + pass + + def sub_end(self): + """Return from a subprogram""" + pass + + ############################################################################ + # Settings + + def imperial(self): + """Set imperial units""" + pass + + def metric(self): + """Set metric units""" + pass + + def absolute(self): + """Set absolute coordinates""" + pass + + def incremental(self): + """Set incremental coordinates""" + pass + + def polar(self, on=True): + """Set polar coordinates""" + pass + + def set_plane(self, plane): + """Set plane""" + pass + + def set_temporary_origin(self, x=None, y=None, z=None, a=None, b=None, c=None): + """Set temporary origin G92""" + pass + + def remove_temporary_origin(self): + """Remote temporary origin G92.1""" + pass + + ############################################################################ + # Tools + + def tool_change(self, id): + """Change the tool""" + pass + + def tool_defn(self, id, name='', params=None): + """Define a tool""" + pass + + def offset_radius(self, id, radius=None): + """Set tool radius offsetting""" + pass + + def offset_length(self, id, length=None): + """Set tool length offsetting""" + pass + + def current_tool(self): + return None + + ############################################################################ + # Datums + + def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None): + """Shift the datum""" + pass - ############################################################################ - ## Internals + def datum_set(self, x=None, y=None, z=None, a=None, b=None, c=None): + """Set the datum""" + pass - def file_open(self, name): - #self.buffer=[] - self.file = open(name, 'w') - self.filename = name + def workplane(self, id): + """Set the workplane""" + pass - def file_close(self): - #self.file.write(''.join(self.buffer)) - #self.buffer=[] - self.file.close() + def clearanceplane(self, z=None): + """set clearance plane""" + pass - def write(self, s): - self.file.write(s) - #self.buffer.append(s) - #if len(self.buffer)>100000: - # self.file.write(''.join(self.buffer)) - # self.buffer=[] + ############################################################################ + # APT360 like Transformation Definitions + # These definitions were created while looking at Irvin Kraal's book on APT + # - Numerical Control Progamming in APT - page 211 - def writem(self, a): - self.file.write(''.join(a)) - #self.buffer.extend(a) - ############################################################################ - ## Programs + def matrix(self, a1=None, b1=None, c1=None, a2=None, b2=None, c2=None, a3=None, b3=None, c3=None): + """Create a matrix for transformations""" + pass - def program_begin(self, id, name=''): - """Begin a program""" - pass + def translate(self, x=None, y=None, z=None): + """Translate in x,y,z direction""" + pass - def add_stock(self, type_name, params): - pass + def rotate(self, xyrot=None, yzrot=None, zxrot=None, angle=None): + """Rotate about a coordinate axis""" + pass - def program_stop(self, optional=False): - """Stop the machine""" - pass + def scale(self, k=None): + """Scale by factor k""" + pass - def program_end(self): - """End the program""" - pass + def matrix_product(self, matrix1=None, matrix2=None): + """Create matrix that is the product of two other matrices""" + pass - def flush_nc(self): - """Flush all pending codes""" - pass + def mirror_plane(self, plane1=None, plane2=None, plane3=None): + """Mirror image about one or more coordinate planes""" + pass - ############################################################################ - ## Subprograms - - def sub_begin(self, id, name=''): - """Begin a subprogram""" - pass - - def sub_call(self, id): - """Call a subprogram""" - pass - - def sub_end(self): - """Return from a subprogram""" - pass - - ############################################################################ - ## Settings - - def imperial(self): - """Set imperial units""" - pass - - def metric(self): - """Set metric units""" - pass - - def absolute(self): - """Set absolute coordinates""" - pass - - def incremental(self): - """Set incremental coordinates""" - pass - - def polar(self, on=True): - """Set polar coordinates""" - pass - - def set_plane(self, plane): - """Set plane""" - pass - - def set_temporary_origin(self, x=None, y=None, z=None, a=None, b=None, c=None): - """Set temporary origin G92""" - pass - - def remove_temporary_origin(self): - """Remote temporary origin G92.1""" - pass - - ############################################################################ - ## Tools - - def tool_change(self, id): - """Change the tool""" - pass - - def tool_defn(self, id, name='', params=None): - """Define a tool""" - pass - - def offset_radius(self, id, radius=None): - """Set tool radius offsetting""" - pass - - def offset_length(self, id, length=None): - """Set tool length offsetting""" - pass - - def current_tool(self): - return None - - ############################################################################ - ## Datums - - def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None): - """Shift the datum""" - pass - - def datum_set(self, x=None, y=None, z=None, a=None, b=None, c=None): - """Set the datum""" - pass - - def workplane(self, id): - """Set the workplane""" - pass - - def clearanceplane(self,z=None): - """set clearance plane""" - pass - - ############################################################################ - ## APT360 like Transformation Definitions - ## These definitions were created while looking at Irvin Kraal's book on APT - ## - Numerical Control Progamming in APT - page 211 - - def matrix(self,a1=None,b1=None,c1=None,a2=None,b2=None,c2=None,a3=None,b3=None,c3=None): - """Create a matrix for transformations""" - pass - def translate(self,x=None,y=None,z=None): - """Translate in x,y,z direction""" - pass - def rotate(self,xyrot=None,yzrot=None,zxrot=None,angle=None): - """Rotate about a coordinate axis""" - pass - def scale(self,k=None): - """Scale by factor k""" - pass - def matrix_product(self,matrix1=None,matrix2=None): - """Create matrix that is the product of two other matrices""" - pass - def mirror_plane(self,plane1=None,plane2=None,plane3=None): - """Mirror image about one or more coordinate planes""" - pass - def mirror_line(self,line=None): - """Mirror about a line""" - pass - - - ############################################################################ - ## Rates + Modes - - def feedrate(self, f): - """Set the feedrate""" - pass - - def feedrate_hv(self, fh, fv): - """Set the horizontal and vertical feedrates""" - pass - - def spindle(self, s, clockwise=True): - """Set the spindle speed""" - pass - - def coolant(self, mode=0): - """Set the coolant mode""" - pass - - def gearrange(self, gear=0): - """Set the gear range""" - pass - - ############################################################################ - ## Moves - - def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): - """Rapid move""" - pass - - def feed(self, x=None, y=None, z=None, a = None, b = None, c = None): - """Feed move""" - pass - - def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): - """Clockwise arc move""" - pass - - def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): - """Counterclockwise arc move""" - pass - - def dwell(self, t): - """Dwell""" - pass + def mirror_line(self, line=None): + """Mirror about a line""" + pass - def rapid_home(self, x=None, y=None, z=None, a=None, b=None, c=None): - """Rapid relative to home position""" - pass + ############################################################################ + ## Rates + Modes - def rapid_unhome(self): - """Return from rapid home""" - pass - - def set_machine_coordinates(self): - """Set machine coordinates""" - pass + def feedrate(self, f): + """Set the feedrate""" + pass - ############################################################################ - ## Cutter radius compensation + def feedrate_hv(self, fh, fv): + """Set the horizontal and vertical feedrates""" + pass - def use_CRC(self): - """CRC""" - return False + def spindle(self, s, clockwise=True): + """Set the spindle speed""" + pass - ############################################################################ - ## Cycles + def coolant(self, mode=0): + """Set the coolant mode""" + pass - def pattern(self): - """Simple pattern eg. circle, rect""" - pass + def gearrange(self, gear=0): + """Set the gear range""" + pass - def pocket(self): - """Pocket routine""" - pass + ############################################################################ + # Moves - def profile(self): - """Profile routine""" - pass + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): + """Rapid move""" + pass - def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): - """Drilling routines""" - pass + def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): + """Feed move""" + pass - # original prototype was: - # def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None): - # - # current call is like so: - # tap(x=10, y=10, z=0, tap_mode=0, depth=12.7, standoff=6.35, direction=0, pitch=1.25) - # just add tap_mode & direction parameters + def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + """Clockwise arc move""" + pass - def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None, tap_mode=None, direction=None): - """Tapping routines""" - pass + def arc_ccw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + """Counterclockwise arc move""" + pass - def bore(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, feed_in=None, feed_out=None, stoppos=None, shift_back=None, shift_right=None, backbore=False, stop=False): - """Boring routines""" - pass + def dwell(self, t): + """Dwell""" + pass - def end_canned_cycle(self): - pass + def rapid_home(self, x=None, y=None, z=None, a=None, b=None, c=None): + """Rapid relative to home position""" + pass - ############################################################################ - ## Misc + def rapid_unhome(self): + """Return from rapid home""" + pass - def comment(self, text): - """Insert a comment""" - pass + def set_machine_coordinates(self): + """Set machine coordinates""" + pass - def insert(self, text): - """APT style INSERT statement""" - pass + ############################################################################ + # Cutter radius compensation - def block_delete(self, on=False): - """block to ignore if block delete switch is on""" - pass + def use_CRC(self): + """CRC""" + return False - def variable(self, id): - """Insert a variable""" - pass + ############################################################################ + # Cycles - def variable_set(self, id, value): - """Set a variable""" - pass + def pattern(self): + """Simple pattern eg. circle, rect""" + pass - def probe_linear_centre_outside(self, x1=None, y1=None, depth=None, x2=None, y2=None ): - pass + def pocket(self): + """Pocket routine""" + pass - def probe_single_point(self, point_along_edge_x=None, point_along_edge_y=None, depth=None, retracted_point_x=None, retracted_point_y=None, destination_point_x=None, destination_point_y=None, intersection_variable_x=None, intersection_variable_y=None, probe_offset_x_component=None, probe_offset_y_component=None ): - pass + def profile(self): + """Profile routine""" + pass - def probe_downward_point(self, x=None, y=None, depth=None, intersection_variable_z=None): - pass + def drill(self, x=None, y=None, dwell=None, depthparams=None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): + """Drilling routines""" + pass - def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None ): - pass + # original prototype was: + # def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None): + # + # current call is like so: + # tap(x=10, y=10, z=0, tap_mode=0, depth=12.7, standoff=6.35, direction=0, pitch=1.25) + # just add tap_mode & direction parameters - def open_log_file(self, xml_file_name=None ): - pass + def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None, tap_mode=None, direction=None): + """Tapping routines""" + pass - def log_coordinate(self, x=None, y=None, z=None): - pass + def bore(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, feed_in=None, feed_out=None, stoppos=None, shift_back=None, shift_right=None, backbore=False, stop=False): + """Boring routines""" + pass - def log_message(self, message=None): - pass + def end_canned_cycle(self): + pass - def close_log_file(self): - pass + ############################################################################ + # Misc - def rapid_to_midpoint(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None): - pass + def comment(self, text): + """Insert a comment""" + pass - def rapid_to_intersection(self, x1, y1, x2, y2, x3, y3, x4, y4, intersection_x, intersection_y, ua_numerator, ua_denominator, ua, ub_numerator, ub): - pass + def insert(self, text): + """APT style INSERT statement""" + pass - def rapid_to_rotated_coordinate(self, x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final): - pass + def block_delete(self, on=False): + """block to ignore if block delete switch is on""" + pass - def set_path_control_mode(self, mode, motion_blending_tolerance, naive_cam_tolerance ): - pass + def variable(self, id): + """Insert a variable""" + pass - ############################################################################ - ## NC code creator for additive machines like RepRap + def variable_set(self, id, value): + """Set a variable""" + pass + def probe_linear_centre_outside(self, x1=None, y1=None, depth=None, x2=None, y2=None): + pass - def wipe(self): - """wipe routine""" - pass + def probe_single_point(self, point_along_edge_x=None, point_along_edge_y=None, depth=None, retracted_point_x=None, retracted_point_y=None, destination_point_x=None, destination_point_y=None, intersection_variable_x=None, intersection_variable_y=None, probe_offset_x_component=None, probe_offset_y_component=None): + pass - def extruder_on(self): - """Turn on the extruder""" - pass + def probe_downward_point(self, x=None, y=None, depth=None, intersection_variable_z=None): + pass - def extruder_off(self): - """turn off the extruder""" - pass + def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None): + pass - def set_extruder_flowrate(self, flowrate): - """Set the flowrate for the extruder""" - pass + def open_log_file(self, xml_file_name=None): + pass - def extruder_temp(self, temp): - """Set the extruder temp in celsius""" - pass + def log_coordinate(self, x=None, y=None, z=None): + pass - def fan_on(self): - """turn on the cooling fan""" - pass + def log_message(self, message=None): + pass - def fan_off(self): - """turn off the cooling fan""" - pass + def close_log_file(self): + pass - def build_bed_temp(self, temp): - """Set the bed temp in celsius""" - pass + def rapid_to_midpoint(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None): + pass - def chamber_temp(self, temp): - """Set the chamber temp in celsius""" - pass + def rapid_to_intersection(self, x1, y1, x2, y2, x3, y3, x4, y4, intersection_x, intersection_y, ua_numerator, ua_denominator, ua, ub_numerator, ub): + pass - def begin_ncblock(self): - # if the moves have come from backplotting nc code, then the nc code text can be given with these three functions - pass + def rapid_to_rotated_coordinate(self, x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final): + pass - def end_ncblock(self): - pass + def set_path_control_mode(self, mode, motion_blending_tolerance, naive_cam_tolerance): + pass - def add_text(self, s, col, cdata): - pass + ############################################################################ + # NC code creator for additive machines like RepRap + + def wipe(self): + """wipe routine""" + pass + + def extruder_on(self): + """Turn on the extruder""" + pass + + def extruder_off(self): + """turn off the extruder""" + pass + + def set_extruder_flowrate(self, flowrate): + """Set the flowrate for the extruder""" + pass + + def extruder_temp(self, temp): + """Set the extruder temp in celsius""" + pass + + def fan_on(self): + """turn on the cooling fan""" + pass + + def fan_off(self): + """turn off the cooling fan""" + pass + + def build_bed_temp(self, temp): + """Set the bed temp in celsius""" + pass + + def chamber_temp(self, temp): + """Set the chamber temp in celsius""" + pass + + def begin_ncblock(self): + # if the moves have come from backplotting nc code, then the nc code text can be given with these three functions + pass + + def end_ncblock(self): + pass + + def add_text(self, s, col, cdata): + pass ################################################################################ + creator = Creator() ############################################################################ -## Internals +# Internals + def write(s): - creator.write(s) + creator.write(s) + def output(filename): - creator.file_open(filename) + creator.file_open(filename) ############################################################################ -## Programs +# Programs + def program_begin(id, name=''): - creator.program_begin(id, name) + creator.program_begin(id, name) + def add_stock(type_name, params): - creator.add_stock(type_name, params) + creator.add_stock(type_name, params) + def program_stop(optional=False): - creator.program_stop(optional) + creator.program_stop(optional) + def program_end(): - creator.program_end() + creator.program_end() + def flush_nc(): - creator.flush_nc() + creator.flush_nc() ############################################################################ -## Subprograms +# Subprograms + def sub_begin(id, name=''): - creator.sub_begin(id, name) + creator.sub_begin(id, name) + def sub_call(id): - creator.sub_call(id) + creator.sub_call(id) + def sub_end(): - creator.sub_end() + creator.sub_end() ############################################################################ -## Settings +# Settings + def imperial(): - creator.imperial() + creator.imperial() + def metric(): - creator.metric() + creator.metric() + def absolute(): - creator.absolute() + creator.absolute() + def incremental(): - creator.incremental() + creator.incremental() + def polar(on=True): - creator.polar(on) + creator.polar(on) + def set_plane(plane): - creator.set_plane(plane) + creator.set_plane(plane) + def set_temporary_origin(x=None, y=None, z=None, a=None, b=None, c=None): - creator.set_temporary_origin(x,y,z,a,b,c) + creator.set_temporary_origin(x, y, z, a, b, c) + def remove_temporary_origin(): - creator.remove_temporary_origin() + creator.remove_temporary_origin() ############################################################################ -## Tools +# Tools + def tool_change(id): - creator.tool_change(id) + creator.tool_change(id) + def tool_defn(id, name='', params=None): - creator.tool_defn(id, name, params) + creator.tool_defn(id, name, params) + def offset_radius(id, radius=None): - creator.offset_radius(id, radius) + creator.offset_radius(id, radius) + def offset_length(id, length=None): - creator.offset_length(id, length) + creator.offset_length(id, length) + def current_tool(self): - return creator.current_tool() + return creator.current_tool() ############################################################################ -## Datums +# Datums + def datum_shift(x=None, y=None, z=None, a=None, b=None, c=None): - creator.datum_shift(x, y, z, a, b, c) + creator.datum_shift(x, y, z, a, b, c) + def datum_set(x=None, y=None, z=None, a=None, b=None, c=None): - creator.datum_set(x, y, z, a, b, c) + creator.datum_set(x, y, z, a, b, c) + def workplane(id): - creator.workplane(id) + creator.workplane(id) + def clearanceplane(z=None): - creator.clearanceplane(z) + creator.clearanceplane(z) ############################################################################ -## APT360 like Transformation Definitions -## These definitions were created while looking at Irvin Kraal's book on APT -## - Numerical Control Progamming in APT - page 211 +# APT360 like Transformation Definitions +# These definitions were created while looking at Irvin Kraal's book on APT +# - Numerical Control Progamming in APT - page 211 -def matrix(a1=None,b1=None,c1=None,a2=None,b2=None,c2=None,a3=None,b3=None,c3=None): - creator.matrix(a1,b1,c1,a2,b2,c2,a3,b3,c3) -def translate(x=None,y=None,z=None): - creator.translate(x,y,z) +def matrix(a1=None, b1=None, c1=None, a2=None, b2=None, c2=None, a3=None, b3=None, c3=None): + creator.matrix(a1, b1, c1, a2, b2, c2, a3, b3, c3) + + +def translate(x=None, y=None, z=None): + creator.translate(x, y, z) + + +def rotate(xyrot=None, yzrot=None, zxrot=None, angle=None): + creator.rotate(xyrot, yzrot, zxrot, angle) -def rotate(xyrot=None,yzrot=None,zxrot=None,angle=None): - creator.rotate(xyrot,yzrot,zxrot,angle) def scale(k=None): - creator.scale(k) + creator.scale(k) -def matrix_product(matrix1=None,matrix2=None): - creator.matrix_product(matrix1,matrix2) -def mirror_plane(plane1=None,plane2=None,plane3=None): - creator.mirror_plane(plane1,plane2,plane3) +def matrix_product(matrix1=None, matrix2=None): + creator.matrix_product(matrix1, matrix2) -def mirror_line(line=None): - creator.mirror_line(line) +def mirror_plane(plane1=None, plane2=None, plane3=None): + creator.mirror_plane(plane1, plane2, plane3) + + +def mirror_line(line=None): + creator.mirror_line(line) ############################################################################ ## Rates + Modes def feedrate(f): - creator.feedrate(f) + creator.feedrate(f) + def feedrate_hv(fh, fv): - creator.feedrate_hv(fh, fv) + creator.feedrate_hv(fh, fv) + def spindle(s, clockwise=True): - creator.spindle(s, clockwise) + creator.spindle(s, clockwise) + def coolant(mode=0): - creator.coolant(mode) + creator.coolant(mode) + def gearrange(gear=0): - creator.gearrange(gear) + creator.gearrange(gear) ############################################################################ -## Moves +# Moves + def rapid(x=None, y=None, z=None, a=None, b=None, c=None): - creator.rapid(x, y, z, a, b, c) + creator.rapid(x, y, z, a, b, c) + + +def feed(x=None, y=None, z=None, a=None, b=None, c=None): + creator.feed(x, y, z) -def feed(x=None, y=None, z=None, a = None, b = None, c = None): - creator.feed(x, y, z) def arc_cw(x=None, y=None, z=None, i=None, j=None, k=None, r=None): - creator.arc_cw(x, y, z, i, j, k, r) + creator.arc_cw(x, y, z, i, j, k, r) + def arc_ccw(x=None, y=None, z=None, i=None, j=None, k=None, r=None): - creator.arc_ccw(x, y, z, i, j, k, r) + creator.arc_ccw(x, y, z, i, j, k, r) + def dwell(t): - creator.dwell(t) + creator.dwell(t) + def rapid_home(x=None, y=None, z=None, a=None, b=None, c=None): - creator.rapid_home(x, y, z, a, b, c) + creator.rapid_home(x, y, z, a, b, c) + def rapid_unhome(): - creator.rapid_unhome() + creator.rapid_unhome() + def set_machine_coordinates(): - creator.set_machine_coordinates() + creator.set_machine_coordinates() ############################################################################ -## Cutter radius compensation +# Cutter radius compensation + def use_CRC(): - return creator.use_CRC() + return creator.use_CRC() + def CRC_nominal_path(): - return creator.CRC_nominal_path() + return creator.CRC_nominal_path() + + +def start_CRC(left=True, radius=0.0): + creator.start_CRC(left, radius) -def start_CRC(left = True, radius = 0.0): - creator.start_CRC(left, radius) def end_CRC(): - creator.end_CRC() + creator.end_CRC() ############################################################################ -## Cycles +# Cycles + def pattern(): - creator.pattern() + creator.pattern() + def pocket(): - creator.pocket() + creator.pocket() + def profile(): - creator.profile() + creator.profile() + -def drill(x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): - creator.drill(x, y, dwell, depthparams, retract_mode, spindle_mode, internal_coolant_on, rapid_to_clearance) +def drill(x=None, y=None, dwell=None, depthparams=None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): + creator.drill(x, y, dwell, depthparams, retract_mode, spindle_mode, + internal_coolant_on, rapid_to_clearance) def tap(x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None, tap_mode=None, direction=None): - creator.tap(x, y, z, zretract, depth, standoff, dwell_bottom, pitch, stoppos, spin_in, spin_out, tap_mode, direction) + creator.tap(x, y, z, zretract, depth, standoff, dwell_bottom, + pitch, stoppos, spin_in, spin_out, tap_mode, direction) + def bore(x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, feed_in=None, feed_out=None, stoppos=None, shift_back=None, shift_right=None, backbore=False, stop=False): - creator.bore(x, y, z, zretract, depth, standoff, dwell_Bottom, feed_in, feed_out, stoppos, shift_back, shift_right, backbore, stop) + creator.bore(x, y, z, zretract, depth, standoff, dwell_Bottom, feed_in, + feed_out, stoppos, shift_back, shift_right, backbore, stop) + def end_canned_cycle(): - creator.end_canned_cycle() + creator.end_canned_cycle() + def peck(count, first, last=None, step=0.0): - pecks = [] - peck = first - if (last == None) : last = first - for i in range(0,count): - pecks.append(peck) - if (peck - step > last) : peck -= step - return pecks + pecks = [] + peck = first + if (last == None): + last = first + for i in range(0, count): + pecks.append(peck) + if (peck - step > last): + peck -= step + return pecks ############################################################################ -## Misc +# Misc + def comment(text): - creator.comment(text) + creator.comment(text) + def insert(text): - creator.insert(text) + creator.insert(text) + def block_delete(on=False): - creator.block_delete(on) + creator.block_delete(on) + def variable(id): - creator.variable(id) + creator.variable(id) + def variable_set(id, value): - creator.variable_set(id, value) + creator.variable_set(id, value) + + +def probe_single_point(point_along_edge_x=None, point_along_edge_y=None, depth=None, retracted_point_x=None, retracted_point_y=None, destination_point_x=None, destination_point_y=None, intersection_variable_x=None, intersection_variable_y=None, probe_offset_x_component=None, probe_offset_y_component=None): + creator.probe_single_point(point_along_edge_x, point_along_edge_y, depth, retracted_point_x, retracted_point_y, destination_point_x, + destination_point_y, intersection_variable_x, intersection_variable_y, probe_offset_x_component, probe_offset_y_component) -def probe_single_point(point_along_edge_x=None, point_along_edge_y=None, depth=None, retracted_point_x=None, retracted_point_y=None, destination_point_x=None, destination_point_y=None, intersection_variable_x=None, intersection_variable_y=None, probe_offset_x_component=None, probe_offset_y_component=None ): - creator.probe_single_point(point_along_edge_x, point_along_edge_y, depth, retracted_point_x, retracted_point_y, destination_point_x, destination_point_y, intersection_variable_x, intersection_variable_y, probe_offset_x_component, probe_offset_y_component ) def probe_downward_point(x=None, y=None, depth=None, intersection_variable_z=None): - creator.probe_downward_point(x, y, depth, intersection_variable_z) + creator.probe_downward_point(x, y, depth, intersection_variable_z) + + +def report_probe_results(x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None): + creator.report_probe_results(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, + y4, z4, x5, y5, z5, x6, y6, z6, xml_file_name) -def report_probe_results(x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None ): - creator.report_probe_results(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, x5, y5, z5, x6, y6, z6, xml_file_name) -def open_log_file(xml_file_name=None ): - creator.open_log_file(xml_file_name) +def open_log_file(xml_file_name=None): + creator.open_log_file(xml_file_name) + def log_coordinate(x=None, y=None, z=None): - creator.log_coordinate(x, y, z) + creator.log_coordinate(x, y, z) + def log_message(message=None): - creator.log_message(message) + creator.log_message(message) + def close_log_file(): - creator.close_log_file() + creator.close_log_file() + def rapid_to_midpoint(x1=None, y1=None, z1=None, x2=None, y2=None, z2=None): - creator.rapid_to_midpoint(x1, y1, z1, x2, y2, z2) + creator.rapid_to_midpoint(x1, y1, z1, x2, y2, z2) + def rapid_to_intersection(x1, y1, x2, y2, x3, y3, x4, y4, intersection_x, intersection_y, ua_numerator, ua_denominator, ua, ub_numerator, ub): - creator.rapid_to_intersection(x1, y1, x2, y2, x3, y3, x4, y4, intersection_x, intersection_y, ua_numerator, ua_denominator, ua, ub_numerator, ub) + creator.rapid_to_intersection(x1, y1, x2, y2, x3, y3, x4, y4, intersection_x, + intersection_y, ua_numerator, ua_denominator, ua, ub_numerator, ub) + def rapid_to_rotated_coordinate(x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final): - creator.rapid_to_rotated_coordinate(x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final) + creator.rapid_to_rotated_coordinate( + x1, y1, x2, y2, ref_x, ref_y, x_current, y_current, x_final, y_final) -def set_path_control_mode(mode, motion_blending_tolerance, naive_cam_tolerance ): - creator.set_path_control_mode(mode, motion_blending_tolerance, naive_cam_tolerance ) + +def set_path_control_mode(mode, motion_blending_tolerance, naive_cam_tolerance): + creator.set_path_control_mode(mode, motion_blending_tolerance, naive_cam_tolerance) ############################################################################ -## NC code creator for additive machines like RepRap +# NC code creator for additive machines like RepRap + def wipe(): - creator.wipe() + creator.wipe() + def extruder_on(): - creator.extruder_on() + creator.extruder_on() + def extruder_off(): - creator.extruder_off() + creator.extruder_off() + def set_extruder_flowrate(flowrate): - creator.set_extruder_flowrate(flowrate) + creator.set_extruder_flowrate(flowrate) + def extruder_temp(temp=None): - creator.extruder_temp(temp) + creator.extruder_temp(temp) + def fan_on(): - creator.fan_on() + creator.fan_on() + def fan_off(): - creator.fan_off() + creator.fan_off() + def build_bed_temp(temp=None): - creator.build_bed_temp(temp) + creator.build_bed_temp(temp) + def chamber_temp(temp=None): - creator.chamber_temp(temp) + creator.chamber_temp(temp) diff --git a/scripts/addons/cam/nc/nc_read.py b/scripts/addons/cam/nc/nc_read.py index b8330107f..06ddd11e1 100644 --- a/scripts/addons/cam/nc/nc_read.py +++ b/scripts/addons/cam/nc/nc_read.py @@ -8,6 +8,7 @@ import math count = 0 + class Parser: def __init__(self, writer): @@ -23,23 +24,31 @@ def __del__(self): self.file_in.close() ############################################################################ - ## Internals + # Internals def readline(self): self.line = self.file_in.readline().rstrip() - if (len(self.line)) : return True - else : return False + if (len(self.line)): + return True + else: + return False def set_current_pos(self, x, y, z): - if (x != None) : - if self.absolute_flag or self.currentx == None: self.currentx = x - else: self.currentx = self.currentx + x - if (y != None) : - if self.absolute_flag or self.currenty == None: self.currenty = y - else: self.currenty = self.currenty + y - if (z != None) : - if self.absolute_flag or self.currentz == None: self.currentz = z - else: self.currentz = self.currentz + z + if (x != None): + if self.absolute_flag or self.currentx == None: + self.currentx = x + else: + self.currentx = self.currentx + x + if (y != None): + if self.absolute_flag or self.currenty == None: + self.currenty = y + else: + self.currenty = self.currenty + y + if (z != None): + if self.absolute_flag or self.currentz == None: + self.currentz = z + else: + self.currentz = self.currentz + z # def add_line(self, x, y, z, a, b, c): # if (x == None and y == None and z == None and a == None and b == None and c == None) : return @@ -98,10 +107,11 @@ def Parse(self, name): if self.t != None: if (self.m6 == True) or (self.need_m6_for_t_change == False): - self.writer.tool_change( self.t ) + self.writer.tool_change(self.t) if (self.drill): - if self.z != None: self.drillz = self.z + if self.z != None: + self.drillz = self.z self.writer.rapid(self.x, self.y, self.r) self.writer.feed(self.x, self.y, self.drillz) self.writer.feed(self.x, self.y, self.r) @@ -111,7 +121,7 @@ def Parse(self, name): else: if (self.move and not self.no_move): - if (self.arc==0): + if (self.arc == 0): if self.path_col == "feed": self.writer.feed(self.x, self.y, self.z) else: @@ -125,15 +135,17 @@ def Parse(self, name): else: if (self.arc_centre_positive == True) and (self.oldx != None) and (self.oldy != None): x = self.oldx - if self.x != None: x = self.x + if self.x != None: + x = self.x if (self.x > self.oldx) != (self.arc > 0): j = -j y = self.oldy - if self.y != None: y = self.y + if self.y != None: + y = self.y if (self.y > self.oldy) != (self.arc < 0): i = -i - #fix centre point + # fix centre point r = math.sqrt(i*i + j*j) p0 = area.Point(self.oldx, self.oldy) p1 = area.Point(x, y) @@ -143,7 +155,8 @@ def Parse(self, name): d = math.sqrt(r*r - h*h) n = area.Point(-v.y, v.x) n.normalize() - if self.arc == -1: d = -d + if self.arc == -1: + d = -d c = p0 + (v * 0.5) + (n * d) i = c.x j = c.y @@ -164,10 +177,11 @@ def Parse(self, name): self.writer.arc_cw(self.x, self.y, self.z, i, j, k) else: self.writer.arc_ccw(self.x, self.y, self.z, i, j, k) - if self.x != None: self.oldx = self.x - if self.y != None: self.oldy = self.y - if self.z != None: self.oldz = self.z + if self.x != None: + self.oldx = self.x + if self.y != None: + self.oldy = self.y + if self.z != None: + self.oldz = self.z self.writer.end_ncblock() - - diff --git a/scripts/addons/cam/nc/nclathe_read.py b/scripts/addons/cam/nc/nclathe_read.py index 25130d107..83d93cf87 100644 --- a/scripts/addons/cam/nc/nclathe_read.py +++ b/scripts/addons/cam/nc/nclathe_read.py @@ -15,10 +15,10 @@ def __init__(self): self.absolute_flag = True ############################################################################ - ## Internals + # Internals def files_open(self, name, oname=None): - if (oname == None ): + if (oname == None): oname = (name+'.nc.xml') self.file_in = open(name, 'r') self.file_out = open(oname, 'w') @@ -34,14 +34,16 @@ def files_close(self): def readline(self): self.line = self.file_in.readline().rstrip() - if (len(self.line)) : return True - else : return False + if (len(self.line)): + return True + else: + return False def write(self, s): self.file_out.write(s) ############################################################################ - ## Xml + # Xml def begin_ncblock(self): self.file_out.write('\t\n') @@ -54,94 +56,118 @@ def add_text(self, s, col=None, cdata=False): s.replace('"', '"') s.replace('<', '<') s.replace('>', '>') - if (cdata) : (cd1, cd2) = ('') - else : (cd1, cd2) = ('', '') - if (col != None) : self.file_out.write('\t\t'+cd1+s+cd2+'\n') - else : self.file_out.write('\t\t'+cd1+s+cd2+'\n') + if (cdata): + (cd1, cd2) = ('') + else: + (cd1, cd2) = ('', '') + if (col != None): + self.file_out.write('\t\t'+cd1+s+cd2+'\n') + else: + self.file_out.write('\t\t'+cd1+s+cd2+'\n') def set_mode(self, units=None): self.file_out.write('\t\t\n') def set_tool(self, number=None): self.file_out.write('\t\t\n') + if (number != None): + self.file_out.write(' number="'+str(number)+'"') + self.file_out.write(' />\n') def begin_path(self, col=None): - if (col != None) : self.file_out.write('\t\t\n') - else : self.file_out.write('\t\t\n') + if (col != None): + self.file_out.write('\t\t\n') + else: + self.file_out.write('\t\t\n') def end_path(self): self.file_out.write('\t\t\n') def add_line(self, x=None, y=None, z=None, a=None, b=None, c=None): - if (x == None and y == None and z == None and a == None and b == None and c == None) : return + if (x == None and y == None and z == None and a == None and b == None and c == None): + return self.file_out.write('\t\t\t\n') - def add_lathe_increment_line(self, u=None, w=None): -# needed for representing U and W moves in lathe code- these are non modal incremental moves -# U == X and W == Z - if (u == None and w == None ) : return + # needed for representing U and W moves in lathe code- these are non modal incremental moves + # U == X and W == Z + if (u == None and w == None): + return self.file_out.write('\t\t\t\n') - - - - - def add_arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, d=None): - if (x == None and y == None and z == None and i == None and j == None and k == None and r == None and d == None) : return + if (x == None and y == None and z == None and i == None and j == None and k == None and r == None and d == None): + return self.file_out.write('\t\t\t\n') def incremental(self): diff --git a/scripts/addons/cam/nc/num_reader.py b/scripts/addons/cam/nc/num_reader.py index 62c722cca..c9bd9cc91 100644 --- a/scripts/addons/cam/nc/num_reader.py +++ b/scripts/addons/cam/nc/num_reader.py @@ -4,6 +4,7 @@ # a base class for hpgl parsers, and maybe others + class NumReader(nc.Parser): def __init__(self): @@ -37,7 +38,7 @@ def add_word(self, color): self.parse_word = "" def Parse(self, name, oname=None): - self.files_open(name,oname) + self.files_open(name, oname) while self.readline(): self.begin_ncblock() @@ -59,4 +60,3 @@ def Parse(self, name, oname=None): self.end_ncblock() self.files_close() - diff --git a/scripts/addons/cam/nc/printbot3d.py b/scripts/addons/cam/nc/printbot3d.py index a450f818c..ac99559e9 100644 --- a/scripts/addons/cam/nc/printbot3d.py +++ b/scripts/addons/cam/nc/printbot3d.py @@ -8,6 +8,8 @@ import math ################################################################################ + + class CreatorPrintbot(iso_modal.CreatorIsoModal): def __init__(self): @@ -20,7 +22,7 @@ def write_blocknum(self): pass def set_plane(self, plane): - pass + pass def workplane(self, id): pass @@ -38,7 +40,7 @@ def set_extruder_flowrate(self, flowrate): self.spindle(flowrate, True) def extruder_temp(self, temp): - self.write((maker.codes.EXTRUDER_TEMP(temp)) + ('\n')) + self.write((maker.codes.EXTRUDER_TEMP(temp)) + ('\n')) # General def rapid(x=None, y=None, z=None, a=None, b=None, c=None): @@ -50,4 +52,5 @@ def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): ################################################################################ + nc.creator = CreatorPrintbot() diff --git a/scripts/addons/cam/nc/printbot3d_read.py b/scripts/addons/cam/nc/printbot3d_read.py index 79c3869f2..775e1ecee 100644 --- a/scripts/addons/cam/nc/printbot3d_read.py +++ b/scripts/addons/cam/nc/printbot3d_read.py @@ -3,6 +3,7 @@ # based on the iso reader + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/recreator.py b/scripts/addons/cam/nc/recreator.py index 5b25bf009..ce5f670bf 100644 --- a/scripts/addons/cam/nc/recreator.py +++ b/scripts/addons/cam/nc/recreator.py @@ -2,6 +2,7 @@ units = 1.0 + class Redirector(nc.Creator): def __init__(self, original): @@ -11,16 +12,19 @@ def __init__(self, original): self.x = None self.y = None self.z = None - if original.x != None: self.x = original.x * units - if original.y != None: self.y = original.y * units - if original.z != None: self.z = original.z * units + if original.x != None: + self.x = original.x * units + if original.y != None: + self.y = original.y * units + if original.z != None: + self.z = original.z * units self.imperial = False def cut_path(self): pass ############################################################################ - ## Programs + # Programs def write(self, s): self.original.write(s) @@ -53,7 +57,7 @@ def flush_nc(self): self.original.flush_nc() ############################################################################ - ## Subprograms + # Subprograms def sub_begin(self, id, name=None): self.cut_path() @@ -74,7 +78,7 @@ def enable_output(self): self.original.enable_output() ############################################################################ - ## Settings + # Settings def imperial(self): self.cut_path() @@ -103,14 +107,14 @@ def set_plane(self, plane): def set_temporary_origin(self, x=None, y=None, z=None, a=None, b=None, c=None): self.cut_path() - self.original.set_temporary_origin(x,y,z,a,b,c) + self.original.set_temporary_origin(x, y, z, a, b, c) def remove_temporary_origin(self): self.cut_path() self.original.remove_temporary_origin() ############################################################################ - ## Tools + # Tools def tool_change(self, id): self.cut_path() @@ -129,7 +133,7 @@ def offset_length(self, id, length=None): self.original.offset_length(id, length) ############################################################################ - ## Datums + # Datums def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None): self.cut_path() @@ -167,14 +171,17 @@ def gearrange(self, gear=0): self.original.gearrange(gear) ############################################################################ - ## Moves + # Moves def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): self.cut_path() self.original.rapid(x, y, z, a, b, c) - if x != None: self.x = x * units - if y != None: self.y = y * units - if z != None: self.z = z * units + if x != None: + self.x = x * units + if y != None: + self.y = y * units + if z != None: + self.z = z * units def cut_path(self): pass @@ -182,13 +189,16 @@ def cut_path(self): def z2(self, z): return z - def feed(self, x=None, y=None, z=None, a = None, b = None, c = None): + def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): px = self.x py = self.y pz = self.z - if x != None: self.x = x * units - if y != None: self.y = y * units - if z != None: self.z = z * units + if x != None: + self.x = x * units + if y != None: + self.y = y * units + if z != None: + self.z = z * units if self.x == None or self.y == None or self.z == None: self.cut_path() self.original.feed(x, y, z) @@ -199,15 +209,18 @@ def feed(self, x=None, y=None, z=None, a = None, b = None, c = None): self.original.feed(self.x/units, self.y/units, self.z2(self.z)/units) return - def arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, ccw = True): + def arc(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None, ccw=True): if self.x == None or self.y == None or self.z == None: raise "first attached move can't be an arc" px = self.x py = self.y pz = self.z - if x != None: self.x = x * units - if y != None: self.y = y * units - if z != None: self.z = z * units + if x != None: + self.x = x * units + if y != None: + self.y = y * units + if z != None: + self.z = z * units def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): self.arc(x, y, z, i, j, k, r, False) @@ -228,12 +241,12 @@ def rapid_unhome(self): self.original.rapid_unhome() ############################################################################ - ## Cutter radius compensation + # Cutter radius compensation def use_CRC(self): return self.original.use_CRC() - def start_CRC(self, left = True, radius = 0.0): + def start_CRC(self, left=True, radius=0.0): self.cut_path() self.original.start_CRC(left, radius) @@ -242,7 +255,7 @@ def end_CRC(self): self.original.end_CRC() ############################################################################ - ## Cycles + # Cycles def pattern(self): self.cut_path() @@ -259,30 +272,33 @@ def profile(self): self.cut_path() self.original.profile() - def circular_pocket(self, x=None, y=None, ToolDiameter=None, HoleDiameter=None, ClearanceHeight=None, StartHeight=None, MaterialTop=None, FeedRate=None, SpindleRPM=None, HoleDepth=None, DepthOfCut=None, StepOver=None ): + def circular_pocket(self, x=None, y=None, ToolDiameter=None, HoleDiameter=None, ClearanceHeight=None, StartHeight=None, MaterialTop=None, FeedRate=None, SpindleRPM=None, HoleDepth=None, DepthOfCut=None, StepOver=None): self.cut_path() - self.circular_pocket(x, y, ToolDiameter, HoleDiameter, ClearanceHeight, StartHeight, MaterialTop, FeedRate, SpindleRPM, HoleDepth, DepthOfCut, StepOver) + self.circular_pocket(x, y, ToolDiameter, HoleDiameter, ClearanceHeight, StartHeight, + MaterialTop, FeedRate, SpindleRPM, HoleDepth, DepthOfCut, StepOver) - def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): + def drill(self, x=None, y=None, dwell=None, depthparams=None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): self.cut_path() - self.original.drill(x, y, dwell, depthparams, spindle_mode, internal_coolant_on, rapid_to_clearance) + self.original.drill(x, y, dwell, depthparams, spindle_mode, + internal_coolant_on, rapid_to_clearance) # argument list adapted for compatibility with Tapping module # wild guess - I'm unsure about the purpose of this file and wether this works -haberlerm def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None, tap_mode=None, direction=None): self.cut_path() - self.original.tap( x, y, self.z2(z), self.z2(zretract), depth, standoff, dwell_bottom, pitch, stoppos, spin_in, spin_out, tap_mode, direction) - + self.original.tap(x, y, self.z2(z), self.z2(zretract), depth, standoff, + dwell_bottom, pitch, stoppos, spin_in, spin_out, tap_mode, direction) def bore(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, feed_in=None, feed_out=None, stoppos=None, shift_back=None, shift_right=None, backbore=False, stop=False): self.cut_path() - self.original.bore(x, y, self.z2(z), self.z2(zretract), depth, standoff, dwell_Bottom, feed_in, feed_out, stoppos, shift_back, shift_right, backbore, stop) + self.original.bore(x, y, self.z2(z), self.z2(zretract), depth, standoff, dwell_Bottom, + feed_in, feed_out, stoppos, shift_back, shift_right, backbore, stop) def end_canned_cycle(self): self.original.end_canned_cycle() ############################################################################ - ## Misc + # Misc def comment(self, text): self.cut_path() diff --git a/scripts/addons/cam/nc/rez2.py b/scripts/addons/cam/nc/rez2.py index 8f9ee1ed4..bbaa82882 100644 --- a/scripts/addons/cam/nc/rez2.py +++ b/scripts/addons/cam/nc/rez2.py @@ -11,6 +11,8 @@ import circular_pocket as circular ################################################################################ + + class Creator(nc.Creator): def __init__(self): @@ -36,145 +38,150 @@ def __init__(self): self.y = 0 self.z = 500 - self.x1=0 - self.x2=0 - self.y1=0 - self.y2=0 - self.SPACE=' ' + self.x1 = 0 + self.x2 = 0 + self.y1 = 0 + self.y2 = 0 + self.SPACE = ' ' self.fmt = self.FORMAT_MM() - pass + pass ############################################################################ - ## Internals + # Internals def FORMAT_IN(self): return('%.5f') def FORMAT_MM(self): return('%.3f') - def COMMENT(self,comment): return( ('(%s)' % comment ) ) + def COMMENT(self, comment): return(('(%s)' % comment)) def write_feedrate(self): - #self.write(self.f) + # self.write(self.f) self.f = '' def write_preps(self): - #self.write(self.g) + # self.write(self.g) self.g = '' def write_misc(self): #if (len(self.m)) : self.write(self.m.pop()) - pass + pass def write_blocknum(self): #self.write(iso.BLOCK % self.n) - #self.write(iso.SPACE) + # self.write(iso.SPACE) #self.n += 10 - pass + pass def write_spindle(self): - #self.write(self.s) + # self.write(self.s) #self.s = '' - pass + pass ############################################################################ - ## Programs + # Programs def program_begin(self, id, name=''): #self.write((iso.PROGRAM % id) + iso.SPACE + (iso.COMMENT % name)) #self.write("// program v jazyku"+self.SPACE+"REZ\nMA 0.0000 , 0.0000\n") - self.write("// program v jazyku"+self.SPACE+"REZ\nGO_LEFT\nMA 0.0000 , 0.0000\n") - #self.write('\n') + self.write("// program v jazyku"+self.SPACE+"REZ\nGO_LEFT\nMA 0.0000 , 0.0000\n") + # self.write('\n') def program_stop(self, optional=False): - #self.write_blocknum() + # self.write_blocknum() #if (optional) : self.write(iso.STOP_OPTIONAL + '\n') - #else : self.write(iso.STOP + '\n') - self.write("// stop programu v jazyku REZ\n") + # else : self.write(iso.STOP + '\n') + self.write("// stop programu v jazyku REZ\n") def program_end(self): - #self.write_blocknum() + # self.write_blocknum() + #self.write(iso.PROGRAM_END + '\n') + self.write('PL_OFF \n') + self.write('PARK \n') + self.write('FS \n') + self.write('MA 0.0000 , 0.0000\n') + self.write("// koniec programu v jazyku REZ\n") #self.write(iso.PROGRAM_END + '\n') - self.write('PL_OFF \n') - self.write('PARK \n') - self.write('FS \n') - self.write('MA 0.0000 , 0.0000\n') - self.write("// koniec programu v jazyku REZ\n") - #self.write(iso.PROGRAM_END + '\n') + def flush_nc(self): - #self.write_blocknum() + # self.write_blocknum() self.write_preps() self.write_misc() - #self.write('\n') + # self.write('\n') ############################################################################ - ## Subprograms + # Subprograms def sub_begin(self, id, name=''): #self.write((iso.PROGRAM % id) + iso.SPACE + (iso.COMMENT % name)) - #self.write('\n') - pass + # self.write('\n') + pass + def sub_call(self, id): - #self.write_blocknum() + # self.write_blocknum() #self.write((iso.SUBPROG_CALL % id) + '\n') - #self.write('\n') - pass + # self.write('\n') + pass def sub_end(self): - #self.write_blocknum() + # self.write_blocknum() #self.write(iso.SUBPROG_END + '\n') - #self.write('\n') - pass + # self.write('\n') + pass ############################################################################ - ## Settings + # Settings def imperial(self): #self.g += iso.IMPERIAL #self.fmt = iso.FORMAT_IN - #self.write('\n') - pass + # self.write('\n') + pass def metric(self): #self.g += iso.METRIC #self.fmt = iso.FORMAT_MM - #self.write('\n') - pass + # self.write('\n') + pass def absolute(self): #self.g += iso.ABSOLUTE - #self.write('\n') - pass + # self.write('\n') + pass def incremental(self): #self.g += iso.INCREMENTAL - #self.write('\n') - pass + # self.write('\n') + pass + def polar(self, on=True): #if (on) : self.g += iso.POLAR_ON - #else : self.g += iso.POLAR_OFF - #self.write('\n') - pass + # else : self.g += iso.POLAR_OFF + # self.write('\n') + pass + def set_plane(self, plane): #if (plane == 0) : self.g += iso.PLANE_XY - #elif (plane == 1) : self.g += iso.PLANE_XZ - #elif (plane == 2) : self.g += iso.PLANE_YZ - #self.write('\n') - pass + # elif (plane == 1) : self.g += iso.PLANE_XZ + # elif (plane == 2) : self.g += iso.PLANE_YZ + # self.write('\n') + pass ############################################################################ - ## Tools + # Tools def tool_change(self, id): - #self.write_blocknum() + # self.write_blocknum() #self.write((iso.TOOL % id) + '\n') - #self.write('\n') - pass + # self.write('\n') + pass + def tool_defn(self, id, name='', params=None): - pass + pass def offset_radius(self, id, radius=None): - pass + pass def offset_length(self, id, length=None): - pass + pass ############################################################################ - ## Datums + # Datums def datum_shift(self, x=None, y=None, z=None, a=None, b=None, c=None): pass @@ -185,142 +192,143 @@ def datum_set(self, x=None, y=None, z=None, a=None, b=None, c=None): # This is the coordinate system we're using. G54->G59, G59.1, G59.2, G59.3 # These are selected by values from 1 to 9 inclusive. def workplane(self, id): - #if ((id >= 1) and (id <= 6)): - # self.g += iso.WORKPLANE % (id + iso.WORKPLANE_BASE) - #if ((id >= 7) and (id <= 9)): - # self.g += ((iso.WORKPLANE % (6 + iso.WORKPLANE_BASE)) + ('.%i' % (id - 6))) - pass + # if ((id >= 1) and (id <= 6)): + # self.g += iso.WORKPLANE % (id + iso.WORKPLANE_BASE) + # if ((id >= 7) and (id <= 9)): + # self.g += ((iso.WORKPLANE % (6 + iso.WORKPLANE_BASE)) + ('.%i' % (id - 6))) + pass ############################################################################ ## Rates + Modes def feedrate(self, f): #self.f = iso.FEEDRATE + (self.fmt % f) #self.fhv = False - pass + pass def feedrate_hv(self, fh, fv): #self.fh = fh #self.fv = fv #self.fhv = True - pass + pass + def calc_feedrate_hv(self, h, v): #l = math.sqrt(h*h+v*v) #if (h == 0) : self.f = iso.FEEDRATE + (self.fmt % self.fv) - #elif (v == 0) : self.f = iso.FEEDRATE + (self.fmt % self.fh) - #else: - # self.f = iso.FEEDRATE + (self.fmt % (self.fh * l * min([1/h, 1/v]))) - pass + # elif (v == 0) : self.f = iso.FEEDRATE + (self.fmt % self.fh) + # else: + # self.f = iso.FEEDRATE + (self.fmt % (self.fh * l * min([1/h, 1/v]))) + pass def spindle(self, s, clockwise): - #if s < 0: clockwise = not clockwise - #s = abs(s) + #if s < 0: clockwise = not clockwise + #s = abs(s) #self.s = iso.SPINDLE % s - #if clockwise: - # self.s = self.s + iso.SPINDLE_CW - #else: - # self.s = self.s + iso.SPINDLE_CCW - pass + # if clockwise: + # self.s = self.s + iso.SPINDLE_CW + # else: + # self.s = self.s + iso.SPINDLE_CCW + pass def coolant(self, mode=0): #if (mode <= 0) : self.m.append(iso.COOLANT_OFF) - #elif (mode == 1) : self.m.append(iso.COOLANT_MIST) - #elif (mode == 2) : self.m.append(iso.COOLANT_FLOOD) - pass + # elif (mode == 1) : self.m.append(iso.COOLANT_MIST) + # elif (mode == 2) : self.m.append(iso.COOLANT_FLOOD) + pass def gearrange(self, gear=0): #if (gear <= 0) : self.m.append(iso.GEAR_OFF) - #elif (gear <= 4) : self.m.append(iso.GEAR % (gear + GEAR_BASE)) - pass + # elif (gear <= 4) : self.m.append(iso.GEAR % (gear + GEAR_BASE)) + pass ############################################################################ - ## Moves + # Moves - #def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): - def rapid(self, x=0.0000, y=0.0000, z=0.0000, a=0.0000, b=0.0000, c=0.0000,how=False): - #self.write_blocknum() + # def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): + def rapid(self, x=0.0000, y=0.0000, z=0.0000, a=0.0000, b=0.0000, c=0.0000, how=False): + # self.write_blocknum() if (x == None): - if (y == None): - return - print 'rychlopsuv' - print x - print y - print z - print a - print b - print c - self.write('PL_OFF\n') - self.write('PARK\n') - self.write('FS\n') - self.write('MA ') - #self.write_preps() - #if (x != None): + if (y == None): + return + print 'rychlopsuv' + print x + print y + print z + print a + print b + print c + self.write('PL_OFF\n') + self.write('PARK\n') + self.write('FS\n') + self.write('MA ') + # self.write_preps() + # if (x != None): # dx = x - self.x # self.write(iso.X + (self.fmt % x)) # self.x = x - #if (y != None): + # if (y != None): # dy = y - self.y # self.write(iso.Y + (self.fmt % y)) # self.y = y - #if (z != None): + # if (z != None): # dz = z - self.z # self.write(iso.Z + (self.fmt % z)) # self.z = z #if (a != None) : self.write(iso.A + (iso.FORMAT_ANG % a)) #if (b != None) : self.write(iso.B + (iso.FORMAT_ANG % b)) #if (c != None) : self.write(iso.C + (iso.FORMAT_ANG % c)) - #self.write_spindle() - #self.write_misc() - self.write((' %.4f' %x)) - #self.write(('%f' %x) ) - self.write(' , ') - self.write((' %.4f' %y)) - self.write('\n') - self.write('SS\n') - self.write('AD_W\n') - self.write('PL_ON') - self.write('\n') - self.x=x - self.y=y - - def feed(self, x=0.0000, y=0.0000, z=0.0000,how=False): + # self.write_spindle() + # self.write_misc() + self.write((' %.4f' % x)) + #self.write(('%f' %x) ) + self.write(' , ') + self.write((' %.4f' % y)) + self.write('\n') + self.write('SS\n') + self.write('AD_W\n') + self.write('PL_ON') + self.write('\n') + self.x = x + self.y = y + + def feed(self, x=0.0000, y=0.0000, z=0.0000, how=False): if self.same_xyz(x, y, z): - return + return if (x == None): - if (y == None): - return - - print 'MA rez' - print x - print y - print z - #self.write_blocknum() - #self.write(iso.FEED) - #self.write_preps() + if (y == None): + return + + print 'MA rez' + print x + print y + print z + # self.write_blocknum() + # self.write(iso.FEED) + # self.write_preps() #dx = dy = dz = 0 - #if (x != None): + # if (x != None): # dx = x - self.x # self.write(iso.X + (self.fmt % x)) # self.x = x - #if (y != None): + # if (y != None): # dy = y - self.y # self.write(iso.Y + (self.fmt % y)) # self.y = y - #if (z != None): + # if (z != None): # dz = z - self.z # self.write(iso.Z + (self.fmt % z)) # self.z = z #if (self.fhv) : self.calc_feedrate_hv(math.sqrt(dx*dx+dy*dy), math.fabs(dz)) - #self.write_feedrate() - #self.write_spindle() - #self.write_misc() + # self.write_feedrate() + # self.write_spindle() + # self.write_misc() self.write('MA ') - self.write((' %.4f' %x)) - #self.write(('%f' %x) ) - self.write(' , ') - self.write((' %.4f' %y)) - self.write('\n') - self.x=x - self.y=y + self.write((' %.4f' % x)) + #self.write(('%f' %x) ) + self.write(' , ') + self.write((' %.4f' % y)) + self.write('\n') + self.x = x + self.y = y def same_xyz(self, x=None, y=None, z=None): if (x != None): @@ -336,96 +344,98 @@ def same_xyz(self, x=None, y=None, z=None): return True def arc(self, cw, x=0.0000, y=0.0000, z=0.0000, i=0.0000, j=0.0000, k=0.0000, r=0.0000): - if self.same_xyz(x, y, z): return - if (x == None): - if (y == None): - return - #self.write_blocknum() - print 'ARC rez' - print x - print y - print z - print i - print j - print k - print r - self.write('C ') - # if cw: self.write(iso.ARC_CW) - # else: self.write(iso.ARC_CCW) - # self.write_preps() - #if (x != None): + if self.same_xyz(x, y, z): + return + if (x == None): + if (y == None): + return + # self.write_blocknum() + print 'ARC rez' + print x + print y + print z + print i + print j + print k + print r + self.write('C ') + # if cw: self.write(iso.ARC_CW) + # else: self.write(iso.ARC_CCW) + # self.write_preps() + # if (x != None): # self.write(iso.X + (self.fmt % x)) # self.x = x - #if (y != None): + # if (y != None): # self.write(iso.Y + (self.fmt % y)) # self.y = y - #if (z != None): - # self.write(iso.Z + (self.fmt % z)) - # self.z = z + # if (z != None): + # self.write(iso.Z + (self.fmt % z)) + # self.z = z #if (i != None) : self.write(iso.CENTRE_X + (self.fmt % i)) #if (j != None) : self.write(iso.CENTRE_Y + (self.fmt % j)) #if (k != None) : self.write(iso.CENTRE_Z + (self.fmt % k)) #if (r != None) : self.write(iso.RADIUS + (self.fmt % r)) # use horizontal feed rate #if (self.fhv) : self.calc_feedrate_hv(1, 0) - #self.write_feedrate() - #self.write_spindle() - #self.write_misc() - self.write((' %.4f' %(self.x + i))) - #self.write(('%f' %x) ) - self.write(' , ') - self.write((' %.4f' %(self.y + j))) - - self.write(' , ') - angle=0.0000 - - self.x1=-i - self.y1=-j - self.x2=x-(self.x+i) - self.y2=y-(self.y+j) - - dx=self.x1*self.x2 - dy=self.y1*self.y2 - ssucin=dx+dy - r=math.sqrt((i*i)+(j*j)) - - #dx=i*(x-(self.x + i)) - #dy=j*(y-(self.y + j)) - #ssucin=dx+dy - #r=math.sqrt((i*i)+(j*j)) - ratio=ssucin/(r*r) - angle= math.acos(ratio) * 180 / math.pi - print 'angle' - print angle + # self.write_feedrate() + # self.write_spindle() + # self.write_misc() + self.write((' %.4f' % (self.x + i))) + #self.write(('%f' %x) ) + self.write(' , ') + self.write((' %.4f' % (self.y + j))) + + self.write(' , ') + angle = 0.0000 + + self.x1 = -i + self.y1 = -j + self.x2 = x-(self.x+i) + self.y2 = y-(self.y+j) + + dx = self.x1*self.x2 + dy = self.y1*self.y2 + ssucin = dx+dy + r = math.sqrt((i*i)+(j*j)) + + #dx=i*(x-(self.x + i)) + #dy=j*(y-(self.y + j)) + # ssucin=dx+dy + # r=math.sqrt((i*i)+(j*j)) + ratio = ssucin/(r*r) + angle = math.acos(ratio) * 180 / math.pi + print 'angle' + print angle # while(angle>=90.0001): # if(cw): self.write((' -90.0000\n' )) # else: self.write((' 90.0000\n')) - #self.write(('90.0000\n' )) + #self.write(('90.0000\n' )) # self.write('C ') # self.write((' %.4f' %( self.x + i) )) # self.write(' , ') # self.write((' %.4f' %( self.y + j))) # self.write(' , ') # angle-=90 - if(cw): self.write((' %.4f' %(-angle))) - else: self.write((' %.4f' %(angle))) - - - #self.write((', %.4f' % )) - self.write('\n') - self.x=x - self.y=y - self.write('// stred') - self.write((' %f' %i)) - self.write(' ') - self.write( (' %f' %j)) - self.write('\n') - - self.write('// koniec') - self.write((' %f' %self.x)) - self.write(' ') - self.write( (' %f' %self.y)) - self.write('\n') + if(cw): + self.write((' %.4f' % (-angle))) + else: + self.write((' %.4f' % (angle))) + + # self.write((', %.4f' % )) + self.write('\n') + self.x = x + self.y = y + self.write('// stred') + self.write((' %f' % i)) + self.write(' ') + self.write((' %f' % j)) + self.write('\n') + + self.write('// koniec') + self.write((' %f' % self.x)) + self.write(' ') + self.write((' %f' % self.y)) + self.write('\n') def arc_cw(self, x=None, y=None, z=None, i=None, j=None, k=None, r=None): self.arc(True, x, y, z, i, j, k, r) @@ -447,7 +457,7 @@ def rapid_unhome(self): pass ############################################################################ - ## Cycles + # Cycles def pattern(self): pass @@ -458,54 +468,56 @@ def pocket(self): def profile(self): pass - def circular_pocket(self, x=None, y=None, ToolDiameter=None, HoleDiameter=None, ClearanceHeight=None, StartHeight=None, MaterialTop=None, FeedRate=None, SpindleRPM=None, HoleDepth=None, DepthOfCut=None, StepOver=None ): - self.write_preps() - circular.pocket.block_number = self.n - (self.g_code, self.n) = circular.pocket.GeneratePath( x,y, ToolDiameter, HoleDiameter, ClearanceHeight, StartHeight, MaterialTop, FeedRate, SpindleRPM, HoleDepth, DepthOfCut, StepOver ) - self.write(self.g_code) - - # The drill routine supports drilling (G81), drilling with dwell (G82) and peck drilling (G83). - # The x,y,z values are INITIAL locations (above the hole to be made. This is in contrast to - # the Z value used in the G8[1-3] cycles where the Z value is that of the BOTTOM of the hole. - # Instead, this routine combines the Z value and the depth value to determine the bottom of - # the hole. - # - # The standoff value is the distance up from the 'z' value (normally just above the surface) where the bit retracts - # to in order to clear the swarf. This combines with 'z' to form the 'R' value in the G8[1-3] cycles. - # - # The peck_depth value is the incremental depth (Q value) that tells the peck drilling - # cycle how deep to go on each peck until the full depth is achieved. - # - # NOTE: This routine forces the mode to absolute mode so that the values passed into - # the G8[1-3] cycles make sense. I don't know how to find the mode to revert it so I won't - # revert it. I must set the mode so that I can be sure the values I'm passing in make - # sense to the end-machine. - # - def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): - if (standoff == None): - # This is a bad thing. All the drilling cycles need a retraction (and starting) height. - return - - if (z == None): return # We need a Z value as well. This input parameter represents the top of the hole + def circular_pocket(self, x=None, y=None, ToolDiameter=None, HoleDiameter=None, ClearanceHeight=None, StartHeight=None, MaterialTop=None, FeedRate=None, SpindleRPM=None, HoleDepth=None, DepthOfCut=None, StepOver=None): + self.write_preps() + circular.pocket.block_number = self.n + (self.g_code, self.n) = circular.pocket.GeneratePath(x, y, ToolDiameter, HoleDiameter, + ClearanceHeight, StartHeight, MaterialTop, FeedRate, SpindleRPM, HoleDepth, DepthOfCut, StepOver) + self.write(self.g_code) + + # The drill routine supports drilling (G81), drilling with dwell (G82) and peck drilling (G83). + # The x,y,z values are INITIAL locations (above the hole to be made. This is in contrast to + # the Z value used in the G8[1-3] cycles where the Z value is that of the BOTTOM of the hole. + # Instead, this routine combines the Z value and the depth value to determine the bottom of + # the hole. + # + # The standoff value is the distance up from the 'z' value (normally just above the surface) where the bit retracts + # to in order to clear the swarf. This combines with 'z' to form the 'R' value in the G8[1-3] cycles. + # + # The peck_depth value is the incremental depth (Q value) that tells the peck drilling + # cycle how deep to go on each peck until the full depth is achieved. + # + # NOTE: This routine forces the mode to absolute mode so that the values passed into + # the G8[1-3] cycles make sense. I don't know how to find the mode to revert it so I won't + # revert it. I must set the mode so that I can be sure the values I'm passing in make + # sense to the end-machine. + # + def drill(self, x=None, y=None, dwell=None, depthparams=None, retract_mode=None, spindle_mode=None, internal_coolant_on=None, rapid_to_clearance=None): + if (standoff == None): + # This is a bad thing. All the drilling cycles need a retraction (and starting) height. + return + + if (z == None): + return # We need a Z value as well. This input parameter represents the top of the hole self.write_blocknum() self.write_preps() - if (peck_depth != 0): - # We're pecking. Let's find a tree. - self.write(iso.PECK_DRILL + iso.SPACE + iso.PECK_DEPTH + (self.fmt % peck_depth )) - else: - # We're either just drilling or drilling with dwell. - if (dwell == 0): - # We're just drilling. - self.write(iso.DRILL + iso.SPACE) - else: - # We're drilling with dwell. - self.write(iso.DRILL_WITH_DWELL + (iso.FORMAT_DWELL % dwell) + iso.SPACE) - - # Set the retraction point to the 'standoff' distance above the starting z height. - retract_height = z + standoff - #self.write(iso.RETRACT + (self.fmt % retract_height)) + if (peck_depth != 0): + # We're pecking. Let's find a tree. + self.write(iso.PECK_DRILL + iso.SPACE + iso.PECK_DEPTH + (self.fmt % peck_depth)) + else: + # We're either just drilling or drilling with dwell. + if (dwell == 0): + # We're just drilling. + self.write(iso.DRILL + iso.SPACE) + else: + # We're drilling with dwell. + self.write(iso.DRILL_WITH_DWELL + (iso.FORMAT_DWELL % dwell) + iso.SPACE) + + # Set the retraction point to the 'standoff' distance above the starting z height. + retract_height = z + standoff + #self.write(iso.RETRACT + (self.fmt % retract_height)) if (x != None): dx = x - self.x self.write(iso.X + (self.fmt % x) + iso.SPACE) @@ -515,11 +527,13 @@ def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=Non self.write(iso.Y + (self.fmt % y) + iso.SPACE) self.y = y - dz = (z + standoff) - self.z - # In the end, we will be standoff distance above the z value passed in. - self.write(iso.Z + (self.fmt % (z - depth)) + iso.SPACE) # This is the 'z' value for the bottom of the hole. - self.z = (z + standoff) # We want to remember where z is at the end (at the top of the hole) - self.write(iso.RETRACT + (self.fmt % retract_height)) + dz = (z + standoff) - self.z + # In the end, we will be standoff distance above the z value passed in. + # This is the 'z' value for the bottom of the hole. + self.write(iso.Z + (self.fmt % (z - depth)) + iso.SPACE) + # We want to remember where z is at the end (at the top of the hole) + self.z = (z + standoff) + self.write(iso.RETRACT + (self.fmt % retract_height)) self.write_spindle() self.write_misc() self.write('\n') @@ -529,6 +543,8 @@ def drill(self, x=None, y=None, dwell=None, depthparams = None, retract_mode=Non # pass # argument list adapted for compatibility with Tapping module + + def tap(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, dwell_bottom=None, pitch=None, stoppos=None, spin_in=None, spin_out=None, tap_mode=None, direction=None): pass @@ -536,10 +552,10 @@ def bore(self, x=None, y=None, z=None, zretract=None, depth=None, standoff=None, pass ############################################################################ - ## Misc + # Misc def comment(self, text): - self.write('// ' + (self.COMMENT( text)) + '\n') + self.write('// ' + (self.COMMENT(text)) + '\n') def variable(self, id): return (iso.VARIABLE % id) @@ -550,4 +566,5 @@ def variable_set(self, id, value): ################################################################################ + nc.creator = Creator() diff --git a/scripts/addons/cam/nc/rez2_read.py b/scripts/addons/cam/nc/rez2_read.py index 277007c76..b562fbfc2 100644 --- a/scripts/addons/cam/nc/rez2_read.py +++ b/scripts/addons/cam/nc/rez2_read.py @@ -9,21 +9,23 @@ import math ################################################################################ -class Parser(nc.Parser): +class Parser(nc.Parser): + def __init__(self, writer): nc.Parser.__init__(self, writer) - #self.pattern_main = re.compile('(\s+|\w(?:[+])?\d*(?:\.\d*)?|\w\#\d+|\(.*?\)|\#\d+\=(?:[+])?\d*(?:\.\d*)?)') + # self.pattern_main = re.compile('(\s+|\w(?:[+])?\d*(?:\.\d*)?|\w\#\d+|\(.*?\)|\#\d+\=(?:[+])?\d*(?:\.\d*)?)') - #rada - self.pattern_main = re.compile(r'(\s+|,|-?\w\+?\d*(?:\.\d*)?|\w#\d+|\(.*?\)|#\d+=\+?\d*(?:\.\d*)?)') - #self.pattern_main = re.compile('\s+\w') - #self.pattern_main = re.compile('(\s+|\w(?:[+])?[+-\w]\d*(?:\.\d*)?|\w\#[+-\w]\d+|\(.*?\)|[\#[+-\w]\d+\=(?:[+])?[+-\w]\d*(?:\.\d*)?)') - #self.pattern_main = re.compile('\s\w[\S]\w,\w[+-\w]\d*\w,\w[+-\w]\d*\w,\w[+-\w]\d*') - #self.pattern_main = re.compile('(\s|\w(?:)?\d*(?:\.\d*)?|\w\#\d+|\(.*?\)|\#\d+\=(?:)?\d*(?:\.\d*)?)') - #self.pattern_main = re.compile(' ') + # rada + self.pattern_main = re.compile( + r'(\s+|,|-?\w\+?\d*(?:\.\d*)?|\w#\d+|\(.*?\)|#\d+=\+?\d*(?:\.\d*)?)') + #self.pattern_main = re.compile('\s+\w') + # self.pattern_main = re.compile('(\s+|\w(?:[+])?[+-\w]\d*(?:\.\d*)?|\w\#[+-\w]\d+|\(.*?\)|[\#[+-\w]\d+\=(?:[+])?[+-\w]\d*(?:\.\d*)?)') + #self.pattern_main = re.compile('\s\w[\S]\w,\w[+-\w]\d*\w,\w[+-\w]\d*\w,\w[+-\w]\d*') + # self.pattern_main = re.compile('(\s|\w(?:)?\d*(?:\.\d*)?|\w\#\d+|\(.*?\)|\#\d+\=(?:)?\d*(?:\.\d*)?)') + #self.pattern_main = re.compile(' ') self.a = 0 self.b = 0 @@ -39,252 +41,266 @@ def __init__(self, writer): self.x = 0 self.y = 0 self.z = 500 - self.FS = 1 - self.endx=0 - self.endy=0 - self.startx=0 - self.starty=0 - self.x1=0 - self.y1=0 - self.dy=0 - self.dx=0 - self.angle=0 - self.SPACE = ' ' + self.FS = 1 + self.endx = 0 + self.endy = 0 + self.startx = 0 + self.starty = 0 + self.x1 = 0 + self.y1 = 0 + self.dy = 0 + self.dx = 0 + self.angle = 0 + self.SPACE = ' ' def add_text(self, s, col=None): s.replace('&', '&') s.replace('"', '"') s.replace('<', '<') s.replace('>', '>') - s+=self.SPACE+'\n' - if (col != None) : self.file_out.write('\t\t'+s+' \n') - else : self.file_out.write('\t\t'+s+' \n') - #def add_text(self, s, col=None): + s += self.SPACE+'\n' + if (col != None): + self.file_out.write('\t\t'+s+' \n') + else: + self.file_out.write('\t\t'+s+' \n') + # def add_text(self, s, col=None): # if (col != None) : self.file_out.write('\t\t'+s+'\n') # else : self.file_out.write('\t\t'+s+'\n') def Parse(self, name, oname=None): - self.files_open(name,oname) + self.files_open(name, oname) while (self.readline()): self.begin_ncblock() - move = False; - arc = 0; + move = False + arc = 0 path_col = None - col=None - if(self.line[0]=='C'): col = "axis" - if(self.line[0]=='M' and self.line[1]=='A'): col = "feed" - if (self.line[0] == "/" and self.line[1] == "/") : col = "comment" + col = None + if(self.line[0] == 'C'): + col = "axis" + if(self.line[0] == 'M' and self.line[1] == 'A'): + col = "feed" + if (self.line[0] == "/" and self.line[1] == "/"): + col = "comment" - if (self.FS==1 and not (self.line[0]=='S' and self.line[1]=='S') and col=="feed" ): col="rapid" - self.add_text(self.line, col) + if (self.FS == 1 and not (self.line[0] == 'S' and self.line[1] == 'S') and col == "feed"): + col = "rapid" + self.add_text(self.line, col) #words = self.pattern_main.findall(self.line) - words=self.line.split() - #print self.line - #print ' AAAA ' - words[0]=words[0]+self.SPACE - print words - for word in words: + words = self.line.split() + # print self.line + # print ' AAAA ' + words[0] = words[0]+self.SPACE + print words + for word in words: col = None - #if (word[0] == 'A' or word[0] == 'a'): + # if (word[0] == 'A' or word[0] == 'a'): # col = "axis" # self.a = eval(word[1:]) # move = True - #elif (word[0] == 'B' or word[0] == 'b'): + # elif (word[0] == 'B' or word[0] == 'b'): # col = "axis" # self.b = eval(word[1:]) - # move = True + # move = True if (word == ('C'+self.SPACE)): - #print words - col = "axis" - self.startx=self.x - self.starty=self.y - words[0]=words[0]+self.SPACE - words[2]=self.SPACE+words[2]+self.SPACE - words[4]=self.SPACE+words[4]+self.SPACE - - #print 'x,y' - #print self.x - #print self.y - #self.x1=self.x-eval(words[1]) - self.x1=self.x-eval(words[1]) - #j=self.y-eval(words[5]) - #self.y1=self.y-eval(words[3]) - self.y1=self.y-eval(words[3]) - #self.c = eval(word[1:]) - #print 'self x,y' - #print self.x1 - #print self.y1 - self.dx=(self.x1)*1 - self.dy=(self.y1)*0 - #print 'x1' - #print self.x1 - #print 'y1' - #print self.y1 - ssucin=self.dx+self.dy - r=math.sqrt(((self.x1)*(self.x1))+((self.y1)*(self.y1))) - #print 'skalarny sucin' - #print ssucin - #print 'r' - #print r - if (ssucin!=0): - ratio=ssucin/(r*1) - #print 'ratio' - #print ratio - angle= (math.acos(ratio) * 180 / math.pi) - if(self.y1<0):angle=360-angle - elif (self.y1>0): angle=+90 - elif (self.y1<0): angle=-90 - else: angle=0 - #print words[8] - #print 'angles' - #print angle - #if (i<0 and j<0): angle=180+angle - #if (i<0 and j>0): angle=180-angle - #if (j>0): angle=-angle - #print ('reverzacia') - #angle= angle+ eval(words[8]) - #print angle - self.angle=+ angle+ eval(words[5]) - #print self.angle - #if(angle>180): angle=360-angle - angle=self.angle*math.pi/180 - #print eval(words[8]) - self.endx=eval(words[1])+(r*math.cos(angle)) - #j=eval(words[5])+(r*math.sin(angle)) - self.endy=eval(words[3])+(r*math.sin(angle)) - self.x=self.endx - self.y=self.endy - path_col = "feed" - #arc=-eval(words[8])/math.fabs(eval(words[8])) - arc=eval(words[5])/math.fabs(eval(words[5])) - #if(arc==-1): arc=0 - #arc=-1 - #col = "feed" - move = True + # print words + col = "axis" + self.startx = self.x + self.starty = self.y + words[0] = words[0]+self.SPACE + words[2] = self.SPACE+words[2]+self.SPACE + words[4] = self.SPACE+words[4]+self.SPACE + # print 'x,y' + # print self.x + # print self.y + # self.x1=self.x-eval(words[1]) + self.x1 = self.x-eval(words[1]) + # j=self.y-eval(words[5]) + # self.y1=self.y-eval(words[3]) + self.y1 = self.y-eval(words[3]) + #self.c = eval(word[1:]) + # print 'self x,y' + # print self.x1 + # print self.y1 + self.dx = (self.x1)*1 + self.dy = (self.y1)*0 + # print 'x1' + # print self.x1 + # print 'y1' + # print self.y1 + ssucin = self.dx+self.dy + r = math.sqrt(((self.x1)*(self.x1))+((self.y1)*(self.y1))) + # print 'skalarny sucin' + # print ssucin + # print 'r' + # print r + if (ssucin != 0): + ratio = ssucin/(r*1) + # print 'ratio' + # print ratio + angle = (math.acos(ratio) * 180 / math.pi) + if(self.y1 < 0): + angle = 360-angle + elif (self.y1 > 0): + angle = +90 + elif (self.y1 < 0): + angle = -90 + else: + angle = 0 + # print words[8] + # print 'angles' + # print angle + #if (i<0 and j<0): angle=180+angle + #if (i<0 and j>0): angle=180-angle + #if (j>0): angle=-angle + #print ('reverzacia') + #angle= angle+ eval(words[8]) + # print angle + self.angle = + angle + eval(words[5]) + # print self.angle + #if(angle>180): angle=360-angle + angle = self.angle*math.pi/180 + # print eval(words[8]) + self.endx = eval(words[1])+(r*math.cos(angle)) + # j=eval(words[5])+(r*math.sin(angle)) + self.endy = eval(words[3])+(r*math.sin(angle)) + self.x = self.endx + self.y = self.endy + path_col = "feed" + # arc=-eval(words[8])/math.fabs(eval(words[8])) + arc = eval(words[5])/math.fabs(eval(words[5])) + #if(arc==-1): arc=0 + # arc=-1 + #col = "feed" + move = True - elif (word == 'P' and words[1]=='L' and words[4]=='F'): - self.FS=1 - elif (word == 'P' and words[1]=='L' and words[4]=='N'): - self.FS=0 - elif (word == ('FS'+self.SPACE)): - self.FS=1 - elif (word == ('SS'+self.SPACE)): - self.FS=0 + elif (word == 'P' and words[1] == 'L' and words[4] == 'F'): + self.FS = 1 + elif (word == 'P' and words[1] == 'L' and words[4] == 'N'): + self.FS = 0 + elif (word == ('FS'+self.SPACE)): + self.FS = 1 + elif (word == ('SS'+self.SPACE)): + self.FS = 0 elif (word == ('MA'+self.SPACE)): - words[2]=self.SPACE+words[2]+self.SPACE - if (self.FS==1): - path_col = "rapid" - col = "rapid" - else: - path_col = "feed" - col = "feed" - self.x=eval(words[1]) - #self.y=eval(words[6]) - self.y=eval(words[3]) - move=True - #elif (word == 'G1' or word == 'G01' or word == 'g1' or word == 'g01'): + words[2] = self.SPACE+words[2]+self.SPACE + if (self.FS == 1): + path_col = "rapid" + col = "rapid" + else: + path_col = "feed" + col = "feed" + self.x = eval(words[1]) + # self.y=eval(words[6]) + self.y = eval(words[3]) + move = True + # elif (word == 'G1' or word == 'G01' or word == 'g1' or word == 'g01'): # path_col = "feed" # col = "feed" - #elif (word == 'G2' or word == 'G02' or word == 'g2' or word == 'g02' or word == 'G12' or word == 'g12'): + # elif (word == 'G2' or word == 'G02' or word == 'g2' or word == 'g02' or word == 'G12' or word == 'g12'): # path_col = "feed" # col = "feed" # arc = -1 - #elif (word == 'G3' or word == 'G03' or word == 'g3' or word == 'g03' or word == 'G13' or word == 'g13'): + # elif (word == 'G3' or word == 'G03' or word == 'g3' or word == 'g03' or word == 'G13' or word == 'g13'): # path_col = "feed" # col = "feed" # arc = +1 - #elif (word == 'G10' or word == 'g10'): - # move = False - #elif (word == 'L1' or word == 'l1'): - # move = False - #elif (word == 'G20'): + # elif (word == 'G10' or word == 'g10'): + # move = False + # elif (word == 'L1' or word == 'l1'): + # move = False + # elif (word == 'G20'): # col = "prep" # self.set_mode(units=25.4) - #elif (word == 'G21'): + # elif (word == 'G21'): # col = "prep" # self.set_mode(units=1.0) - #elif (word == 'G81' or word == 'g81'): + # elif (word == 'G81' or word == 'g81'): # path_col = "feed" # col = "feed" - #elif (word == 'G82' or word == 'g82'): + # elif (word == 'G82' or word == 'g82'): # path_col = "feed" # col = "feed" - #elif (word == 'G83' or word == 'g83'): + # elif (word == 'G83' or word == 'g83'): # path_col = "feed" # col = "feed" - #elif (word[0] == 'G') : col = "prep" - #elif (word[0] == 'I' or word[0] == 'i'): + # elif (word[0] == 'G') : col = "prep" + # elif (word[0] == 'I' or word[0] == 'i'): # col = "axis" # self.i = eval(word[1:]) # move = True - #elif (word[0] == 'J' or word[0] == 'j'): + # elif (word[0] == 'J' or word[0] == 'j'): # col = "axis" # self.j = eval(word[1:]) # move = True - #elif (word[0] == 'K' or word[0] == 'k'): + # elif (word[0] == 'K' or word[0] == 'k'): # col = "axis" # self.k = eval(word[1:]) # move = True - #elif (word[0] == 'M') : col = "misc" - #elif (word[0] == 'N') : col = "blocknum" - #elif (word[0] == 'O') : col = "program" - #elif (word[0] == 'P' or word[0] == 'p'): + # elif (word[0] == 'M') : col = "misc" + # elif (word[0] == 'N') : col = "blocknum" + # elif (word[0] == 'O') : col = "program" + # elif (word[0] == 'P' or word[0] == 'p'): # col = "axis" # self.p = eval(word[1:]) # move = True - #elif (word[0] == 'Q' or word[0] == 'q'): + # elif (word[0] == 'Q' or word[0] == 'q'): # col = "axis" # self.q = eval(word[1:]) # move = True - #elif (word[0] == 'R' or word[0] == 'r'): + # elif (word[0] == 'R' or word[0] == 'r'): # col = "axis" # self.r = eval(word[1:]) # move = True - #elif (word[0] == 'S' or word[0zypp] == 's'): + # elif (word[0] == 'S' or word[0zypp] == 's'): # col = "axis" # self.s = eval(word[1:]) # move = True - #elif (word[0] == 'T') : col = "tool" - #elif (word[0] == 'X' or word[0] == 'x'): + # elif (word[0] == 'T') : col = "tool" + # elif (word[0] == 'X' or word[0] == 'x'): # col = "axis" # = eval(word[1:]) # move = True - #elif (word[0] == 'Y' or word[0] == 'y'): + # elif (word[0] == 'Y' or word[0] == 'y'): # col = "axis" # self.y = eval(word[1:]) # move = True - #elif (word[0] == 'Z' or word[0] == 'z'): + # elif (word[0] == 'Z' or word[0] == 'z'): # col = "axis" # self.z = eval(word[1:]) # move = True - elif (word[0] == '(') : col = "comment" - elif (word[0] == '#') : col = "variable" - elif (words[0] == ("//"+self.SPACE)) : col = "comment" - elif (words[0] == ("/*"+self.SPACE)) : col = "comment" - #self.add_text(word, col) + elif (word[0] == '('): + col = "comment" + elif (word[0] == '#'): + col = "variable" + elif (words[0] == ("//"+self.SPACE)): + col = "comment" + elif (words[0] == ("/*"+self.SPACE)): + col = "comment" + #self.add_text(word, col) if (move): self.begin_path(path_col) - if (arc) : - #self.add_arc(self.x, self.y, 0.0000, self.i, self.j, 0.0000, arc) - #self.add_arc(self.i, self.j, 0.0000, eval(words[2])-self.x, eval(words[5])-self.y, 0.0000, arc) - #print '' - #print eval(words[2])-self.startx - #print eval(words[6])-self.starty - #self.add_arc(self.endx, self.endy, 0.0000, eval(words[1])-self.startx, eval(words[3])-self.starty, 0.0000, arc) - print arc - self.add_arc(self.endx, self.endy, 0.0000,eval(words[1])-self.startx, eval(words[3])-self.starty, 0.0000,0.0000, arc) + if (arc): + #self.add_arc(self.x, self.y, 0.0000, self.i, self.j, 0.0000, arc) + #self.add_arc(self.i, self.j, 0.0000, eval(words[2])-self.x, eval(words[5])-self.y, 0.0000, arc) + # print '' + # print eval(words[2])-self.startx + # print eval(words[6])-self.starty + #self.add_arc(self.endx, self.endy, 0.0000, eval(words[1])-self.startx, eval(words[3])-self.starty, 0.0000, arc) + print arc + self.add_arc(self.endx, self.endy, 0.0000, eval( + words[1])-self.startx, eval(words[3])-self.starty, 0.0000, 0.0000, arc) - #self.add_arc(self.x, self.y, 0.0000, self.i, self.j, 0.0000, arc) - #self.x=self.i - #self.y=self.j - else : - self.add_line(self.x, self.y) - self.end_path() + #self.add_arc(self.x, self.y, 0.0000, self.i, self.j, 0.0000, arc) + # self.x=self.i + # self.y=self.j + else: + self.add_line(self.x, self.y) + self.end_path() self.end_ncblock() diff --git a/scripts/addons/cam/nc/series1.py b/scripts/addons/cam/nc/series1.py index c33539412..4bda80351 100644 --- a/scripts/addons/cam/nc/series1.py +++ b/scripts/addons/cam/nc/series1.py @@ -3,6 +3,8 @@ import math ################################################################################ + + class Creator(iso_modal.Creator): def __init__(self): @@ -24,8 +26,9 @@ def metric(self): self.fmt.number_of_decimal_places = 2 def SPACE(self): - return('') + return('') ################################################################################ + nc.creator = Creator() diff --git a/scripts/addons/cam/nc/series1_read.py b/scripts/addons/cam/nc/series1_read.py index e94a2b4b2..f07ff91f3 100644 --- a/scripts/addons/cam/nc/series1_read.py +++ b/scripts/addons/cam/nc/series1_read.py @@ -3,6 +3,7 @@ # use the iso reader, but with i_and_j_always_positive + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/shopbot_mtc.py b/scripts/addons/cam/nc/shopbot_mtc.py index 773c2e101..57276d902 100644 --- a/scripts/addons/cam/nc/shopbot_mtc.py +++ b/scripts/addons/cam/nc/shopbot_mtc.py @@ -19,206 +19,205 @@ import bpy ################################################################################ + + class Creator(nc.Creator): - def __init__(self): - nc.Creator.__init__(self) - - s=bpy.context.scene - cm=s.cam_machine - self.fmt = Format() - self.ffmt = Format(number_of_decimal_places = 2) - self.sfmt = Format(number_of_decimal_places = 1) - self.unitscale = 1 - self.startx = cm.starting_position.x - self.starty = cm.starting_position.y - self.startz = cm.starting_position.z - self.tcx = cm.mtc_position.x - self.tcy = cm.mtc_position.y - self.tcz = cm.mtc_position.z - self.endx = cm.ending_position.x - self.endy = cm.ending_position.y - self.endz = cm.ending_position.z - self.metric_flag = False - self.absolute_flag = True - - ############################################################################ - ## Codes - - def SPACE(self): return('') - def COMMENT(self,comment): return( '\' %s' % comment ) - def TOOL(self): return('T%i' + self.SPACE() + 'M06') - - ############################################################################ - ## Internals - - def write_feedrate(self): - self.f.write(self) - - def write_spindle(self): - self.s.write(self) - - ############################################################################ - ## Programs - - def program_begin(self, id, name=''): - self.writem([self.SPACE() , self.COMMENT(name)]) - self.write('\n\n') - self.write('IF %(25)=1 THEN GOTO UNIT_ERROR\n') - self.write('\n') - - def program_end(self): - self.write('C7\n') - self.write('JZ,' + self.fmt.string(self.endz) + '\n') - self.write('J2,' + self.fmt.string(self.endx) + ',' + self.fmt.string(self.endy) + '\n') - self.write('END\n') - self.write('\'\n') - self.write('UNIT_ERROR:\n') - self.write('C#,91\n') - self.write('END\n') - - def flush_nc(self): - return - - ############################################################################ - ## Subprograms - - - ############################################################################ - ## Settings - - def imperial(self): - self.fmt.number_of_decimal_places = 4 - self.unitscale = 0.0254 - self.metric_flag=False - - def metric(self): - self.metric_flag=True - self.unitscale = 1 - self.fmt.number_of_decimal_places = 3 - - def absolute(self): - self.absolute_flag = True - self.write('SA\n') - - def incremental(self): - self.absolute_flag = False - self.write('SR\n') - - def set_plane(self, plane): - return - - ############################################################################ - ## new graphics origin- make a new coordinate system and snap it onto the geometry - ## the toolpath generated should be translated - - - ############################################################################ - ## Tools - - def tool_change(self, id): - self.write('C7\n') - self.write('&Tool=' + self.fmt.string(id) + '\n') - self.write('JZ,' + (self.fmt.string(self.tcz)) + '\n') - self.write('J2,' + (self.fmt.string(self.tcx)) + ',' + (self.fmt.string(self.tcy)) + '\n') - self.write('\' Use tool '+ self.fmt.string(id) + '\n') - self.write('PAUSE\n') - self.write('C2\n') - self.t = id - self.write('C6\n') - self.write('PAUSE 2\n') - - ############################################################################ - ## Datums - - - ############################################################################ - ## Rates + Modes - - def feedrate(self, f): - if (self.metric_flag == True): - self.write('MS,' + (self.sfmt.string(f)) + ',' + (self.sfmt.string(f)) + '\n') - else: - self.write('MS,' + (self.sfmt.string(f*self.unitscale)) + ',' + (self.sfmt.string(f*self.unitscale)) + '\n') - - def spindle(self, s, clockwise): - self.write('TR,' + self.fmt.string(s) + '\n') - - ############################################################################ - ## Moves - - def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): - # different commands for X only, or Y only, or Z only, or (X and Y), or (X, Y, and Z) - if (x != None and y != None and z != None): - self.write('J3,' + (self.fmt.string(x * self.unitscale))) - self.write(',' + (self.fmt.string(y * self.unitscale))) - self.write(',' + (self.fmt.string(z * self.unitscale))) - self.write('\n') - elif (x != None and y != None and z == None): - self.write('J2,' + (self.fmt.string(x * self.unitscale))) - self.write(',' + (self.fmt.string(y * self.unitscale))) - self.write('\n') - elif (x != None): - self.write('JX,' + (self.fmt.string(x * self.unitscale)) + '\n') - elif (y != None): - self.write('JY,' + (self.fmt.string(y * self.unitscale)) + '\n') - elif (z != None): - self.write('JZ,' + (self.fmt.string(z * self.unitscale)) + '\n') - - def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): - if (x != None and y != None and z != None): - self.write('M3,' + (self.fmt.string(x * self.unitscale))) - self.write(',' + (self.fmt.string(y * self.unitscale))) - self.write(',' + (self.fmt.string(z * self.unitscale))) - self.write('\n') - elif (x != None and y != None and z == None): - self.write('M2,' + (self.fmt.string(x * self.unitscale))) - self.write(',' + (self.fmt.string(y * self.unitscale))) - self.write('\n') - elif (x != None): - self.write('MX,' + (self.fmt.string(x * self.unitscale)) + '\n') - elif (y != None): - self.write('MY,' + (self.fmt.string(y * self.unitscale)) + '\n') - elif (z != None): - self.write('MZ,' + (self.fmt.string(z * self.unitscale)) + '\n') - - def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): - if (r != None): - self.write('CG,' + self.fmt.string(r * self.unitscale * 2)) - self.write(',' + self.fmt.string(x * self.unitscale)) - self.write(',' + self.fmt.string(y * self.unitscale)) - self.write(', ,') - self.write(',T,1') - self.write('\n') - else: - self.write('CG, ') - self.write(',' + self.fmt.string(x * self.unitscale)) - self.write(',' + self.fmt.string(y * self.unitscale)) - self.write(',' + self.fmt.string(i * self.unitscale)) - self.write(',' + self.fmt.string(j * self.unitscale)) - self.write(',T,1') - self.write('\n') - return - - ############################################################################ - ## CRC - - - ############################################################################ - ## Cycles - - - ############################################################################ - ## Misc - - def comment(self, text): - self.write((self.COMMENT(text) + '\n')) - - def variable(self, id): - return (self.VARIABLE() % id) - - def variable_set(self, id, value): - self.write('&' + (self.VARIABLE() % id) + self.SPACE() + (self.VARIABLE_SET() % value) + '\n') + def __init__(self): + nc.Creator.__init__(self) + + s = bpy.context.scene + cm = s.cam_machine + self.fmt = Format() + self.ffmt = Format(number_of_decimal_places=2) + self.sfmt = Format(number_of_decimal_places=1) + self.unitscale = 1 + self.startx = cm.starting_position.x + self.starty = cm.starting_position.y + self.startz = cm.starting_position.z + self.tcx = cm.mtc_position.x + self.tcy = cm.mtc_position.y + self.tcz = cm.mtc_position.z + self.endx = cm.ending_position.x + self.endy = cm.ending_position.y + self.endz = cm.ending_position.z + self.metric_flag = False + self.absolute_flag = True + + ############################################################################ + # Codes + + def SPACE(self): return('') + def COMMENT(self, comment): return('\' %s' % comment) + def TOOL(self): return('T%i' + self.SPACE() + 'M06') + + ############################################################################ + # Internals + + def write_feedrate(self): + self.f.write(self) + + def write_spindle(self): + self.s.write(self) + + ############################################################################ + # Programs + + def program_begin(self, id, name=''): + self.writem([self.SPACE(), self.COMMENT(name)]) + self.write('\n\n') + self.write('IF %(25)=1 THEN GOTO UNIT_ERROR\n') + self.write('\n') + + def program_end(self): + self.write('C7\n') + self.write('JZ,' + self.fmt.string(self.endz) + '\n') + self.write('J2,' + self.fmt.string(self.endx) + ',' + self.fmt.string(self.endy) + '\n') + self.write('END\n') + self.write('\'\n') + self.write('UNIT_ERROR:\n') + self.write('C#,91\n') + self.write('END\n') + + def flush_nc(self): + return + + ############################################################################ + # Subprograms + + ############################################################################ + # Settings + + def imperial(self): + self.fmt.number_of_decimal_places = 4 + self.unitscale = 0.0254 + self.metric_flag = False + + def metric(self): + self.metric_flag = True + self.unitscale = 1 + self.fmt.number_of_decimal_places = 3 + + def absolute(self): + self.absolute_flag = True + self.write('SA\n') + + def incremental(self): + self.absolute_flag = False + self.write('SR\n') + + def set_plane(self, plane): + return + + ############################################################################ + # new graphics origin- make a new coordinate system and snap it onto the geometry + # the toolpath generated should be translated + + ############################################################################ + # Tools + + def tool_change(self, id): + self.write('C7\n') + self.write('&Tool=' + self.fmt.string(id) + '\n') + self.write('JZ,' + (self.fmt.string(self.tcz)) + '\n') + self.write('J2,' + (self.fmt.string(self.tcx)) + ',' + (self.fmt.string(self.tcy)) + '\n') + self.write('\' Use tool ' + self.fmt.string(id) + '\n') + self.write('PAUSE\n') + self.write('C2\n') + self.t = id + self.write('C6\n') + self.write('PAUSE 2\n') + + ############################################################################ + # Datums + + ############################################################################ + ## Rates + Modes + + def feedrate(self, f): + if (self.metric_flag == True): + self.write('MS,' + (self.sfmt.string(f)) + ',' + (self.sfmt.string(f)) + '\n') + else: + self.write('MS,' + (self.sfmt.string(f*self.unitscale)) + + ',' + (self.sfmt.string(f*self.unitscale)) + '\n') + + def spindle(self, s, clockwise): + self.write('TR,' + self.fmt.string(s) + '\n') + + ############################################################################ + # Moves + + def rapid(self, x=None, y=None, z=None, a=None, b=None, c=None): + # different commands for X only, or Y only, or Z only, or (X and Y), or (X, Y, and Z) + if (x != None and y != None and z != None): + self.write('J3,' + (self.fmt.string(x * self.unitscale))) + self.write(',' + (self.fmt.string(y * self.unitscale))) + self.write(',' + (self.fmt.string(z * self.unitscale))) + self.write('\n') + elif (x != None and y != None and z == None): + self.write('J2,' + (self.fmt.string(x * self.unitscale))) + self.write(',' + (self.fmt.string(y * self.unitscale))) + self.write('\n') + elif (x != None): + self.write('JX,' + (self.fmt.string(x * self.unitscale)) + '\n') + elif (y != None): + self.write('JY,' + (self.fmt.string(y * self.unitscale)) + '\n') + elif (z != None): + self.write('JZ,' + (self.fmt.string(z * self.unitscale)) + '\n') + + def feed(self, x=None, y=None, z=None, a=None, b=None, c=None): + if (x != None and y != None and z != None): + self.write('M3,' + (self.fmt.string(x * self.unitscale))) + self.write(',' + (self.fmt.string(y * self.unitscale))) + self.write(',' + (self.fmt.string(z * self.unitscale))) + self.write('\n') + elif (x != None and y != None and z == None): + self.write('M2,' + (self.fmt.string(x * self.unitscale))) + self.write(',' + (self.fmt.string(y * self.unitscale))) + self.write('\n') + elif (x != None): + self.write('MX,' + (self.fmt.string(x * self.unitscale)) + '\n') + elif (y != None): + self.write('MY,' + (self.fmt.string(y * self.unitscale)) + '\n') + elif (z != None): + self.write('MZ,' + (self.fmt.string(z * self.unitscale)) + '\n') + + def arc(self, cw, x=None, y=None, z=None, i=None, j=None, k=None, r=None): + if (r != None): + self.write('CG,' + self.fmt.string(r * self.unitscale * 2)) + self.write(',' + self.fmt.string(x * self.unitscale)) + self.write(',' + self.fmt.string(y * self.unitscale)) + self.write(', ,') + self.write(',T,1') + self.write('\n') + else: + self.write('CG, ') + self.write(',' + self.fmt.string(x * self.unitscale)) + self.write(',' + self.fmt.string(y * self.unitscale)) + self.write(',' + self.fmt.string(i * self.unitscale)) + self.write(',' + self.fmt.string(j * self.unitscale)) + self.write(',T,1') + self.write('\n') + return + + ############################################################################ + # CRC + + ############################################################################ + # Cycles + + ############################################################################ + # Misc + + def comment(self, text): + self.write((self.COMMENT(text) + '\n')) + + def variable(self, id): + return (self.VARIABLE() % id) + + def variable_set(self, id, value): + self.write('&' + (self.VARIABLE() % id) + self.SPACE() + + (self.VARIABLE_SET() % value) + '\n') ################################################################################ diff --git a/scripts/addons/cam/nc/siegkx1.py b/scripts/addons/cam/nc/siegkx1.py index 7aa272cdf..5fc50586c 100644 --- a/scripts/addons/cam/nc/siegkx1.py +++ b/scripts/addons/cam/nc/siegkx1.py @@ -11,6 +11,8 @@ import math ################################################################################ + + class Creator(iso_modal.Creator): def __init__(self): diff --git a/scripts/addons/cam/nc/siegkx1_read.py b/scripts/addons/cam/nc/siegkx1_read.py index 075c24e09..2ca0b6f50 100644 --- a/scripts/addons/cam/nc/siegkx1_read.py +++ b/scripts/addons/cam/nc/siegkx1_read.py @@ -2,6 +2,8 @@ import sys # just use the iso reader + + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/tnc151.py b/scripts/addons/cam/nc/tnc151.py index 3975e5f45..4dd5cce52 100644 --- a/scripts/addons/cam/nc/tnc151.py +++ b/scripts/addons/cam/nc/tnc151.py @@ -9,6 +9,8 @@ import math ################################################################################ + + class Creator(iso_modal.Creator): def __init__(self): @@ -21,12 +23,12 @@ def __init__(self): self.waiting_t = None self.waiting_for_program_begin = False - ######## Codes + # Codes def SPACE(self): return(' ') def TOOL(self): return('T%i') - ######## Overridden functions + # Overridden functions def write_blocknum(self): self.write(self.BLOCK() % self.n) @@ -50,8 +52,8 @@ def metric(self): self.write(' G71\n') self.fmt.number_of_decimal_places = 3 - # no tool definition lines wanted + def tool_defn(self, id, name='', params=None): pass @@ -87,4 +89,5 @@ def workplane(self, id): pass ################################################################################ + nc.creator = Creator() diff --git a/scripts/addons/cam/nc/tnc151_read.py b/scripts/addons/cam/nc/tnc151_read.py index 075c24e09..2ca0b6f50 100644 --- a/scripts/addons/cam/nc/tnc151_read.py +++ b/scripts/addons/cam/nc/tnc151_read.py @@ -2,6 +2,8 @@ import sys # just use the iso reader + + class Parser(iso.Parser): def __init__(self, writer): iso.Parser.__init__(self, writer) diff --git a/scripts/addons/cam/nc/winpc.py b/scripts/addons/cam/nc/winpc.py index 82a47214b..e92b3de95 100644 --- a/scripts/addons/cam/nc/winpc.py +++ b/scripts/addons/cam/nc/winpc.py @@ -6,6 +6,7 @@ now = datetime.datetime.now() + class Creator(iso_modal.Creator): def __init__(self): iso_modal.Creator.__init__(self) @@ -16,21 +17,25 @@ def __init__(self): def SPACE_STR(self): return ' ' - def TOOL(self): return('T%i' + self.SPACE() ) + def TOOL(self): return('T%i' + self.SPACE()) ############################################################################ -## Internal +# Internal + + def write_spindle(self): return '' ############################################################################ -## Programs +# Programs def program_begin(self, id, comment): if (self.useCrc == False): - self.write( ('(Created with win-pc post processor ' + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n') ) + self.write(('(Created with win-pc post processor ' + + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n')) else: - self.write( ('(Created with win-pc Cutter Radius Compensation post processor ' + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n') ) + self.write(('(Created with win-pc Cutter Radius Compensation post processor ' + + str(now.strftime("%Y/%m/%d %H:%M")) + ')' + '\n')) #self.rapid( x=0.0, y=0.0, z=30.0 ) def program_end(self): @@ -38,7 +43,7 @@ def program_end(self): #self.rapid( x=0.0, y=0.0, z=30.0 ) ############################################################################ -## Settings +# Settings def tool_defn(self, id, name='', params=None): #self.write('G43 \n') @@ -55,18 +60,22 @@ def comment(self, text): # These are selected by values from 1 to 9 inclusive. def workplane(self, id): if ((id >= 1) and (id <= 6)): - self.write( (self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + '\t (Select Relative Coordinate System)\n') + self.write((self.WORKPLANE() % (id + self.WORKPLANE_BASE())) + + '\t (Select Relative Coordinate System)\n') if ((id >= 7) and (id <= 9)): - self.write( ((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % (id - 6))) + '\t (Select Relative Coordinate System)\n') + self.write(((self.WORKPLANE() % (6 + self.WORKPLANE_BASE())) + ('.%i' % + (id - 6))) + '\t (Select Relative Coordinate System)\n') ############################################################################ -## Moves +# Moves ############################################################################ ## Rates + Modes -## All feedrates are given in mm/s +# All feedrates are given in mm/s + + def feedrate(self, f): self.f.set(f/60) self.fhv = False @@ -86,10 +95,12 @@ def calc_feedrate_hv(self, h, v): ############################################################################ -## Probe routines - def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None ): +# Probe routines + + + def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=None, x3=None, y3=None, z3=None, x4=None, y4=None, z4=None, x5=None, y5=None, z5=None, x6=None, y6=None, z6=None, xml_file_name=None): if (xml_file_name != None): - self.comment('Generate an XML document describing the probed coordinates found'); + self.comment('Generate an XML document describing the probed coordinates found') self.write_blocknum() self.write('(LOGOPEN,') self.write(xml_file_name) @@ -261,7 +272,7 @@ def report_probe_results(self, x1=None, y1=None, z1=None, x2=None, y2=None, z2=N self.write_blocknum() self.write('(LOGCLOSE)\n') - def open_log_file(self, xml_file_name=None ): + def open_log_file(self, xml_file_name=None): self.write_blocknum() self.write('(LOGOPEN,') self.write(xml_file_name) @@ -298,8 +309,9 @@ def log_coordinate(self, x=None, y=None, z=None): self.write_blocknum() self.write('(LOG,)\n') - def log_message(self, message=None ): + def log_message(self, message=None): self.write_blocknum() self.write('(LOG,' + message + ')\n') + nc.creator = Creator() diff --git a/scripts/addons/cam/numba_wrapper.py b/scripts/addons/cam/numba_wrapper.py index a7fc96413..0da9255db 100644 --- a/scripts/addons/cam/numba_wrapper.py +++ b/scripts/addons/cam/numba_wrapper.py @@ -1,14 +1,15 @@ try: - from numba import jit,prange + from numba import jit, prange print("numba: yes") except: print("numba: no") + def jit(f=None, *args, **kwargs): def decorator(func): - return func + return func if callable(f): return f else: - return decorator + return decorator prange = range diff --git a/scripts/addons/cam/opencamlib/oclSample.py b/scripts/addons/cam/opencamlib/oclSample.py index b9036bf2a..2d9a1baf1 100644 --- a/scripts/addons/cam/opencamlib/oclSample.py +++ b/scripts/addons/cam/opencamlib/oclSample.py @@ -17,38 +17,41 @@ OCL_SCALE = 1000.0 -_PREVIOUS_OCL_MESH=None +_PREVIOUS_OCL_MESH = None + def get_oclSTL(operation): me = None oclSTL = ocl.STLSurf() - found_mesh=False + found_mesh = False for collision_object in operation.objects: activate(collision_object) - if collision_object.type == "MESH" or collision_object.type== "CURVE" or collision_object.type== "FONT" or collision_object.type== "SURFACE": - found_mesh=True + if collision_object.type == "MESH" or collision_object.type == "CURVE" or collision_object.type == "FONT" or collision_object.type == "SURFACE": + found_mesh = True global_matrix = mathutils.Matrix.Identity(4) - faces = blender_utils.faces_from_mesh(collision_object, global_matrix, operation.use_modifiers) + faces = blender_utils.faces_from_mesh( + collision_object, global_matrix, operation.use_modifiers) for face in faces: t = ocl.Triangle(ocl.Point(face[0][0]*OCL_SCALE, face[0][1]*OCL_SCALE, (face[0][2]+operation.skin)*OCL_SCALE), - ocl.Point(face[1][0]*OCL_SCALE, face[1][1]*OCL_SCALE, (face[1][2]+operation.skin)*OCL_SCALE), - ocl.Point(face[2][0]*OCL_SCALE, face[2][1]*OCL_SCALE, (face[2][2]+operation.skin)*OCL_SCALE)) + ocl.Point(face[1][0]*OCL_SCALE, face[1][1]*OCL_SCALE, + (face[1][2]+operation.skin)*OCL_SCALE), + ocl.Point(face[2][0]*OCL_SCALE, face[2][1]*OCL_SCALE, (face[2][2]+operation.skin)*OCL_SCALE)) oclSTL.addTriangle(t) # FIXME needs to work with collections if not found_mesh: - raise CamException("This operation requires a mesh or curve object or equivalent (e.g. text, volume).") + raise CamException( + "This operation requires a mesh or curve object or equivalent (e.g. text, volume).") return oclSTL -async def ocl_sample(operation, chunks,use_cached_mesh = False): +async def ocl_sample(operation, chunks, use_cached_mesh=False): global _PREVIOUS_OCL_MESH - op_cutter_type = operation.cutter_type op_cutter_diameter = operation.cutter_diameter op_minz = operation.minz op_cutter_tip_angle = math.radians(operation.cutter_tip_angle)/2 - if op_cutter_type == "VCARVE": + if op_cutter_type == "VCARVE": cutter_length = (op_cutter_diameter/math.tan(op_cutter_tip_angle))/2 else: cutter_length = 10 @@ -60,24 +63,27 @@ async def ocl_sample(operation, chunks,use_cached_mesh = False): elif op_cutter_type == 'BALLNOSE': cutter = ocl.BallCutter((op_cutter_diameter + operation.skin * 2) * 1000, cutter_length) elif op_cutter_type == 'VCARVE': - cutter = ocl.ConeCutter((op_cutter_diameter + operation.skin * 2) * 1000, op_cutter_tip_angle, cutter_length) - elif op_cutter_type =='CYLCONE': - cutter = ocl.CylConeCutter((operation.cylcone_diameter/2+operation.skin)*2000,(op_cutter_diameter + operation.skin * 2) * 1000, op_cutter_tip_angle) + cutter = ocl.ConeCutter((op_cutter_diameter + operation.skin * 2) + * 1000, op_cutter_tip_angle, cutter_length) + elif op_cutter_type == 'CYLCONE': + cutter = ocl.CylConeCutter((operation.cylcone_diameter/2+operation.skin)*2000, + (op_cutter_diameter + operation.skin * 2) * 1000, op_cutter_tip_angle) elif op_cutter_type == 'BALLCONE': cutter = ocl.BallConeCutter((operation.ball_radius + operation.skin) * 2000, (op_cutter_diameter + operation.skin * 2) * 1000, op_cutter_tip_angle) - elif op_cutter_type =='BULLNOSE': - cutter = ocl.BullCutter((op_cutter_diameter + operation.skin * 2) * 1000,operation.bull_corner_radius*1000, cutter_length) + elif op_cutter_type == 'BULLNOSE': + cutter = ocl.BullCutter((op_cutter_diameter + operation.skin * 2) * + 1000, operation.bull_corner_radius*1000, cutter_length) else: print("Cutter unsupported: {0}\n".format(op_cutter_type)) quit() bdc = ocl.BatchDropCutter() if use_cached_mesh and _PREVIOUS_OCL_MESH is not None: - oclSTL=_PREVIOUS_OCL_MESH + oclSTL = _PREVIOUS_OCL_MESH else: oclSTL = get_oclSTL(operation) - _PREVIOUS_OCL_MESH=oclSTL + _PREVIOUS_OCL_MESH = oclSTL bdc.setSTL(oclSTL) bdc.setCutter(cutter) diff --git a/scripts/addons/cam/opencamlib/opencamlib.py b/scripts/addons/cam/opencamlib/opencamlib.py index 5ecbb3ef4..e7c507a24 100644 --- a/scripts/addons/cam/opencamlib/opencamlib.py +++ b/scripts/addons/cam/opencamlib/opencamlib.py @@ -28,18 +28,20 @@ PYTHON_BIN = None + def pointSamplesFromOCL(points, samples): for index, point in enumerate(points): point[2] = samples[index].z / OCL_SCALE + def chunkPointSamplesFromOCL(chunks, samples): s_index = 0 for ch in chunks: - ch_points=ch.count() - z_vals=np.array([p.z for p in samples[s_index:s_index+ch_points]]) + ch_points = ch.count() + z_vals = np.array([p.z for p in samples[s_index:s_index+ch_points]]) z_vals /= OCL_SCALE ch.setZ(z_vals) - s_index+=ch_points + s_index += ch_points # p_index = 0 # for point in ch.points: # if len(point) == 2 or point[2] != 2: @@ -51,14 +53,15 @@ def chunkPointSamplesFromOCL(chunks, samples): # p_index += 1 # s_index += 1 + def chunkPointsResampleFromOCL(chunks, samples): s_index = 0 for ch in chunks: - ch_points=ch.count() - z_vals=np.array([p.z for p in samples[s_index:s_index+ch_points]]) + ch_points = ch.count() + z_vals = np.array([p.z for p in samples[s_index:s_index+ch_points]]) z_vals /= OCL_SCALE ch.setZ(z_vals) - s_index+=ch_points + s_index += ch_points # s_index = 0 # for ch in chunks: @@ -105,21 +108,21 @@ async def oclSample(operation, chunks): chunkPointSamplesFromOCL(chunks, samples) -async def oclResampleChunks(operation, chunks_to_resample,use_cached_mesh): +async def oclResampleChunks(operation, chunks_to_resample, use_cached_mesh): tmp_chunks = list() tmp_chunks.append(camPathChunk(inpoints=[])) for chunk, i_start, i_length in chunks_to_resample: tmp_chunks[0].extend(chunk.get_points_np()[i_start:i_start+i_length]) - print(i_start,i_length,len(tmp_chunks[0].points)) - - samples = await ocl_sample(operation, tmp_chunks,use_cached_mesh=use_cached_mesh) + print(i_start, i_length, len(tmp_chunks[0].points)) + + samples = await ocl_sample(operation, tmp_chunks, use_cached_mesh=use_cached_mesh) sample_index = 0 for chunk, i_start, i_length in chunks_to_resample: z = np.array([p.z for p in samples[sample_index:sample_index+i_length]]) / OCL_SCALE pts = chunk.get_points_np() - pt_z = pts[i_start:i_start+i_length,2] - pt_z = np.where(z>pt_z,z,pt_z) + pt_z = pts[i_start:i_start+i_length, 2] + pt_z = np.where(z > pt_z, z, pt_z) sample_index += i_length # for p_index in range(i_start, i_start + i_length): @@ -141,6 +144,7 @@ def oclWaterlineLayerHeights(operation): layers.append(l_last) return layers + def oclGetMedialAxis(operation, chunks): oclWaterlineHeightsToOCL(operation) operationSettingsToOCL(operation) @@ -160,42 +164,42 @@ async def oclGetWaterline(operation, chunks): op_cutter_tip_angle = operation['cutter_tip_angle'] cutter = None - cutter_length = 150 #TODO: automatically determine necessary cutter length depending on object size + cutter_length = 150 # TODO: automatically determine necessary cutter length depending on object size if op_cutter_type == 'END': cutter = ocl.CylCutter((op_cutter_diameter + operation.skin * 2) * 1000, cutter_length) elif op_cutter_type == 'BALLNOSE': cutter = ocl.BallCutter((op_cutter_diameter + operation.skin * 2) * 1000, cutter_length) elif op_cutter_type == 'VCARVE': - cutter = ocl.ConeCutter((op_cutter_diameter + operation.skin * 2) * 1000, op_cutter_tip_angle, cutter_length) + cutter = ocl.ConeCutter((op_cutter_diameter + operation.skin * 2) + * 1000, op_cutter_tip_angle, cutter_length) else: print("Cutter unsupported: {0}\n".format(op_cutter_type)) quit() - waterline = ocl.Waterline() waterline.setSTL(oclSTL) waterline.setCutter(cutter) - waterline.setSampling(0.1)#TODO: add sampling setting to UI - last_pos=[0,0,0] - for count,height in enumerate(layers): - layer_chunks=[] - await progress_async("Waterline",int((100*count)/len(layers))) + waterline.setSampling(0.1) # TODO: add sampling setting to UI + last_pos = [0, 0, 0] + for count, height in enumerate(layers): + layer_chunks = [] + await progress_async("Waterline", int((100*count)/len(layers))) waterline.reset() waterline.setZ(height * OCL_SCALE) waterline.run2() wl_loops = waterline.getLoops() for l in wl_loops: - inpoints=[] + inpoints = [] for p in l: inpoints.append((p.x / OCL_SCALE, p.y / OCL_SCALE, p.z / OCL_SCALE)) inpoints.append(inpoints[0]) - chunk=camPathChunk(inpoints=inpoints) + chunk = camPathChunk(inpoints=inpoints) chunk.closed = True layer_chunks.append(chunk) # sort chunks so that ordering is stable - chunks.extend(await utils.sortChunks(layer_chunks,operation,last_pos=last_pos)) - if len(chunks)>0: - last_pos=chunks[-1].get_point(-1) + chunks.extend(await utils.sortChunks(layer_chunks, operation, last_pos=last_pos)) + if len(chunks) > 0: + last_pos = chunks[-1].get_point(-1) # def oclFillMedialAxis(operation): diff --git a/scripts/addons/cam/ops.py b/scripts/addons/cam/ops.py index 6b3ab9f39..53e54151a 100644 --- a/scripts/addons/cam/ops.py +++ b/scripts/addons/cam/ops.py @@ -26,9 +26,11 @@ from bpy.props import * from bpy_extras.io_utils import ImportHelper -import subprocess, os, threading +import subprocess +import os +import threading from cam import utils, pack, polygon_utils_cam, simple, gcodepath, bridges, simulation -from cam.async_op import AsyncOperatorMixin,AsyncCancelledException +from cam.async_op import AsyncOperatorMixin, AsyncCancelledException import shapely import mathutils import math @@ -38,6 +40,7 @@ import cam from cam.exception import * + class threadCom: # object passed to threads to read background process stdout info def __init__(self, o, proc): self.opname = o.name @@ -55,6 +58,7 @@ def threadread(tcom): e = inline.find('}') tcom.outtext = inline[s + 9:e] + @bpy.app.handlers.persistent def timer_update(context): """monitoring of background processes""" @@ -119,7 +123,8 @@ def execute(self, context): # self.__class__.cam_processes=[] if not hasattr(bpy.ops.object.calculate_cam_paths_background.__class__, 'cam_processes'): bpy.ops.object.calculate_cam_paths_background.__class__.cam_processes = [] - bpy.ops.object.calculate_cam_paths_background.__class__.cam_processes.append([readthread, tcom]) + bpy.ops.object.calculate_cam_paths_background.__class__.cam_processes.append([ + readthread, tcom]) return {'FINISHED'} @@ -146,7 +151,7 @@ def execute(self, context): return {'FINISHED'} -async def _calc_path(operator,context): +async def _calc_path(operator, context): s = bpy.context.scene o = s.cam_operations[s.cam_active_operation] if o.geometry_source == 'OBJECT': @@ -172,18 +177,20 @@ async def _calc_path(operator,context): bpy.ops.object.delete() if not o.valid: - operator.report({'ERROR_INVALID_INPUT'}, "Operation can't be performed, see warnings for info") + operator.report({'ERROR_INVALID_INPUT'}, + "Operation can't be performed, see warnings for info") progress_async("Operation can't be performed, see warnings for info") - return {'FINISHED',False} - - #check for free movement height < maxz and return with error + return {'FINISHED', False} + + # check for free movement height < maxz and return with error if(o.movement.free_height < o.maxz): - operator.report({'ERROR_INVALID_INPUT'}, "Free movement height is less than Operation depth start \n correct and try again.") + operator.report({'ERROR_INVALID_INPUT'}, + "Free movement height is less than Operation depth start \n correct and try again.") progress_async("Operation can't be performed, see warnings for info") - return {'FINISHED',False} + return {'FINISHED', False} if o.computing: - return {'FINISHED',False} + return {'FINISHED', False} o.operator = operator @@ -194,40 +201,40 @@ async def _calc_path(operator,context): print("Got path okay") except CamException as e: traceback.print_tb(e.__traceback__) - error_str="\n".join(textwrap.wrap(str(e),width=80)) - operator.report({'ERROR'},error_str) - return {'FINISHED',False} + error_str = "\n".join(textwrap.wrap(str(e), width=80)) + operator.report({'ERROR'}, error_str) + return {'FINISHED', False} except AsyncCancelledException as e: - return {'CANCELLED',False} + return {'CANCELLED', False} except Exception as e: - print("FAIL",e) + print("FAIL", e) traceback.print_tb(e.__traceback__) - operator.report({'ERROR'},str(e)) - return {'FINISHED',False} + operator.report({'ERROR'}, str(e)) + return {'FINISHED', False} coll = bpy.data.collections.get('RigidBodyWorld') if coll: bpy.data.collections.remove(coll) - return {'FINISHED',True} + return {'FINISHED', True} -class CalculatePath(bpy.types.Operator,AsyncOperatorMixin): +class CalculatePath(bpy.types.Operator, AsyncOperatorMixin): """calculate CAM paths""" bl_idname = "object.calculate_cam_path" bl_label = "Calculate CAM paths" - bl_options = {'REGISTER', 'UNDO','BLOCKING'} - + bl_options = {'REGISTER', 'UNDO', 'BLOCKING'} + @classmethod - def poll(cls,context): + def poll(cls, context): s = context.scene o = s.cam_operations[s.cam_active_operation] if o is not None: - if cam.isValid(o,context): + if cam.isValid(o, context): return True return False async def execute_async(self, context): - (retval,success) = await _calc_path(self,context) + (retval, success) = await _calc_path(self, context) print(f"CALCULATED PATH (success={success},retval={retval}") return retval @@ -298,17 +305,17 @@ def getChainOperations(chain): return chop -class PathsChain(bpy.types.Operator,AsyncOperatorMixin): +class PathsChain(bpy.types.Operator, AsyncOperatorMixin): """calculate a chain and export the gcode alltogether. """ bl_idname = "object.calculate_cam_paths_chain" bl_label = "Calculate CAM paths in current chain and export chain gcode" - bl_options = {'REGISTER', 'UNDO','BLOCKING'} + bl_options = {'REGISTER', 'UNDO', 'BLOCKING'} @classmethod def poll(cls, context): s = context.scene chain = s.cam_chains[s.cam_active_chain] - return cam.isChainValid(chain,context)[0] + return cam.isChainValid(chain, context)[0] async def execute_async(self, context): s = context.scene @@ -319,14 +326,14 @@ async def execute_async(self, context): try: for i in range(0, len(chainops)): s.cam_active_operation = s.cam_operations.find(chainops[i].name) - self.report({'INFO'},f"Calculating path: {chainops[i].name}") - result,success=await _calc_path(self,context) + self.report({'INFO'}, f"Calculating path: {chainops[i].name}") + result, success = await _calc_path(self, context) if not success and 'FINISHED' in result: - self.report({'ERROR'},f"Couldn't calculate path: {chainops[i].name}") + self.report({'ERROR'}, f"Couldn't calculate path: {chainops[i].name}") except Exception as e: - print("FAIL",e) + print("FAIL", e) traceback.print_tb(e.__traceback__) - operator.report({'ERROR'},str(e)) + operator.report({'ERROR'}, str(e)) return {'FINISHED'} for o in chainops: @@ -345,7 +352,7 @@ class PathExportChain(bpy.types.Operator): def poll(cls, context): s = context.scene chain = s.cam_chains[s.cam_active_chain] - return cam.isChainValid(chain,context)[0] + return cam.isChainValid(chain, context)[0] def execute(self, context): s = bpy.context.scene @@ -374,20 +381,21 @@ def execute(self, context): s = bpy.context.scene operation = s.cam_operations[s.cam_active_operation] - print("EXPORTING", operation.filename, bpy.data.objects["cam_path_{}".format(operation.name)].data, operation) + print("EXPORTING", operation.filename, + bpy.data.objects["cam_path_{}".format(operation.name)].data, operation) gcodepath.exportGcodePath(operation.filename, [bpy.data.objects["cam_path_{}".format(operation.name)].data], [operation]) return {'FINISHED'} -class CAMSimulate(bpy.types.Operator,AsyncOperatorMixin): +class CAMSimulate(bpy.types.Operator, AsyncOperatorMixin): """simulate CAM operation this is performed by: creating an image, painting Z depth of the brush substractively. Works only for some operations, can not be used for 4-5 axis.""" bl_idname = "object.cam_simulate" bl_label = "CAM simulation" - bl_options = {'REGISTER', 'UNDO','BLOCKING'} + bl_options = {'REGISTER', 'UNDO', 'BLOCKING'} operation: StringProperty(name="Operation", description="Specify the operation to calculate", default='Operation') @@ -404,7 +412,7 @@ async def execute_async(self, context): except AsyncCancelledException as e: return {'CANCELLED'} else: - self.report({'ERROR'},'no computed path to simulate') + self.report({'ERROR'}, 'no computed path to simulate') return {'FINISHED'} return {'FINISHED'} @@ -418,13 +426,13 @@ class CAMSimulateChain(bpy.types.Operator, AsyncOperatorMixin): to see how ops work together.""" bl_idname = "object.cam_simulate_chain" bl_label = "CAM simulation" - bl_options = {'REGISTER', 'UNDO','BLOCKING'} + bl_options = {'REGISTER', 'UNDO', 'BLOCKING'} @classmethod def poll(cls, context): s = context.scene chain = s.cam_chains[s.cam_active_chain] - return cam.isChainValid(chain,context)[0] + return cam.isChainValid(chain, context)[0] operation: StringProperty(name="Operation", description="Specify the operation to calculate", default='Operation') @@ -608,7 +616,7 @@ def Add_Pocket(self, maxdepth, sname, new_cutter_diameter): if not mpocket_exists: # create a pocket operation if it does not exist already s.cam_operations.add() o = s.cam_operations[-1] - o.object_name= 'medial_pocket' + o.object_name = 'medial_pocket' s.cam_active_operation = len(s.cam_operations) - 1 o.name = 'MedialPocket' o.filename = o.name @@ -618,6 +626,7 @@ def Add_Pocket(self, maxdepth, sname, new_cutter_diameter): o.material.size[2] = -maxdepth o.minz_from = 'MATERIAL' + class CamOperationAdd(bpy.types.Operator): """Add new CAM operation""" bl_idname = "scene.cam_operation_add" @@ -671,7 +680,8 @@ def execute(self, context): fixUnits() scene = bpy.context.scene - if len(scene.cam_operations) == 0: return {'CANCELLED'} + if len(scene.cam_operations) == 0: + return {'CANCELLED'} copyop = scene.cam_operations[scene.cam_active_operation] scene.cam_operations.add() scene.cam_active_operation += 1 @@ -716,7 +726,8 @@ def poll(cls, context): def execute(self, context): scene = context.scene try: - if len(scene.cam_operations) == 0: return {'CANCELLED'} + if len(scene.cam_operations) == 0: + return {'CANCELLED'} active_op = scene.cam_operations[scene.cam_active_operation] active_op_object = bpy.data.objects[active_op.name] scene.objects.active = active_op_object diff --git a/scripts/addons/cam/pack.py b/scripts/addons/cam/pack.py index ce72f8fe0..8f32735c4 100644 --- a/scripts/addons/cam/pack.py +++ b/scripts/addons/cam/pack.py @@ -25,7 +25,8 @@ from shapely import geometry as sgeometry from shapely import affinity, prepared from shapely import speedups -import random, time +import random +import time import mathutils from mathutils import Vector diff --git a/scripts/addons/cam/parametric.py b/scripts/addons/cam/parametric.py index 0b24fbd99..2f734c99c 100644 --- a/scripts/addons/cam/parametric.py +++ b/scripts/addons/cam/parametric.py @@ -36,13 +36,14 @@ """ + + + import math from math import sin, cos, pi import bmesh import bpy from mathutils import Vector - - def derive_bezier_handles(a, b, c, d, tb, tc): """ Derives bezier handles by using the start and end of the curve with 2 intermediate @@ -182,7 +183,7 @@ def create_parametric_curve( curve_object = bpy.data.objects.new('Parametric', curve) context = bpy.context scene = context.scene - link_object = scene.collection.objects.link + link_object = scene.collection.objects.link link_object(curve_object) # Return the new object @@ -246,4 +247,3 @@ def make_edge_loops(*objects): # Join them together bpy.ops.object.join(ctx) - diff --git a/scripts/addons/cam/pattern.py b/scripts/addons/cam/pattern.py index 3fbc415ba..0533489c6 100644 --- a/scripts/addons/cam/pattern.py +++ b/scripts/addons/cam/pattern.py @@ -93,11 +93,13 @@ def getPathPatternParallel(o, angle): v1.rotate(e1) axis_across_paths = numpy.array((numpy.arange(int(-dim / pathd), int(dim / pathd)) * pathd * v1.x + xm, - numpy.arange(int(-dim / pathd), int(dim / pathd)) * pathd * v1.y + ym, + numpy.arange(int(-dim / pathd), + int(dim / pathd)) * pathd * v1.y + ym, numpy.arange(int(-dim / pathd), int(dim / pathd)) * 0)) axis_along_paths = numpy.array((numpy.arange(int(-dim / pathstep), int(dim / pathstep)) * pathstep * v.x, - numpy.arange(int(-dim / pathstep), int(dim / pathstep)) * pathstep * v.y, + numpy.arange(int(-dim / pathstep), + int(dim / pathstep)) * pathstep * v.y, numpy.arange(int(-dim / pathstep), int(dim / pathstep)) * 0 + zlevel)) # rotate this first progress(axis_along_paths) @@ -247,7 +249,7 @@ def getPathPattern(operation): if (o.movement.type == 'CONVENTIONAL' and o.movement.spindle_rotation == 'CW') or ( o.movement.type == 'CLIMB' and o.movement.spindle_rotation == 'CCW'): - ##TODO + # TODO chunk.flipX(o.max.x+o.min.x) # for si in range(0, len(chunk.points)): # s = chunk.points[si] @@ -289,7 +291,7 @@ def getPathPattern(operation): else: if len(chunk.points) > 0: chunk.closed = False - chunk=chunk.to_chunk() + chunk = chunk.to_chunk() pathchunks.append(chunk) currentstepchunks.append(chunk) chunk = camPathChunkBuilder([]) @@ -299,7 +301,7 @@ def getPathPattern(operation): chunk.points.append(firstchunk.points[0]) if chunk == firstchunk: chunk.closed = True - chunk=chunk.to_chunk() + chunk = chunk.to_chunk() pathchunks.append(chunk) currentstepchunks.append(chunk) chunk = camPathChunkBuilder([]) @@ -541,7 +543,7 @@ def getPathPattern4axis(operation): cutterstart.rotate(e) cutterend.rotate(e) - chunk=chunk.to_chunk() + chunk = chunk.to_chunk() chunk.depth = radiusend - radius pathchunks.append(chunk) diff --git a/scripts/addons/cam/polygon_utils_cam.py b/scripts/addons/cam/polygon_utils_cam.py index e845dca62..362911f4c 100644 --- a/scripts/addons/cam/polygon_utils_cam.py +++ b/scripts/addons/cam/polygon_utils_cam.py @@ -127,7 +127,8 @@ def shapelyToCoords(anydata): def shapelyToCurve(name, p, z): - import bpy, bmesh + import bpy + import bmesh from bpy_extras import object_utils verts = [] edges = [] diff --git a/scripts/addons/cam/presets/cam_cutters/BALLCONE_1.00mm.py b/scripts/addons/cam/presets/cam_cutters/BALLCONE_1.00mm.py index 8c244cfac..acdd2c874 100644 --- a/scripts/addons/cam/presets/cam_cutters/BALLCONE_1.00mm.py +++ b/scripts/addons/cam/presets/cam_cutters/BALLCONE_1.00mm.py @@ -6,4 +6,4 @@ d.ball_cone_flute = 0.03 d.cutter_diameter = 0.006 d.cutter_length = 25.0 -d.cutter_tip_angle = 60.0 \ No newline at end of file +d.cutter_tip_angle = 60.0 diff --git a/scripts/addons/cam/puzzle_joinery.py b/scripts/addons/cam/puzzle_joinery.py index 674f842b0..19b26faf5 100644 --- a/scripts/addons/cam/puzzle_joinery.py +++ b/scripts/addons/cam/puzzle_joinery.py @@ -198,7 +198,7 @@ def bar(width, thick, diameter, tolerance, amount=0, stem=1, twist=False, tneck= simple.make_active('twist_keep_f') simple.rotate(-math.pi / 2) simple.move(x=-width / 2) - + simple.remove_multiple("_") # Remove temporary base and holes simple.remove_multiple("fingers") # Remove temporary base and holes @@ -755,4 +755,3 @@ def tile(diameter, tolerance, tile_x_amount, tile_y_amount, stem=1): simple.move(x=width/2) simple.difference('_', '_base') simple.active_name('tile_ ' + str(tile_x_amount) + '_' + str(tile_y_amount)) - diff --git a/scripts/addons/cam/simple.py b/scripts/addons/cam/simple.py index ff9419b62..fe9d4d8b5 100644 --- a/scripts/addons/cam/simple.py +++ b/scripts/addons/cam/simple.py @@ -20,7 +20,7 @@ # ***** END GPL LICENCE BLOCK ***** # Solves: No module named 'shapely' (even if it is installed) -#help('modules') +# help('modules') import math import sys @@ -256,6 +256,7 @@ def union(name): remove_multiple(name) rename('unionboolean', name) + def intersect(name): select_multiple(name) bpy.ops.object.curve_boolean(boolean_type='INTERSECT') @@ -263,6 +264,8 @@ def intersect(name): # boolean difference of objects starting with name result is object from basename. # all objects starting with name will be deleted and the result will be basename + + def difference(name, basename): # name is the series to select # basename is what the base you want to cut including name diff --git a/scripts/addons/cam/simulation.py b/scripts/addons/cam/simulation.py index 436cd71c5..13b10be32 100644 --- a/scripts/addons/cam/simulation.py +++ b/scripts/addons/cam/simulation.py @@ -42,7 +42,8 @@ def createSimulationObject(name, operations, i): if oname in bpy.data.objects: ob = bpy.data.objects[oname] else: - bpy.ops.mesh.primitive_plane_add(align='WORLD', enter_editmode=False, location=(0, 0, 0), rotation=(0, 0, 0)) + bpy.ops.mesh.primitive_plane_add( + align='WORLD', enter_editmode=False, location=(0, 0, 0), rotation=(0, 0, 0)) ob = bpy.context.active_object ob.name = oname @@ -120,13 +121,13 @@ async def generateSimulationImage(operations, limits): resy = math.ceil(sy / simulation_detail) + 2 * borderwidth # create array in which simulation happens, similar to an image to be painted in. - si = np.full(shape=(resx,resy),fill_value=maxz,dtype=float) + si = np.full(shape=(resx, resy), fill_value=maxz, dtype=float) - num_operations=len(operations) + num_operations = len(operations) - start_time=time.time() + start_time = time.time() - for op_count,o in enumerate(operations): + for op_count, o in enumerate(operations): ob = bpy.data.objects["cam_path_{}".format(o.name)] m = ob.data verts = m.vertices @@ -160,8 +161,8 @@ async def generateSimulationImage(operations, limits): for i, vert in enumerate(verts): if perc != int(100 * i / vtotal): perc = int(100 * i / vtotal) - total_perc = (perc+ op_count*100) / num_operations - await progress_async(f'Simulation',int(total_perc)) + total_perc = (perc + op_count*100) / num_operations + await progress_async(f'Simulation', int(total_perc)) if i > 0: volume = 0 @@ -183,14 +184,17 @@ async def generateSimulationImage(operations, limits): lastxs = xs lastys = ys while v.length < l: - xs = int((lasts.x + v.x - minx) / simulation_detail + borderwidth + simulation_detail / 2) + xs = int((lasts.x + v.x - minx) / simulation_detail + + borderwidth + simulation_detail / 2) # -middle - ys = int((lasts.y + v.y - miny) / simulation_detail + borderwidth + simulation_detail / 2) + ys = int((lasts.y + v.y - miny) / simulation_detail + + borderwidth + simulation_detail / 2) # -middle z = lasts.z + v.z # print(z) if lastxs != xs or lastys != ys: - volume_partial = simCutterSpot(xs, ys, z, cutterArray, si, o.do_simulation_feedrate) + volume_partial = simCutterSpot( + xs, ys, z, cutterArray, si, o.do_simulation_feedrate) if o.do_simulation_feedrate: totalvolume += volume volume += volume_partial @@ -200,9 +204,12 @@ async def generateSimulationImage(operations, limits): dropped += 1 v.length += simulation_detail - xs = int((s.x - minx) / simulation_detail + borderwidth + simulation_detail / 2) # -middle - ys = int((s.y - miny) / simulation_detail + borderwidth + simulation_detail / 2) # -middle - volume_partial = simCutterSpot(xs, ys, s.z, cutterArray, si, o.do_simulation_feedrate) + xs = int((s.x - minx) / simulation_detail + + borderwidth + simulation_detail / 2) # -middle + ys = int((s.y - miny) / simulation_detail + + borderwidth + simulation_detail / 2) # -middle + volume_partial = simCutterSpot( + xs, ys, s.z, cutterArray, si, o.do_simulation_feedrate) if o.do_simulation_feedrate: # compute volumes and write data into shapekey. volume += volume_partial totalvolume += volume @@ -278,7 +285,7 @@ async def generateSimulationImage(operations, limits): si = si[borderwidth:-borderwidth, borderwidth:-borderwidth] si += -minz - await progress_async("Simulated:",time.time()-start_time,'s') + await progress_async("Simulated:", time.time()-start_time, 's') return si @@ -288,7 +295,7 @@ def getCutterArray(operation, pixsize): r = operation.cutter_diameter / 2 + operation.skin # /operation.pixsize res = math.ceil((r * 2) / pixsize) m = res / 2.0 - car = np.full(shape=(res,res),fill_value=-10.0,dtype=float) + car = np.full(shape=(res, res), fill_value=-10.0, dtype=float) v = mathutils.Vector((0, 0, 0)) ps = pixsize diff --git a/scripts/addons/cam/slice.py b/scripts/addons/cam/slice.py index 1b4639f5a..887da44b8 100644 --- a/scripts/addons/cam/slice.py +++ b/scripts/addons/cam/slice.py @@ -48,7 +48,7 @@ def slicing2d(ob, height): # April 2020 Alain Pelletier return True -def slicing3d(ob, start, end): # April 2020 Alain Pelletier +def slicing3d(ob, start, end): # April 2020 Alain Pelletier # let's slice things bpy.ops.object.transform_apply(location=True, rotation=False, scale=False) bpy.ops.object.mode_set(mode='EDIT') # force edit mode @@ -111,7 +111,8 @@ def sliceObject(ob): # April 2020 Alain Pelletier obslice = bpy.context.view_layer.objects.active # attribute active object to obslice scollection.objects.link(obslice) # link obslice to scollecton if slice3d: - slicing3d(obslice, height, height + thickness) # slice 3d at desired height and stop at desired height + # slice 3d at desired height and stop at desired height + slicing3d(obslice, height, height + thickness) else: slicesuccess = slicing2d(obslice, height) # slice object at desired height diff --git a/scripts/addons/cam/strategy.py b/scripts/addons/cam/strategy.py index 7b637c91f..5e36950ee 100644 --- a/scripts/addons/cam/strategy.py +++ b/scripts/addons/cam/strategy.py @@ -63,7 +63,7 @@ async def cutout(o): c_offset = -max_depth * math.tan(cutter_angle) + o.ball_radius elif o.cutter_type == 'BALLNOSE': r = o.cutter_diameter / 2 - print("cutter radius:", r," skin",o.skin) + print("cutter radius:", r, " skin", o.skin) if -max_depth < r: c_offset = math.sqrt(r ** 2 - (r + max_depth) ** 2) print("offset:", c_offset) @@ -106,7 +106,7 @@ async def cutout(o): path_distance = o.dist_between_paths if o.cut_type == "INSIDE": path_distance *= -1 - p = p.buffer(distance = path_distance, resolution=o.optimisation.circle_detail, join_style=join, + p = p.buffer(distance=path_distance, resolution=o.optimisation.circle_detail, join_style=join, mitre_limit=2) chunksFromCurve.extend(shapelyToChunks(p, -1)) @@ -267,7 +267,7 @@ async def proj_curve(s, o): for chi, ch in enumerate(pathSamples): cht = tsamples[chi].get_points() ch.depth = 0 - ch_points=ch.get_points() + ch_points = ch.get_points() for i, s in enumerate(ch_points): # move the points a bit ep = Vector(cht[i]) @@ -333,14 +333,16 @@ async def pocket(o): prest = p.buffer(-c_offset, o.optimisation.circle_detail) while not p.is_empty: if o.pocketToCurve: - polygon_utils_cam.shapelyToCurve('3dpocket', p, 0.0) # make a curve starting with _3dpocket + # make a curve starting with _3dpocket + polygon_utils_cam.shapelyToCurve('3dpocket', p, 0.0) nchunks = shapelyToChunks(p, o.min.z) # print("nchunks") pnew = p.buffer(-o.dist_between_paths, o.optimisation.circle_detail) if pnew.is_empty: - pt = p.buffer(-c_offset, o.optimisation.circle_detail) # test if the last curve will leave material + # test if the last curve will leave material + pt = p.buffer(-c_offset, o.optimisation.circle_detail) if not pt.is_empty: pnew = pt # print("pnew") @@ -403,7 +405,7 @@ async def pocket(o): for v in h: nhelix.append((2 * p[0] - v[0], v[1], v[2])) h = nhelix - ch.extend(h,at_index=0) + ch.extend(h, at_index=0) # ch.points = h + ch.points else: @@ -411,7 +413,8 @@ async def pocket(o): ch.closed = True ch.rampZigZag(l[0], l[1], o) # Arc retract here first try: - if o.movement.retract_tangential: # TODO: check for entry and exit point before actual computing... will be much better. + # TODO: check for entry and exit point before actual computing... will be much better. + if o.movement.retract_tangential: # TODO: fix this for CW and CCW! for chi, ch in enumerate(lchunks): # print(chunksFromCurve[chi]) @@ -437,7 +440,8 @@ async def pocket(o): p = (p.x, p.y, p.z) # progress(str((v1,v,p))) - h = Helix(o.movement.retract_radius, o.optimisation.circle_detail, p[2] + o.movement.retract_height, p, revolutions) + h = Helix(o.movement.retract_radius, o.optimisation.circle_detail, + p[2] + o.movement.retract_height, p, revolutions) e = Euler((0, 0, rotangle + pi)) # angle to rotate whole retract move rothelix = [] @@ -476,10 +480,10 @@ async def pocket(o): if o.movement.ramp: for ch in chunks: ch.rampZigZag(ch.zstart, ch.get_point(0)[2], o) - + if o.first_down: if o.pocket_option == "OUTSIDE": - chunks.reverse() + chunks.reverse() chunks = await utils.sortChunks(chunks, o) if o.pocketToCurve: # make curve instead of a path @@ -626,16 +630,16 @@ async def medial_axis(o): polys = utils.getOperationSilhouete(o) if isinstance(polys, list): - if len(polys)==1 and isinstance(polys[0],shapely.MultiPolygon): - mpoly=polys[0] + if len(polys) == 1 and isinstance(polys[0], shapely.MultiPolygon): + mpoly = polys[0] else: - mpoly=sgeometry.MultiPolygon(polys) - elif isinstance(polys,shapely.MultiPolygon): + mpoly = sgeometry.MultiPolygon(polys) + elif isinstance(polys, shapely.MultiPolygon): # just a multipolygon - mpoly=polys + mpoly = polys else: raise CamException("Failed getting object silhouette. Is input curve closed?") - + mpoly_boundary = mpoly.boundary ipol = 0 for poly in mpoly.geoms: @@ -671,7 +675,8 @@ async def medial_axis(o): vertsPts = [Point(vert[0], vert[1], vert[2]) for vert in verts] # vertsPts= [Point(vert[0], vert[1]) for vert in verts] - pts, edgesIdx = computeVoronoiDiagram(vertsPts, xbuff, ybuff, polygonsOutput=False, formatOutput=True) + pts, edgesIdx = computeVoronoiDiagram( + vertsPts, xbuff, ybuff, polygonsOutput=False, formatOutput=True) # pts=[[pt[0], pt[1], zPosition] for pt in pts] newIdx = 0 @@ -727,7 +732,8 @@ async def medial_axis(o): do = False if do: filteredEdgs.append((vertr[e[0]][1], vertr[e[1]][1])) - ledges.append(sgeometry.LineString((filteredPts[vertr[e[0]][1]], filteredPts[vertr[e[1]][1]]))) + ledges.append(sgeometry.LineString( + (filteredPts[vertr[e[0]][1]], filteredPts[vertr[e[1]][1]]))) # print(ledges[-1].has_z) bufpoly = poly.buffer(-new_cutter_diameter / 2, resolution=64) @@ -785,8 +791,8 @@ def getLayers(operation, startdepth, enddepth): """ if startdepth < enddepth: raise CamException("Start depth is lower than end depth. " - "If you have set a custom depth end, it must be lower than depth start, " - "and should usually be negative. Set this in the CAM Operation Area panel.") + "If you have set a custom depth end, it must be lower than depth start, " + "and should usually be negative. Set this in the CAM Operation Area panel.") if operation.use_layers: layers = [] n = math.ceil((startdepth - enddepth) / operation.stepdown) @@ -848,7 +854,8 @@ def chunksToMesh(chunks, o): ch = chunks[chi] # print(chunks) # print (ch) - if ch.count() > 0: # TODO: there is a case where parallel+layers+zigzag ramps send empty chunks here... + # TODO: there is a case where parallel+layers+zigzag ramps send empty chunks here... + if ch.count() > 0: # print(len(ch.points)) nverts = [] if o.optimisation.optimize: @@ -930,7 +937,8 @@ def chunksToMesh(chunks, o): print(len(shapek.data)) print(len(verts_rotations)) - for i, co in enumerate(verts_rotations): # TODO: optimize this. this is just rewritten too many times... + # TODO: optimize this. this is just rewritten too many times... + for i, co in enumerate(verts_rotations): shapek.data[i].co = co print(time.time() - t) diff --git a/scripts/addons/cam/testing.py b/scripts/addons/cam/testing.py index 5c124c23c..0ad5bd99b 100644 --- a/scripts/addons/cam/testing.py +++ b/scripts/addons/cam/testing.py @@ -27,7 +27,8 @@ def addTestCurve(loc): - bpy.ops.curve.primitive_bezier_circle_add(radius=.05, align='WORLD', enter_editmode=False, location=loc) + bpy.ops.curve.primitive_bezier_circle_add( + radius=.05, align='WORLD', enter_editmode=False, location=loc) bpy.ops.object.editmode_toggle() bpy.ops.curve.duplicate() bpy.ops.transform.resize(value=(0.5, 0.5, 0.5), constraint_axis=(False, False, False), @@ -121,7 +122,7 @@ def testWaterline(pos): def testSimulation(): - pass; + pass def cleanUp(): diff --git a/scripts/addons/cam/tests/gcode_generator.py b/scripts/addons/cam/tests/gcode_generator.py index e3ad5bba2..a74adb8c2 100644 --- a/scripts/addons/cam/tests/gcode_generator.py +++ b/scripts/addons/cam/tests/gcode_generator.py @@ -17,4 +17,3 @@ bpy.ops.object.calculate_cam_path() sys.exit(0) - diff --git a/scripts/addons/cam/tests/install_addon.py b/scripts/addons/cam/tests/install_addon.py index 7384e25ce..fd55faa51 100644 --- a/scripts/addons/cam/tests/install_addon.py +++ b/scripts/addons/cam/tests/install_addon.py @@ -4,7 +4,7 @@ import pathlib import shutil -INSTALL_CODE=f""" +INSTALL_CODE = f""" import bpy bpy.ops.preferences.addon_install(filepath='{sys.argv[1]}') bpy.ops.preferences.addon_enable(module='cam') @@ -12,30 +12,29 @@ import cam """ -NUM_RETRIES=10 +NUM_RETRIES = 10 with tempfile.TemporaryDirectory() as td: - file=pathlib.Path(td,"install.py") - file.write_text(INSTALL_CODE) - - # blender 4.0 installing addon crashes sometimes on mac github actions... - for x in range(NUM_RETRIES): - try: - subprocess.run([shutil.which('blender'),'-b','-P',str(file)], shell=False, check=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True) - print("installed addon okay") - sys.exit(0) - except subprocess.CalledProcessError as e: - print("Install addon failed, retrying:",e) - print("Command output:") - print("------------------------------") - print(e.output) - print("------------------------------") - for line in str(e.output): - if line.startswith("Writing: "): - crash_file=pathlib.Path(line[len("Writing: "):]) - if crash_file.exists(): - print("Crash log:\n================") - print(crash_file.read_text()) - print("============================") - + file = pathlib.Path(td, "install.py") + file.write_text(INSTALL_CODE) + # blender 4.0 installing addon crashes sometimes on mac github actions... + for x in range(NUM_RETRIES): + try: + subprocess.run([shutil.which('blender'), '-b', '-P', str(file)], shell=False, + check=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) + print("installed addon okay") + sys.exit(0) + except subprocess.CalledProcessError as e: + print("Install addon failed, retrying:", e) + print("Command output:") + print("------------------------------") + print(e.output) + print("------------------------------") + for line in str(e.output): + if line.startswith("Writing: "): + crash_file = pathlib.Path(line[len("Writing: "):]) + if crash_file.exists(): + print("Crash log:\n================") + print(crash_file.read_text()) + print("============================") diff --git a/scripts/addons/cam/tests/test_suite.py b/scripts/addons/cam/tests/test_suite.py index 0ad4c79e4..6fcbf386f 100644 --- a/scripts/addons/cam/tests/test_suite.py +++ b/scripts/addons/cam/tests/test_suite.py @@ -4,6 +4,7 @@ import os import sys + class BlenderCAMTest(unittest.TestCase): @classmethod def setUpClass(cls): @@ -66,25 +67,26 @@ def run_test_case(self, test_case): # Compare the generated and expected gcode for each operation for gcode_file in test_case['gcode_files']: with self.subTest(operation=f"{test_case['subdir_name']}/{gcode_file}"): - generated = self.get_gcode_from_file(gcode_file[1:]) + generated = self.get_gcode_from_file(gcode_file[1:]) expected = self.get_gcode_from_file(gcode_file) - if sys.platform=='darwin' and os.path.exists(gcode_file+".mac"): + if sys.platform == 'darwin' and os.path.exists(gcode_file+".mac"): # bullet physics gives slightly different results on mac sometimes... # this is something we can't fix, so compare against mac generated test # file - print("Using mac test file",len(expected),len(generated)) + print("Using mac test file", len(expected), len(generated)) expected = self.get_gcode_from_file(gcode_file+".mac") self.assertMultiLineEqual(generated, expected, - msg = "\n"+self.get_diff(gcode_file[1:], gcode_file+".mac")) + msg="\n"+self.get_diff(gcode_file[1:], gcode_file+".mac")) else: self.assertMultiLineEqual(generated, expected, - msg = "\n"+self.get_diff(gcode_file[1:], gcode_file)) + msg="\n"+self.get_diff(gcode_file[1:], gcode_file)) os.remove(gcode_file[1:]) # cleanup generated file unless test fails + if __name__ == '__main__': # Add a test method for each test case to the TestCase class for test_case in BlenderCAMTest.get_test_cases(): - test_func = lambda self, tc=test_case: self.run_test_case(tc) + def test_func(self, tc=test_case): return self.run_test_case(tc) setattr(BlenderCAMTest, f'test_{test_case["subdir_name"]}', test_func) unittest.main() diff --git a/scripts/addons/cam/ui.py b/scripts/addons/cam/ui.py index 165286227..ba094b310 100644 --- a/scripts/addons/cam/ui.py +++ b/scripts/addons/cam/ui.py @@ -34,22 +34,22 @@ from cam import gcodeimportparser, simple from cam.simple import * -from cam.ui_panels.buttons_panel import CAMButtonsPanel -from cam.ui_panels.interface import * -from cam.ui_panels.info import * -from cam.ui_panels.operations import * -from cam.ui_panels.cutter import * -from cam.ui_panels.machine import * -from cam.ui_panels.material import * -from cam.ui_panels.chains import * -from cam.ui_panels.op_properties import * -from cam.ui_panels.movement import * -from cam.ui_panels.feedrate import * -from cam.ui_panels.optimisation import * -from cam.ui_panels.area import * -from cam.ui_panels.gcode import * -from cam.ui_panels.pack import * -from cam.ui_panels.slice import * +from cam.ui_panels.buttons_panel import CAMButtonsPanel +from cam.ui_panels.interface import * +from cam.ui_panels.info import * +from cam.ui_panels.operations import * +from cam.ui_panels.cutter import * +from cam.ui_panels.machine import * +from cam.ui_panels.material import * +from cam.ui_panels.chains import * +from cam.ui_panels.op_properties import * +from cam.ui_panels.movement import * +from cam.ui_panels.feedrate import * +from cam.ui_panels.optimisation import * +from cam.ui_panels.area import * +from cam.ui_panels.gcode import * +from cam.ui_panels.pack import * +from cam.ui_panels.slice import * class CAM_UL_orientations(UIList): @@ -62,8 +62,6 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn layout.label(text="", icon_value=icon) - - # panel containing all tools class VIEW3D_PT_tools_curvetools(bpy.types.Panel): diff --git a/scripts/addons/cam/ui_panels/area.py b/scripts/addons/cam/ui_panels/area.py index c52456939..4f5b7db22 100644 --- a/scripts/addons/cam/ui_panels/area.py +++ b/scripts/addons/cam/ui_panels/area.py @@ -20,7 +20,8 @@ class CAM_AREA_Panel(CAMButtonsPanel, bpy.types.Panel): } def draw_use_layers(self): - if not self.has_correct_level(): return + if not self.has_correct_level(): + return col = self.layout.column(align=True) row = col.row(align=True) row.prop(self.op, 'use_layers') @@ -29,14 +30,16 @@ def draw_use_layers(self): 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']: + 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 + 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: @@ -45,7 +48,8 @@ def draw_maxz(self): self.layout.label(text='!ERROR! COLLISION!') def draw_minz(self): - if not self.has_correct_level(): 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") @@ -75,7 +79,8 @@ def draw_minz(self): col.prop(self.op, 'source_image_crop_end_y', text='end y') def draw_ambient(self): - if not self.has_correct_level(): 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': @@ -83,7 +88,8 @@ def draw_ambient(self): self.layout.prop(self.op, "ambient_cutter_restrict") def draw_limit_curve(self): - if not self.has_correct_level(): 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: @@ -97,4 +103,3 @@ def draw(self, context): self.draw_minz() self.draw_ambient() self.draw_limit_curve() - diff --git a/scripts/addons/cam/ui_panels/buttons_panel.py b/scripts/addons/cam/ui_panels/buttons_panel.py index e645085be..c3a118acf 100644 --- a/scripts/addons/cam/ui_panels/buttons_panel.py +++ b/scripts/addons/cam/ui_panels/buttons_panel.py @@ -2,6 +2,8 @@ import inspect # Panel definitions + + class CAMButtonsPanel: bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' diff --git a/scripts/addons/cam/ui_panels/chains.py b/scripts/addons/cam/ui_panels/chains.py index 663d0b247..fdd61e414 100644 --- a/scripts/addons/cam/ui_panels/chains.py +++ b/scripts/addons/cam/ui_panels/chains.py @@ -57,7 +57,8 @@ def draw(self, context): row = layout.row(align=True) if chain: - row.template_list("CAM_UL_operations", '', chain, "operations", chain, 'active_operation') + 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="") @@ -66,13 +67,14 @@ def draw(self, context): 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") + layout.operator("object.calculate_cam_paths_chain", + text="Calculate chain paths & Export Gcode") layout.operator("object.cam_export_paths_chain", text="Export chain gcode") layout.operator("object.cam_simulate_chain", text="Simulate this chain") - valid,reason=cam.isChainValid(chain,context) + valid, reason = cam.isChainValid(chain, context) if not valid: - layout.label(icon="ERROR",text=f"Can't compute chain - reason:\n") + layout.label(icon="ERROR", text=f"Can't compute chain - reason:\n") layout.label(text=reason) else: layout.label(text='chain is currently computing') diff --git a/scripts/addons/cam/ui_panels/cutter.py b/scripts/addons/cam/ui_panels/cutter.py index e4640a755..a74fa7da7 100644 --- a/scripts/addons/cam/ui_panels/cutter.py +++ b/scripts/addons/cam/ui_panels/cutter.py @@ -27,42 +27,50 @@ class CAM_CUTTER_Panel(CAMButtonsPanel, bpy.types.Panel): } def draw_cutter_preset_menu(self): - if not self.has_correct_level(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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') @@ -70,7 +78,8 @@ def draw_laser(self): self.layout.prop(self.op, 'Laser_delay') def draw_plasma(self): - if not self.has_correct_level(): 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') @@ -80,31 +89,39 @@ def draw_plasma(self): self.layout.prop(self.op, 'lead_out') def draw_custom(self): - if not self.has_correct_level(): 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') + 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 + 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 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 + 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 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) @@ -133,5 +150,3 @@ def draw(self, context): self.draw_cutter_flutes() self.draw_cutter_description() self.draw_engagement() - - diff --git a/scripts/addons/cam/ui_panels/feedrate.py b/scripts/addons/cam/ui_panels/feedrate.py index b71a3cfe1..88cdf330b 100644 --- a/scripts/addons/cam/ui_panels/feedrate.py +++ b/scripts/addons/cam/ui_panels/feedrate.py @@ -1,6 +1,7 @@ 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" @@ -16,23 +17,28 @@ class CAM_FEEDRATE_Panel(CAMButtonsPanel, bpy.types.Panel): } def draw_feedrate(self): - if not self.has_correct_level(): 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(): 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(): 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(): 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(): return + if not self.has_correct_level(): + return self.layout.prop(self.op, 'spindle_rpm') def draw(self, context): diff --git a/scripts/addons/cam/ui_panels/gcode.py b/scripts/addons/cam/ui_panels/gcode.py index 20c934100..9e1b20b48 100644 --- a/scripts/addons/cam/ui_panels/gcode.py +++ b/scripts/addons/cam/ui_panels/gcode.py @@ -18,33 +18,38 @@ class CAM_GCODE_Panel(CAMButtonsPanel, bpy.types.Panel): } def draw_output_header(self): - if not self.has_correct_level(): 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(): 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(): 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(): 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(): 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') diff --git a/scripts/addons/cam/ui_panels/info.py b/scripts/addons/cam/ui_panels/info.py index 313640cbc..b59f6a731 100644 --- a/scripts/addons/cam/ui_panels/info.py +++ b/scripts/addons/cam/ui_panels/info.py @@ -9,6 +9,7 @@ # Info panel # This panel gives general information about the current operation + class CAM_INFO_Properties(bpy.types.PropertyGroup): warnings: bpy.props.StringProperty( @@ -45,16 +46,19 @@ class CAM_INFO_Panel(CAMButtonsPanel, bpy.types.Panel): # Draw blendercam version (and whether there are updates available) def draw_blendercam_version(self): - if not self.has_correct_level(): return + if not self.has_correct_level(): + return self.layout.label(text=f'Blendercam version: {".".join([str(x) for x in cam_version])}') - if len(bpy.context.preferences.addons['cam'].preferences.new_version_available)>0: + if len(bpy.context.preferences.addons['cam'].preferences.new_version_available) > 0: self.layout.label(text=f"New version available:") - self.layout.label(text=f" {bpy.context.preferences.addons['cam'].preferences.new_version_available}") + self.layout.label( + text=f" {bpy.context.preferences.addons['cam'].preferences.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 + 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") @@ -64,14 +68,16 @@ def draw_opencamlib_version(self): # Display warnings related to the current operation def draw_op_warnings(self): - if not self.has_correct_level(): 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(): return + if not self.has_correct_level(): + return if not int(self.op.info.duration * 60) > 0: return @@ -86,7 +92,8 @@ def draw_op_time(self): # Display the chipload (does this work ?) def draw_op_chipload(self): - if not self.has_correct_level(): return + if not self.has_correct_level(): + return if not self.op.info.chipload > 0: return @@ -95,7 +102,8 @@ def draw_op_chipload(self): # Display the current operation money cost def draw_op_money_cost(self): - if not self.has_correct_level(): return + if not self.has_correct_level(): + return if not int(self.op.info.duration * 60) > 0: return @@ -119,4 +127,4 @@ def draw(self, context): if self.op: self.draw_op_warnings() self.draw_op_time() - self.draw_op_money_cost() \ No newline at end of file + self.draw_op_money_cost() diff --git a/scripts/addons/cam/ui_panels/interface.py b/scripts/addons/cam/ui_panels/interface.py index f6fdf863f..56bccaf7a 100644 --- a/scripts/addons/cam/ui_panels/interface.py +++ b/scripts/addons/cam/ui_panels/interface.py @@ -6,9 +6,9 @@ import cam.constants -def update_interface(self,context): +def update_interface(self, context): # set default for new files - context.preferences.addons['cam'].preferences.default_interface_level=context.scene.interface.level + context.preferences.addons['cam'].preferences.default_interface_level = context.scene.interface.level bpy.ops.wm.save_userpref() diff --git a/scripts/addons/cam/ui_panels/machine.py b/scripts/addons/cam/ui_panels/machine.py index dec24a448..6ffe2c9f3 100644 --- a/scripts/addons/cam/ui_panels/machine.py +++ b/scripts/addons/cam/ui_panels/machine.py @@ -2,6 +2,7 @@ import bpy from cam.ui_panels.buttons_panel import CAMButtonsPanel + class CAM_MACHINE_Panel(CAMButtonsPanel, bpy.types.Panel): """CAM machine panel""" bl_label = "CAM Machine" @@ -26,28 +27,33 @@ class CAM_MACHINE_Panel(CAMButtonsPanel, bpy.types.Panel): } def draw_presets(self): - if not self.has_correct_level(): return + 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 + 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 + 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 + 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 + 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') @@ -55,17 +61,20 @@ def draw_position_definitions(self): self.layout.prop(self.machine, 'ending_position') def draw_working_area(self): - if not self.has_correct_level(): return + 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 + 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 + 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') @@ -73,30 +82,35 @@ def draw_splindle_speeds(self): self.layout.prop(self.machine, 'spindle_default') def draw_tool_options(self): - if not self.has_correct_level(): return + 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 + 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 + 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 + 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 + if not self.has_correct_level(): + return self.layout.prop(self.machine, 'hourly_rate') def draw(self, context): @@ -116,4 +130,3 @@ def draw(self, context): self.draw_collet_size() self.draw_block_numbers() self.draw_hourly_rate() - diff --git a/scripts/addons/cam/ui_panels/material.py b/scripts/addons/cam/ui_panels/material.py index 68856ee13..9400afafe 100644 --- a/scripts/addons/cam/ui_panels/material.py +++ b/scripts/addons/cam/ui_panels/material.py @@ -76,9 +76,11 @@ def execute(self, context): return {'FINISHED'} def draw(self, context): - if not self.interface_level <= int(self.context.scene.interface.level): return + if not self.interface_level <= int(self.context.scene.interface.level): + return self.layout.prop_search(self, "operation", bpy.context.scene, "cam_operations") + class CAM_MATERIAL_Panel(CAMButtonsPanel, bpy.types.Panel): bl_label = "CAM Material size and position" bl_idname = "WORLD_PT_CAM_MATERIAL" @@ -91,12 +93,14 @@ class CAM_MATERIAL_Panel(CAMButtonsPanel, bpy.types.Panel): } def draw_estimate_from_image(self): - if not self.has_correct_level(): return + 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 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: @@ -109,7 +113,8 @@ def draw_estimate_from_object(self): # Display Axis alignment section def draw_axis_alignment(self): - if not self.has_correct_level(): 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') diff --git a/scripts/addons/cam/ui_panels/movement.py b/scripts/addons/cam/ui_panels/movement.py index 302fa35f2..fbb96f73d 100644 --- a/scripts/addons/cam/ui_panels/movement.py +++ b/scripts/addons/cam/ui_panels/movement.py @@ -10,7 +10,7 @@ class CAM_MOVEMENT_Properties(bpy.types.PropertyGroup): # movement parallel_step_back type: bpy.props.EnumProperty(name='Movement type', items=( ('CONVENTIONAL', 'Conventional / Up milling', - 'cutter rotates against the direction of the feed'), + 'cutter rotates against the direction of the feed'), ('CLIMB', 'Climb / Down milling', 'cutter rotates with the direction of the feed'), ('MEANDER', 'Meander / Zig Zag', @@ -18,87 +18,90 @@ class CAM_MOVEMENT_Properties(bpy.types.PropertyGroup): description='movement type', default='CLIMB', update=cam.utils.update_operation) insideout: bpy.props.EnumProperty(name='Direction', - items=(('INSIDEOUT', 'Inside out', 'a'), ('OUTSIDEIN', 'Outside in', 'a')), - description='approach to the piece', default='INSIDEOUT', - update=cam.utils.update_operation) + items=(('INSIDEOUT', 'Inside out', 'a'), + ('OUTSIDEIN', 'Outside in', 'a')), + description='approach to the piece', default='INSIDEOUT', + update=cam.utils.update_operation) spindle_rotation: bpy.props.EnumProperty(name='Spindle rotation', - items=(('CW', 'Clock wise', 'a'), ('CCW', 'Counter clock wise', 'a')), - description='Spindle rotation direction', default='CW', - update=cam.utils.update_operation) + items=(('CW', 'Clock wise', 'a'), + ('CCW', 'Counter clock wise', 'a')), + description='Spindle rotation direction', default='CW', + update=cam.utils.update_operation) free_height: bpy.props.FloatProperty(name="Free movement height", - default=0.01, min=0.0000, max=32, - precision=cam.constants.PRECISION, unit="LENGTH", - update=cam.utils.update_operation) + default=0.01, min=0.0000, max=32, + precision=cam.constants.PRECISION, unit="LENGTH", + update=cam.utils.update_operation) useG64: bpy.props.BoolProperty(name="G64 trajectory", - description='Use only if your machine supports G64 code. LinuxCNC and Mach3 do', - default=False, update=cam.utils.update_operation) + description='Use only if your machine supports G64 code. LinuxCNC and Mach3 do', + default=False, update=cam.utils.update_operation) G64: bpy.props.FloatProperty(name="Path Control Mode with Optional Tolerance", - default=0.0001, min=0.0000, max=0.005, - precision=cam.constants.PRECISION, unit="LENGTH", update=cam.utils.update_operation) + default=0.0001, min=0.0000, max=0.005, + precision=cam.constants.PRECISION, unit="LENGTH", update=cam.utils.update_operation) parallel_step_back: bpy.props.BoolProperty(name="Parallel step back", - description='For roughing and finishing in one pass: mills material in climb mode, then steps back and goes between 2 last chunks back', - default=False, update=cam.utils.update_operation) + description='For roughing and finishing in one pass: mills material in climb mode, then steps back and goes between 2 last chunks back', + default=False, update=cam.utils.update_operation) helix_enter: bpy.props.BoolProperty(name="Helix enter - EXPERIMENTAL", - description="Enter material in helix", - default=False, update=cam.utils.update_operation) + description="Enter material in helix", + default=False, update=cam.utils.update_operation) ramp_in_angle: bpy.props.FloatProperty(name="Ramp in angle", default=math.pi / 6, - min=0, max=math.pi * 0.4999, - precision=1, subtype="ANGLE", unit="ROTATION", update=cam.utils.update_operation) + min=0, max=math.pi * 0.4999, + precision=1, subtype="ANGLE", unit="ROTATION", update=cam.utils.update_operation) helix_diameter: bpy.props.FloatProperty(name='Helix diameter - % of cutter diameter', - default=90, min=10, max=100, - precision=1, subtype='PERCENTAGE', update=cam.utils.update_operation) + default=90, min=10, max=100, + precision=1, subtype='PERCENTAGE', update=cam.utils.update_operation) ramp: bpy.props.BoolProperty(name="Ramp in - EXPERIMENTAL", - description="Ramps down the whole contour, so the cutline looks like helix", - default=False, update=cam.utils.update_operation) + description="Ramps down the whole contour, so the cutline looks like helix", + default=False, update=cam.utils.update_operation) ramp_out: bpy.props.BoolProperty(name="Ramp out - EXPERIMENTAL", - description="Ramp out to not leave mark on surface", default=False, - update=cam.utils.update_operation) + description="Ramp out to not leave mark on surface", default=False, + update=cam.utils.update_operation) ramp_out_angle: bpy.props.FloatProperty(name="Ramp out angle", - default=math.pi / 6, min=0, max=math.pi * 0.4999, - precision=1, subtype="ANGLE", unit="ROTATION", update=cam.utils.update_operation) + default=math.pi / 6, min=0, max=math.pi * 0.4999, + precision=1, subtype="ANGLE", unit="ROTATION", update=cam.utils.update_operation) retract_tangential: bpy.props.BoolProperty(name="Retract tangential - EXPERIMENTAL", - description="Retract from material in circular motion", default=False, - update=cam.utils.update_operation) + description="Retract from material in circular motion", default=False, + update=cam.utils.update_operation) retract_radius: bpy.props.FloatProperty(name='Retract arc radius', - default=0.001, min=0.000001, max=100, - precision=cam.constants.PRECISION, unit="LENGTH", - update=cam.utils.update_operation) + default=0.001, min=0.000001, max=100, + precision=cam.constants.PRECISION, unit="LENGTH", + update=cam.utils.update_operation) retract_height: bpy.props.FloatProperty(name='Retract arc height', - default=0.001, min=0.00000, max=100, - precision=cam.constants.PRECISION, unit="LENGTH", - update=cam.utils.update_operation) + default=0.001, min=0.00000, max=100, + precision=cam.constants.PRECISION, unit="LENGTH", + update=cam.utils.update_operation) stay_low: bpy.props.BoolProperty(name="Stay low if possible", - default=True, update=cam.utils.update_operation) + default=True, update=cam.utils.update_operation) merge_dist: bpy.props.FloatProperty(name="Merge distance - EXPERIMENTAL", - default=0.0, min=0.0000, max=0.1, - precision=cam.constants.PRECISION, unit="LENGTH", - update=cam.utils.update_operation) + default=0.0, min=0.0000, max=0.1, + precision=cam.constants.PRECISION, unit="LENGTH", + update=cam.utils.update_operation) protect_vertical: bpy.props.BoolProperty(name="Protect vertical", - description="The path goes only vertically next to steep areas", - default=True, - update=cam.utils.update_operation) + description="The path goes only vertically next to steep areas", + default=True, + update=cam.utils.update_operation) protect_vertical_limit: bpy.props.FloatProperty(name="Verticality limit", - description="What angle is allready considered vertical", - default=math.pi / 45, min=0, max=math.pi * 0.5, precision=0, - subtype="ANGLE", unit="ROTATION", update=cam.utils.update_operation) + description="What angle is allready considered vertical", + default=math.pi / 45, min=0, max=math.pi * 0.5, precision=0, + subtype="ANGLE", unit="ROTATION", update=cam.utils.update_operation) + class CAM_MOVEMENT_Panel(CAMButtonsPanel, bpy.types.Panel): """CAM movement panel""" @@ -120,37 +123,43 @@ class CAM_MOVEMENT_Panel(CAMButtonsPanel, bpy.types.Panel): } def draw_cut_type(self): - if not self.has_correct_level(): 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(): 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(): 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(): return + if not self.has_correct_level(): + return if self.context.scene.cam_machine.post_processor not in cam.constants.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.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 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: @@ -158,7 +167,8 @@ def draw_helix_enter(self): self.layout.prop(self.op.movement, 'helix_diameter') def draw_ramp(self): - if not self.has_correct_level(): 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') @@ -167,7 +177,8 @@ def draw_ramp(self): self.layout.prop(self.op.movement, 'ramp_out_angle') def draw_retract_tangential(self): - if not self.has_correct_level(): 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: @@ -175,13 +186,15 @@ def draw_retract_tangential(self): self.layout.prop(self.op.movement, 'retract_height') def draw_stay_low(self): - if not self.has_correct_level(): 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(): 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: @@ -200,4 +213,3 @@ def draw(self, context): self.draw_retract_tangential() self.draw_stay_low() self.draw_protect_vertical() - diff --git a/scripts/addons/cam/ui_panels/op_properties.py b/scripts/addons/cam/ui_panels/op_properties.py index 40d0996ec..05f343bb0 100644 --- a/scripts/addons/cam/ui_panels/op_properties.py +++ b/scripts/addons/cam/ui_panels/op_properties.py @@ -34,7 +34,8 @@ class CAM_OPERATION_PROPERTIES_Panel(CAMButtonsPanel, bpy.types.Panel): # 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 + if not self.has_correct_level(): + return if self.op.cutter_type in ['BALLCONE']: engagement = round(100 * self.op.dist_between_paths / self.op.ball_radius, 1) @@ -47,11 +48,13 @@ def draw_cutter_engagement(self): self.layout.label(text=f"Cutter engagement: {engagement}%") def draw_machine_axis(self): - if not self.has_correct_level(): return + 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 not self.has_correct_level(): + return if self.op.machine_axes == '4': self.layout.prop(self.op, 'strategy4axis') if self.op.strategy4axis == 'INDEXED': @@ -67,7 +70,8 @@ def draw_strategy(self): self.layout.prop(self.op, 'strategy') def draw_enable_A_B_axis(self): - if not self.has_correct_level(): return + 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') @@ -81,26 +85,30 @@ def draw_enable_A_B_axis(self): if self.op.enable_B: self.layout.prop(self.op, 'rotation_B') - def draw_cutout_type(self): - if not self.has_correct_level(): return + if not self.has_correct_level(): + return self.layout.prop(self.op, 'cut_type') def draw_overshoot(self): - if not self.has_correct_level(): return + if not self.has_correct_level(): + return self.layout.prop(self.op, 'straight') def draw_startpoint(self): - if not self.has_correct_level(): return + 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 + 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 + 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') @@ -108,11 +116,13 @@ def draw_outlines(self): self.layout.prop(self.op.movement, 'insideout') def draw_merge(self): - if not self.has_correct_level(): return + 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 not self.has_correct_level(): + return if self.op.strategy in ['CUTOUT']: self.draw_cutout_type() self.draw_overshoot() @@ -124,9 +134,9 @@ def draw_cutout_options(self): self.draw_outlines() self.draw_merge() - def draw_waterline_options(self): - if not self.has_correct_level(): return + 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: @@ -138,29 +148,31 @@ def draw_waterline_options(self): self.layout.label(text="Waterline needs a skin margin") def draw_carve_options(self): - if not self.has_correct_level(): return + 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 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 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 not self.has_correct_level(): + return if self.op.strategy in ['POCKET']: self.layout.prop(self.op, 'pocket_option') self.layout.prop(self.op, 'pocketToCurve') @@ -168,20 +180,21 @@ def draw_pocket_options(self): self.draw_cutter_engagement() self.draw_enable_A_B_axis() - def draw_default_options(self): - if not self.has_correct_level(): return + 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']: + 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 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: @@ -192,11 +205,13 @@ def draw_bridges_options(self): self.layout.operator("scene.cam_bridges_add", text="Autogenerate bridges") def draw_skin(self): - if not self.has_correct_level(): return + 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 not self.has_correct_level(): + return if self.op.machine_axes == '3': self.layout.prop(self.op, 'array') if self.op.array: @@ -205,7 +220,6 @@ def draw_array(self): self.layout.prop(self.op, 'array_y_count') self.layout.prop(self.op, 'array_y_distance') - def draw(self, context): self.context = context diff --git a/scripts/addons/cam/ui_panels/operations.py b/scripts/addons/cam/ui_panels/operations.py index 11cf591c7..3ac0a0ede 100644 --- a/scripts/addons/cam/ui_panels/operations.py +++ b/scripts/addons/cam/ui_panels/operations.py @@ -11,6 +11,7 @@ # # For each operation, generate the corresponding gcode and export the gcode file + class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.Panel): """CAM operations panel""" bl_label = "CAM operations" @@ -33,7 +34,8 @@ class CAM_OPERATIONS_Panel(CAMButtonsPanel, bpy.types.Panel): # create, delete, duplicate, reorder def draw_operations_list(self): row = self.layout.row() - row.template_list("CAM_UL_operations", '', bpy.context.scene, "cam_operations", bpy.context.scene, 'cam_active_operation') + row.template_list("CAM_UL_operations", '', bpy.context.scene, + "cam_operations", bpy.context.scene, 'cam_active_operation') col = row.column(align=True) col.operator("scene.cam_operation_add", icon='ADD', text="") col.operator("scene.cam_operation_copy", icon='COPYDOWN', text="") @@ -42,18 +44,19 @@ def draw_operations_list(self): 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 + 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 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') @@ -62,11 +65,12 @@ def draw_calculate_path(self): if not self.op.valid: self.layout.label(text="Select a valid object to calculate the path.") - # will be disable if not valid + # 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 not self.has_correct_level(): + return if self.op.valid: if self.op.name is not None: name = f"cam_path_{self.op.name}" @@ -74,24 +78,28 @@ def draw_export_gcode(self): self.layout.operator("object.cam_export", text="Export Gcode ") def draw_simulate_op(self): - if not self.has_correct_level(): return + 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 + 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 + 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 + if not self.has_correct_level(): + return self.layout.prop(self.op, 'geometry_source') if self.op.strategy == 'CURVE': @@ -117,15 +125,14 @@ def draw_operation_source(self): 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 + if self.op is None: + return self.draw_calculate_path() self.draw_export_gcode() @@ -133,5 +140,3 @@ def draw(self, context): self.draw_op_name() self.draw_op_filename() self.draw_operation_source() - - diff --git a/scripts/addons/cam/ui_panels/optimisation.py b/scripts/addons/cam/ui_panels/optimisation.py index db0e7700c..3c5d8cccd 100644 --- a/scripts/addons/cam/ui_panels/optimisation.py +++ b/scripts/addons/cam/ui_panels/optimisation.py @@ -53,16 +53,17 @@ class CAM_OPTIMISATION_Panel(CAMButtonsPanel, bpy.types.Panel): bl_idname = "WORLD_PT_CAM_OPTIMISATION" panel_interface_level = 2 - def draw_optimize(self): - if not self.has_correct_level(): return + 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.has_correct_level(): + return if not self.op.geometry_source == 'OBJECT' or self.op.geometry_source == 'COLLECTION': return @@ -87,7 +88,8 @@ def draw_exact_mode(self): self.layout.label(text=resolution) def draw_use_opencamlib(self): - if not self.has_correct_level(): return + if not self.has_correct_level(): + return if not (self.exact_possible and self.op.optimisation.use_exact): return @@ -101,14 +103,15 @@ def draw_use_opencamlib(self): self.layout.prop(self.op.optimisation, 'use_opencamlib') def draw_simulation_detail(self): - if not self.has_correct_level(): return + 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 not self.has_correct_level(): + return if self.op.strategy not in ['DRILL']: self.layout.prop(self.op, 'remove_redundant_points') @@ -117,16 +120,19 @@ def draw_simplify_gcode(self): self.layout.prop(self.op, 'simplify_tol') def draw_use_modifiers(self): - if not self.has_correct_level(): return + 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 + 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 + if not self.has_correct_level(): + return self.layout.prop(self.op, 'parent_path_to_object') def draw(self, context): diff --git a/scripts/addons/cam/ui_panels/slice.py b/scripts/addons/cam/ui_panels/slice.py index 602da9c65..b8fab4c78 100644 --- a/scripts/addons/cam/ui_panels/slice.py +++ b/scripts/addons/cam/ui_panels/slice.py @@ -21,4 +21,3 @@ def draw(self, context): layout.prop(settings, 'slice_above0') layout.prop(settings, 'slice_3d') layout.prop(settings, 'indexes') - diff --git a/scripts/addons/cam/utils.py b/scripts/addons/cam/utils.py index 33284d65b..3314de7ce 100644 --- a/scripts/addons/cam/utils.py +++ b/scripts/addons/cam/utils.py @@ -61,31 +61,31 @@ def update_material(self, context): addMaterialAreaObject() + def update_operation(self, context): from . import updateRest active_op = bpy.context.scene.cam_operations[bpy.context.scene.cam_active_operation] updateRest(active_op, bpy.context) + def update_exact_mode(self, context): from . import updateExact active_op = bpy.context.scene.cam_operations[bpy.context.scene.cam_active_operation] updateExact(active_op, bpy.context) + def update_opencamlib(self, context): from . import updateOpencamlib active_op = bpy.context.scene.cam_operations[bpy.context.scene.cam_active_operation] updateOpencamlib(active_op, bpy.context) + def update_zbuffer_image(self, context): from . import updateZbufferImage active_op = bpy.context.scene.cam_operations[bpy.context.scene.cam_active_operation] updateZbufferImage(active_op, bpy.context) - - - - # Import OpencamLib # Return available OpenCamLib version on success, None otherwise def opencamlib_version(): @@ -98,6 +98,7 @@ def opencamlib_version(): return return(ocl.version()) + def positionObject(operation): ob = bpy.data.objects[operation.object_name] bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS') @@ -179,7 +180,7 @@ def getBoundsWorldspace(obs, use_modifiers=False): bpy.ops.object.delete() bpy.ops.outliner.orphans_purge() else: - if not hasattr(ob.data,"splines"): + if not hasattr(ob.data, "splines"): raise CamException("Can't do CAM operation on the selected object type") # for coord in bb: for c in ob.data.splines: @@ -339,7 +340,7 @@ def getBounds(o): s = bpy.context.scene m = s.cam_machine # make sure this message only shows once and goes away once fixed - o.info.warnings.replace('Operation exceeds your machine limits\n','') + o.info.warnings.replace('Operation exceeds your machine limits\n', '') if o.max.x - o.min.x > m.working_area.x or o.max.y - o.min.y > m.working_area.y \ or o.max.z - o.min.z > m.working_area.z: o.info.warnings += 'Operation exceeds your machine limits\n' @@ -456,7 +457,6 @@ async def sampleChunks(o, pathSamples, layers): print(f"Total sample points {totlen}") - n = 0 last_percent = -1 # timing for optimisation @@ -475,9 +475,9 @@ async def sampleChunks(o, pathSamples, layers): # threads_count=4 # for t in range(0,threads): - our_points=patternchunk.get_points_np() - ambient_contains=shapely.contains(o.ambient,shapely.points(our_points[:,0:2])) - for s,in_ambient in zip(our_points,ambient_contains): + our_points = patternchunk.get_points_np() + ambient_contains = shapely.contains(o.ambient, shapely.points(our_points[:, 0:2])) + for s, in_ambient in zip(our_points, ambient_contains): if o.strategy != 'WATERLINE' and int(100 * n / totlen) != last_percent: last_percent = int(100 * n / totlen) await progress_async('sampling paths ', last_percent) @@ -500,7 +500,8 @@ async def sampleChunks(o, pathSamples, layers): z = getSampleBullet(cutter, x, y, cutterdepth, 1, lastsample[2] - o.dist_along_paths) # first try to the last sample if z < minz - 1: - z = getSampleBullet(cutter, x, y, cutterdepth, lastsample[2] - o.dist_along_paths, minz) + z = getSampleBullet(cutter, x, y, cutterdepth, + lastsample[2] - o.dist_along_paths, minz) else: z = getSampleBullet(cutter, x, y, cutterdepth, 1, minz) @@ -534,7 +535,8 @@ async def sampleChunks(o, pathSamples, layers): lastlayer = i2 currentlayer = i - if lastlayer is not None and lastlayer != currentlayer: # and lastsample[2]!=newsample[2]: + # and lastsample[2]!=newsample[2]: + if lastlayer is not None and lastlayer != currentlayer: # #sampling for sorted paths in layers- to go to the border of the sampled layer at least... # there was a bug here, but should be fixed. if currentlayer < lastlayer: @@ -566,7 +568,8 @@ async def sampleChunks(o, pathSamples, layers): if growing: if li > 0: - layeractivechunks[ls].points.insert(-1, betweensample.to_tuple()) + layeractivechunks[ls].points.insert(-1, + betweensample.to_tuple()) else: layeractivechunks[ls].points.append(betweensample.to_tuple()) layeractivechunks[ls + 1].points.append(betweensample.to_tuple()) @@ -587,17 +590,16 @@ async def sampleChunks(o, pathSamples, layers): if terminatechunk: if len(ch.points) > 0: - as_chunk=ch.to_chunk() + as_chunk = ch.to_chunk() layerchunks[i].append(as_chunk) thisrunchunks[i].append(as_chunk) layeractivechunks[i] = camPathChunkBuilder([]) lastsample = newsample - for i, l in enumerate(layers): ch = layeractivechunks[i] if len(ch.points) > 0: - as_chunk=ch.to_chunk() + as_chunk = ch.to_chunk() layerchunks[i].append(as_chunk) thisrunchunks[i].append(as_chunk) layeractivechunks[i] = camPathChunkBuilder([]) @@ -627,7 +629,8 @@ async def sampleChunks(o, pathSamples, layers): if not ch1.parents: children.append(ch1) - parentChild(parents, children, o) # parent only last and first chunk, before it did this for all. + # parent only last and first chunk, before it did this for all. + parentChild(parents, children, o) timingadd(sortingtime) chunks = [] @@ -675,7 +678,7 @@ async def sampleChunksNAxis(o, pathSamples, layers): lastrunchunks.append([]) n = 0 - last_percent=-1 + last_percent = -1 lastz = minz for patternchunk in pathSamples: # print (patternchunk.endpoints) @@ -694,10 +697,10 @@ async def sampleChunksNAxis(o, pathSamples, layers): # #TODO: seems we are writing into the source chunk , # and that is why we need to write endpoints everywhere too? - percent=int(100 * n / totlen) - if percent!=last_percent: + percent = int(100 * n / totlen) + if percent != last_percent: await progress_async('sampling paths', percent) - last_percent=percent + last_percent = percent n += 1 sampled = False # print(si) @@ -777,14 +780,16 @@ async def sampleChunksNAxis(o, pathSamples, layers): betweenrotation = tuple_add(lastrotation, tuple_mul(tuple_sub(rotation, lastrotation), ratio)) # startpoint = retract point, it has to be always available... - betweenstartpoint = laststartpoint + (startp - laststartpoint) * ratio + betweenstartpoint = laststartpoint + \ + (startp - laststartpoint) * ratio # here, we need to have also possible endpoints always.. betweenendpoint = lastendpoint + (endp - lastendpoint) * ratio if growing: if li > 0: layeractivechunks[ls].points.insert(-1, betweensample) layeractivechunks[ls].rotations.insert(-1, betweenrotation) - layeractivechunks[ls].startpoints.insert(-1, betweenstartpoint) + layeractivechunks[ls].startpoints.insert( + -1, betweenstartpoint) layeractivechunks[ls].endpoints.insert(-1, betweenendpoint) else: layeractivechunks[ls].points.append(betweensample) @@ -836,11 +841,11 @@ async def sampleChunksNAxis(o, pathSamples, layers): laststartpoint = startp lastendpoint = endp - # convert everything to actual chunks - # rather than chunkBuilders + # convert everything to actual chunks + # rather than chunkBuilders for i, l in enumerate(layers): - layeractivechunks[i]=layeractivechunks[i].to_chunk() if layeractivechunks[i] is not None else None - + layeractivechunks[i] = layeractivechunks[i].to_chunk( + ) if layeractivechunks[i] is not None else None for i, l in enumerate(layers): ch = layeractivechunks[i] @@ -964,7 +969,7 @@ def polygonConvexHull(context): bpy.ops.object.duplicate() bpy.ops.object.join() - bpy.context.object.data.dimensions = '3D' # force curve to be a 3D curve + bpy.context.object.data.dimensions = '3D' # force curve to be a 3D curve bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) bpy.context.active_object.name = "_tmp" @@ -1061,7 +1066,7 @@ async def connectChunksLow(chunks, o): pos = lastch.get_point(-1) if o.optimisation.use_opencamlib and o.optimisation.use_exact and o.strategy != 'CUTOUT' and o.strategy != 'POCKET' and o.strategy != 'WATERLINE': - await oclResampleChunks(o, chunks_to_resample,use_cached_mesh=True) + await oclResampleChunks(o, chunks_to_resample, use_cached_mesh=True) return connectedchunks @@ -1085,22 +1090,23 @@ def getClosest(o, pos, chunks): return ch -async def sortChunks(chunks, o,last_pos=None): +async def sortChunks(chunks, o, last_pos=None): if o.strategy != 'WATERLINE': await progress_async('sorting paths') - sys.setrecursionlimit(100000) # the getNext() function of CamPathChunk was running out of recursion limits. + # the getNext() function of CamPathChunk was running out of recursion limits. + sys.setrecursionlimit(100000) sortedchunks = [] chunks_to_resample = [] lastch = None - last_progress_time=time.time() - total= len(chunks) + last_progress_time = time.time() + total = len(chunks) i = len(chunks) pos = (0, 0, 0) if last_pos is None else last_pos - while len(chunks) > 0: - if o.strategy != 'WATERLINE' and time.time()-last_progress_time>0.1: - await progress_async("Sorting paths",100.0*(total-len(chunks))/total) - last_progress_time=time.time() + while len(chunks) > 0: + if o.strategy != 'WATERLINE' and time.time()-last_progress_time > 0.1: + await progress_async("Sorting paths", 100.0*(total-len(chunks))/total) + last_progress_time = time.time() ch = None if len(sortedchunks) == 0 or len( lastch.parents) == 0: # first chunk or when there are no parents -> parents come after children here... @@ -1166,7 +1172,8 @@ def getVectorRight(lastv, verts): # most right vector from a set regarding angl def cleanUpDict(ndict): - print('removing lonely points') # now it should delete all junk first, iterate over lonely verts. + # now it should delete all junk first, iterate over lonely verts. + print('removing lonely points') # found_solitaires=True # while found_solitaires: found_solitaires = False @@ -1246,7 +1253,8 @@ def getOperationSilhouete(operation): # #the small number solves issue with totally flat meshes, which people tend to mill instead of # proper pockets. then the minimum was also maximum, and it didn't detect contour. else: - i = samples > numpy.min(operation.zbuffer_image) # this fixes another numeric imprecision. + # this fixes another numeric imprecision. + i = samples > numpy.min(operation.zbuffer_image) chunks = imageToChunks(operation, i) operation.silhouete = chunksToShapely(chunks) @@ -1348,7 +1356,8 @@ def getAmbient(o): if o.ambient_behaviour == 'AROUND': r = o.ambient_radius - m - o.ambient = getObjectOutline(r, o, True) # in this method we need ambient from silhouete + # in this method we need ambient from silhouete + o.ambient = getObjectOutline(r, o, True) else: o.ambient = spolygon.Polygon(((o.min.x + m, o.min.y + m), (o.min.x + m, o.max.y - m), (o.max.x - m, o.max.y - m), (o.max.x - m, o.min.y + m))) @@ -1360,7 +1369,8 @@ def getAmbient(o): o.limit_poly = shapely.ops.unary_union(polys) if o.ambient_cutter_restrict: - o.limit_poly = o.limit_poly.buffer(o.cutter_diameter / 2, resolution=o.optimisation.circle_detail) + o.limit_poly = o.limit_poly.buffer( + o.cutter_diameter / 2, resolution=o.optimisation.circle_detail) o.ambient = o.ambient.intersection(o.limit_poly) o.update_ambient_tag = False @@ -1394,7 +1404,8 @@ def getObjectOutline(radius, o, Offset): # FIXME: make this one operation indep # print(p1.type, len(polygons)) i += 1 if radius > 0: - p1 = p1.buffer(radius * offset, resolution=o.optimisation.circle_detail, join_style=join, mitre_limit=2) + p1 = p1.buffer(radius * offset, resolution=o.optimisation.circle_detail, + join_style=join, mitre_limit=2) outlines.append(p1) # print(outlines) @@ -1473,7 +1484,8 @@ def addMachineAreaObject(): # need to be in metric units when adding machine mesh object # in order for location to work properly s.unit_settings.system = 'METRIC' - bpy.ops.mesh.primitive_cube_add(align='WORLD', enter_editmode=False, location=(1, 1, -1), rotation=(0, 0, 0)) + bpy.ops.mesh.primitive_cube_add( + align='WORLD', enter_editmode=False, location=(1, 1, -1), rotation=(0, 0, 0)) o = bpy.context.active_object o.name = 'CAM_machine' o.data.name = 'CAM_machine' @@ -1487,7 +1499,8 @@ def addMachineAreaObject(): fractal_along_normal=0, seed=0) bpy.ops.mesh.select_nth(nth=2, offset=0) bpy.ops.mesh.delete(type='EDGE') - bpy.ops.mesh.primitive_cube_add(align='WORLD', enter_editmode=False, location=(1, 1, -1), rotation=(0, 0, 0)) + bpy.ops.mesh.primitive_cube_add( + align='WORLD', enter_editmode=False, location=(1, 1, -1), rotation=(0, 0, 0)) bpy.ops.object.editmode_toggle() # addTranspMat(o, "violet_transparent", (0.800000, 0.530886, 0.725165), 0.1) @@ -1506,6 +1519,7 @@ def addMachineAreaObject(): # else: # bpy.context.scene.objects.active = None + def addMaterialAreaObject(): s = bpy.context.scene operation = s.cam_operations[s.cam_active_operation] @@ -1516,7 +1530,8 @@ def addMaterialAreaObject(): if s.objects.get('CAM_material') is not None: o = s.objects['CAM_material'] else: - bpy.ops.mesh.primitive_cube_add(align='WORLD', enter_editmode=False, location=(1, 1, -1), rotation=(0, 0, 0)) + bpy.ops.mesh.primitive_cube_add( + align='WORLD', enter_editmode=False, location=(1, 1, -1), rotation=(0, 0, 0)) o = bpy.context.active_object o.name = 'CAM_material' o.data.name = 'CAM_material' @@ -1569,7 +1584,8 @@ def unique(L): # deleting duplicates as you go nDupli = 0 nZcolinear = 0 - L.sort() # sort() brings the equal elements together; then duplicates are easy to weed out in a single pass. + # sort() brings the equal elements together; then duplicates are easy to weed out in a single pass. + L.sort() last = L[-1] for i in range(len(L) - 2, -1, -1): if last[:2] == L[i][:2]: # XY coordinates compararison @@ -1650,7 +1666,8 @@ def cleanupIndexed(operation): path.rotation_euler = ori.rotation_euler print(ori.matrix_world, operation.orientation_matrix) - for i, ob in enumerate(operation.objects): # TODO: fix this here wrong order can cause objects out of place + # TODO: fix this here wrong order can cause objects out of place + for i, ob in enumerate(operation.objects): ob.parent = operation.parents[i] for i, ob in enumerate(operation.objects): ob.matrix_world = operation.matrices[i] diff --git a/scripts/addons/cam/version.py b/scripts/addons/cam/version.py index d0d207b2b..714c3d665 100644 --- a/scripts/addons/cam/version.py +++ b/scripts/addons/cam/version.py @@ -1 +1 @@ -__version__=(1,0,3) \ No newline at end of file +__version__ = (1, 0, 4) diff --git a/scripts/addons/cam/voronoi.py b/scripts/addons/cam/voronoi.py index f15a0a9c4..58cc84d41 100644 --- a/scripts/addons/cam/voronoi.py +++ b/scripts/addons/cam/voronoi.py @@ -82,8 +82,10 @@ def __init__(self): self.extent = () # tuple (xmin, xmax, ymin, ymax) self.triangulate = False self.vertices = [] # list of vertex 2-tuples: (x,y) - self.lines = [] # equation of line 3-tuple (a b c), for the equation of the line a*x+b*y = c - self.edges = [] # edge 3-tuple: (line index, vertex 1 index, vertex 2 index) if either vertex index is -1, the edge extends to infinity + # equation of line 3-tuple (a b c), for the equation of the line a*x+b*y = c + self.lines = [] + # edge 3-tuple: (line index, vertex 1 index, vertex 2 index) if either vertex index is -1, the edge extends to infinity + self.edges = [] self.triangles = [] # 3-tuple of vertex indices self.polygons = {} # a dict of site:[edges] pairs @@ -228,7 +230,8 @@ def orderPts(self, edges): pts.extend([pt for pt in edge]) # try to get start & end point try: - startPt, endPt = [pt for pt in pts if pts.count(pt) < 2] # start and end point aren't duplicate + # start and end point aren't duplicate + startPt, endPt = [pt for pt in pts if pts.count(pt) < 2] except: # all points are duplicate --> polygon is complete --> append some or other edge points complete = True firstIdx = 0 @@ -291,7 +294,8 @@ def outVertex(self, s): def outTriple(self, s1, s2, s3): self.triangles.append((s1.sitenum, s2.sitenum, s3.sitenum)) if self.debug: - print("circle through left=%d right=%d bottom=%d" % (s1.sitenum, s2.sitenum, s3.sitenum)) + print("circle through left=%d right=%d bottom=%d" % + (s1.sitenum, s2.sitenum, s3.sitenum)) elif self.triangulate and self.doPrint: print("%d %d %d" % (s1.sitenum, s2.sitenum, s3.sitenum)) @@ -299,7 +303,7 @@ def outBisector(self, edge): self.lines.append((edge.a, edge.b, edge.c)) if self.debug: print("line(%d) %gx+%gy=%g, bisecting %d %d" % ( - edge.edgenum, edge.a, edge.b, edge.c, edge.reg[0].sitenum, edge.reg[1].sitenum)) + edge.edgenum, edge.a, edge.b, edge.c, edge.reg[0].sitenum, edge.reg[1].sitenum)) elif self.doPrint: print("l %f %f %f" % (edge.a, edge.b, edge.c)) @@ -648,7 +652,8 @@ def isPointRightOf(self, pt): fast = 1 if not fast: dxs = topsite.x - (e.reg[0]).x - above = e.b * (dxp * dxp - dyp * dyp) < dxs * dyp * (1.0 + 2.0 * dxp / dxs + e.b * e.b) + above = e.b * (dxp * dxp - dyp * dyp) < dxs * dyp * \ + (1.0 + 2.0 * dxp / dxs + e.b * e.b) if e.b < 0.0: above = not above else: # e.b == 1.0