kopia lustrzana https://github.com/vilemduha/blendercam
2.80_BETA
rodzic
edcd11f13f
commit
9b24469cfb
|
@ -19,3 +19,5 @@ scripts/addons_contrib/online_mat_lib/material-library/bundled/cycles/wood/rough
|
|||
acp
|
||||
syncWithUpstream
|
||||
forceSyncWithUpstream
|
||||
|
||||
\.idea/
|
||||
|
|
Plik diff jest za duży
Load Diff
Plik diff jest za duży
Load Diff
|
@ -25,217 +25,252 @@ import time
|
|||
from cam import simple
|
||||
from cam.simple import *
|
||||
|
||||
BULLET_SCALE=10000 # this is a constant for scaling the rigidbody collision world for higher precision from bullet library
|
||||
CUTTER_OFFSET = (-5*BULLET_SCALE,-5*BULLET_SCALE,-5*BULLET_SCALE)# the cutter object has to be present in the scene , so we need to put it aside for sweep collisions, otherwise it collides itself.
|
||||
BULLET_SCALE = 10000 # this is a constant for scaling the rigidbody collision world for higher precision from bullet library
|
||||
CUTTER_OFFSET = (-5 * BULLET_SCALE, -5 * BULLET_SCALE,
|
||||
-5 * BULLET_SCALE) # the cutter object has to be present in the scene , so we need to put it aside for sweep collisions, otherwise it collides itself.
|
||||
|
||||
|
||||
#
|
||||
def getCutterBullet(o):
|
||||
'''cutter for rigidbody simulation collisions
|
||||
note that everything is 100x bigger for simulation precision.'''
|
||||
s=bpy.context.scene
|
||||
if s.objects.get('cutter')!= None:
|
||||
c=s.objects['cutter']
|
||||
activate(c)
|
||||
'''cutter for rigidbody simulation collisions
|
||||
note that everything is 100x bigger for simulation precision.'''
|
||||
|
||||
s = bpy.context.scene
|
||||
if s.objects.get('cutter') is not None:
|
||||
c = s.objects['cutter']
|
||||
activate(c)
|
||||
|
||||
type = o.cutter_type
|
||||
if type == 'END':
|
||||
bpy.ops.mesh.primitive_cylinder_add(vertices=32, radius=BULLET_SCALE * o.cutter_diameter / 2,
|
||||
depth=BULLET_SCALE * o.cutter_diameter, end_fill_type='NGON',
|
||||
view_align=False, enter_editmode=False, location=CUTTER_OFFSET,
|
||||
rotation=(0, 0, 0))
|
||||
bpy.ops.rigidbody.object_add(type='ACTIVE')
|
||||
cutter = bpy.context.active_object
|
||||
cutter.rigid_body.collision_shape = 'CYLINDER'
|
||||
elif type == 'BALL' or type == 'BALLNOSE':
|
||||
if o.strategy != 'PROJECTED_CURVE' or type == 'BALL': # only sphere, good for 3 axis and real ball cutters for undercuts and projected curve
|
||||
|
||||
bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=2, radius=BULLET_SCALE * o.cutter_diameter / 2,
|
||||
view_align=False, enter_editmode=False, location=CUTTER_OFFSET,
|
||||
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, raius=BULLET_SCALE * o.cutter_diameter / 2,
|
||||
view_align=False, enter_editmode=False, location=CUTTER_OFFSET,
|
||||
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
|
||||
s = math.tan(math.pi * (90 - angle / 2) / 180) / 2
|
||||
bpy.ops.mesh.primitive_cone_add(vertices=32, radius1=BULLET_SCALE * o.cutter_diameter / 2, radius2=0,
|
||||
depth=BULLET_SCALE * o.cutter_diameter * s, end_fill_type='NGON',
|
||||
view_align=False, enter_editmode=False, location=CUTTER_OFFSET,
|
||||
rotation=(math.pi, 0, 0))
|
||||
bpy.ops.rigidbody.object_add(type='ACTIVE')
|
||||
cutter = bpy.context.active_object
|
||||
cutter.rigid_body.collision_shape = 'CONE'
|
||||
elif type == 'CUSTOM':
|
||||
cutob = bpy.data.objects[o.cutter_object_name]
|
||||
activate(cutob)
|
||||
bpy.ops.object.duplicate()
|
||||
bpy.ops.rigidbody.object_add(type='ACTIVE')
|
||||
cutter = bpy.context.active_object
|
||||
scale = o.cutter_diameter / cutob.dimensions.x
|
||||
cutter.scale *= BULLET_SCALE * scale
|
||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='BOUNDS')
|
||||
|
||||
# print(cutter.dimensions,scale)
|
||||
bpy.ops.rigidbody.object_add(type='ACTIVE')
|
||||
cutter.rigid_body.collision_shape = 'CONVEX_HULL'
|
||||
cutter.location = CUTTER_OFFSET
|
||||
|
||||
cutter.name = 'cam_cutter'
|
||||
o.cutter_shape = cutter
|
||||
return cutter
|
||||
|
||||
type=o.cutter_type
|
||||
if type=='END':
|
||||
bpy.ops.mesh.primitive_cylinder_add(vertices=32, radius=BULLET_SCALE*o.cutter_diameter/2, depth=BULLET_SCALE*o.cutter_diameter, end_fill_type='NGON', view_align=False, enter_editmode=False, location=CUTTER_OFFSET, rotation=(0, 0, 0))
|
||||
bpy.ops.rigidbody.object_add(type='ACTIVE')
|
||||
cutter=bpy.context.active_object
|
||||
cutter.rigid_body.collision_shape = 'CYLINDER'
|
||||
elif type=='BALL' or type=='BALLNOSE':
|
||||
if o.strategy!='PROJECTED_CURVE' or type=='BALL':#only sphere, good for 3 axis and real ball cutters for undercuts and projected curve
|
||||
|
||||
bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=2, size=BULLET_SCALE*o.cutter_diameter/2, view_align=False, enter_editmode=False, location=CUTTER_OFFSET, 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=CUTTER_OFFSET, 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
|
||||
s=math.tan(math.pi*(90-angle/2)/180)/2
|
||||
bpy.ops.mesh.primitive_cone_add(vertices=32, radius1=BULLET_SCALE*o.cutter_diameter/2, radius2=0, depth = BULLET_SCALE*o.cutter_diameter*s, end_fill_type='NGON', view_align=False, enter_editmode=False, location=CUTTER_OFFSET, rotation=(math.pi, 0, 0))
|
||||
bpy.ops.rigidbody.object_add(type='ACTIVE')
|
||||
cutter=bpy.context.active_object
|
||||
cutter.rigid_body.collision_shape = 'CONE'
|
||||
elif type=='CUSTOM':
|
||||
cutob=bpy.data.objects[o.cutter_object_name]
|
||||
activate(cutob)
|
||||
bpy.ops.object.duplicate()
|
||||
bpy.ops.rigidbody.object_add(type='ACTIVE')
|
||||
cutter=bpy.context.active_object
|
||||
scale=o.cutter_diameter/cutob.dimensions.x
|
||||
cutter.scale*=BULLET_SCALE*scale
|
||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN',center = 'BOUNDS')
|
||||
|
||||
#print(cutter.dimensions,scale)
|
||||
bpy.ops.rigidbody.object_add(type='ACTIVE')
|
||||
cutter.rigid_body.collision_shape = 'CONVEX_HULL'
|
||||
cutter.location=CUTTER_OFFSET
|
||||
|
||||
cutter.name='cam_cutter'
|
||||
o.cutter_shape=cutter
|
||||
return cutter
|
||||
|
||||
def subdivideLongEdges(ob, threshold):
|
||||
|
||||
print('subdividing long edges')
|
||||
m=ob.data
|
||||
scale=(ob.scale.x+ob.scale.y+ob.scale.z)/3
|
||||
subdivides=[]
|
||||
n=1
|
||||
iter=0
|
||||
while n>0:
|
||||
n=0
|
||||
for i,e in enumerate(m.edges):
|
||||
v1=m.vertices[e.vertices[0]].co
|
||||
v2=m.vertices[e.vertices[1]].co
|
||||
vec=v2-v1
|
||||
l=vec.length
|
||||
if l*scale>threshold:
|
||||
n+=1
|
||||
subdivides.append(i)
|
||||
if n>0:
|
||||
print(len(subdivides))
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
print('subdividing long edges')
|
||||
m = ob.data
|
||||
scale = (ob.scale.x + ob.scale.y + ob.scale.z) / 3
|
||||
subdivides = []
|
||||
n = 1
|
||||
iter = 0
|
||||
while n > 0:
|
||||
n = 0
|
||||
for i, e in enumerate(m.edges):
|
||||
v1 = m.vertices[e.vertices[0]].co
|
||||
v2 = m.vertices[e.vertices[1]].co
|
||||
vec = v2 - v1
|
||||
l = vec.length
|
||||
if l * scale > threshold:
|
||||
n += 1
|
||||
subdivides.append(i)
|
||||
if n > 0:
|
||||
print(len(subdivides))
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
#bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
|
||||
#bpy.ops.mesh.tris_convert_to_quads()
|
||||
# bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
|
||||
# bpy.ops.mesh.tris_convert_to_quads()
|
||||
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
|
||||
bpy.ops.object.editmode_toggle()
|
||||
for i in subdivides:
|
||||
m.edges[i].select = True
|
||||
bpy.ops.object.editmode_toggle()
|
||||
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.select_all(action='DESELECT')
|
||||
bpy.ops.object.editmode_toggle()
|
||||
ob.update_from_editmode()
|
||||
iter += 1
|
||||
|
||||
|
||||
# n=0
|
||||
#
|
||||
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
|
||||
bpy.ops.object.editmode_toggle()
|
||||
for i in subdivides:
|
||||
m.edges[i].select=True
|
||||
bpy.ops.object.editmode_toggle()
|
||||
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.select_all(action='DESELECT')
|
||||
bpy.ops.object.editmode_toggle()
|
||||
ob.update_from_editmode()
|
||||
iter+=1
|
||||
#n=0
|
||||
#
|
||||
|
||||
def prepareBulletCollision(o):
|
||||
'''prepares all objects needed for sampling with bullet collision'''
|
||||
progress('preparing collisions')
|
||||
t=time.time()
|
||||
s=bpy.context.scene
|
||||
s.gravity=(0,0,0)
|
||||
#cleanup rigidbodies wrongly placed somewhere in the scene
|
||||
for ob in bpy.context.scene.objects:
|
||||
if ob.rigid_body != None and (bpy.data.groups.find('machine')>-1 and ob.name not in bpy.data.groups['machine'].objects):
|
||||
activate(ob)
|
||||
bpy.ops.rigidbody.object_remove()
|
||||
|
||||
for collisionob in o.objects:
|
||||
activate(collisionob)
|
||||
bpy.ops.object.duplicate(linked=False)
|
||||
collisionob=bpy.context.active_object
|
||||
if collisionob.type=='CURVE' or collisionob.type=='FONT':#support for curve objects collision
|
||||
if collisionob.type=='CURVE':
|
||||
odata=collisionob.data.dimensions
|
||||
collisionob.data.dimensions='2D'
|
||||
bpy.ops.object.convert(target='MESH', keep_original=False)
|
||||
'''prepares all objects needed for sampling with bullet collision'''
|
||||
progress('preparing collisions')
|
||||
|
||||
print(o.name)
|
||||
t = time.time()
|
||||
s = bpy.context.scene
|
||||
s.gravity = (0, 0, 0)
|
||||
# cleanup rigidbodies wrongly placed somewhere in the scene
|
||||
for ob in bpy.context.scene.objects:
|
||||
if ob.rigid_body is not None and (bpy.data.objects.find('machine') > -1 and ob.name not in bpy.data.objects['machine'].objects):
|
||||
activate(ob)
|
||||
bpy.ops.rigidbody.object_remove()
|
||||
|
||||
for collisionob in o.objects:
|
||||
activate(collisionob)
|
||||
bpy.ops.object.duplicate(linked=False)
|
||||
collisionob = bpy.context.active_object
|
||||
if collisionob.type == 'CURVE' or collisionob.type == 'FONT': # support for curve objects collision
|
||||
if collisionob.type == 'CURVE':
|
||||
odata = collisionob.data.dimensions
|
||||
collisionob.data.dimensions = '2D'
|
||||
bpy.ops.object.convert(target='MESH', keep_original=False)
|
||||
|
||||
if o.use_modifiers:
|
||||
newmesh = collisionob.to_mesh(bpy.context.depsgraph, True, calc_undeformed=False)
|
||||
oldmesh = collisionob.data
|
||||
collisionob.modifiers.clear()
|
||||
collisionob.data = newmesh
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
|
||||
# subdivide long edges here:
|
||||
if o.exact_subdivide_edges:
|
||||
subdivideLongEdges(collisionob, o.cutter_diameter * 2)
|
||||
|
||||
bpy.ops.rigidbody.object_add(type='ACTIVE') # using active instead of passive because of performance.TODO: check if this works also with 4axis...
|
||||
collisionob.rigid_body.collision_shape = 'MESH'
|
||||
collisionob.rigid_body.kinematic = True # this fixed a serious bug and gave big speedup, rbs could move since they are now active...
|
||||
collisionob.rigid_body.collision_margin = o.skin * BULLET_SCALE
|
||||
bpy.ops.transform.resize(value=(BULLET_SCALE, BULLET_SCALE, BULLET_SCALE),
|
||||
constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False,
|
||||
proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1,
|
||||
snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False,
|
||||
snap_normal=(0, 0, 0), texture_space=False, release_confirm=False)
|
||||
collisionob.location = collisionob.location * BULLET_SCALE
|
||||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||||
|
||||
|
||||
getCutterBullet(o)
|
||||
|
||||
# machine objects scaling up to simulation scale
|
||||
if bpy.data.objects.find('machine') > -1:
|
||||
for ob in bpy.data.objects['machine'].objects:
|
||||
activate(ob)
|
||||
bpy.ops.transform.resize(value=(BULLET_SCALE, BULLET_SCALE, BULLET_SCALE),
|
||||
constraint_axis=(False, False, False), constraint_orientation='GLOBAL',
|
||||
mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH',
|
||||
proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0),
|
||||
snap_align=False, snap_normal=(0, 0, 0), texture_space=False,
|
||||
release_confirm=False)
|
||||
ob.location = ob.location * BULLET_SCALE
|
||||
# stepping simulation so that objects are up to date
|
||||
bpy.context.scene.frame_set(0)
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.context.scene.frame_set(2)
|
||||
progress(time.time() - t)
|
||||
|
||||
|
||||
if o.use_modifiers:
|
||||
newmesh = collisionob.to_mesh(bpy.context.scene, True, 'RENDER', False)
|
||||
oldmesh = collisionob.data
|
||||
collisionob.modifiers.clear()
|
||||
collisionob.data = newmesh
|
||||
bpy.data.meshes.remove(oldmesh)
|
||||
|
||||
#subdivide long edges here:
|
||||
if o.exact_subdivide_edges:
|
||||
subdivideLongEdges(collisionob, o.cutter_diameter*2)
|
||||
|
||||
bpy.ops.rigidbody.object_add(type='ACTIVE')#using active instead of passive because of performance.TODO: check if this works also with 4axis...
|
||||
collisionob.rigid_body.collision_shape = 'MESH'
|
||||
collisionob.rigid_body.kinematic=True#this fixed a serious bug and gave big speedup, rbs could move since they are now active...
|
||||
collisionob.rigid_body.collision_margin = o.skin*BULLET_SCALE
|
||||
bpy.ops.transform.resize(value=(BULLET_SCALE, BULLET_SCALE, BULLET_SCALE), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), texture_space=False, release_confirm=False)
|
||||
collisionob.location=collisionob.location*BULLET_SCALE
|
||||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||||
|
||||
getCutterBullet(o)
|
||||
#machine objects scaling up to simulation scale
|
||||
if bpy.data.groups.find('machine')>-1:
|
||||
for ob in bpy.data.groups['machine'].objects:
|
||||
activate(ob)
|
||||
bpy.ops.transform.resize(value=(BULLET_SCALE, BULLET_SCALE, BULLET_SCALE), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), texture_space=False, release_confirm=False)
|
||||
ob.location=ob.location*BULLET_SCALE
|
||||
#stepping simulation so that objects are up to date
|
||||
bpy.context.scene.frame_set(0)
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.context.scene.frame_set(2)
|
||||
progress(time.time()-t)
|
||||
|
||||
|
||||
def cleanupBulletCollision(o):
|
||||
if bpy.data.groups.find('machine')>-1:
|
||||
machinepresent=True
|
||||
else:
|
||||
machinepresent=False
|
||||
for ob in bpy.context.scene.objects:
|
||||
if ob.rigid_body != None and not (machinepresent and ob.name in bpy.data.groups['machine'].objects):
|
||||
delob(ob)
|
||||
#machine objects scaling up to simulation scale
|
||||
if machinepresent:
|
||||
for ob in bpy.data.groups['machine'].objects:
|
||||
activate(ob)
|
||||
bpy.ops.transform.resize(value=(1.0/BULLET_SCALE, 1.0/BULLET_SCALE, 1.0/BULLET_SCALE), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), texture_space=False, release_confirm=False)
|
||||
ob.location=ob.location/BULLET_SCALE
|
||||
if bpy.data.objects.find('machine') > -1:
|
||||
machinepresent = True
|
||||
else:
|
||||
machinepresent = False
|
||||
for ob in bpy.context.scene.objects:
|
||||
if ob.rigid_body is not None and not (machinepresent and ob.name in bpy.data.objects['machine'].objects):
|
||||
delob(ob)
|
||||
# machine objects scaling up to simulation scale
|
||||
if machinepresent:
|
||||
for ob in bpy.data.objects['machine'].objects:
|
||||
activate(ob)
|
||||
bpy.ops.transform.resize(value=(1.0 / BULLET_SCALE, 1.0 / BULLET_SCALE, 1.0 / BULLET_SCALE),
|
||||
constraint_axis=(False, False, False), constraint_orientation='GLOBAL',
|
||||
mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH',
|
||||
proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0),
|
||||
snap_align=False, snap_normal=(0, 0, 0), texture_space=False,
|
||||
release_confirm=False)
|
||||
ob.location = ob.location / BULLET_SCALE
|
||||
|
||||
def getSampleBullet(cutter, x,y, radius, startz, endz):
|
||||
'''collision test for 3 axis milling. Is simplified compared to the full 3d test'''
|
||||
pos=bpy.context.scene.rigidbody_world.convex_sweep_test(cutter, (x*BULLET_SCALE, y*BULLET_SCALE, startz*BULLET_SCALE), (x*BULLET_SCALE, y*BULLET_SCALE, endz*BULLET_SCALE))
|
||||
|
||||
#radius is subtracted because we are interested in cutter tip position, this gets collision object center
|
||||
|
||||
if pos[3]==1:
|
||||
return (pos[0][2]-radius)/BULLET_SCALE
|
||||
else:
|
||||
return endz-10;
|
||||
|
||||
def getSampleBulletNAxis(cutter, startpoint,endpoint,rotation, cutter_compensation):
|
||||
'''fully 3d collision test for NAxis milling'''
|
||||
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))
|
||||
#print(rotation)
|
||||
#print(cutterVec)
|
||||
#cutterVec=Vector((0,0,0))
|
||||
#cutterVec = startpoint-endpoint
|
||||
#cutterVec.normalize()
|
||||
#cutterVec*=cutter_compensation
|
||||
#cutterVec=Vector((0,0,0))
|
||||
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)
|
||||
|
||||
|
||||
if pos[3]==1:
|
||||
pos=Vector(pos[0])
|
||||
#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)
|
||||
|
||||
return res
|
||||
else:
|
||||
return None;
|
||||
|
||||
def getSampleBullet(cutter, x, y, radius, startz, endz):
|
||||
'''collision test for 3 axis milling. Is simplified compared to the full 3d test'''
|
||||
pos = bpy.context.scene.rigidbody_world.convex_sweep_test(cutter, (
|
||||
x * BULLET_SCALE, y * BULLET_SCALE, startz * BULLET_SCALE),
|
||||
(x * BULLET_SCALE, y * BULLET_SCALE, endz * BULLET_SCALE))
|
||||
|
||||
# radius is subtracted because we are interested in cutter tip position, this gets collision object center
|
||||
|
||||
if pos[3] == 1:
|
||||
return (pos[0][2] - radius) / BULLET_SCALE
|
||||
else:
|
||||
return endz - 10
|
||||
|
||||
|
||||
def getSampleBulletNAxis(cutter, startpoint, endpoint, rotation, cutter_compensation):
|
||||
'''fully 3d collision test for NAxis milling'''
|
||||
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))
|
||||
# print(rotation)
|
||||
# print(cutterVec)
|
||||
# cutterVec=Vector((0,0,0))
|
||||
# cutterVec = startpoint-endpoint
|
||||
# cutterVec.normalize()
|
||||
# cutterVec*=cutter_compensation
|
||||
# cutterVec=Vector((0,0,0))
|
||||
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)
|
||||
|
||||
if pos[3] == 1:
|
||||
pos = Vector(pos[0])
|
||||
# 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)
|
||||
|
||||
return res
|
||||
else:
|
||||
return None
|
||||
|
|
Plik diff jest za duży
Load Diff
Plik diff jest za duży
Load Diff
|
@ -20,7 +20,7 @@
|
|||
# ***** END GPL LICENCE BLOCK *****
|
||||
|
||||
import bpy
|
||||
from cam import utils, simple,polygon_utils_cam
|
||||
from cam import utils, simple, polygon_utils_cam
|
||||
import shapely
|
||||
from shapely import geometry as sgeometry
|
||||
from shapely import affinity, prepared
|
||||
|
@ -28,198 +28,198 @@ from shapely import speedups
|
|||
import random, time
|
||||
import mathutils
|
||||
from mathutils import Vector
|
||||
#this algorithm takes all selected curves,
|
||||
#converts them to polygons,
|
||||
|
||||
|
||||
# this algorithm takes all selected curves,
|
||||
# converts them to polygons,
|
||||
# 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 and STUPID, a collision algorithm would be much much faster...
|
||||
def translate(s,x,y):
|
||||
ncoords=[]
|
||||
for p in s.exterior.coords:
|
||||
|
||||
ncoords.append((p[0]+x,p[1]+y))
|
||||
|
||||
return sgeometry.Polygon(ncoords)
|
||||
# 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 and STUPID, a collision algorithm would be much much faster...
|
||||
def translate(s, x, y):
|
||||
ncoords = []
|
||||
for p in s.exterior.coords:
|
||||
ncoords.append((p[0] + x, p[1] + y))
|
||||
|
||||
return sgeometry.Polygon(ncoords)
|
||||
|
||||
|
||||
def srotate(s, r, x, y):
|
||||
ncoords = []
|
||||
e = mathutils.Euler((0, 0, r))
|
||||
for p in s.exterior.coords:
|
||||
v1 = Vector((p[0], p[1], 0))
|
||||
v2 = Vector((x, y, 0))
|
||||
v = v1 - v2
|
||||
v.rotate(e)
|
||||
ncoords.append((v[0], v[1]))
|
||||
|
||||
return sgeometry.Polygon(ncoords)
|
||||
|
||||
|
||||
def srotate(s,r,x,y):
|
||||
ncoords=[]
|
||||
e=mathutils.Euler((0,0,r))
|
||||
for p in s.exterior.coords:
|
||||
v1=Vector((p[0],p[1],0))
|
||||
v2=Vector((x,y,0))
|
||||
v=v1-v2
|
||||
v.rotate(e)
|
||||
ncoords.append((v[0],v[1]))
|
||||
|
||||
return sgeometry.Polygon(ncoords)
|
||||
|
||||
def packCurves():
|
||||
if speedups.available:
|
||||
speedups.enable()
|
||||
t=time.time()
|
||||
packsettings=bpy.context.scene.cam_pack
|
||||
|
||||
sheetsizex=packsettings.sheet_x
|
||||
sheetsizey=packsettings.sheet_y
|
||||
direction=packsettings.sheet_fill_direction
|
||||
distance=packsettings.distance
|
||||
rotate = packsettings.rotate
|
||||
|
||||
polyfield=[]#in this, position, rotation, and actual poly will be stored.
|
||||
for ob in bpy.context.selected_objects:
|
||||
allchunks=[]
|
||||
simple.activate(ob)
|
||||
bpy.ops.object.make_single_user(type='SELECTED_OBJECTS')
|
||||
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
|
||||
z=ob.location.z
|
||||
bpy.ops.object.location_clear()
|
||||
bpy.ops.object.rotation_clear()
|
||||
if speedups.available:
|
||||
speedups.enable()
|
||||
t = time.time()
|
||||
packsettings = bpy.context.scene.cam_pack
|
||||
|
||||
chunks=utils.curveToChunks(ob)
|
||||
npolys=utils.chunksToShapely(chunks)
|
||||
#add all polys in silh to one poly
|
||||
poly=shapely.ops.unary_union(npolys)
|
||||
|
||||
poly=poly.buffer(distance/1.5,8)
|
||||
poly=poly.simplify(0.0003)
|
||||
polyfield.append([[0,0],0.0,poly,ob,z])
|
||||
random.shuffle(polyfield)
|
||||
#primitive layout here:
|
||||
allpoly=prepared.prep(sgeometry.Polygon())#main collision poly.
|
||||
#allpoly=sgeometry.Polygon()#main collision poly.
|
||||
|
||||
shift=0.0015#one milimeter by now.
|
||||
rotchange=.3123456#in radians
|
||||
|
||||
xmin,ymin,xmax,ymax=polyfield[0][2].bounds
|
||||
if direction=='X':
|
||||
mindist=-xmin
|
||||
else:
|
||||
mindist=-ymin
|
||||
i=0
|
||||
p=polyfield[0][2]
|
||||
placedpolys=[]
|
||||
rotcenter=sgeometry.Point(0,0)
|
||||
for pf in polyfield:
|
||||
print(i)
|
||||
rot=0
|
||||
porig=pf[2]
|
||||
placed=False
|
||||
xmin,ymin,xmax,ymax=p.bounds
|
||||
#p.shift(-xmin,-ymin)
|
||||
if direction=='X':
|
||||
x=mindist
|
||||
y=-ymin
|
||||
if direction=='Y':
|
||||
x=-xmin
|
||||
y=mindist
|
||||
|
||||
iter=0
|
||||
best=None
|
||||
hits=0
|
||||
besthit=None
|
||||
while not placed:
|
||||
|
||||
#swap x and y, and add to x
|
||||
#print(x,y)
|
||||
p=porig
|
||||
|
||||
if rotate:
|
||||
#ptrans=srotate(p,rot,0,0)
|
||||
ptrans=affinity.rotate(p,rot,origin = rotcenter, use_radians=True)
|
||||
#ptrans = translate(ptrans,x,y)
|
||||
ptrans = affinity.translate(ptrans,x,y)
|
||||
else:
|
||||
#ptrans = translate(p,x,y)
|
||||
ptrans = affinity.translate(p,x,y)
|
||||
xmin,ymin,xmax,ymax=ptrans.bounds
|
||||
#print(iter,p.bounds)
|
||||
|
||||
if xmin>0 and ymin>0 and ((direction=='Y' and xmax<sheetsizex) or (direction=='X' and ymax<sheetsizey)):
|
||||
if not allpoly.intersects(ptrans):
|
||||
#if allpoly.disjoint(ptrans):
|
||||
#print('gothit')
|
||||
#we do more good solutions, choose best out of them:
|
||||
hits+=1
|
||||
if best==None:
|
||||
best=[x,y,rot,xmax,ymax]
|
||||
besthit=hits
|
||||
if direction=='X':
|
||||
if xmax<best[3]:
|
||||
best=[x,y,rot,xmax,ymax]
|
||||
besthit=hits
|
||||
elif ymax<best[4]:
|
||||
best=[x,y,rot,xmax,ymax]
|
||||
besthit=hits
|
||||
|
||||
sheetsizex = packsettings.sheet_x
|
||||
sheetsizey = packsettings.sheet_y
|
||||
direction = packsettings.sheet_fill_direction
|
||||
distance = packsettings.distance
|
||||
rotate = packsettings.rotate
|
||||
|
||||
polyfield = [] # in this, position, rotation, and actual poly will be stored.
|
||||
for ob in bpy.context.selected_objects:
|
||||
allchunks = []
|
||||
simple.activate(ob)
|
||||
bpy.ops.object.make_single_user(type='SELECTED_OBJECTS')
|
||||
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
|
||||
z = ob.location.z
|
||||
bpy.ops.object.location_clear()
|
||||
bpy.ops.object.rotation_clear()
|
||||
|
||||
|
||||
|
||||
if hits>=15 or (iter>10000 and hits>0):#here was originally more, but 90% of best solutions are still 1
|
||||
placed=True
|
||||
pf[3].location.x=best[0]
|
||||
pf[3].location.y=best[1]
|
||||
pf[3].location.z=pf[4]
|
||||
pf[3].rotation_euler.z=best[2]
|
||||
|
||||
|
||||
pf[3].select=True
|
||||
|
||||
#print(mindist)
|
||||
mindist=mindist-0.5*(xmax-xmin)
|
||||
#print(mindist)
|
||||
#print(iter)
|
||||
|
||||
#reset polygon to best position here:
|
||||
ptrans=affinity.rotate(porig,best[2],rotcenter, use_radians = True)
|
||||
#ptrans=srotate(porig,best[2],0,0)
|
||||
ptrans = affinity.translate(ptrans,best[0],best[1])
|
||||
#ptrans = translate(ptrans,best[0],best[1])
|
||||
|
||||
#polygon_utils_cam.polyToMesh(p,0.1)#debug visualisation
|
||||
keep=[]
|
||||
print(best[0],best[1])
|
||||
#print(len(ptrans.exterior))
|
||||
#npoly=allpoly.union(ptrans)
|
||||
'''
|
||||
for ci in range(0,len(allpoly)):
|
||||
cminx,cmaxx,cminy,cmaxy=allpoly.boundingBox(ci)
|
||||
if direction=='X' and cmaxx>mindist-.1:
|
||||
npoly.addContour(allpoly[ci])
|
||||
if direction=='Y' and cmaxy>mindist-.1:
|
||||
npoly.addContour(allpoly[ci])
|
||||
'''
|
||||
#allpoly=npoly
|
||||
placedpolys.append(ptrans)
|
||||
allpoly=prepared.prep(sgeometry.MultiPolygon(placedpolys))
|
||||
#*** temporary fix until prepared geometry code is setup properly
|
||||
#allpoly=sgeometry.MultiPolygon(placedpolys)
|
||||
|
||||
#polygon_utils_cam.polyToMesh(allpoly,0.1)#debug visualisation
|
||||
|
||||
#for c in p:
|
||||
# allpoly.addContour(c)
|
||||
#cleanup allpoly
|
||||
print(iter,hits,besthit)
|
||||
if not placed:
|
||||
if direction=='Y':
|
||||
x+=shift
|
||||
mindist=y
|
||||
if (xmax+shift>sheetsizex):
|
||||
x=x-xmin
|
||||
y+=shift
|
||||
if direction=='X':
|
||||
y+=shift
|
||||
mindist=x
|
||||
if (ymax+shift>sheetsizey):
|
||||
y=y-ymin
|
||||
x+=shift
|
||||
if rotate: rot+=rotchange
|
||||
iter+=1
|
||||
i+=1
|
||||
t=time.time()-t
|
||||
chunks = utils.curveToChunks(ob)
|
||||
npolys = utils.chunksToShapely(chunks)
|
||||
# add all polys in silh to one poly
|
||||
poly = shapely.ops.unary_union(npolys)
|
||||
|
||||
polygon_utils_cam.shapelyToCurve('test',sgeometry.MultiPolygon(placedpolys),0)
|
||||
print(t)
|
||||
poly = poly.buffer(distance / 1.5, 8)
|
||||
poly = poly.simplify(0.0003)
|
||||
polyfield.append([[0, 0], 0.0, poly, ob, z])
|
||||
random.shuffle(polyfield)
|
||||
# primitive layout here:
|
||||
allpoly = prepared.prep(sgeometry.Polygon()) # main collision poly.
|
||||
# allpoly=sgeometry.Polygon()#main collision poly.
|
||||
|
||||
shift = 0.0015 # one milimeter by now.
|
||||
rotchange = .3123456 # in radians
|
||||
|
||||
xmin, ymin, xmax, ymax = polyfield[0][2].bounds
|
||||
if direction == 'X':
|
||||
mindist = -xmin
|
||||
else:
|
||||
mindist = -ymin
|
||||
i = 0
|
||||
p = polyfield[0][2]
|
||||
placedpolys = []
|
||||
rotcenter = sgeometry.Point(0, 0)
|
||||
for pf in polyfield:
|
||||
print(i)
|
||||
rot = 0
|
||||
porig = pf[2]
|
||||
placed = False
|
||||
xmin, ymin, xmax, ymax = p.bounds
|
||||
# p.shift(-xmin,-ymin)
|
||||
if direction == 'X':
|
||||
x = mindist
|
||||
y = -ymin
|
||||
if direction == 'Y':
|
||||
x = -xmin
|
||||
y = mindist
|
||||
|
||||
iter = 0
|
||||
best = None
|
||||
hits = 0
|
||||
besthit = None
|
||||
while not placed:
|
||||
|
||||
# swap x and y, and add to x
|
||||
# print(x,y)
|
||||
p = porig
|
||||
|
||||
if rotate:
|
||||
# ptrans=srotate(p,rot,0,0)
|
||||
ptrans = affinity.rotate(p, rot, origin=rotcenter, use_radians=True)
|
||||
# ptrans = translate(ptrans,x,y)
|
||||
ptrans = affinity.translate(ptrans, x, y)
|
||||
else:
|
||||
# ptrans = translate(p,x,y)
|
||||
ptrans = affinity.translate(p, x, y)
|
||||
xmin, ymin, xmax, ymax = ptrans.bounds
|
||||
# print(iter,p.bounds)
|
||||
|
||||
if xmin > 0 and ymin > 0 and (
|
||||
(direction == 'Y' and xmax < sheetsizex) or (direction == 'X' and ymax < sheetsizey)):
|
||||
if not allpoly.intersects(ptrans):
|
||||
# if allpoly.disjoint(ptrans):
|
||||
# print('gothit')
|
||||
# we do more good solutions, choose best out of them:
|
||||
hits += 1
|
||||
if best == None:
|
||||
best = [x, y, rot, xmax, ymax]
|
||||
besthit = hits
|
||||
if direction == 'X':
|
||||
if xmax < best[3]:
|
||||
best = [x, y, rot, xmax, ymax]
|
||||
besthit = hits
|
||||
elif ymax < best[4]:
|
||||
best = [x, y, rot, xmax, ymax]
|
||||
besthit = hits
|
||||
|
||||
if hits >= 15 or (
|
||||
iter > 10000 and hits > 0): # here was originally more, but 90% of best solutions are still 1
|
||||
placed = True
|
||||
pf[3].location.x = best[0]
|
||||
pf[3].location.y = best[1]
|
||||
pf[3].location.z = pf[4]
|
||||
pf[3].rotation_euler.z = best[2]
|
||||
|
||||
pf[3].select = True
|
||||
|
||||
# print(mindist)
|
||||
mindist = mindist - 0.5 * (xmax - xmin)
|
||||
# print(mindist)
|
||||
# print(iter)
|
||||
|
||||
# reset polygon to best position here:
|
||||
ptrans = affinity.rotate(porig, best[2], rotcenter, use_radians=True)
|
||||
# ptrans=srotate(porig,best[2],0,0)
|
||||
ptrans = affinity.translate(ptrans, best[0], best[1])
|
||||
# ptrans = translate(ptrans,best[0],best[1])
|
||||
|
||||
# polygon_utils_cam.polyToMesh(p,0.1)#debug visualisation
|
||||
keep = []
|
||||
print(best[0], best[1])
|
||||
# print(len(ptrans.exterior))
|
||||
# npoly=allpoly.union(ptrans)
|
||||
'''
|
||||
for ci in range(0,len(allpoly)):
|
||||
cminx,cmaxx,cminy,cmaxy=allpoly.boundingBox(ci)
|
||||
if direction=='X' and cmaxx>mindist-.1:
|
||||
npoly.addContour(allpoly[ci])
|
||||
if direction=='Y' and cmaxy>mindist-.1:
|
||||
npoly.addContour(allpoly[ci])
|
||||
'''
|
||||
# allpoly=npoly
|
||||
placedpolys.append(ptrans)
|
||||
allpoly = prepared.prep(sgeometry.MultiPolygon(placedpolys))
|
||||
# *** temporary fix until prepared geometry code is setup properly
|
||||
# allpoly=sgeometry.MultiPolygon(placedpolys)
|
||||
|
||||
# polygon_utils_cam.polyToMesh(allpoly,0.1)#debug visualisation
|
||||
|
||||
# for c in p:
|
||||
# allpoly.addContour(c)
|
||||
# cleanup allpoly
|
||||
print(iter, hits, besthit)
|
||||
if not placed:
|
||||
if direction == 'Y':
|
||||
x += shift
|
||||
mindist = y
|
||||
if (xmax + shift > sheetsizex):
|
||||
x = x - xmin
|
||||
y += shift
|
||||
if direction == 'X':
|
||||
y += shift
|
||||
mindist = x
|
||||
if (ymax + shift > sheetsizey):
|
||||
y = y - ymin
|
||||
x += shift
|
||||
if rotate: rot += rotchange
|
||||
iter += 1
|
||||
i += 1
|
||||
t = time.time() - t
|
||||
|
||||
polygon_utils_cam.shapelyToCurve('test', sgeometry.MultiPolygon(placedpolys), 0)
|
||||
print(t)
|
||||
|
|
|
@ -29,144 +29,150 @@ import shapely
|
|||
from shapely.geometry import polygon as spolygon
|
||||
from shapely import ops
|
||||
from shapely import geometry as sgeometry
|
||||
SHAPELY=True
|
||||
#except:
|
||||
|
||||
SHAPELY = True
|
||||
|
||||
|
||||
# except:
|
||||
# SHAPELY=False
|
||||
|
||||
def Circle(r,np):
|
||||
c=[]
|
||||
pi=math.pi
|
||||
v=mathutils.Vector((r,0,0))
|
||||
e=mathutils.Euler((0,0,2.0*pi/np))
|
||||
for a in range(0,np):
|
||||
c.append((v.x,v.y))
|
||||
v.rotate(e)
|
||||
|
||||
p=spolygon.Polygon(c)
|
||||
return p
|
||||
def Circle(r, np):
|
||||
c = []
|
||||
pi = math.pi
|
||||
v = mathutils.Vector((r, 0, 0))
|
||||
e = mathutils.Euler((0, 0, 2.0 * pi / np))
|
||||
for a in range(0, np):
|
||||
c.append((v.x, v.y))
|
||||
v.rotate(e)
|
||||
|
||||
p = spolygon.Polygon(c)
|
||||
return p
|
||||
|
||||
|
||||
def shapelyRemoveDoubles(p, optimize_threshold):
|
||||
optimize_threshold *= 0.000001
|
||||
# vecs=[]
|
||||
|
||||
soptions = ['distance', 'distance', 0.0, 5, optimize_threshold, 5, optimize_threshold]
|
||||
for ci, c in enumerate(p.boundary): # in range(0,len(p)):
|
||||
|
||||
veclist = []
|
||||
for v in c:
|
||||
veclist.append(Vector((v[0], v[1])))
|
||||
# progress(len(veclist))
|
||||
s = curve_simplify.simplify_RDP(veclist, soptions)
|
||||
# progress(len(s))
|
||||
nc = []
|
||||
for i in range(0, len(s)):
|
||||
nc.append(c[s[i]])
|
||||
|
||||
if len(nc) > 2:
|
||||
pnew.addContour(nc, p.isHole(ci))
|
||||
else:
|
||||
pnew.addContour(p[ci], p.isHole(ci))
|
||||
# progress(time.time()-t)
|
||||
return pnew
|
||||
|
||||
def shapelyRemoveDoubles(p,optimize_threshold):
|
||||
optimize_threshold*=0.000001
|
||||
#vecs=[]
|
||||
|
||||
soptions=['distance','distance',0.0,5,optimize_threshold,5,optimize_threshold]
|
||||
for ci,c in enumerate(p.boundary):# in range(0,len(p)):
|
||||
|
||||
veclist=[]
|
||||
for v in c:
|
||||
veclist.append(Vector((v[0],v[1])))
|
||||
#progress(len(veclist))
|
||||
s=curve_simplify.simplify_RDP(veclist, soptions)
|
||||
#progress(len(s))
|
||||
nc=[]
|
||||
for i in range(0,len(s)):
|
||||
nc.append(c[s[i]])
|
||||
|
||||
if len(nc)>2:
|
||||
pnew.addContour(nc,p.isHole(ci))
|
||||
else:
|
||||
pnew.addContour(p[ci],p.isHole(ci))
|
||||
#progress(time.time()-t)
|
||||
return pnew
|
||||
|
||||
def shapelyToMultipolygon(anydata):
|
||||
if anydata.type == 'MultiPolygon':
|
||||
return anydata
|
||||
elif anydata.type == 'Polygon':
|
||||
if not anydata.is_empty:
|
||||
return shapely.geometry.MultiPolygon([anydata])
|
||||
else:
|
||||
return sgeometry.MultiPolygon()
|
||||
else:
|
||||
print(anydata.type, 'shapely conversion aborted')
|
||||
return sgeometry.MultiPolygon()
|
||||
|
||||
if anydata.type == 'MultiPolygon':
|
||||
return anydata
|
||||
elif anydata.type == 'Polygon':
|
||||
if not anydata.is_empty:
|
||||
return shapely.geometry.MultiPolygon([anydata])
|
||||
else:
|
||||
return sgeometry.MultiPolygon()
|
||||
else:
|
||||
print(anydata.type, 'shapely conversion aborted')
|
||||
return sgeometry.MultiPolygon()
|
||||
|
||||
|
||||
def shapelyToCoords(anydata):
|
||||
p=anydata
|
||||
seq=[]
|
||||
#print(p.type)
|
||||
#print(p.geom_type)
|
||||
if p.is_empty:
|
||||
return seq
|
||||
elif p.type=='Polygon':
|
||||
|
||||
#print('polygon')
|
||||
clen=len(p.exterior.coords)
|
||||
#seq = sgeometry.asMultiLineString(p)
|
||||
seq=[p.exterior.coords]
|
||||
#print(len(p.interiors))
|
||||
for interior in p.interiors:
|
||||
seq.append(interior.coords)
|
||||
elif p.type=='MultiPolygon':
|
||||
clen=0
|
||||
seq=[]
|
||||
for sp in p:
|
||||
clen+=len(sp.exterior.coords)
|
||||
seq.append(sp.exterior.coords)
|
||||
for interior in sp.interiors:
|
||||
seq.append(interior.coords)
|
||||
|
||||
elif p.type=='MultiLineString':
|
||||
seq=[]
|
||||
for linestring in p:
|
||||
seq.append(linestring.coords)
|
||||
elif p.type=='LineString':
|
||||
seq=[]
|
||||
seq.append(p.coords)
|
||||
|
||||
elif p.type=='MultiPoint':
|
||||
return;
|
||||
elif p.type=='GeometryCollection':
|
||||
#print(dir(p))
|
||||
#print(p.geometryType, p.geom_type)
|
||||
clen=0
|
||||
seq=[]
|
||||
#print(p.boundary.coordsd)
|
||||
for sp in p:#TODO
|
||||
#seq.append(shapelyToCoords(sp))
|
||||
clen+=len(sp.exterior.coords)
|
||||
seq.append(sp.exterior.coords)
|
||||
for interior in sp.interiors:
|
||||
seq.extend(interior.coords)
|
||||
p = anydata
|
||||
seq = []
|
||||
# print(p.type)
|
||||
# print(p.geom_type)
|
||||
if p.is_empty:
|
||||
return seq
|
||||
elif p.type == 'Polygon':
|
||||
|
||||
#for g in p.geom:
|
||||
# print(g.type)
|
||||
|
||||
return seq
|
||||
|
||||
def shapelyToCurve(name,p,z):
|
||||
import bpy,bmesh
|
||||
from bpy_extras import object_utils
|
||||
verts=[]
|
||||
edges=[]
|
||||
vi=0
|
||||
ci=0
|
||||
#for c in p.exterior.coords:
|
||||
|
||||
#print(p.type)
|
||||
seq = shapelyToCoords(p)
|
||||
w = 1 # weight
|
||||
# print('polygon')
|
||||
clen = len(p.exterior.coords)
|
||||
# seq = sgeometry.asMultiLineString(p)
|
||||
seq = [p.exterior.coords]
|
||||
# print(len(p.interiors))
|
||||
for interior in p.interiors:
|
||||
seq.append(interior.coords)
|
||||
elif p.type == 'MultiPolygon':
|
||||
clen = 0
|
||||
seq = []
|
||||
for sp in p:
|
||||
clen += len(sp.exterior.coords)
|
||||
seq.append(sp.exterior.coords)
|
||||
for interior in sp.interiors:
|
||||
seq.append(interior.coords)
|
||||
|
||||
|
||||
curvedata = bpy.data.curves.new(name=name, type='CURVE')
|
||||
curvedata.dimensions = '3D'
|
||||
|
||||
objectdata = bpy.data.objects.new(name, curvedata)
|
||||
objectdata.location = (0,0,0) #object origin
|
||||
bpy.context.scene.objects.link(objectdata)
|
||||
|
||||
for c in seq:
|
||||
polyline = curvedata.splines.new('POLY')
|
||||
polyline.points.add(len(c)-1)
|
||||
for num in range(len(c)):
|
||||
x, y = c[num][0],c[num][1]
|
||||
|
||||
polyline.points[num].co = (x, y, z, w)
|
||||
objectdata.select = True
|
||||
bpy.context.scene.objects.active = objectdata
|
||||
for c in objectdata.data.splines:
|
||||
c.use_cyclic_u=True
|
||||
objectdata.data.show_handles=False
|
||||
objectdata.data.show_normal_face=False
|
||||
|
||||
return objectdata#bpy.context.active_object
|
||||
elif p.type == 'MultiLineString':
|
||||
seq = []
|
||||
for linestring in p:
|
||||
seq.append(linestring.coords)
|
||||
elif p.type == 'LineString':
|
||||
seq = []
|
||||
seq.append(p.coords)
|
||||
|
||||
elif p.type == 'MultiPoint':
|
||||
return;
|
||||
elif p.type == 'GeometryCollection':
|
||||
# print(dir(p))
|
||||
# print(p.geometryType, p.geom_type)
|
||||
clen = 0
|
||||
seq = []
|
||||
# print(p.boundary.coordsd)
|
||||
for sp in p: # TODO
|
||||
# seq.append(shapelyToCoords(sp))
|
||||
clen += len(sp.exterior.coords)
|
||||
seq.append(sp.exterior.coords)
|
||||
for interior in sp.interiors:
|
||||
seq.extend(interior.coords)
|
||||
|
||||
# for g in p.geom:
|
||||
# print(g.type)
|
||||
|
||||
return seq
|
||||
|
||||
|
||||
def shapelyToCurve(name, p, z):
|
||||
import bpy, bmesh
|
||||
from bpy_extras import object_utils
|
||||
verts = []
|
||||
edges = []
|
||||
vi = 0
|
||||
ci = 0
|
||||
# for c in p.exterior.coords:
|
||||
|
||||
# print(p.type)
|
||||
seq = shapelyToCoords(p)
|
||||
w = 1 # weight
|
||||
|
||||
curvedata = bpy.data.curves.new(name=name, type='CURVE')
|
||||
curvedata.dimensions = '3D'
|
||||
|
||||
objectdata = bpy.data.objects.new(name, curvedata)
|
||||
objectdata.location = (0, 0, 0) # object origin
|
||||
bpy.context.scene.objects.link(objectdata)
|
||||
|
||||
for c in seq:
|
||||
polyline = curvedata.splines.new('POLY')
|
||||
polyline.points.add(len(c) - 1)
|
||||
for num in range(len(c)):
|
||||
x, y = c[num][0], c[num][1]
|
||||
|
||||
polyline.points[num].co = (x, y, z, w)
|
||||
objectdata.select = True
|
||||
bpy.context.scene.objects.active = objectdata
|
||||
for c in objectdata.data.splines:
|
||||
c.use_cyclic_u = True
|
||||
objectdata.data.show_handles = False
|
||||
objectdata.data.show_normal_face = False
|
||||
|
||||
return objectdata # bpy.context.active_object
|
||||
|
|
|
@ -19,152 +19,171 @@
|
|||
#
|
||||
# ***** END GPL LICENCE BLOCK *****
|
||||
|
||||
import math,sys,os,string
|
||||
import math, sys, os, string
|
||||
import time
|
||||
import bpy
|
||||
import mathutils
|
||||
import mathutils
|
||||
from mathutils import *
|
||||
from math import *
|
||||
|
||||
def tuple_add(t,t1):#add two tuples as Vectors
|
||||
return (t[0]+t1[0],t[1]+t1[1],t[2]+t1[2])
|
||||
|
||||
def tuple_sub(t,t1):#sub two tuples as Vectors
|
||||
return (t[0]-t1[0],t[1]-t1[1],t[2]-t1[2])
|
||||
def tuple_add(t, t1): # add two tuples as Vectors
|
||||
return (t[0] + t1[0], t[1] + t1[1], t[2] + t1[2])
|
||||
|
||||
def tuple_mul(t,c):#multiply two tuples with a number
|
||||
return (t[0]*c,t[1]*c,t[2]*c)
|
||||
|
||||
def tuple_length(t):#get length of vector, but passed in as tuple.
|
||||
return (Vector(t).length)
|
||||
#timing functions for optimisation purposes...
|
||||
|
||||
def tuple_sub(t, t1): # sub two tuples as Vectors
|
||||
return (t[0] - t1[0], t[1] - t1[1], t[2] - t1[2])
|
||||
|
||||
|
||||
def tuple_mul(t, c): # multiply two tuples with a number
|
||||
return (t[0] * c, t[1] * c, t[2] * c)
|
||||
|
||||
|
||||
def tuple_length(t): # get length of vector, but passed in as tuple.
|
||||
return (Vector(t).length)
|
||||
|
||||
|
||||
# timing functions for optimisation purposes...
|
||||
def timinginit():
|
||||
return[0,0]
|
||||
|
||||
return [0, 0]
|
||||
|
||||
|
||||
def timingstart(tinf):
|
||||
t=time.time()
|
||||
tinf[1]=t
|
||||
t = time.time()
|
||||
tinf[1] = t
|
||||
|
||||
|
||||
def timingadd(tinf):
|
||||
t=time.time()
|
||||
tinf[0]+=t-tinf[1]
|
||||
t = time.time()
|
||||
tinf[0] += t - tinf[1]
|
||||
|
||||
|
||||
def timingprint(tinf):
|
||||
print('time '+str(tinf[0])+'seconds')
|
||||
|
||||
def progress(text,n=None):
|
||||
'''function for reporting during the script, works for background operations in the header.'''
|
||||
#for i in range(n+1):
|
||||
#sys.stdout.flush()
|
||||
text=str(text)
|
||||
if n== None:
|
||||
n=''
|
||||
else:
|
||||
n=' ' + str(int(n*1000)/1000) + '%'
|
||||
#d=int(n/2)
|
||||
spaces=' '*(len(text)+55)
|
||||
sys.stdout.write('progress{%s%s}\n' % (text,n))
|
||||
sys.stdout.flush()
|
||||
#bpy.data.window_managers['WinMan'].progress_update(n)
|
||||
#if bpy.context.scene.o
|
||||
#bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||
#time.sleep(0.5)
|
||||
print('time ' + str(tinf[0]) + 'seconds')
|
||||
|
||||
|
||||
def progress(text, n=None):
|
||||
'''function for reporting during the script, works for background operations in the header.'''
|
||||
# for i in range(n+1):
|
||||
# sys.stdout.flush()
|
||||
text = str(text)
|
||||
if n == None:
|
||||
n = ''
|
||||
else:
|
||||
n = ' ' + str(int(n * 1000) / 1000) + '%'
|
||||
# d=int(n/2)
|
||||
spaces = ' ' * (len(text) + 55)
|
||||
sys.stdout.write('progress{%s%s}\n' % (text, n))
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
# bpy.data.window_managers['WinMan'].progress_update(n)
|
||||
# if bpy.context.scene.o
|
||||
# bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||
# time.sleep(0.5)
|
||||
|
||||
#
|
||||
def activate(o):
|
||||
'''makes an object active, used many times in blender'''
|
||||
s=bpy.context.scene
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
o.select=True
|
||||
s.objects.active=o
|
||||
|
||||
'''makes an object active, used many times in blender'''
|
||||
s = bpy.context.scene
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
o.select_set(state=True, view_layer=None)
|
||||
s.objects[o.name].select_set(state=True, view_layer=None)
|
||||
|
||||
|
||||
def dist2d(v1, v2):
|
||||
'''distance between two points in 2d'''
|
||||
return math.sqrt((v1[0] - v2[0]) * (v1[0] - v2[0]) + (v1[1] - v2[1]) * (v1[1] - v2[1]))
|
||||
|
||||
def dist2d(v1,v2):
|
||||
'''distance between two points in 2d'''
|
||||
return math.sqrt((v1[0]-v2[0])*(v1[0]-v2[0])+(v1[1]-v2[1])*(v1[1]-v2[1]))
|
||||
|
||||
def delob(ob):
|
||||
'''object deletion for multiple uses'''
|
||||
activate(ob)
|
||||
bpy.ops.object.delete(use_global=False)
|
||||
'''object deletion for multiple uses'''
|
||||
activate(ob)
|
||||
bpy.ops.object.delete(use_global=False)
|
||||
|
||||
def dupliob(o,pos):
|
||||
'''helper function for visualising cutter positions in bullet simulation'''
|
||||
activate(o)
|
||||
bpy.ops.object.duplicate()
|
||||
s=1.0/BULLET_SCALE
|
||||
bpy.ops.transform.resize(value=(s, s, s), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
|
||||
o=bpy.context.active_object
|
||||
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 dupliob(o, pos):
|
||||
'''helper function for visualising cutter positions in bullet simulation'''
|
||||
activate(o)
|
||||
bpy.ops.object.duplicate()
|
||||
s = 1.0 / BULLET_SCALE
|
||||
bpy.ops.transform.resize(value=(s, s, s), constraint_axis=(False, False, False), constraint_orientation='GLOBAL',
|
||||
mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH',
|
||||
proportional_size=1)
|
||||
o = bpy.context.active_object
|
||||
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
|
||||
v1 = Vector(v1)
|
||||
v2 = Vector(v2)
|
||||
vmiddle = Vector(vmiddle)
|
||||
vect1 = v2 - v1
|
||||
vect2 = vmiddle - v1
|
||||
vect1.normalize()
|
||||
vect1 *= vect2.length
|
||||
v = vect2 - vect1
|
||||
if v.length < e:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isVerticalLimit(v1, v2, limit):
|
||||
'''test path segment on verticality threshold, for protect_vertical option'''
|
||||
z = abs(v1[2] - v2[2])
|
||||
# verticality=0.05
|
||||
# this will be better.
|
||||
#
|
||||
# print(a)
|
||||
if z > 0:
|
||||
v2d = Vector((0, 0, -1))
|
||||
v3d = Vector((v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2]))
|
||||
a = v3d.angle(v2d)
|
||||
if a > pi / 2:
|
||||
a = abs(a - pi)
|
||||
# print(a)
|
||||
if a < limit:
|
||||
# print(abs(v1[0]-v2[0])/z)
|
||||
# print(abs(v1[1]-v2[1])/z)
|
||||
if v1[2] > v2[2]:
|
||||
v1 = (v2[0], v2[1], v1[2])
|
||||
return v1, v2
|
||||
else:
|
||||
v2 = (v1[0], v1[1], v2[2])
|
||||
return v1, v2
|
||||
return v1, v2
|
||||
|
||||
|
||||
def compare(v1,v2,vmiddle,e):
|
||||
'''comparison for optimisation of paths'''
|
||||
#e=0.0001
|
||||
v1=Vector(v1)
|
||||
v2=Vector(v2)
|
||||
vmiddle=Vector(vmiddle)
|
||||
vect1=v2-v1
|
||||
vect2=vmiddle-v1
|
||||
vect1.normalize()
|
||||
vect1*=vect2.length
|
||||
v=vect2-vect1
|
||||
if v.length<e:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isVerticalLimit(v1,v2,limit):
|
||||
'''test path segment on verticality threshold, for protect_vertical option'''
|
||||
z=abs(v1[2]-v2[2])
|
||||
#verticality=0.05
|
||||
#this will be better.
|
||||
#
|
||||
#print(a)
|
||||
if z>0:
|
||||
v2d=Vector((0,0,-1))
|
||||
v3d=Vector((v1[0]-v2[0],v1[1]-v2[1],v1[2]-v2[2]))
|
||||
a=v3d.angle(v2d)
|
||||
if a>pi/2:
|
||||
a=abs(a-pi)
|
||||
#print(a)
|
||||
if a<limit:
|
||||
#print(abs(v1[0]-v2[0])/z)
|
||||
#print(abs(v1[1]-v2[1])/z)
|
||||
if v1[2]>v2[2]:
|
||||
v1=(v2[0],v2[1],v1[2])
|
||||
return v1,v2
|
||||
else:
|
||||
v2=(v1[0],v1[1],v2[2])
|
||||
return v1,v2
|
||||
return v1,v2
|
||||
|
||||
def getCachePath(o):
|
||||
fn=bpy.data.filepath
|
||||
l=len(bpy.path.basename(fn))
|
||||
bn=bpy.path.basename(fn)[:-6]
|
||||
|
||||
|
||||
iname=fn[:-l]+'temp_cam'+os.sep+bn+'_'+o.name
|
||||
return iname
|
||||
fn = bpy.data.filepath
|
||||
l = len(bpy.path.basename(fn))
|
||||
bn = bpy.path.basename(fn)[:-6]
|
||||
|
||||
def safeFileName(name):#for export gcode
|
||||
valid_chars = "-_.()%s%s" % (string.ascii_letters, string.digits)
|
||||
filename=''.join(c for c in name if c in valid_chars)
|
||||
return filename
|
||||
iname = fn[:-l] + 'temp_cam' + os.sep + bn + '_' + o.name
|
||||
return iname
|
||||
|
||||
def strInUnits(x,precision=5):
|
||||
if bpy.context.scene.unit_settings.system == 'METRIC':
|
||||
return str( round(x * 1000,precision) )+' mm '
|
||||
elif bpy.context.scene.unit_settings.system == 'IMPERIAL':
|
||||
return str( round(x * 1000/25.4,precision) )+"'' "
|
||||
else:
|
||||
return str(x)
|
||||
|
||||
def safeFileName(name): # for export gcode
|
||||
valid_chars = "-_.()%s%s" % (string.ascii_letters, string.digits)
|
||||
filename = ''.join(c for c in name if c in valid_chars)
|
||||
return filename
|
||||
|
||||
|
||||
def strInUnits(x, precision=5):
|
||||
if bpy.context.scene.unit_settings.system == 'METRIC':
|
||||
return str(round(x * 1000, precision)) + ' mm '
|
||||
elif bpy.context.scene.unit_settings.system == 'IMPERIAL':
|
||||
return str(round(x * 1000 / 25.4, precision)) + "'' "
|
||||
else:
|
||||
return str(x)
|
||||
|
|
|
@ -19,257 +19,249 @@
|
|||
#
|
||||
# ***** END GPL LICENCE BLOCK *****
|
||||
|
||||
#very simple slicing for 3d meshes, usefull for plywood cutting.
|
||||
# very simple slicing for 3d meshes, usefull for plywood cutting.
|
||||
from cam import chunk, polygon_utils_cam
|
||||
import bpy
|
||||
|
||||
def getSlices(ob,slice_distance):
|
||||
'''function for slicing a mesh. It is now not used, but can be used for e.g. lasercutting from sheets a 3d model in the future.'''
|
||||
|
||||
layer_thickness=slice_distance
|
||||
edges=[]
|
||||
verts = []
|
||||
i=0
|
||||
slices=[]#slice format is [length, minx,miny, maxx, maxy,verts,z]
|
||||
firstslice=None
|
||||
lastslice=None
|
||||
maxzt = -100000000000000000000000000
|
||||
minzt = 1000000000000000000000000000
|
||||
#progress('slicing object')
|
||||
m=ob.to_mesh(scene=bpy.context.scene, apply_modifiers=True, settings='PREVIEW')
|
||||
#d={}#!
|
||||
for p in m.polygons:
|
||||
#a=i*50+12
|
||||
|
||||
v1=m.vertices[p.vertices[0]].co
|
||||
v2=m.vertices[p.vertices[1]].co
|
||||
v3=m.vertices[p.vertices[2]].co
|
||||
if len(p.vertices)==3:
|
||||
tris=[[v1,v2,v3]]
|
||||
else:
|
||||
v4=m.vertices[p.vertices[3]].co
|
||||
tris=[[v1,v2,v3],[v3,v4,v1]]
|
||||
|
||||
for v in tris:
|
||||
#print(v)
|
||||
minz=min(v[0].z,v[1].z,v[2].z)
|
||||
maxz=max(v[0].z,v[1].z,v[2].z)
|
||||
|
||||
|
||||
t=layer_thickness
|
||||
|
||||
start=int(minz // t)
|
||||
end=int(maxz // t +2)
|
||||
if firstslice==None:
|
||||
firstslice = start
|
||||
lastslice = end
|
||||
#print start, end
|
||||
for s in range(firstslice,lastslice):
|
||||
sz= s*t
|
||||
slices.append([0.0,100000000000.0,100000000000.0,-100000000000.0,-100000000000.0,[],sz])
|
||||
|
||||
if start<firstslice:
|
||||
ns=[]
|
||||
ind=0
|
||||
for s in range(start, firstslice):
|
||||
sz=s*t
|
||||
slices.insert(ind,[0.0,100000000000.0,100000000000.0,-100000000000.0,-100000000000.0,[],sz])
|
||||
ind+=1
|
||||
firstslice=start
|
||||
if end>lastslice:
|
||||
for s in range(lastslice,end):
|
||||
sz=s*t
|
||||
slices.append([0.0,100000000000.0,100000000000.0,-100000000000.0,-100000000000.0,[],sz])
|
||||
#i+=1
|
||||
lastslice=end
|
||||
|
||||
|
||||
for s in range(start,end):
|
||||
si=s-firstslice
|
||||
sc=slices[si]
|
||||
sz = sc[6]#s * t
|
||||
|
||||
over=[]
|
||||
under=[]
|
||||
onslice=[]
|
||||
iv=[]
|
||||
for vert in v:
|
||||
if vert[2]>sz:
|
||||
over.append(vert)
|
||||
elif vert[2]<sz:
|
||||
under.append(vert)
|
||||
elif vert[2]==sz:
|
||||
onslice.append(vert)
|
||||
if len(onslice)==1:
|
||||
#pass
|
||||
iv.append((onslice[0][0],onslice[0][1],sz))
|
||||
#iv[-1]=(int(1000000000*iv[-1][0])/1000000000,int(1000000000*iv[-1][1])/1000000000,int(1000000000*iv[-1][2])/1000000000)
|
||||
elif len(onslice)==2:
|
||||
#if p.normal.z<1.0:
|
||||
#iv.extend([onslice[0],onslice[1]])
|
||||
iv.append((onslice[0][0],onslice[0][1],sz))
|
||||
iv.append((onslice[1][0],onslice[1][1],sz))
|
||||
#iv[-2]=(int(1000000000*iv[-2][0])/1000000000,int(1000000000*iv[-2][1])/1000000000,int(1000000000*iv[-2][2])/1000000000)
|
||||
#iv[-1]=(int(1000000000*iv[-1][0])/1000000000,int(1000000000*iv[-1][1])/1000000000,int(1000000000*iv[-1][2])/1000000000)
|
||||
elif len(onslice)==3:
|
||||
print('flat face')#,v)
|
||||
for v1 in under:
|
||||
for v2 in over:
|
||||
coef=(sz-v1[2])/(v2[2]-v1[2])
|
||||
x=v1[0]+(v2[0]-v1[0])*coef
|
||||
y=v1[1]+(v2[1]-v1[1])*coef
|
||||
z=sz#!
|
||||
#iv.append((int(100000000*x)/100000000,int(100000000*y)/100000000,int(100000000*z)/100000000))#! z not needed!
|
||||
iv.append((x,y,sz))
|
||||
if len(iv)==2:
|
||||
#d{iv[0]}
|
||||
#sc=slices[si]
|
||||
#print(iv)
|
||||
sc[5].append(iv[0])
|
||||
sc[5].append(iv[1])
|
||||
|
||||
else:
|
||||
pass
|
||||
# print('strange count of layer faces',iv)
|
||||
|
||||
|
||||
|
||||
|
||||
if i % 10000 == 0:
|
||||
print ('parsed faces', i, firstslice, lastslice, len(slices))
|
||||
i+=1
|
||||
#sliceobs=[]
|
||||
print('sorting slices')
|
||||
slicechunks=[]
|
||||
obs=[]
|
||||
for sc in slices:
|
||||
if len(sc[5])>0:
|
||||
i=0
|
||||
chi=0
|
||||
|
||||
edges=[]
|
||||
z=sc[5][0][2]
|
||||
|
||||
slicechunks.append([])
|
||||
|
||||
|
||||
d={}
|
||||
for i in range(0,len(sc[5])):
|
||||
d[sc[5][i]]=[]
|
||||
for i in range(0,int(len(sc[5])/2)):
|
||||
verts1=d[sc[5][i*2]]
|
||||
verts2=d[sc[5][i*2+1]]
|
||||
|
||||
if len(verts1)==2:
|
||||
if verts1[0]==verts1[1]:
|
||||
verts1.pop()
|
||||
if len(verts1)<2:
|
||||
verts1.append(sc[5][i*2+1])
|
||||
|
||||
if len(verts2)==2:
|
||||
if verts2[0]==verts2[1]:
|
||||
verts2.pop()
|
||||
|
||||
if len(verts2)<2:
|
||||
verts2.append(sc[5][i*2])
|
||||
|
||||
|
||||
|
||||
ch=[sc[5][0],sc[5][1]]#first and his reference
|
||||
|
||||
d.pop(ch[0])
|
||||
|
||||
i=0
|
||||
verts=[123]
|
||||
|
||||
while len(d)>0 and i<200000:# and verts!=[]:
|
||||
verts=d.get(ch[-1],[])
|
||||
if len(verts)<=1:
|
||||
if len(ch)>2:
|
||||
slicechunks[-1].append(ch)
|
||||
v1=d.popitem()
|
||||
ch=[v1[0],v1[1][0]]
|
||||
|
||||
elif len(verts)>2:
|
||||
pass;
|
||||
i+=1
|
||||
|
||||
|
||||
else:
|
||||
done=False
|
||||
for v in verts:
|
||||
|
||||
if not done:
|
||||
if v[0]==ch[-2][0] and v[1]==ch[-2][1]:# and v[2]==ch[-2][2]:
|
||||
pass
|
||||
else:
|
||||
#print(v,ch[-2])
|
||||
ch.append(v)
|
||||
|
||||
d.pop(ch[-2])
|
||||
done=True
|
||||
if v[0]==ch[0][0] and v[1]==ch[0][1]:# and v[2]==ch[0][2]:
|
||||
slicechunks[-1].append(ch)
|
||||
print('closed')
|
||||
#d.pop(ch[-1])
|
||||
if len(d)>0:
|
||||
v1=d.popitem()
|
||||
ch=[v1[0],v1[1][0]]
|
||||
i+=1
|
||||
|
||||
slicechunks[-1].append(ch)################3this might be a bug!!!!
|
||||
#print(len(slicechunks))
|
||||
return slicechunks
|
||||
|
||||
def getSlices(ob, slice_distance):
|
||||
'''function for slicing a mesh. It is now not used, but can be used for e.g. lasercutting from sheets a 3d model in the future.'''
|
||||
|
||||
layer_thickness = slice_distance
|
||||
edges = []
|
||||
verts = []
|
||||
i = 0
|
||||
slices = [] # slice format is [length, minx,miny, maxx, maxy,verts,z]
|
||||
firstslice = None
|
||||
lastslice = None
|
||||
maxzt = -100000000000000000000000000
|
||||
minzt = 1000000000000000000000000000
|
||||
# progress('slicing object')
|
||||
m = ob.to_mesh(bpy.context.depsgraph, True, calc_undeformed=False)
|
||||
# d={}#!
|
||||
for p in m.polygons:
|
||||
# a=i*50+12
|
||||
|
||||
v1 = m.vertices[p.vertices[0]].co
|
||||
v2 = m.vertices[p.vertices[1]].co
|
||||
v3 = m.vertices[p.vertices[2]].co
|
||||
if len(p.vertices) == 3:
|
||||
tris = [[v1, v2, v3]]
|
||||
else:
|
||||
v4 = m.vertices[p.vertices[3]].co
|
||||
tris = [[v1, v2, v3], [v3, v4, v1]]
|
||||
|
||||
for v in tris:
|
||||
# print(v)
|
||||
minz = min(v[0].z, v[1].z, v[2].z)
|
||||
maxz = max(v[0].z, v[1].z, v[2].z)
|
||||
|
||||
t = layer_thickness
|
||||
|
||||
start = int(minz // t)
|
||||
end = int(maxz // t + 2)
|
||||
if firstslice == None:
|
||||
firstslice = start
|
||||
lastslice = end
|
||||
# print start, end
|
||||
for s in range(firstslice, lastslice):
|
||||
sz = s * t
|
||||
slices.append([0.0, 100000000000.0, 100000000000.0, -100000000000.0, -100000000000.0, [], sz])
|
||||
|
||||
if start < firstslice:
|
||||
ns = []
|
||||
ind = 0
|
||||
for s in range(start, firstslice):
|
||||
sz = s * t
|
||||
slices.insert(ind, [0.0, 100000000000.0, 100000000000.0, -100000000000.0, -100000000000.0, [], sz])
|
||||
ind += 1
|
||||
firstslice = start
|
||||
if end > lastslice:
|
||||
for s in range(lastslice, end):
|
||||
sz = s * t
|
||||
slices.append([0.0, 100000000000.0, 100000000000.0, -100000000000.0, -100000000000.0, [], sz])
|
||||
# i+=1
|
||||
lastslice = end
|
||||
|
||||
for s in range(start, end):
|
||||
si = s - firstslice
|
||||
sc = slices[si]
|
||||
sz = sc[6] # s * t
|
||||
|
||||
over = []
|
||||
under = []
|
||||
onslice = []
|
||||
iv = []
|
||||
for vert in v:
|
||||
if vert[2] > sz:
|
||||
over.append(vert)
|
||||
elif vert[2] < sz:
|
||||
under.append(vert)
|
||||
elif vert[2] == sz:
|
||||
onslice.append(vert)
|
||||
if len(onslice) == 1:
|
||||
# pass
|
||||
iv.append((onslice[0][0], onslice[0][1], sz))
|
||||
# iv[-1]=(int(1000000000*iv[-1][0])/1000000000,int(1000000000*iv[-1][1])/1000000000,int(1000000000*iv[-1][2])/1000000000)
|
||||
elif len(onslice) == 2:
|
||||
# if p.normal.z<1.0:
|
||||
# iv.extend([onslice[0],onslice[1]])
|
||||
iv.append((onslice[0][0], onslice[0][1], sz))
|
||||
iv.append((onslice[1][0], onslice[1][1], sz))
|
||||
# iv[-2]=(int(1000000000*iv[-2][0])/1000000000,int(1000000000*iv[-2][1])/1000000000,int(1000000000*iv[-2][2])/1000000000)
|
||||
# iv[-1]=(int(1000000000*iv[-1][0])/1000000000,int(1000000000*iv[-1][1])/1000000000,int(1000000000*iv[-1][2])/1000000000)
|
||||
elif len(onslice) == 3:
|
||||
print('flat face') # ,v)
|
||||
for v1 in under:
|
||||
for v2 in over:
|
||||
coef = (sz - v1[2]) / (v2[2] - v1[2])
|
||||
x = v1[0] + (v2[0] - v1[0]) * coef
|
||||
y = v1[1] + (v2[1] - v1[1]) * coef
|
||||
z = sz # !
|
||||
# iv.append((int(100000000*x)/100000000,int(100000000*y)/100000000,int(100000000*z)/100000000))#! z not needed!
|
||||
iv.append((x, y, sz))
|
||||
if len(iv) == 2:
|
||||
# d{iv[0]}
|
||||
# sc=slices[si]
|
||||
# print(iv)
|
||||
sc[5].append(iv[0])
|
||||
sc[5].append(iv[1])
|
||||
|
||||
else:
|
||||
pass
|
||||
# print('strange count of layer faces',iv)
|
||||
|
||||
if i % 10000 == 0:
|
||||
print('parsed faces', i, firstslice, lastslice, len(slices))
|
||||
i += 1
|
||||
# sliceobs=[]
|
||||
print('sorting slices')
|
||||
slicechunks = []
|
||||
obs = []
|
||||
for sc in slices:
|
||||
if len(sc[5]) > 0:
|
||||
i = 0
|
||||
chi = 0
|
||||
|
||||
edges = []
|
||||
z = sc[5][0][2]
|
||||
|
||||
slicechunks.append([])
|
||||
|
||||
d = {}
|
||||
for i in range(0, len(sc[5])):
|
||||
d[sc[5][i]] = []
|
||||
for i in range(0, int(len(sc[5]) / 2)):
|
||||
verts1 = d[sc[5][i * 2]]
|
||||
verts2 = d[sc[5][i * 2 + 1]]
|
||||
|
||||
if len(verts1) == 2:
|
||||
if verts1[0] == verts1[1]:
|
||||
verts1.pop()
|
||||
if len(verts1) < 2:
|
||||
verts1.append(sc[5][i * 2 + 1])
|
||||
|
||||
if len(verts2) == 2:
|
||||
if verts2[0] == verts2[1]:
|
||||
verts2.pop()
|
||||
|
||||
if len(verts2) < 2:
|
||||
verts2.append(sc[5][i * 2])
|
||||
|
||||
ch = [sc[5][0], sc[5][1]] # first and his reference
|
||||
|
||||
d.pop(ch[0])
|
||||
|
||||
i = 0
|
||||
verts = [123]
|
||||
|
||||
while len(d) > 0 and i < 200000: # and verts!=[]:
|
||||
verts = d.get(ch[-1], [])
|
||||
if len(verts) <= 1:
|
||||
if len(ch) > 2:
|
||||
slicechunks[-1].append(ch)
|
||||
v1 = d.popitem()
|
||||
ch = [v1[0], v1[1][0]]
|
||||
|
||||
elif len(verts) > 2:
|
||||
pass;
|
||||
i += 1
|
||||
|
||||
|
||||
else:
|
||||
done = False
|
||||
for v in verts:
|
||||
|
||||
if not done:
|
||||
if v[0] == ch[-2][0] and v[1] == ch[-2][1]: # and v[2]==ch[-2][2]:
|
||||
pass
|
||||
else:
|
||||
# print(v,ch[-2])
|
||||
ch.append(v)
|
||||
|
||||
d.pop(ch[-2])
|
||||
done = True
|
||||
if v[0] == ch[0][0] and v[1] == ch[0][1]: # and v[2]==ch[0][2]:
|
||||
slicechunks[-1].append(ch)
|
||||
print('closed')
|
||||
# d.pop(ch[-1])
|
||||
if len(d) > 0:
|
||||
v1 = d.popitem()
|
||||
ch = [v1[0], v1[1][0]]
|
||||
i += 1
|
||||
|
||||
slicechunks[-1].append(ch) ################3this might be a bug!!!!
|
||||
# print(len(slicechunks))
|
||||
return slicechunks
|
||||
|
||||
|
||||
|
||||
def sliceObject(ob):
|
||||
settings=bpy.context.scene.cam_slice
|
||||
|
||||
layers = getSlices(ob, settings.slice_distance)
|
||||
#print(layers)
|
||||
sliceobjects=[]
|
||||
i=1
|
||||
for layer in layers:
|
||||
pi=1
|
||||
layerpolys=[]
|
||||
for slicechunk in layer:
|
||||
#these functions here are totally useless conversions, could generate slices more directly, just lazy to write new functions
|
||||
#print (slicechunk)
|
||||
nchp=[]
|
||||
for p in slicechunk:
|
||||
nchp.append((p[0],p[1]))
|
||||
#print(slicechunk)
|
||||
ch = chunk.camPathChunk(nchp)
|
||||
settings = bpy.context.scene.cam_slice
|
||||
|
||||
#print(ch)
|
||||
pslices=chunk.chunksToShapely([ch])
|
||||
#print(pslices)
|
||||
for pslice in pslices:
|
||||
p = pslice#-p1
|
||||
#print(p)
|
||||
text = '%i - %i' % (i,pi)
|
||||
bpy.ops.object.text_add()
|
||||
textob = bpy.context.active_object
|
||||
textob.data.size = 0.0035
|
||||
textob.data.body = text
|
||||
textob.data.align = 'CENTER'
|
||||
|
||||
#print(len(ch.points))
|
||||
sliceobject = polygon_utils_cam.shapelyToCurve('slice',p,slicechunk[0][2])
|
||||
textob.location=(0,0,0)
|
||||
|
||||
textob.parent=sliceobject
|
||||
|
||||
sliceobject.data.extrude = settings.slice_distance/2
|
||||
sliceobject.data.dimensions = '2D'
|
||||
sliceobjects.append(sliceobject)
|
||||
pi+=1
|
||||
#FIXME: the polys on same layer which are hollow are not joined by now, this prevents doing hollow surfaces :(
|
||||
#for p in layerpolys:
|
||||
#for p1 in layerpolys:
|
||||
|
||||
i+=1
|
||||
for o in sliceobjects:
|
||||
o.select=True
|
||||
bpy.ops.group.create(name='slices')
|
||||
layers = getSlices(ob, settings.slice_distance)
|
||||
# print(layers)
|
||||
sliceobjects = []
|
||||
i = 1
|
||||
for layer in layers:
|
||||
pi = 1
|
||||
layerpolys = []
|
||||
for slicechunk in layer:
|
||||
# these functions here are totally useless conversions, could generate slices more directly, just lazy to write new functions
|
||||
# print (slicechunk)
|
||||
nchp = []
|
||||
for p in slicechunk:
|
||||
nchp.append((p[0], p[1]))
|
||||
# print(slicechunk)
|
||||
ch = chunk.camPathChunk(nchp)
|
||||
|
||||
# print(ch)
|
||||
pslices = chunk.chunksToShapely([ch])
|
||||
# print(pslices)
|
||||
for pslice in pslices:
|
||||
p = pslice # -p1
|
||||
# print(p)
|
||||
text = '%i - %i' % (i, pi)
|
||||
bpy.ops.object.text_add()
|
||||
textob = bpy.context.active_object
|
||||
textob.data.size = 0.0035
|
||||
textob.data.body = text
|
||||
textob.data.align = 'CENTER'
|
||||
|
||||
# print(len(ch.points))
|
||||
sliceobject = polygon_utils_cam.shapelyToCurve('slice', p, slicechunk[0][2])
|
||||
textob.location = (0, 0, 0)
|
||||
|
||||
textob.parent = sliceobject
|
||||
|
||||
sliceobject.data.extrude = settings.slice_distance / 2
|
||||
sliceobject.data.dimensions = '2D'
|
||||
sliceobjects.append(sliceobject)
|
||||
pi += 1
|
||||
# FIXME: the polys on same layer which are hollow are not joined by now, this prevents doing hollow surfaces :(
|
||||
# for p in layerpolys:
|
||||
# for p1 in layerpolys:
|
||||
|
||||
i += 1
|
||||
for o in sliceobjects:
|
||||
o.select = True
|
||||
bpy.ops.group.create(name='slices')
|
||||
|
|
|
@ -1,180 +1,197 @@
|
|||
# blender CAM testing.py (c) 2012 Vilem Novak
|
||||
#
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ***** END GPL LICENCE BLOCK *****
|
||||
|
||||
import bpy
|
||||
from cam import simple, utils
|
||||
from cam.simple import *
|
||||
|
||||
def addTestCurve(loc):
|
||||
bpy.ops.curve.primitive_bezier_circle_add(radius=.05, view_align=False, 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), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
|
||||
bpy.ops.curve.duplicate()
|
||||
bpy.ops.transform.resize(value=(0.5, 0.5, 0.5), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
def addTestMesh(loc):
|
||||
bpy.ops.mesh.primitive_monkey_add(radius=.01, view_align=False, enter_editmode=False, location=loc)
|
||||
bpy.ops.transform.rotate(value=-1.5708, axis=(1, 0, 0), constraint_axis=(True, False, False), constraint_orientation='GLOBAL')
|
||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
bpy.ops.object.editmode_toggle()
|
||||
bpy.ops.mesh.primitive_plane_add(radius=1, view_align=False, enter_editmode=False, location=loc)
|
||||
bpy.ops.transform.resize(value=(0.01, 0.01, 0.01), constraint_axis=(False, False, False), constraint_orientation='GLOBAL')
|
||||
bpy.ops.transform.translate(value=(-0.01, 0, 0), constraint_axis=(True, False, False), constraint_orientation='GLOBAL')
|
||||
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
def deleteFirstVert(ob):
|
||||
activate(ob)
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
|
||||
|
||||
bpy.ops.object.editmode_toggle()
|
||||
for i,v in enumerate(ob.data.vertices):
|
||||
v.select=False
|
||||
if i==0:
|
||||
v.select=True
|
||||
ob.data.update()
|
||||
|
||||
bpy.ops.object.editmode_toggle()
|
||||
bpy.ops.mesh.delete(type='VERT')
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
def testCalc(o):
|
||||
bpy.ops.object.calculate_cam_path()
|
||||
deleteFirstVert(bpy.data.objects[o.path_object_name])
|
||||
|
||||
def testCutout(pos):
|
||||
addTestCurve((pos[0],pos[1],-.05))
|
||||
bpy.ops.scene.cam_operation_add()
|
||||
o=bpy.context.scene.cam_operations[-1]
|
||||
o.strategy = 'CUTOUT'
|
||||
testCalc(o)
|
||||
|
||||
def testPocket(pos):
|
||||
addTestCurve((pos[0],pos[1],-.01))
|
||||
bpy.ops.scene.cam_operation_add()
|
||||
o=bpy.context.scene.cam_operations[-1]
|
||||
o.strategy = 'POCKET'
|
||||
o.helix_enter = True
|
||||
o.retract_tangential=True
|
||||
testCalc(o)
|
||||
|
||||
def testParallel(pos):
|
||||
addTestMesh((pos[0],pos[1],-.02))
|
||||
bpy.ops.scene.cam_operation_add()
|
||||
o=bpy.context.scene.cam_operations[-1]
|
||||
o.ambient_behaviour='AROUND'
|
||||
o.material_radius_around_model=0.01
|
||||
bpy.ops.object.calculate_cam_path()
|
||||
|
||||
def testWaterline(pos):
|
||||
addTestMesh((pos[0],pos[1],-.02))
|
||||
bpy.ops.scene.cam_operation_add()
|
||||
o=bpy.context.scene.cam_operations[-1]
|
||||
o.strategy='WATERLINE'
|
||||
o.pixsize=.0002
|
||||
#o.ambient_behaviour='AROUND'
|
||||
#o.material_radius_around_model=0.01
|
||||
|
||||
testCalc(o)
|
||||
#bpy.ops.object.cam_simulate()
|
||||
|
||||
|
||||
def testSimulation():
|
||||
pass;
|
||||
|
||||
def cleanUp():
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
bpy.ops.object.delete(use_global=False)
|
||||
while len(bpy.context.scene.cam_operations)>0:
|
||||
bpy.ops.scene.cam_operation_remove()
|
||||
|
||||
|
||||
def testOperation(i):
|
||||
|
||||
s=bpy.context.scene
|
||||
o = s.cam_operations[i]
|
||||
report=''
|
||||
report+='testing operation '+ o.name+ '\n'
|
||||
|
||||
utils.getPath(bpy.context,o)
|
||||
|
||||
newresult=bpy.data.objects[o.path_object_name]
|
||||
origname="test_cam_path_"+o.name
|
||||
if origname not in s.objects:
|
||||
report+='operation test has nothing to compare with, making the new result as comparable result.\n\n'
|
||||
newresult.name = origname
|
||||
else:
|
||||
testresult = bpy.data.objects[origname]
|
||||
m1 = testresult.data
|
||||
m2 = newresult.data
|
||||
test_ok=True
|
||||
if len(m1.vertices) != len(m2.vertices):
|
||||
report += "vertex counts don't match\n\n"
|
||||
test_ok = False
|
||||
else:
|
||||
different_co_count=0
|
||||
for i in range(0,len(m1.vertices)):
|
||||
v1=m1.vertices[i]
|
||||
v2=m2.vertices[i]
|
||||
if v1.co != v2.co:
|
||||
different_co_count+=1
|
||||
if different_co_count>0:
|
||||
report += 'vertex position is different on %i vertices \n\n' % (different_co_count)
|
||||
test_ok = False
|
||||
if test_ok:
|
||||
report += 'test ok\n\n'
|
||||
else:
|
||||
report += 'test result is different\n \n '
|
||||
print(report)
|
||||
return report
|
||||
|
||||
def testAll():
|
||||
s=bpy.context.scene
|
||||
report=''
|
||||
for i in range(0, len(s.cam_operations)):
|
||||
report+=testOperation(i)
|
||||
print(report)
|
||||
|
||||
''''
|
||||
tests=[
|
||||
testCutout,
|
||||
testParallel,
|
||||
testWaterline,
|
||||
testPocket,
|
||||
|
||||
]
|
||||
|
||||
cleanUp()
|
||||
|
||||
#deleteFirstVert(bpy.context.active_object)
|
||||
for i,t in enumerate(tests):
|
||||
p=i*.2
|
||||
t((p,0,0))
|
||||
# cleanUp()
|
||||
|
||||
|
||||
#cleanUp()
|
||||
'''
|
||||
# blender CAM testing.py (c) 2012 Vilem Novak
|
||||
#
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ***** END GPL LICENCE BLOCK *****
|
||||
|
||||
import sys
|
||||
import bpy
|
||||
from cam import simple, utils
|
||||
from cam.simple import *
|
||||
|
||||
|
||||
def addTestCurve(loc):
|
||||
bpy.ops.curve.primitive_bezier_circle_add(radius=.05, view_align=False, 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),
|
||||
constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
|
||||
proportional_edit_falloff='SMOOTH', proportional_size=1)
|
||||
bpy.ops.curve.duplicate()
|
||||
bpy.ops.transform.resize(value=(0.5, 0.5, 0.5), constraint_axis=(False, False, False),
|
||||
constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
|
||||
proportional_edit_falloff='SMOOTH', proportional_size=1)
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
|
||||
def addTestMesh(loc):
|
||||
bpy.ops.mesh.primitive_monkey_add(radius=.01, view_align=False, enter_editmode=False, location=loc)
|
||||
bpy.ops.transform.rotate(value=-1.5708, axis=(1, 0, 0), constraint_axis=(True, False, False),
|
||||
constraint_orientation='GLOBAL')
|
||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
bpy.ops.object.editmode_toggle()
|
||||
bpy.ops.mesh.primitive_plane_add(radius=1, view_align=False, enter_editmode=False, location=loc)
|
||||
bpy.ops.transform.resize(value=(0.01, 0.01, 0.01), constraint_axis=(False, False, False),
|
||||
constraint_orientation='GLOBAL')
|
||||
bpy.ops.transform.translate(value=(-0.01, 0, 0), constraint_axis=(True, False, False),
|
||||
constraint_orientation='GLOBAL')
|
||||
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
|
||||
def deleteFirstVert(ob):
|
||||
activate(ob)
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
|
||||
bpy.ops.object.editmode_toggle()
|
||||
for i, v in enumerate(ob.data.vertices):
|
||||
v.select = False
|
||||
if i == 0:
|
||||
v.select = True
|
||||
ob.data.update()
|
||||
|
||||
bpy.ops.object.editmode_toggle()
|
||||
bpy.ops.mesh.delete(type='VERT')
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
|
||||
def testCalc(o):
|
||||
bpy.ops.object.calculate_cam_path()
|
||||
deleteFirstVert(bpy.data.objects[o.name])
|
||||
|
||||
|
||||
def testCutout(pos):
|
||||
addTestCurve((pos[0], pos[1], -.05))
|
||||
bpy.ops.scene.cam_operation_add()
|
||||
o = bpy.context.scene.cam_operations[-1]
|
||||
o.strategy = 'CUTOUT'
|
||||
testCalc(o)
|
||||
|
||||
|
||||
def testPocket(pos):
|
||||
addTestCurve((pos[0], pos[1], -.01))
|
||||
bpy.ops.scene.cam_operation_add()
|
||||
o = bpy.context.scene.cam_operations[-1]
|
||||
o.strategy = 'POCKET'
|
||||
o.helix_enter = True
|
||||
o.retract_tangential = True
|
||||
testCalc(o)
|
||||
|
||||
|
||||
def testParallel(pos):
|
||||
addTestMesh((pos[0], pos[1], -.02))
|
||||
bpy.ops.scene.cam_operation_add()
|
||||
o = bpy.context.scene.cam_operations[-1]
|
||||
o.ambient_behaviour = 'AROUND'
|
||||
o.material_radius_around_model = 0.01
|
||||
bpy.ops.object.calculate_cam_path()
|
||||
|
||||
|
||||
def testWaterline(pos):
|
||||
addTestMesh((pos[0], pos[1], -.02))
|
||||
bpy.ops.scene.cam_operation_add()
|
||||
o = bpy.context.scene.cam_operations[-1]
|
||||
o.strategy = 'WATERLINE'
|
||||
o.pixsize = .0002
|
||||
# o.ambient_behaviour='AROUND'
|
||||
# o.material_radius_around_model=0.01
|
||||
|
||||
testCalc(o)
|
||||
|
||||
|
||||
# bpy.ops.object.cam_simulate()
|
||||
|
||||
|
||||
def testSimulation():
|
||||
pass;
|
||||
|
||||
|
||||
def cleanUp():
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
bpy.ops.object.delete(use_global=False)
|
||||
while len(bpy.context.scene.cam_operations):
|
||||
bpy.ops.scene.cam_operation_remove()
|
||||
|
||||
|
||||
def testOperation(i):
|
||||
s = bpy.context.scene
|
||||
o = s.cam_operations[i]
|
||||
report = ''
|
||||
report += 'testing operation ' + o.name + '\n'
|
||||
|
||||
utils.getPath(bpy.context, o)
|
||||
|
||||
newresult = bpy.data.objects[o.path_object_name]
|
||||
origname = "test_cam_path_" + o.name
|
||||
if origname not in s.objects:
|
||||
report += 'operation test has nothing to compare with, making the new result as comparable result.\n\n'
|
||||
newresult.name = origname
|
||||
else:
|
||||
testresult = bpy.data.objects[origname]
|
||||
m1 = testresult.data
|
||||
m2 = newresult.data
|
||||
test_ok = True
|
||||
if len(m1.vertices) != len(m2.vertices):
|
||||
report += "vertex counts don't match\n\n"
|
||||
test_ok = False
|
||||
else:
|
||||
different_co_count = 0
|
||||
for i in range(0, len(m1.vertices)):
|
||||
v1 = m1.vertices[i]
|
||||
v2 = m2.vertices[i]
|
||||
if v1.co != v2.co:
|
||||
different_co_count += 1
|
||||
if different_co_count > 0:
|
||||
report += 'vertex position is different on %i vertices \n\n' % (different_co_count)
|
||||
test_ok = False
|
||||
if test_ok:
|
||||
report += 'test ok\n\n'
|
||||
else:
|
||||
report += 'test result is different\n \n '
|
||||
print(report)
|
||||
return report
|
||||
|
||||
|
||||
def testAll():
|
||||
s = bpy.context.scene
|
||||
report = ''
|
||||
for i in range(0, len(s.cam_operations)):
|
||||
report += testOperation(i)
|
||||
print(report)
|
||||
|
||||
|
||||
tests = [
|
||||
testCutout,
|
||||
testParallel,
|
||||
testWaterline,
|
||||
testPocket,
|
||||
|
||||
]
|
||||
|
||||
cleanUp()
|
||||
|
||||
# deleteFirstVert(bpy.context.active_object)
|
||||
for i, t in enumerate(tests):
|
||||
p = i * .2
|
||||
t((p, 0, 0))
|
||||
# cleanUp()
|
||||
|
||||
|
||||
# cleanUp()
|
||||
|
|
Plik diff jest za duży
Load Diff
Plik diff jest za duży
Load Diff
Plik diff jest za duży
Load Diff
Ładowanie…
Reference in New Issue