cumulative commit:

fixed bug in nc, when exporting more than 1 axis.
tried to generalize 4 axis path generation code
re-allowed more panels hooked to specific renderers in the interface
Changed the way rotations are stored in the path mesh object - now they are stored as shape keys
pull/4/head
vilda.novak@gmail.com 2014-12-08 00:14:13 +00:00
rodzic 15215e9de3
commit 06dda5a50b
6 zmienionych plików z 200 dodań i 116 usunięć

Wyświetl plik

@ -262,12 +262,12 @@ class camOperation(bpy.types.PropertyGroup):
update = updateStrategy)
strategy4axis = EnumProperty(name='Strategy',
items=(
('PARALLELA','Parallel around A', 'Parallel lines around A axis'),
('PARALLELX','Parallel along X', 'Parallel lines along X axis'),
('HELIXA','Helix around A', 'Helix around A axis, growing in X axis direction'),
('PARALLELR','Parallel around 1st rotary axis', 'Parallel lines around first rotary axis'),
('PARALLEL','Parallel along 1st rotary axis', 'Parallel lines along first rotary axis'),
('HELIX','Helix around 1st rotary axis', 'Helix around rotary axis'),
('CROSS','Cross', 'Cross paths')),
description='Strategy',
default='PARALLELA',
default='PARALLEL',
update = updateStrategy)
strategy5axis = EnumProperty(name='Strategy',
items=(
@ -289,7 +289,7 @@ class camOperation(bpy.types.PropertyGroup):
update = updateStrategy)
#active_orientation = bpy.props.IntProperty(name="active orientation",description="active orientation", default=0,min=0, max=32000, update = updateRest)
rotary_axis_1 = EnumProperty(name='Rotary axis - A',
rotary_axis_1 = EnumProperty(name='Rotary axis',
items=(
('X','X', ''),
('Y','Y', ''),
@ -662,6 +662,7 @@ def compatible_panels():
'''gets panels that are for blender internal, but are compatible with blender CAM'''
t = bpy.types
return (
#textures
t.TEXTURE_PT_context_texture,
t.TEXTURE_PT_preview,
t.TEXTURE_PT_colors,
@ -687,13 +688,65 @@ def compatible_panels():
t.TEXTURE_PT_influence,
t.TEXTURE_PT_custom_props,
#meshes
t.DATA_PT_shape_keys,
t.DATA_PT_uv_texture,
t.DATA_PT_vertex_colors,
t.DATA_PT_customdata,
t.DATA_PT_custom_props_mesh,
#materials
t.MATERIAL_PT_context_material,
t.MATERIAL_PT_preview,
t.MATERIAL_PT_pipeline,
t.MATERIAL_PT_diffuse,
t.MATERIAL_PT_specular,
t.MATERIAL_PT_shading,
t.MATERIAL_PT_transp,
t.MATERIAL_PT_mirror,
t.MATERIAL_PT_sss,
t.MATERIAL_PT_halo,
t.MATERIAL_PT_flare,
t.MATERIAL_PT_game_settings,
t.MATERIAL_PT_physics,
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,
t.MATERIAL_PT_volume_lighting,
t.MATERIAL_PT_volume_transp,
t.MATERIAL_PT_volume_integration,
t.MATERIAL_PT_volume_options,
t.MATERIAL_PT_custom_props,
#particles
t.PARTICLE_PT_context_particles,
t.PARTICLE_PT_emission,
t.PARTICLE_PT_hair_dynamics,
t.PARTICLE_PT_cache,
t.PARTICLE_PT_velocity,
t.PARTICLE_PT_rotation,
t.PARTICLE_PT_physics,
t.PARTICLE_PT_boidbrain,
t.PARTICLE_PT_render,
t.PARTICLE_PT_draw,
t.PARTICLE_PT_children,
t.PARTICLE_PT_field_weights,
t.PARTICLE_PT_force_fields,
t.PARTICLE_PT_vertexgroups,
#scene
t.SCENE_PT_scene,
t.SCENE_PT_unit,
#t.SCENE_PT_keying_sets,
#t.SCENE_PT_keying_set_paths,
#t.SCENE_PT_color_management,
#t.SCENE_PT_audio,
t.SCENE_PT_keying_sets,
t.SCENE_PT_keying_set_paths,
t.SCENE_PT_color_management,
t.SCENE_PT_audio,
t.SCENE_PT_physics,
t.SCENE_PT_rigid_body_world,
t.SCENE_PT_rigid_body_cache,
@ -705,12 +758,12 @@ def compatible_panels():
t.WORLD_PT_context_world,
t.WORLD_PT_preview,
t.WORLD_PT_world,
#t.WORLD_PT_ambient_occlusion,
#t.WORLD_PT_environment_lighting,
#t.WORLD_PT_indirect_lighting,
#t.WORLD_PT_gather,
#t.WORLD_PT_mist,
#t.WORLD_PT_stars,
t.WORLD_PT_ambient_occlusion,
t.WORLD_PT_environment_lighting,
t.WORLD_PT_indirect_lighting,
t.WORLD_PT_gather,
t.WORLD_PT_mist,
t.WORLD_PT_stars,
t.WORLD_PT_custom_props
)

Wyświetl plik

@ -22,11 +22,21 @@ def getCutterBullet(o):
bpy.ops.rigidbody.object_add(type='ACTIVE')
cutter=bpy.context.active_object
cutter.rigid_body.collision_shape = 'CYLINDER'
elif type=='BALL':
bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=1, size=BULLET_SCALE*o.cutter_diameter/2, view_align=False, enter_editmode=False, location=(-100,-100, -100), rotation=(0, 0, 0))
bpy.ops.rigidbody.object_add(type='ACTIVE')
cutter=bpy.context.active_object
cutter.rigid_body.collision_shape = 'SPHERE'
elif type=='BALL' or type=='BALLNOSE':
if o.strategy!='PROJECTED_CURVE' and type=='BALLNOSE':#only ball, good for 3 axis and real ball cutters
bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=2, size=BULLET_SCALE*o.cutter_diameter/2, view_align=False, enter_editmode=False, location=(-100,-100, -100), rotation=(0, 0, 0))
bpy.ops.rigidbody.object_add(type='ACTIVE')
cutter=bpy.context.active_object
cutter.rigid_body.collision_shape = 'SPHERE'
else:#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=2, size=BULLET_SCALE*o.cutter_diameter/2, view_align=False, enter_editmode=False, location=(-100,-100, -100), rotation=(0, 0, 0))
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
@ -126,20 +136,22 @@ def getSampleBullet(cutter, x,y, radius, startz, endz):
else:
return endz-10;
def getSampleBulletNAxis(cutter, startpoint,endpoint,rotation, radius):
def getSampleBulletNAxis(cutter, startpoint,endpoint,rotation, cutter_compensation):
'''fully 3d collision test for NAxis milling'''
start=(startpoint*BULLET_SCALE).to_tuple()
end=(endpoint*BULLET_SCALE).to_tuple()
cutterVec=Vector((0,0,1))*cutter_compensation#cutter compensation vector - cutter physics object has center in the middle, while cam needs the tip position.
cutterVec.rotate(Euler(rotation))
start=(startpoint*BULLET_SCALE+cutterVec).to_tuple()
end=((endpoint)*BULLET_SCALE+cutterVec).to_tuple()
#cutter.rotation_euler=rotation
pos=bpy.context.scene.rigidbody_world.convex_sweep_test(cutter, start, end)
#radius is subtracted because we are interested in cutter tip position, this gets collision object center
if pos[3]==1:
pos=Vector(pos[0])
v=endpoint-startpoint# a vector in the opposite direction of sweep test
v.normalize()
res=(pos+v*radius)/BULLET_SCALE
#rescale and compensate from center to tip.
res=pos/BULLET_SCALE-cutterVec/BULLET_SCALE
#this is a debug loop that duplicates the cutter on sampling positions, to see where it was moving...
#if random.random()<0.01:
# dupliob(cutter,res)

Wyświetl plik

@ -454,17 +454,17 @@ class Creator(nc.Creator):
if (b != None):
db = b - self.b
if (self.absolute_flag ):
self.write([self.SPACE() , self.B() , (self.fmt.string(b))])
self.writem([self.SPACE() , self.B() , (self.fmt.string(b))])
else:
self.write([self.SPACE() , self.B() , (self.fmt.string(db))])
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.write([self.SPACE() , self.C() , (self.fmt.string(c))])
self.writem([self.SPACE() , self.C() , (self.fmt.string(c))])
else:
self.write([self.SPACE() , self.C() , (self.fmt.string(dc))])
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))

Wyświetl plik

@ -7,7 +7,8 @@ import random
# offsets them by the pre-set margin
#then chooses a starting location possibly inside the allready occupied area and moves and rotates the polygon out of the occupied area
#if one or more positions are found where the poly doesn't overlap, it is placed and added to the occupied area - allpoly
#this algorithm is very slow, a collision algorithm would be much much faster...
#this algorithm is very slow and STUPID, a collision algorithm would be much much faster...
def packCurves():
packsettings=bpy.context.scene.cam_pack

Wyświetl plik

@ -382,31 +382,62 @@ def getPathPattern4axis(operation):
minx,miny,minz,maxx,maxy,maxz=o.min.x,o.min.y,o.min.z,o.max.x,o.max.y,o.max.z
pathchunks=[]
zlevel=1#minz#this should do layers...
if o.strategy4axis=='PARALLELA':
cutterstart=Vector((0,0,0))#start point for casting
cutterend=Vector((0,0,0))#end point for casting
my=max(abs(o.min.y),abs(o.max.y))
mz=max(abs(o.min.z),abs(o.max.z))
radius=math.sqrt(my*my+mz*mz)#max radius estimation
circlesteps=(radius*pi*2)/o.dist_along_paths
steps=(o.max.x-o.min.x)/o.dist_between_paths
anglestep = 2*pi/circlesteps
e=Euler((anglestep,0,0))
#set axes for various options, Z option is obvious nonsense now.
if o.rotary_axis_1=='X':
a1=0
a2=1
a3=2
if o.rotary_axis_1=='Y':
a1=1
a2=0
a3=2
if o.rotary_axis_1=='Z':
a1=2
a2=0
a3=1
#set radius for all types of operation
m1=max(abs(o.min[a2]),abs(o.max[a2]))
m2=max(abs(o.min[a3]),abs(o.max[a3]))
radius=math.sqrt(m1*m1+m2*m2)#max radius estimation
circlesteps=(radius*pi*2)/o.dist_along_paths
anglestep = 2*pi/circlesteps
#generalized rotation
e=Euler((0,0,0))
e[a1]=anglestep
#generalized length of the operation
maxl=o.max[a1]
minl=o.min[a1]
steps=(maxl-minl)/o.dist_between_paths
#set starting positions for cutter e.t.c.
cutterstart=Vector((0,0,0))
cutterend=Vector((0,0,0))#end point for casting
if o.strategy4axis=='PARALLELR':
for a in range(0,floor(steps)+1):
chunk=camPathChunk([])
cutterstart.x=o.min.x+a*o.dist_between_paths
cutterend.x=cutterstart.x
cutterstart.y=0
cutterstart.z=radius
cutterstart[a1]=o[a1]+a*o.dist_between_paths
cutterend[a1]=cutterstart[a1]
cutterstart[a2]=radius
for b in range(0,floor(circlesteps)+1):
#print(cutterstart,cutterend)
chunk.startpoints.append(cutterstart.to_tuple())
chunk.endpoints.append(cutterend.to_tuple())
chunk.rotations.append((b*anglestep,0,0))
rot=[0,0,0]
rot[a1]=b*anglestep
chunk.rotations.append(rot)
cutterstart.rotate(e)
chunk.depth=-radius
@ -417,38 +448,28 @@ def getPathPattern4axis(operation):
pathchunks.append(chunk)
if o.strategy4axis=='PARALLELX':
cutterstart=Vector((0,0,0))#start point for casting
cutterend=Vector((0,0,0))#end point for casting
my=max(abs(o.min.y),abs(o.max.y))
mz=max(abs(o.min.z),abs(o.max.z))
radius=math.sqrt(my*my+mz*mz)#max radius estimation
circlesteps=(radius*pi*2)/o.dist_along_paths
steps=(o.max.x-o.min.x)/o.dist_between_paths
anglestep = 2*pi/circlesteps
e=Euler((anglestep,0,0))
if o.strategy4axis=='PARALLEL':
reverse=False
for b in range(0,floor(circlesteps)+1):
chunk=camPathChunk([])
cutterstart.y=0
cutterstart.z=radius
e.x=anglestep*b
cutterstart[a2]=0
cutterstart[a3]=radius
e[a1]=anglestep*b
cutterstart.rotate(e)
for a in range(0,floor(steps)+1):
cutterstart.x=o.min.x+a*o.dist_between_paths
cutterend.x=cutterstart.x
cutterstart[a1]=o.min[a1]+a*o.dist_between_paths
cutterend[a1]=cutterstart[a1]
chunk.startpoints.append(cutterstart.to_tuple())
chunk.endpoints.append(cutterend.to_tuple())
chunk.rotations.append((b*anglestep,0,0))
rot=[0,0,0]
rot[a1]=b*anglestep
chunk.rotations.append(rot)
chunk.depth=-radius
#last point = first
pathchunks.append(chunk)
if (reverse and o.movement_type=='MEANDER') or (o.movement_type=='CONVENTIONAL' and o.spindle_rotation_direction=='CW') or (o.movement_type=='CLIMB' and o.spindle_rotation_direction=='CCW') :
@ -456,47 +477,42 @@ def getPathPattern4axis(operation):
reverse=not reverse
if o.strategy4axis=='HELIXA':
if o.strategy4axis=='HELIX':
print('helix')
cutterstart=Vector((0,0,0))#start point for casting
cutterend=Vector((0,0,0))#end point for casting
my=max(abs(o.min.y),abs(o.max.y))
mz=max(abs(o.min.z),abs(o.max.z))
radius=math.sqrt(my*my+mz*mz)#max radius estimation
circlesteps=(radius*pi*2)/o.dist_along_paths
steps=(o.max.x-o.min.x)/o.dist_between_paths
anglestep = 2*pi/circlesteps
xstep=o.dist_between_paths / circlesteps
e=Euler((anglestep,0,0))
a1step=o.dist_between_paths / circlesteps
chunk=camPathChunk([])#only one chunk, init here
for a in range(0,floor(steps)+1):
cutterstart.x=o.min.x+a*o.dist_between_paths
cutterend.x=cutterstart.x
cutterstart.y=0
cutterstart.z=radius
cutterstart[a1]=o.min[a1]+a*o.dist_between_paths
cutterend[a1]=cutterstart[a1]
cutterstart[a2]=0
cutterstart[a3]=radius
for b in range(0,floor(circlesteps)+1):
#print(cutterstart,cutterend)
cutterstart.x+=xstep
cutterend.x+=xstep
cutterstart[a1]+=a1step
cutterend[a1]+=a1step
chunk.startpoints.append(cutterstart.to_tuple())
chunk.endpoints.append(cutterend.to_tuple())
chunk.rotations.append((b*anglestep,0,0))
rot=[0,0,0]
rot[a1]=b*anglestep
chunk.rotations.append(rot)
cutterstart.rotate(e)
chunk.depth=-radius
#last point = first
pathchunks.append(chunk)
pathchunks.append(chunk)
#print(chunk.startpoints)
#print(pathchunks)
#sprint(len(pathchunks))
#print(o.strategy4axis)
return pathchunks

Wyświetl plik

@ -982,27 +982,30 @@ def chunksToMesh(chunks,o):
mesh = bpy.data.meshes.new(oname)
mesh.name=oname
mesh.from_pydata(verts, edges, [])
if o.machine_axes!='3':
x=[]
y=[]
z=[]
for a,b,c in verts_rotations:#TODO: optimize this. this is just rewritten too many times...
#print(r)
x.append(a)
y.append(b)
z.append(c)
mesh['rot_x']=x
mesh['rot_y']=y
mesh['rot_z']=z
if oname in s.objects:
s.objects[oname].data=mesh
ob=s.objects[oname]
else:
ob=object_utils.object_data_add(bpy.context, mesh, operator=None)
ob=ob.object
if o.machine_axes!='3':
#store rotations into shape keys, only way to store large arrays with correct floating point precision - object/mesh attributes can only store array up to 32000 intems.
x=[]
y=[]
z=[]
ob.shape_key_add()
ob.shape_key_add()
shapek=mesh.shape_keys.key_blocks[1]
shapek.name='rotations'
for i,co in enumerate(verts_rotations):#TODO: optimize this. this is just rewritten too many times...
#print(r)
shapek.data[i].co=co
print(time.time()-t)
@ -1052,7 +1055,7 @@ def exportGcodePath(filename,vertslist,operations):
elif m.post_processor=='HEIDENHAIN':
extension='.H'
from .nc import heiden as postprocessor
elif m.post_processor=='TNC151':
elif m.post_processor=='TNC11':
from .nc import tnc151 as postprocessor
elif m.post_processor=='SIEGKX1':
from .nc import siegkx1 as postprocessor
@ -1105,9 +1108,7 @@ def exportGcodePath(filename,vertslist,operations):
mesh=vertslist[i]
verts=mesh.vertices[:]
if o.machine_axes!='3':
rx=mesh['rot_x']
ry=mesh['rot_y']
rz=mesh['rot_z']
rots=mesh.shape_keys.key_blocks['rotations'].data
#spindle rpm and direction
###############
@ -1149,7 +1150,7 @@ def exportGcodePath(filename,vertslist,operations):
v=vert.co
if o.machine_axes!='3':
v=v.copy()#we rotate it so we need to copy the vector
r=Euler((rx[vi],ry[vi],rz[vi]))
r=Euler(rots[vi].co)
#conversion to N-axis coordinates
# this seems to work correctly
rcompensate=r.copy()
@ -1162,7 +1163,7 @@ def exportGcodePath(filename,vertslist,operations):
else: ra=r.x*rotcorr
if r.y==lastrot.y: rb=None;
else: rb=r.y*rotcorr
#print ( ra,rb)
if vi>0 and v.x==last.x: vx=None;
else: vx=v.x*unitcorr
if vi>0 and v.y==last.y: vy=None;
@ -1183,6 +1184,7 @@ def exportGcodePath(filename,vertslist,operations):
if o.machine_axes=='3':
c.feed( x=vx, y=vy, z=vz )
else:
#print(ra,rb)
c.feed( x=vx, y=vy, z=vz ,a = ra, b = rb)
@ -2048,7 +2050,7 @@ def getPath3and5axis(context,operation):
ch.depth=0
for i,s in enumerate(ch.points):
np=c.closest_point_on_mesh(s)
ch.startpoints.append(s)
ch.startpoints.append(Vector(s))
ch.endpoints.append(np[0])
ch.rotations.append((0,0,0))
vect = np[0]-Vector(s)
@ -2813,7 +2815,7 @@ def getPath4axis(context,operation):
s=bpy.context.scene
o=operation
getBounds(o)
if o.strategy4axis=='PARALLELA' or o.strategy4axis=='PARALLELX' or o.strategy4axis=='HELIXA' or o.strategy4axis=='CROSS':
if o.strategy4axis=='PARALLELR' or o.strategy4axis=='PARALLEL' or o.strategy4axis=='HELIX' or o.strategy4axis=='CROSS':
pathSamples=getPathPattern4axis(o)
depth=pathSamples[0].depth