diff --git a/config/startup.blend b/config/startup.blend index 142cc458..5f79a5b6 100644 Binary files a/config/startup.blend and b/config/startup.blend differ diff --git a/config/userpref.blend b/config/userpref.blend index 41fcc4e6..c2ddd9e3 100644 Binary files a/config/userpref.blend and b/config/userpref.blend differ diff --git a/scripts/addons/cam/__init__.py b/scripts/addons/cam/__init__.py index 4f52b1af..636bbdbf 100644 --- a/scripts/addons/cam/__init__.py +++ b/scripts/addons/cam/__init__.py @@ -453,6 +453,7 @@ class camOperation(bpy.types.PropertyGroup): movement_insideout = EnumProperty(name='Direction', items=(('INSIDEOUT','Inside out', 'a'),('OUTSIDEIN', 'Outside in', 'a')),description='approach to the piece',default='INSIDEOUT', update = updateRest) 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 = updateRest) stay_low = bpy.props.BoolProperty(name="Stay low if possible", default=True, update = updateRest) + merge_dist = bpy.props.FloatProperty(name="Merge distance - EXPERIMENTAL", default=0.0, min=0.0000, max=0.1,precision=PRECISION, unit="LENGTH", update = updateRest) #optimization and performance circle_detail = bpy.props.IntProperty(name="Detail of circles used for curve offsets", default=64, min=12, max=512, update = updateRest) use_exact = bpy.props.BoolProperty(name="Use exact mode",description="Exact mode allows greater precision, but is slower with complex meshes", default=True, update = updateExact) @@ -481,6 +482,15 @@ class camOperation(bpy.types.PropertyGroup): use_bridges = bpy.props.BoolProperty(name="Use bridges",description="use bridges in cutout", default=False, update = updateBridges) bridges_width = bpy.props.FloatProperty(name = 'width of bridges', default=0.002, unit='LENGTH', precision=PRECISION, update = updateBridges) bridges_height = bpy.props.FloatProperty(name = 'height of bridges', description="Height from the bottom of the cutting operation", default=0.0005, unit='LENGTH', precision=PRECISION, update = updateBridges) + bridges_placement = bpy.props.EnumProperty(name='Bridge placement', + items=( + ('AUTO','Automatic', 'Automatic bridges with a set distance'), + ('MANUAL','Manual', 'Manual placement of bridges'), + ), + description='Bridge placement', + default='AUTO', + update = updateStrategy) + bridges_per_curve = bpy.props.IntProperty(name="minimum bridges per curve", description="", default=4, min=1, max=512, update = updateBridges) bridges_max_distance = bpy.props.FloatProperty(name = 'Maximum distance between bridges', default=0.08, unit='LENGTH', precision=PRECISION, update = updateBridges) #optimisation panel @@ -696,6 +706,8 @@ def get_panels():#convenience function for bot register and unregister functions ops.CamOperationCopy, ops.CamOperationRemove, ops.CamOperationMove, + #bridges related + ops.CamBridgeAdd, #5 axis ops ops.CamOrientationAdd, #shape packing diff --git a/scripts/addons/cam/chunk.py b/scripts/addons/cam/chunk.py index 6d2e004d..d75fb280 100644 --- a/scripts/addons/cam/chunk.py +++ b/scripts/addons/cam/chunk.py @@ -152,8 +152,8 @@ class camPathChunk: print('found some') return ch #self.unsortedchildren=False - print('returning orig') - return self + print('returning none') + return None def getLength(self): diff --git a/scripts/addons/cam/ops.py b/scripts/addons/cam/ops.py index d52d6c37..9d2b0bba 100644 --- a/scripts/addons/cam/ops.py +++ b/scripts/addons/cam/ops.py @@ -23,7 +23,7 @@ import bpy import subprocess,os, sys, threading -from cam import utils, pack,polygon_utils_cam,chunk +from cam import utils, pack,polygon_utils_cam,chunk,simple from bpy.props import * import Polygon @@ -635,9 +635,9 @@ class CamOperationMove(bpy.types.Operator): return {'FINISHED'} -#move cam operation in the list up or down + class CamOrientationAdd(bpy.types.Operator): - '''Add orientation to cam operation''' + '''Add orientation to cam operation, for multiaxis operations''' bl_idname = "scene.cam_orientation_add" bl_label = "Add orientation" bl_options = {'REGISTER', 'UNDO'} @@ -652,20 +652,35 @@ class CamOrientationAdd(bpy.types.Operator): s=bpy.context.scene a=s.cam_active_operation o=s.cam_operations[a] - cops=bpy.context.scene.cam_operations gname=o.name+'_orientations' bpy.ops.object.empty_add(type='ARROWS') oriob=bpy.context.active_object oriob.empty_draw_size=0.02 # 2 cm - - if not gname in bpy.data.groups: - bpy.ops.group.create(name=gname) - else: - bpy.data.groups[gname].objects.link(oriob) + simple.addToGroup(oriob,gname) oriob.name='ori_'+o.name+'.'+str(len(bpy.data.groups[gname].objects)).zfill(3) + + return {'FINISHED'} + +class CamBridgeAdd(bpy.types.Operator): + '''Add orientation to cam operation, for multiaxis operations''' + bl_idname = "scene.cam_bridge_add" + bl_label = "Add bridge" + bl_options = {'REGISTER', 'UNDO'} + + + @classmethod + def poll(cls, context): + return context.scene is not None + + def execute(self, context): + #main(context) + s=bpy.context.scene + a=s.cam_active_operation + o=s.cam_operations[a] + utils.addBridge(o) return {'FINISHED'} diff --git a/scripts/addons/cam/simple.py b/scripts/addons/cam/simple.py index 483ad0b9..136150b3 100644 --- a/scripts/addons/cam/simple.py +++ b/scripts/addons/cam/simple.py @@ -77,6 +77,14 @@ def dupliob(o,pos): bpy.ops.rigidbody.object_remove() o.location=pos +def addToGroup(ob,groupname): + activate(ob) + if bpy.data.groups.get(groupname)==None: + bpy.ops.group.create(name = groupname) + else: + bpy.ops.object.group_link(group=groupname) + + def compare(v1,v2,vmiddle,e): '''comparison for optimisation of paths''' #e=0.0001 diff --git a/scripts/addons/cam/ui.py b/scripts/addons/cam/ui.py index 8539439e..18fc9e74 100644 --- a/scripts/addons/cam/ui.py +++ b/scripts/addons/cam/ui.py @@ -405,10 +405,12 @@ class CAM_OPERATION_PROPERTIES_Panel(CAMButtonsPanel, bpy.types.Panel): layout.prop(ao,'dont_merge') layout.prop(ao,'use_bridges') if ao.use_bridges: + layout.prop(ao,'bridges_placement') layout.prop(ao,'bridges_width') layout.prop(ao,'bridges_height') - layout.prop(ao,'bridges_per_curve') - layout.prop(ao,'bridges_max_distance') + if ao.bridges_placement == 'AUTO': + layout.prop(ao,'bridges_per_curve') + layout.prop(ao,'bridges_max_distance') elif ao.strategy=='WATERLINE': layout.prop(ao,'slice_detail') @@ -442,8 +444,7 @@ class CAM_OPERATION_PROPERTIES_Panel(CAMButtonsPanel, bpy.types.Panel): layout.prop(ao,'dist_along_paths') if ao.strategy=='PARALLEL' or ao.strategy=='CROSS': layout.prop(ao,'parallel_angle') - if not ao.ramp: - layout.prop(ao,'parallel_step_back') + layout.prop(ao,'skin') layout.prop(ao,'inverse') @@ -485,11 +486,15 @@ class CAM_MOVEMENT_Panel(CAMButtonsPanel, bpy.types.Panel): ao=scene.cam_operations[scene.cam_active_operation] if ao.valid: layout.prop(ao,'movement_type') + if ao.movement_type=='BLOCK' or ao.movement_type=='SPIRAL' or ao.movement_type=='CIRCLES': layout.prop(ao,'movement_insideout') layout.prop(ao,'spindle_rotation_direction') layout.prop(ao,'free_movement_height') + if ao.strategy=='PARALLEL' or ao.strategy=='CROSS': + if not ao.ramp: + layout.prop(ao,'parallel_step_back') if ao.strategy=='CUTOUT': layout.prop(ao,'first_down') #if ao.first_down: @@ -513,6 +518,8 @@ class CAM_MOVEMENT_Panel(CAMButtonsPanel, bpy.types.Panel): layout.prop(ao,'ramp_out_angle') layout.prop(ao,'stay_low') + if ao.stay_low: + layout.prop(ao,'merge_dist') layout.prop(ao,'protect_vertical') if ao.protect_vertical: layout.prop(ao,'protect_vertical_limit') diff --git a/scripts/addons/cam/utils.py b/scripts/addons/cam/utils.py index b7c1f265..c99f8321 100644 --- a/scripts/addons/cam/utils.py +++ b/scripts/addons/cam/utils.py @@ -1423,7 +1423,9 @@ def connectChunksLow(chunks,o): if o.parallel_step_back: mergedist*=2 - + if o.merge_dist>0: + mergedist=o.merge_dist + #mergedist=10 lastch=None i=len(chunks) pos=(0,0,0) @@ -1904,88 +1906,155 @@ def addMaterialAreaObject(): else: bpy.context.scene.objects.active=None + +def getContainer(): + s=bpy.context.scene + if s.objects.get('CAM_OBJECTS')==None: + bpy.ops.object.empty_add(type='PLAIN_AXES', view_align=False) + container=bpy.context.active_object + container.name='CAM_OBJECTS' + container.location=[0,0,0] + container.hide=True + else: + container=s.objects['CAM_OBJECTS'] + + return container + + +def getSnappingObject(o): + s=bpy.context.scene + + bproj_name='cam_snap_object_'+o.name + + if s.objects.get(bproj_name)!=None: + ob=s.objects[bproj_name] + else: + getOperationSources(o) + ob=o.objects[0] + activate(ob) + bpy.ops.object.duplicate() + ob=bpy.context.active_object + #should do with ALL objects from the operation sources, and join them! + ob.name=bproj_name + #ensure the object is 'flat' + ob.data.dimensions = '2D' + ob.data.dimensions = '3D' + ob.location.z=0 + ob.data.extrude=0.01 + bpy.ops.object.convert(target='MESH') + bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM') + ob.parent=getContainer() + ob.hide=True + return ob + +def addBridge(o): + container=getContainer() + bpy.ops.object.empty_add(type='CUBE',location=[0,0,0], view_align=False) + b=bpy.context.active_object + b.name='bridge_'+o.name + b.rotation_euler.x=math.pi/2 + b.empty_draw_size=0.01 + b.lock_location[2]=True + b.parent=container + ob=getSnappingObject(o) + activate(b) + bpy.ops.object.constraint_add(type='SHRINKWRAP') + b.constraints[-1].target=ob + addToGroup(b,'cam_bridges_' + o.name ) + + def getBridges(p,o): # this function finds positions of the bridges, and returns these. - pass; -def addBridges(ch,o,z): - #this functions adds Bridges to the finished chunks. - ch.getLength() - n=int(ch.length/o.bridges_max_distance) - bpc=o.bridges_per_curve - if o.bridges_width*bpc>ch.length/2: - bpc=math.floor(ch.length/(2*o.bridges_width)) - n = max(n,bpc) - if n>0: - dist=ch.length/n - pos=[] - for i in range(0,n): - pos.append([i*dist+0.00001+dist/2.0,i*dist+0.00001+dist/2.0+o.bridges_width+o.cutter_diameter]) - dist=0 - bridgeheight=min(0,o.min.z+o.bridges_height) - inbridge=False - posi=0 - insertpoints=[] - changepoints=[] - vi=0 - while vich.length/2: + bpc=math.floor(ch.length/(2*o.bridges_width)) + n = max(n,bpc) + if n>0: + dist=ch.length/n + pos=[] + for i in range(0,n): + pos.append([i*dist+0.00001+dist/2.0,i*dist+0.00001+dist/2.0+o.bridges_width+o.cutter_diameter]) + dist=0 + bridgeheight=min(0,o.min.z+o.bridges_height) + inbridge=False + posi=0 + insertpoints=[] + changepoints=[] + vi=0 + while vipoint1.z: - point1.z=min(point1.z,bridgeheight) - point2.z=max(point2.z,bridgeheight) - #ch.points.insert(vi-1,point1) - #ch.points.insert(vi,point2) - insertpoints.append([vi+1,point1.to_tuple()]) - insertpoints.append([vi+1,point2.to_tuple()]) - inbridge=True + wasinbridge=inbridge + if not inbridge and posipoint1.z: + point1.z=min(point1.z,bridgeheight) + point2.z=max(point2.z,bridgeheight) + #ch.points.insert(vi-1,point1) + #ch.points.insert(vi,point2) + insertpoints.append([vi+1,point1.to_tuple()]) + insertpoints.append([vi+1,point2.to_tuple()]) + inbridge=True + + if wasinbridge and inbridge:#still in bridge, raise the point up.# + changepoints.append([vi,(v1.x,v1.y,max(v1.z,bridgeheight))]) + #ch.points[vi]=(v1.x,v1.y,max(v1.z,bridgeheight)) + + if inbridge and pos[posi][1]point1.z: + point1.z=max(point1.z,bridgeheight) + point2.z=min(point2.z,bridgeheight) + #ch.points.insert(vi,point1) + #ch.points.insert(vi+1,point2) + #vi+=2 + insertpoints.append([vi+1,point1.to_tuple()]) + insertpoints.append([vi+1,point2.to_tuple()]) + inbridge=False + posi+=1 + vi-=1 + dist-=v.length + vi+=1 + - if wasinbridge and inbridge:#still in bridge, raise the point up.# - changepoints.append([vi,(v1.x,v1.y,max(v1.z,bridgeheight))]) - #ch.points[vi]=(v1.x,v1.y,max(v1.z,bridgeheight)) - if inbridge and pos[posi][1]point1.z: - point1.z=max(point1.z,bridgeheight) - point2.z=min(point2.z,bridgeheight) - #ch.points.insert(vi,point1) - #ch.points.insert(vi+1,point2) - #vi+=2 - insertpoints.append([vi+1,point1.to_tuple()]) - insertpoints.append([vi+1,point2.to_tuple()]) - inbridge=False - posi+=1 - vi-=1 - dist-=v.length - vi+=1 - - - - if posi>=len(pos): - #print('added bridges') - break; - for p in changepoints: - ch.points[p[0]]=p[1] - for pi in range(len(insertpoints)-1,-1,-1): - ch.points.insert(insertpoints[pi][0],insertpoints[pi][1]) + if posi>=len(pos): + #print('added bridges') + break; + for p in changepoints: + ch.points[p[0]]=p[1] + for pi in range(len(insertpoints)-1,-1,-1): + ch.points.insert(insertpoints[pi][0],insertpoints[pi][1]) #this is the main function. #FIXME: split strategies into separate file! #def cutoutStrategy(o): @@ -2088,7 +2157,7 @@ def getPath3axis(context,operation): chunk=chl[0] layer=chl[1] if layer[1]