Merge pull request #136 from joemarshall/numpy_chunk

Make chunk use numpy (and optimise with numba)
pull/258/head
Alain Pelletier 2024-01-29 10:50:54 -04:00 zatwierdzone przez GitHub
commit b3c1eeda06
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
42 zmienionych plików z 145432 dodań i 2561 usunięć

Wyświetl plik

@ -25,7 +25,7 @@ jobs:
- name: Cache blender
id: cache-blender
if: runner.os != 'macOS'
uses: actions/cache/restore@v3
uses: actions/cache/restore@v4
with:
path: blender
key: ${{ matrix.os }}-${{ matrix.blender_version}}-blender
@ -57,7 +57,7 @@ jobs:
shutil.unpack_archive("${{ steps.download.outputs.BLENDER_ARCHIVE }}","blender")
shell: python
- name: Save blender
uses: actions/cache/save@v3
uses: actions/cache/save@v4
if: steps.cache-blender.outputs.cache-hit != 'true' && runner.os != 'macOS'
with:
path: blender
@ -79,21 +79,10 @@ jobs:
export ADDON_PATH=${PWD}/scripts/addons/blendercam.zip
cd scripts/addons/cam/tests
python install_addon.py ${ADDON_PATH}
python test_suite.py
python test_suite.py -vvv
- uses: actions/upload-artifact@v4
if: always()
with:
name: blendercam-${{matrix.os}}-${{matrix.blender_version}}
path: ./scripts/addons/cam
rerun-failed-jobs:
runs-on: ubuntu-latest
needs: [ build_and_test ]
if: failure()
steps:
- name: Checkout repository
uses: actions/checkout@v4.1.1
- name: Rerun failed jobs in the current workflow (because mac blender is unstable)
env:
GH_TOKEN: ${{ github.token }}
run: gh run rerun ${{ github.run_id }} --failed

2
.gitignore vendored
Wyświetl plik

@ -23,3 +23,5 @@ forceSyncWithUpstream
.project
.pydevproject
__pycache__/
temp_cam/

Wyświetl plik

@ -17,9 +17,7 @@
# 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 bgl
# ***** END GPL LICENCE BLOCK ****
import bl_operators
import blf
import bpy
@ -43,6 +41,8 @@ except ImportError:
subprocess.check_call([sys.executable, "-m", "ensurepip"])
subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", " pip"])
subprocess.check_call([sys.executable, "-m", "pip", "install", "shapely","Equation","opencamlib"])
# install numba if available for this platform, ignore failure
subprocess.run([sys.executable, "-m", "pip", "install", "numba"])
from bpy.app.handlers import persistent

Wyświetl plik

@ -21,6 +21,9 @@ class AsyncOperatorMixin:
self._is_cancelled=False
def modal(self,context,event):
if bpy.app.background:
return {'PASS_THROUGH'}
if event.type == 'TIMER':
try:
if self.tick(context):
@ -70,6 +73,8 @@ class AsyncOperatorMixin:
return True
except StopIteration:
return False
except Exception as e:
print("Exception thrown in tick:",e)
def execute(self, context):
if bpy.app.background:

Wyświetl plik

@ -18,6 +18,8 @@ class UpdateChecker(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
if bpy.app.background:
return {"FINISHED"}
last_update_check = bpy.context.preferences.addons['cam'].preferences.last_update_check
today=date.today().toordinal()
update_source = bpy.context.preferences.addons['cam'].preferences.update_source

Wyświetl plik

@ -138,20 +138,21 @@ def useBridges(ch, o):
vi = 0
newpoints = []
p1 = sgeometry.Point(ch.points[0])
ch_points=ch.get_points_np()
p1 = sgeometry.Point(ch_points[0])
startinside = o.bridgespoly.contains(p1)
interrupted = False
verts = []
edges = []
faces = []
while vi < len(ch.points):
while vi < len(ch_points):
i1 = vi
i2 = vi
chp1 = ch.points[i1]
chp2 = ch.points[i1] # Vector(v1)#this is for case of last point and not closed chunk..
if vi + 1 < len(ch.points):
chp1 = ch_points[i1]
chp2 = ch_points[i1] # Vector(v1)#this is for case of last point and not closed chunk..
if vi + 1 < len(ch_points):
i2 = vi + 1
chp2 = ch.points[vi + 1] # Vector(ch.points[vi+1])
chp2 = ch_points[vi + 1] # Vector(ch_points[vi+1])
v1 = mathutils.Vector(chp1)
v2 = mathutils.Vector(chp2)
if v1.z < bridgeheight or v2.z < bridgeheight:
@ -230,7 +231,7 @@ def useBridges(ch, o):
newpoints.append(chp1)
vi += 1
interrupted = True
ch.points = newpoints
ch.set_points(newpoints)
# create bridge cut curve here
count = 0

Wyświetl plik

@ -59,6 +59,7 @@ from cam.image_utils import *
from cam.opencamlib.opencamlib import *
from cam.nc import iso
PROFILING=False # set this true to run cprofile on code
def pointonline(a, b, c, tolerence):
b = b - a # convert to vector by subtracting origin
@ -158,6 +159,7 @@ def exportGcodePath(filename, vertslist, operations):
if split:
fileindex = '_' + str(findex)
filename = basefilename + fileindex + extension
print("writing: ",filename)
c = postprocessor.Creator()
# process user overrides for post processor settings
@ -517,8 +519,6 @@ def exportGcodePath(filename, vertslist, operations):
async def getPath(context, operation): # should do all path calculations.
t = time.process_time()
# print('ahoj0')
if shapely.speedups.available:
shapely.speedups.enable()
# these tags are for caching of some of the results. Not working well still
# - although it can save a lot of time during calculation...
@ -543,7 +543,19 @@ async def getPath(context, operation): # should do all path calculations.
print(operation.machine_axes)
if operation.machine_axes == '3':
await getPath3axis(context, operation)
if PROFILING==True: # profiler
import cProfile, pstats, io
pr = cProfile.Profile()
pr.enable()
await getPath3axis(context, operation)
pr.disable()
s = io.StringIO()
sortby = pstats.SortKey.CALLS
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
print(s.getvalue())
else:
await getPath3axis(context, operation)
elif (operation.machine_axes == '5' and operation.strategy5axis == 'INDEXED') or (
operation.machine_axes == '4' and operation.strategy4axis == 'INDEXED'):
@ -680,12 +692,13 @@ async def getPath3axis(context, operation):
chunks = await utils.connectChunksLow(chunks, o)
if o.movement.ramp:
for ch in chunks:
ch.rampZigZag(ch.zstart, ch.points[0][2], o)
ch.rampZigZag(ch.zstart, None, o)
# print(chunks)
if o.strategy == 'CARVE':
for ch in chunks:
for vi in range(0, len(ch.points)):
ch.points[vi] = (ch.points[vi][0], ch.points[vi][1], ch.points[vi][2] - o.carve_depth)
ch.offsetZ(-o.carve_depth)
# for vi in range(0, len(ch.points)):
# ch.points[vi] = (ch.points[vi][0], ch.points[vi][1], ch.points[vi][2] - o.carve_depth)
if o.use_bridges:
print(chunks)
for bridge_chunk in chunks:
@ -701,7 +714,8 @@ async def getPath3axis(context, operation):
if (o.movement.type == 'CLIMB' and o.movement.spindle_rotation == 'CW') or (
o.movement.type == 'CONVENTIONAL' and o.movement.spindle_rotation == 'CCW'):
for ch in chunks:
ch.points.reverse()
ch.reverse()
strategy.chunksToMesh(chunks, o)
elif o.strategy == 'WATERLINE' and not o.optimisation.use_opencamlib:
@ -826,7 +840,7 @@ async def getPath3axis(context, operation):
if (o.movement.type == 'CONVENTIONAL' and o.movement.spindle_rotation == 'CCW') or (
o.movement.type == 'CLIMB' and o.movement.spindle_rotation == 'CW'):
for chunk in slicechunks:
chunk.points.reverse()
chunk.reverse()
slicechunks = await utils.sortChunks(slicechunks, o)
if topdown:
slicechunks.reverse()
@ -835,7 +849,6 @@ async def getPath3axis(context, operation):
chunks.extend(slicechunks)
if topdown:
chunks.reverse()
strategy.chunksToMesh(chunks, o)
elif o.strategy == 'DRILL':

Wyświetl plik

@ -35,6 +35,9 @@ from cam.chunk import *
from cam import simulation
from cam.async_op import progress_async
from cam.numba_wrapper import jit,prange
def getCircle(r, z):
car = numpy.full(shape=(r*2,r*2),fill_value=-10,dtype=numpy.double)
res = 2 * r
@ -128,6 +131,12 @@ def imagetonumpy(i):
return na
@jit(nopython=True,parallel=True,fastmath=False,cache=True)
def _offset_inner_loop(y1,y2,cutterArrayNan,cwidth,sourceArray,width,height,comparearea):
for y in prange(y1,y2):
for x in range(0,width-cwidth):
comparearea[x,y] = numpy.nanmax(sourceArray[x:x+cwidth,y:y+cwidth] + cutterArrayNan)
async def offsetArea(o, samples):
""" offsets the whole image with the cutter + skin offsets """
if o.update_offsetimage_tag:
@ -141,7 +150,7 @@ async def offsetArea(o, samples):
width = len(sourceArray)
height = len(sourceArray[0])
cwidth = len(cutterArray)
o.offset_image= numpy.full(shape=(width,height),fill_value=-10,dtype=numpy.float)
o.offset_image= numpy.full(shape=(width,height),fill_value=-10.0,dtype=numpy.double)
t = time.time()
@ -149,18 +158,14 @@ async def offsetArea(o, samples):
if o.inverse:
sourceArray = -sourceArray + minz
print(o.offset_image.shape)
comparearea = o.offset_image[m: width - cwidth + m, m:height - cwidth + m]
# i=0
for x in range(0, cwidth): # cwidth):
text = "Offsetting depth " + str(int(x * 100 / cwidth))
# o.operator.report({"INFO"}, text)
await progress_async('offset depth image', int(x * 100 / cwidth))
for y in range(0, cwidth):
if cutterArray[x, y] > -10:
numpy.maximum(sourceArray[x: width - cwidth + x, y: height - cwidth + y] + cutterArray[x, y],
comparearea, comparearea)
cutterArrayNan=np.where(cutterArray>-10,cutterArray,np.full(cutterArray.shape,np.nan))
for y in range(0,10):
y1 = (y * comparearea.shape[1])//10
y2 = ((y+1) * comparearea.shape[1])//10
_offset_inner_loop(y1,y2,cutterArrayNan,cwidth,sourceArray,width,height,comparearea)
await progress_async('offset depth image', int((y2 * 100) / comparearea.shape[1]))
o.offset_image[m: width - cwidth + m, m:height - cwidth + m] = comparearea
print('\nOffset image time ' + str(time.time() - t))
@ -196,10 +201,11 @@ def getOffsetImageCavities(o, i): # for pencil operation mainly
# ##crop pixels that are on outer borders
for chi in range(len(chunks) - 1, -1, -1):
chunk = chunks[chi]
for si in range(len(chunk.points) - 1, -1, -1):
if not (o.min.x < chunk.points[si][0] < o.max.x and o.min.y < chunk.points[si][1] < o.max.y):
chunk.points.pop(si)
if len(chunk.points) < 2:
chunk.clip_points(o.min.x,o.max.x,o.min.y,o.max.y)
# for si in range(len(chunk.points) - 1, -1, -1):
# if not (o.min.x < chunk.points[si][0] < o.max.x and o.min.y < chunk.points[si][1] < o.max.y):
# chunk.points.pop(si)
if chunk.count() < 2:
chunks.pop(chi)
return chunks
@ -217,10 +223,10 @@ def imageEdgeSearch_online(o, ar, zimage): # search edges for pencil strategy,
indices = ar.nonzero() # first get white pixels
startpix = ar.sum()
totpix = startpix
chunks = []
chunk_builders = []
xs = indices[0][0]
ys = indices[1][0]
nchunk = camPathChunk([(xs, ys, zimage[xs, ys])]) # startposition
nchunk = camPathChunkBuilder([(xs, ys, zimage[xs, ys])]) # startposition
dindex = 0 # index in the directions list
last_direction = directions[dindex]
test_direction = directions[dindex]
@ -276,15 +282,15 @@ def imageEdgeSearch_online(o, ar, zimage): # search edges for pencil strategy,
testangulardistance = 0
indices = ar.nonzero()
totpix = len(indices[0])
chunks.append(nchunk)
chunk_builders.append(nchunk)
if len(indices[0] > 0):
xs = indices[0][0]
ys = indices[1][0]
nchunk = camPathChunk([(xs, ys, zimage[xs, ys])]) # startposition
nchunk = camPathChunkBuilder([(xs, ys, zimage[xs, ys])]) # startposition
ar[xs, ys] = False
else:
nchunk = camPathChunk([])
nchunk = camPathChunkBuilder([])
test_direction = directions[3]
last_direction = directions[3]
@ -321,19 +327,19 @@ def imageEdgeSearch_online(o, ar, zimage): # search edges for pencil strategy,
# print(totpix)
# print(totaltests)
i = 0
chunks.append(nchunk)
for ch in chunks:
chunk_builders.append(nchunk)
for ch in chunk_builders:
ch = ch.points
for i in range(0, len(ch)):
ch[i] = ((ch[i][0] + coef - o.borderwidth) * o.optimisation.pixsize + minx,
(ch[i][1] + coef - o.borderwidth) * o.optimisation.pixsize + miny, ch[i][2])
return chunks
return [c.to_chunk() for c in chunk_builders]
def crazyPath(o):
async def crazyPath(o):
# TODO: try to do something with this stuff, it's just a stub. It should be a greedy adaptive algorithm.
# started another thing below.
prepareArea(o)
await prepareArea(o)
sx = o.max.x - o.min.x
sy = o.max.y - o.min.y
@ -394,14 +400,14 @@ def crazyStrokeImage(o):
indices = ar.nonzero() # first get white pixels
startpix = ar.sum() #
totpix = startpix
chunks = []
chunk_builders = []
xs = indices[0][0] - r
if xs < r:
xs = r
ys = indices[1][0] - r
if ys < r:
ys = r
nchunk = camPathChunk([(xs, ys)]) # startposition
nchunk = camPathChunkBuilder([(xs, ys)]) # startposition
print(indices)
print(indices[0][0], indices[1][0])
lastvect = Vector((r, 0, 0)) # vector is 3d, blender somehow doesn't rotate 2d vectors with angles.
@ -514,7 +520,7 @@ def crazyStrokeImage(o):
if itests > maxtests or testlength > r * 1.5:
# print('resetting location')
indices = ar.nonzero()
chunks.append(nchunk)
chunk_builders.append(nchunk)
if len(indices[0]) > 0:
xs = indices[0][0] - r
if xs < r:
@ -522,7 +528,7 @@ def crazyStrokeImage(o):
ys = indices[1][0] - r
if ys < r:
ys = r
nchunk = camPathChunk([(xs, ys)]) # startposition
nchunk = camPathChunkBuilder([(xs, ys)]) # startposition
ar[xs - r:xs - r + d, ys - r:ys - r + d] = ar[xs - r:xs - r + d,
ys - r:ys - r + d] * cutterArrayNegative
r = random.random() * 2 * pi
@ -539,13 +545,13 @@ def crazyStrokeImage(o):
print(totpix)
print(totaltests)
i = 0
chunks.append(nchunk)
for ch in chunks:
chunk_builders.append(nchunk)
for ch in chunk_builders:
ch = ch.points
for i in range(0, len(ch)):
ch[i] = ((ch[i][0] + coef - o.borderwidth) * o.optimisation.pixsize + minx,
(ch[i][1] + coef - o.borderwidth) * o.optimisation.pixsize + miny, 0)
return chunks
return [c.to_chunk() for c in chunk_builders]
def crazyStrokeImageBinary(o, ar, avoidar):
@ -582,7 +588,7 @@ def crazyStrokeImageBinary(o, ar, avoidar):
startpix = ar.sum() #
totpix = startpix
chunks = []
chunk_builders = []
# try to find starting point here
xs = indices[0][0] - r / 2
@ -592,7 +598,7 @@ def crazyStrokeImageBinary(o, ar, avoidar):
if ys < r:
ys = r
nchunk = camPathChunk([(xs, ys)]) # startposition
nchunk = camPathChunkBuilder([(xs, ys)]) # startposition
print(indices)
print(indices[0][0], indices[1][0])
lastvect = Vector((r, 0, 0)) # vector is 3d, blender somehow doesn't rotate 2d vectors with angles.
@ -741,7 +747,7 @@ def crazyStrokeImageBinary(o, ar, avoidar):
indices = andar.nonzero()
if len(nchunk.points) > 1:
chunk.parentChildDist([nchunk], chunks, o, distance=r)
chunks.append(nchunk)
chunk_builders.append(nchunk)
if totpix > startpix * 0.001:
found = False
@ -797,15 +803,15 @@ def crazyStrokeImageBinary(o, ar, avoidar):
i = 0
if len(nchunk.points) > 1:
chunk.parentChildDist([nchunk], chunks, o, distance=r)
chunks.append(nchunk)
chunk_builders.append(nchunk)
for ch in chunks:
for ch in chunk_builders:
ch = ch.points
for i in range(0, len(ch)):
ch[i] = ((ch[i][0] + coef - o.borderwidth) * o.optimisation.pixsize + minx,
(ch[i][1] + coef - o.borderwidth) * o.optimisation.pixsize + miny, o.minz)
return chunks
return [c.to_chunk for c in chunk_builders]
def imageToChunks(o, image, with_border=False):
@ -967,12 +973,12 @@ def imageToChunks(o, image, with_border=False):
s = curve_simplify.simplify_RDP(ch, soptions)
# print(s)
nch = camPathChunk([])
nch = camPathChunkBuilder([])
for i in range(0, len(s)):
nch.points.append((ch[s[i]].x, ch[s[i]].y))
if len(nch.points) > 2:
nchunks.append(nch)
nchunks.append(nch.to_chunk())
return nchunks
else:
@ -985,7 +991,6 @@ def imageToShapely(o, i, with_border=False):
return polys
def getSampleImage(s, sarray, minz):
x = s[0]
y = s[1]
@ -996,10 +1001,14 @@ def getSampleImage(s, sarray, minz):
maxx = minx + 1
miny = floor(y)
maxy = miny + 1
s1a = sarray.item(minx, miny) # most optimal access to array so far
s2a = sarray.item(maxx, miny)
s1b = sarray.item(minx, maxy)
s2b = sarray.item(maxx, maxy)
s1a=sarray[minx,miny]
s2a=sarray[maxx,miny]
s1b=sarray[minx,maxy]
s2b=sarray[maxx,maxy]
# s1a = sarray.item(minx, miny) # most optimal access to array so far
# s2a = sarray.item(maxx, miny)
# s1b = sarray.item(minx, maxy)
# s2b = sarray.item(maxx, maxy)
sa = s1a * (maxx - x) + s2a * (x - minx)
sb = s1b * (maxx - x) + s2b * (x - minx)
@ -1057,7 +1066,18 @@ def renderSampleImage(o):
r = s.render
r.resolution_x = resx
r.resolution_y = resy
r.engine = 'BLENDER_EEVEE'
if bpy.app.background:
# in CI we use cycles because it
# works without opengl support
r.engine = 'CYCLES'
cycles_settings=s.cycles.items()
s.cycles.samples = 1
bpy.context.view_layer.samples=1
vl_settings=bpy.context.view_layer.cycles
vl_settings.use_denoising=False
else:
r.engine = 'BLENDER_EEVEE'
n.links.clear()
n.nodes.clear()

Wyświetl plik

@ -0,0 +1,14 @@
try:
from numba import jit,prange
print("numba: yes")
except:
print("numba: no")
def jit(f=None, *args, **kwargs):
def decorator(func):
return func
if callable(f):
return f
else:
return decorator
prange = range

Wyświetl plik

@ -82,7 +82,7 @@ async def ocl_sample(operation, chunks,use_cached_mesh = False):
bdc.setCutter(cutter)
for chunk in chunks:
for coord in chunk.points:
for coord in chunk.get_points_np():
bdc.appendPoint(ocl.CLPoint(coord[0] * 1000, coord[1] * 1000, op_minz * 1000))
await progress_async("OpenCAMLib sampling")
bdc.run()

Wyświetl plik

@ -10,6 +10,8 @@ except ImportError:
pass
import os
import tempfile
import numpy as np
from subprocess import call
from cam.collision import BULLET_SCALE
from cam import simple
@ -18,6 +20,7 @@ from cam.simple import *
from cam.async_op import progress_async
from shapely import geometry as sgeometry
from .oclSample import get_oclSTL
from cam import utils
from cam.opencamlib.oclSample import ocl_sample
@ -32,30 +35,43 @@ def pointSamplesFromOCL(points, samples):
def chunkPointSamplesFromOCL(chunks, samples):
s_index = 0
for ch in chunks:
p_index = 0
for point in ch.points:
if len(point) == 2 or point[2] != 2:
z_sample = samples[s_index].z / OCL_SCALE
ch.points[p_index] = (point[0], point[1], z_sample)
# print(str(point[2]))
else:
ch.points[p_index] = (point[0], point[1], 1)
p_index += 1
s_index += 1
ch_points=ch.count()
z_vals=np.array([p.z for p in samples[s_index:s_index+ch_points]])
z_vals /= OCL_SCALE
ch.setZ(z_vals)
s_index+=ch_points
# p_index = 0
# for point in ch.points:
# if len(point) == 2 or point[2] != 2:
# z_sample = samples[s_index].z / OCL_SCALE
# ch.points[p_index] = (point[0], point[1], z_sample)
# # print(str(point[2]))
# else:
# ch.points[p_index] = (point[0], point[1], 1)
# p_index += 1
# s_index += 1
def chunkPointsResampleFromOCL(chunks, samples):
s_index = 0
for ch in chunks:
p_index = 0
for point in ch.points:
if len(point) == 2 or point[2] != 2:
z_sample = samples[s_index].z / OCL_SCALE
ch.points[p_index] = (point[0], point[1], z_sample)
# print(str(point[2]))
else:
ch.points[p_index] = (point[0], point[1], 1)
p_index += 1
s_index += 1
ch_points=ch.count()
z_vals=np.array([p.z for p in samples[s_index:s_index+ch_points]])
z_vals /= OCL_SCALE
ch.setZ(z_vals)
s_index+=ch_points
# s_index = 0
# for ch in chunks:
# p_index = 0
# for point in ch.points:
# if len(point) == 2 or point[2] != 2:
# z_sample = samples[s_index].z / OCL_SCALE
# ch.points[p_index] = (point[0], point[1], z_sample)
# # print(str(point[2]))
# else:
# ch.points[p_index] = (point[0], point[1], 1)
# p_index += 1
# s_index += 1
def exportModelsToSTL(operation):
@ -100,11 +116,14 @@ async def oclResampleChunks(operation, chunks_to_resample,use_cached_mesh):
sample_index = 0
for chunk, i_start, i_length in chunks_to_resample:
for p_index in range(i_start, i_start + i_length):
z = samples[sample_index].z / OCL_SCALE
sample_index += 1
if z > chunk.points[p_index][2]:
chunk.points[p_index][2] = z
z = np.array(p.z for p in samples[sample_index:sample_index+i_length]) / OCL_SCALE
chunk.setZ(z,if_bigger=True)
# sample_index += i_length
# for p_index in range(i_start, i_start + i_length):
# z = samples[sample_index].z / OCL_SCALE
# sample_index += 1
# if z > chunk.points[p_index][2]:
# chunk.points[p_index][2] = z
def oclWaterlineLayerHeights(operation):
@ -155,18 +174,25 @@ async def oclGetWaterline(operation, chunks):
waterline.setSTL(oclSTL)
waterline.setCutter(cutter)
waterline.setSampling(0.1)#TODO: add sampling setting to UI
last_pos=[0,0,0]
for count,height in enumerate(layers):
layer_chunks=[]
await progress_async("Waterline",int((100*count)/len(layers)))
waterline.reset()
waterline.setZ(height * OCL_SCALE)
waterline.run2()
wl_loops = waterline.getLoops()
for l in wl_loops:
chunks.append(camPathChunk(inpoints=[]))
inpoints=[]
for p in l:
chunks[-1].points.append((p.x / OCL_SCALE, p.y / OCL_SCALE, p.z / OCL_SCALE))
chunks[-1].append(chunks[-1].points[0])
chunks[-1].closed = True
chunks[-1].poly = sgeometry.Polygon(chunks[-1].points)
inpoints.append((p.x / OCL_SCALE, p.y / OCL_SCALE, p.z / OCL_SCALE))
inpoints.append(inpoints[0])
chunk=camPathChunk(inpoints=inpoints)
chunk.closed = True
layer_chunks.append(chunk)
# sort chunks so that ordering is stable
chunks.extend(await utils.sortChunks(layer_chunks,operation,last_pos=last_pos))
if len(chunks)>0:
last_pos=chunks[-1].get_point(-1)
# def oclFillMedialAxis(operation):

Wyświetl plik

@ -190,9 +190,8 @@ async def _calc_path(operator,context):
if o.use_layers:
o.movement.parallel_step_back = False
try:
print("Get path:",context)
await gcodepath.getPath(context, o)
print("Got path:",context)
print("Got path okay")
except CamException as e:
traceback.print_tb(e.__traceback__)
error_str="\n".join(textwrap.wrap(str(e),width=80))
@ -229,6 +228,7 @@ class CalculatePath(bpy.types.Operator,AsyncOperatorMixin):
async def execute_async(self, context):
(retval,success) = await _calc_path(self,context)
print(f"CALCULATED PATH (success={success},retval={retval}")
return retval

Wyświetl plik

@ -55,7 +55,7 @@ def getPathPatternParallel(o, angle):
dirvect *= pathstep
for a in range(int(-dim / pathd),
int(dim / pathd)): # this is highly ineffective, computes path2x the area needed...
chunk = camPathChunk([])
chunk = camPathChunkBuilder([])
v = Vector((a * pathd, int(-dim / pathstep) * pathstep, 0))
v.rotate(e)
v += vm # shifting for the rotation, so pattern rotates around middle...
@ -70,14 +70,14 @@ def getPathPatternParallel(o, angle):
chunk.points.reverse()
if len(chunk.points) > 0:
pathchunks.append(chunk)
pathchunks.append(chunk.to_chunk())
if len(pathchunks) > 1 and reverse and o.movement.parallel_step_back and not o.use_layers:
# parallel step back - for finishing, best with climb movement, saves cutter life by going into
# material with climb, while using move back on the surface to improve finish
# (which would otherwise be a conventional move in the material)
if o.movement.type == 'CONVENTIONAL' or o.movement.type == 'CLIMB':
pathchunks[-2].points.reverse()
pathchunks[-2].reverse()
changechunk = pathchunks[-1]
pathchunks[-1] = pathchunks[-2]
pathchunks[-2] = changechunk
@ -125,6 +125,7 @@ def getPathPatternParallel(o, angle):
chunks.append(nax.swapaxes(0, 1))
# chunks
pathchunks = []
print("WOO")
for ch in chunks:
ch = ch.tolist()
pathchunks.append(camPathChunk(ch))
@ -160,7 +161,7 @@ def getPathPattern(operation):
y = 0.0
incx = 1
incy = 0
chunk = camPathChunk([])
chunk = camPathChunkBuilder([])
i = 0
while maxxp - minxp > 0 and maxyp - minyp > 0:
@ -206,10 +207,10 @@ def getPathPattern(operation):
for si in range(0, len(chunk.points)):
s = chunk.points[si]
chunk.points[si] = (o.max.x + o.min.x - s[0], s[1], s[2])
pathchunks = [chunk]
pathchunks = [chunk.to_chunk()]
elif o.strategy == 'SPIRAL':
chunk = camPathChunk([])
chunk = camPathChunkBuilder([])
pathd = o.dist_between_paths
pathstep = o.dist_along_paths
midx = (o.max.x + o.min.x) / 2
@ -234,21 +235,23 @@ def getPathPattern(operation):
if o.max.x > midx + v.x > o.min.x and o.max.y > midy + v.y > o.min.y:
chunk.points.append((midx + v.x, midy + v.y, zlevel))
else:
pathchunks.append(chunk)
chunk = camPathChunk([])
pathchunks.append(chunk.to_chunk())
chunk = camPathChunkBuilder([])
if len(chunk.points) > 0:
pathchunks.append(chunk)
pathchunks.append(chunk.to_chunk())
if o.movement.insideout == 'OUTSIDEIN':
pathchunks.reverse()
for chunk in pathchunks:
if o.movement.insideout == 'OUTSIDEIN':
chunk.points.reverse()
chunk.reverse()
if (o.movement.type == 'CONVENTIONAL' and o.movement.spindle_rotation == 'CW') or (
o.movement.type == 'CLIMB' and o.movement.spindle_rotation == 'CCW'):
for si in range(0, len(chunk.points)):
s = chunk.points[si]
chunk.points[si] = (o.max.x + o.min.x - s[0], s[1], s[2])
##TODO
chunk.flipX(o.max.x+o.min.x)
# for si in range(0, len(chunk.points)):
# s = chunk.points[si]
# chunk.points[si] = (o.max.x + o.min.x - s[0], s[1], s[2])
elif o.strategy == 'CIRCLES':
@ -263,14 +266,14 @@ def getPathPattern(operation):
# progress(x,y,midx,midy)
e = Euler((0, 0, 0))
pi = math.pi
chunk = camPathChunk([])
chunk = camPathChunkBuilder([])
chunk.points.append((midx, midy, zlevel))
pathchunks.append(chunk)
pathchunks.append(chunk.to_chunk())
r = 0
while r < maxr:
r += pathd
chunk = camPathChunk([])
chunk = camPathChunkBuilder([])
firstchunk = chunk
v = Vector((-r, 0, 0))
steps = 2 * pi * r / pathstep
@ -286,18 +289,20 @@ def getPathPattern(operation):
else:
if len(chunk.points) > 0:
chunk.closed = False
chunk=chunk.to_chunk()
pathchunks.append(chunk)
currentstepchunks.append(chunk)
chunk = camPathChunk([])
chunk = camPathChunkBuilder([])
v.rotate(e)
if len(chunk.points) > 0:
chunk.points.append(firstchunk.points[0])
if chunk == firstchunk:
chunk.closed = True
chunk=chunk.to_chunk()
pathchunks.append(chunk)
currentstepchunks.append(chunk)
chunk = camPathChunk([])
chunk = camPathChunkBuilder([])
for ch in laststepchunks:
for p in currentstepchunks:
parentChildDist(p, ch, o)
@ -306,13 +311,10 @@ def getPathPattern(operation):
pathchunks.reverse()
for chunk in pathchunks:
if o.movement.insideout == 'OUTSIDEIN':
chunk.points.reverse()
chunk.reverse()
if (o.movement.type == 'CONVENTIONAL' and o.movement.spindle_rotation == 'CW') or (
o.movement.type == 'CLIMB' and o.movement.spindle_rotation == 'CCW'):
chunk.points.reverse()
# for si in range(0,len(chunk.points)):
# s=chunk.points[si]
# chunk.points[si]=(o.max.x+o.min.x-s[0],s[1],s[2])
chunk.reverse()
# pathchunks=sortChunks(pathchunks,o)not until they get hierarchy parents!
elif o.strategy == 'OUTLINEFILL':
@ -379,10 +381,10 @@ def getPathPattern(operation):
for chunk in pathchunks:
if o.movement.insideout == 'OUTSIDEIN':
chunk.points.reverse()
chunk.reverse()
if (o.movement.type == 'CLIMB' and o.movement.spindle_rotation == 'CW') or (
o.movement.type == 'CONVENTIONAL' and o.movement.spindle_rotation == 'CCW'):
chunk.points.reverse()
chunk.reverse()
chunksRefine(pathchunks, o)
progress(time.time() - t)
@ -436,7 +438,7 @@ def getPathPattern4axis(operation):
if o.strategy4axis == 'PARALLELR':
for a in range(0, floor(steps) + 1):
chunk = camPathChunk([])
chunk = camPathChunkBuilder([])
cutterstart[a1] = o.min[a1] + a * o.dist_between_paths
cutterend[a1] = cutterstart[a1]
@ -464,7 +466,7 @@ def getPathPattern4axis(operation):
chunk.endpoints.append(chunk.endpoints[0])
chunk.rotations.append(chunk.rotations[0])
pathchunks.append(chunk)
pathchunks.append(chunk.to_chunk())
if o.strategy4axis == 'PARALLEL':
circlesteps = (mradius * pi * 2) / o.dist_between_paths
@ -478,7 +480,7 @@ def getPathPattern4axis(operation):
reverse = False
for b in range(0, floor(circlesteps) + 1):
chunk = camPathChunk([])
chunk = camPathChunkBuilder([])
cutterstart[a2] = 0
cutterstart[a3] = radius
@ -500,7 +502,7 @@ def getPathPattern4axis(operation):
chunk.rotations.append(rot)
chunk.depth = radiusend - radius
pathchunks.append(chunk)
pathchunks.append(chunk.to_chunk())
if (reverse and o.movement.type == 'MEANDER') or (
o.movement.type == 'CONVENTIONAL' and o.movement.spindle_rotation == 'CW') or (
@ -514,7 +516,7 @@ def getPathPattern4axis(operation):
a1step = o.dist_between_paths / circlesteps
chunk = camPathChunk([]) # only one chunk, init here
chunk = camPathChunkBuilder([]) # only one chunk, init here
for a in range(0, floor(steps) + 1):
@ -540,7 +542,7 @@ def getPathPattern4axis(operation):
chunk.depth = radiusend - radius
pathchunks.append(chunk)
pathchunks.append(chunk.to_chunk())
# print(chunk.startpoints)
# print(pathchunks)
# sprint(len(pathchunks))

Wyświetl plik

@ -120,7 +120,7 @@ async def generateSimulationImage(operations, limits):
resy = math.ceil(sy / simulation_detail) + 2 * borderwidth
# create array in which simulation happens, similar to an image to be painted in.
si = np.full(shape=(resx,resy),fill_value=maxz,dtype=np.float)
si = np.full(shape=(resx,resy),fill_value=maxz,dtype=float)
num_operations=len(operations)
@ -288,7 +288,7 @@ def getCutterArray(operation, pixsize):
r = operation.cutter_diameter / 2 + operation.skin # /operation.pixsize
res = math.ceil((r * 2) / pixsize)
m = res / 2.0
car = np.full(shape=(res,res),fill_value=-10.0,dtype=np.float)
car = np.full(shape=(res,res),fill_value=-10.0,dtype=float)
v = mathutils.Vector((0, 0, 0))
ps = pixsize

Wyświetl plik

@ -81,11 +81,12 @@ async def cutout(o):
chunksFromCurve = []
for ob in o.objects:
chunksFromCurve.extend(curveToChunks(ob, o.use_modifiers))
for ch in chunksFromCurve:
# print(ch.points)
# chunks always have polys now
# for ch in chunksFromCurve:
# # print(ch.points)
if len(ch.points) > 2:
ch.poly = chunkToShapely(ch)
# if len(ch.points) > 2:
# ch.poly = chunkToShapely(ch)
# p.addContour(ch.poly)
else:
@ -122,12 +123,12 @@ async def cutout(o):
if (o.movement.type == 'CLIMB' and o.movement.spindle_rotation == 'CCW') or (
o.movement.type == 'CONVENTIONAL' and o.movement.spindle_rotation == 'CW'):
for ch in chunksFromCurve:
ch.points.reverse()
ch.reverse()
if o.cut_type == 'INSIDE': # there would bee too many conditions above,
# so for now it gets reversed once again when inside cutting.
for ch in chunksFromCurve:
ch.points.reverse()
ch.reverse()
layers = getLayers(o, o.maxz, checkminz(o))
extendorder = []
@ -141,7 +142,7 @@ async def cutout(o):
for layer in layers:
chunk_copy = chunk.copy()
if dir_switch:
chunk_copy.points.reverse()
chunk_copy.reverse()
extendorder.append([chunk_copy, layer])
if (not chunk.closed) and o.movement.type == "MEANDER":
dir_switch = not dir_switch
@ -264,12 +265,13 @@ async def proj_curve(s, o):
extend_down = 0.04
tsamples = curveToChunks(targetCurve)
for chi, ch in enumerate(pathSamples):
cht = tsamples[chi].points
cht = tsamples[chi].get_points()
ch.depth = 0
for i, s in enumerate(ch.points):
ch_points=ch.get_points()
for i, s in enumerate(ch_points):
# move the points a bit
ep = Vector(cht[i])
sp = Vector(ch.points[i])
sp = Vector(ch_points[i])
# extend startpoint
vecs = sp - ep
vecs.normalize()
@ -288,8 +290,8 @@ async def proj_curve(s, o):
vec = sp - ep
ch.depth = min(ch.depth, -vec.length)
ch.points[i] = sp.copy()
ch_points[i] = sp.copy()
ch.set_points(ch_points)
layers = getLayers(o, 0, ch.depth)
chunks.extend(utils.sampleChunksNAxis(o, pathSamples, layers))
@ -358,7 +360,7 @@ async def pocket(o):
if (o.movement.type == 'CLIMB' and o.movement.spindle_rotation == 'CW') or (
o.movement.type == 'CONVENTIONAL' and o.movement.spindle_rotation == 'CCW'):
for ch in chunksFromCurve:
ch.points.reverse()
ch.reverse()
chunksFromCurve = await utils.sortChunks(chunksFromCurve, o)
@ -380,7 +382,7 @@ async def pocket(o):
revheight = helix_circumference * tan(o.movement.ramp_in_angle)
for chi, ch in enumerate(lchunks):
if not chunksFromCurve[chi].children:
p = ch.points[0] # TODO:intercept closest next point when it should stay low
p = ch.get_point(0) # TODO:intercept closest next point when it should stay low
# first thing to do is to check if helix enter can really enter.
checkc = Circle(helix_radius + c_offset, o.optimisation.circle_detail)
checkc = affinity.translate(checkc, p[0], p[1])
@ -401,7 +403,9 @@ async def pocket(o):
for v in h:
nhelix.append((2 * p[0] - v[0], v[1], v[2]))
h = nhelix
ch.points = h + ch.points
ch.extend(h,at_index=0)
# ch.points = h + ch.points
else:
o.info.warnings += 'Helix entry did not fit! \n '
ch.closed = True
@ -415,13 +419,13 @@ async def pocket(o):
if chunksFromCurve[chi].parents == [] or len(chunksFromCurve[chi].parents) == 1:
revolutions = 0.25
v1 = Vector(ch.points[-1])
v1 = Vector(ch.get_point(-1))
i = -2
v2 = Vector(ch.points[i])
v2 = Vector(ch.get_point(i))
v = v1 - v2
while v.length == 0:
i = i - 1
v2 = Vector(ch.points[i])
v2 = Vector(ch.get_point(i))
v = v1 - v2
v.normalize()
@ -465,13 +469,13 @@ async def pocket(o):
break
if covers:
ch.points.extend(rothelix)
ch.extend(rothelix)
chunks.extend(lchunks)
if o.movement.ramp:
for ch in chunks:
ch.rampZigZag(ch.zstart, ch.points[0][2], o)
ch.rampZigZag(ch.zstart, ch.get_point(0)[2], o)
if o.first_down:
if o.pocket_option == "OUTSIDE":
@ -559,7 +563,7 @@ async def drill(o):
for chunk in chunks:
# If using object for minz then use z from points in object
if o.minz_from == 'OBJECT':
z = chunk.points[0][2]
z = chunk.get_point(0)[2]
else: # using operation minz
z = o.minz
# only add a chunk layer if the chunk z point is in or lower than the layer
@ -626,16 +630,16 @@ async def medial_axis(o):
ipol = 0
for poly in polys.geoms:
ipol = ipol + 1
print("polygon:", ipol)
schunks = shapelyToChunks(poly, -1)
schunks = chunksRefineThreshold(schunks, o.medial_axis_subdivision,
o.medial_axis_threshold) # chunksRefine(schunks,o)
verts = []
for ch in schunks:
for pt in ch.points:
# pvoro = Site(pt[0], pt[1])
verts.append(pt) # (pt[0], pt[1]), pt[2])
verts.extend(ch.get_points())
# for pt in ch.get_points():
# # pvoro = Site(pt[0], pt[1])
# verts.append(pt) # (pt[0], pt[1]), pt[2])
# verts= points#[[vert.x, vert.y, vert.z] for vert in vertsPts]
nDupli, nZcolinear = unique(verts)
nVerts = len(verts)
@ -831,12 +835,10 @@ def chunksToMesh(chunks, o):
for chi in range(0, len(chunks)):
# print(chi)
ch = chunks[chi]
# print(chunks)
# print (ch)
if len(ch.points) > 0: # TODO: there is a case where parallel+layers+zigzag ramps send empty chunks here...
if ch.count() > 0: # TODO: there is a case where parallel+layers+zigzag ramps send empty chunks here...
# print(len(ch.points))
nverts = []
if o.optimisation.optimize:
@ -847,14 +849,14 @@ def chunksToMesh(chunks, o):
if lifted: # did the cutter lift before? if yes, put a new position above of the first point of next chunk.
if o.machine_axes == '3' or (o.machine_axes == '5' and o.strategy5axis == 'INDEXED') or (
o.machine_axes == '4' and o.strategy4axis == 'INDEXED'):
v = (ch.points[0][0], ch.points[0][1], free_height)
v = (ch.get_point(0)[0], ch.get_point(0)[1], free_height)
else: # otherwise, continue with the next chunk without lifting/dropping
v = ch.startpoints[0] # startpoints=retract points
verts_rotations.append(ch.rotations[0])
verts.append(v)
# add whole chunk
verts.extend(ch.points)
verts.extend(ch.get_points())
# add rotations for n-axis
if o.machine_axes != '3':
@ -862,10 +864,10 @@ def chunksToMesh(chunks, o):
lift = True
# check if lifting should happen
if chi < len(chunks) - 1 and len(chunks[chi + 1].points) > 0:
if chi < len(chunks) - 1 and chunks[chi + 1].count() > 0:
# TODO: remake this for n axis, and this check should be somewhere else...
last = Vector(ch.points[-1])
first = Vector(chunks[chi + 1].points[0])
last = Vector(ch.get_point(-1))
first = Vector(chunks[chi + 1].get_point(0))
vect = first - last
if (o.machine_axes == '3' and (o.strategy == 'PARALLEL' or o.strategy == 'CROSS')
and vect.z == 0 and vect.length < o.dist_between_paths * 2.5) \
@ -878,7 +880,7 @@ def chunksToMesh(chunks, o):
if lift:
if o.machine_axes == '3' or (o.machine_axes == '5' and o.strategy5axis == 'INDEXED') or (
o.machine_axes == '4' and o.strategy4axis == 'INDEXED'):
v = (ch.points[-1][0], ch.points[-1][1], free_height)
v = (ch.get_point(-1)[0], ch.get_point(-1)[1], free_height)
else:
v = ch.startpoints[-1]
verts_rotations.append(ch.rotations[-1])

Wyświetl plik

@ -1,4 +1,8 @@
import bpy
import sys
import warnings
warnings.simplefilter("once")
# Get the scene
s = bpy.context.scene
@ -12,3 +16,5 @@ for i, operation in enumerate(s.cam_operations):
# Run the calculate_cam_path() operator
bpy.ops.object.calculate_cam_path()
sys.exit(0)

Wyświetl plik

@ -2,12 +2,14 @@ import tempfile
import sys
import subprocess
import pathlib
import shutil
INSTALL_CODE=f"""
import bpy
bpy.ops.preferences.addon_install(filepath='{sys.argv[1]}')
bpy.ops.preferences.addon_enable(module='cam')
bpy.ops.wm.save_userpref()
import cam
"""
NUM_RETRIES=10
@ -15,15 +17,20 @@ NUM_RETRIES=10
with tempfile.TemporaryDirectory() as td:
file=pathlib.Path(td,"install.py")
file.write_text(INSTALL_CODE)
command = f'blender -b -P "{str(file)}"'
# blender 4.0 installing addon crashes sometimes on mac github actions...
for x in range(NUM_RETRIES):
try:
subprocess.run(command, shell=True, check=True,capture_output=True)
subprocess.run([shutil.which('blender'),'-b','-P',str(file)], shell=False, check=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
print("installed addon okay")
sys.exit(0)
except subprocess.CalledProcessError as e:
except subprocess.CalledProcessError as e:
print("Install addon failed, retrying:",e)
for line in e.stderr:
print("Command output:")
print("------------------------------")
print(e.output)
print("------------------------------")
for line in str(e.output):
if line.startswith("Writing: "):
crash_file=pathlib.Path(line[len("Writing: "):])
if crash_file.exists():

Wyświetl plik

@ -0,0 +1,362 @@
(Created with grbl post processor 2024/01/26 12:08)
G21
(G-code generated with BlenderCAM and NC library)
G17G90
(Tool: D = 3.0 mm type END flutes 2)
S12000M03
G00 Z10.0
G0X0Y0Z10
X0.999Y2
G1Z-5.1F500
Y0.999F1000
X-0.999
Y-0.999
X3.999
Y3.999
X-3.999
Y-3.999
X6.999
Y6.999
X-6.999
Y-6.999
X9.999
Y9.999
X-9.999
Y-9.999
X12.999
Y12.999
X-12.999
Y-12.999
X15.999
Y15.999
X-15.999
Y-15.999
X18.999
Y18.999
X-18.999
Y-18.999
X21.999
Y21.999
X-21.999
Y-21.999
X24.999
Y24.999
X-24.999
Y-24.999
X27.999
Y27.999
X-27.999
Y-27.999
X30.999
Y30.999
X-30.999
Y-30.999
X33.999
Y33.999
X-33.999
Y-33.999
X36.999
Y36.999
X-36.999
Y-36.999
X39.999
Y39.999
X-39.999
Y-39.999
X42.999
Y42.999
X-42.999
Y-42.999
X45.999
Y45.999
X-45.999
Y-45.999
X48.999
Y47.5
G0Z10
X47.5Y48.999
G1Z-5.1F500
X-47.5F1000
G0Z10
X-48.999Y47.5
G1Z-5.1F500
Y-47.5F1000
G0Z10
X-47.5Y-48.999
G1Z-5.1F500
X47.5F1000
G0Z10
X51.999Y-44.5
G1Z-5.1F500
Y44.5F1000
G0Z10
X44.5Y51.999
G1Z-5.1F500
X-44.5F1000
G0Z10
X-51.999Y44.5
G1Z-5.1F500
Y-44.5F1000
G0Z10
X-44.5Y-51.999
G1Z-5.1F500
X44.5F1000
G0Z10
X54.999Y-40.5
G1Z-5.1F500
Y40.5F1000
G0Z10
X40.5Y54.999
G1Z-5.1F500
X-40.5F1000
G0Z10
X-54.999Y40.5
G1Z-5.1F500
Y-40.5F1000
G0Z10
X-40.5Y-54.999
G1Z-5.1F500
X40.5F1000
G0Z10
X57.999Y-36
G1Z-5.1F500
Y36F1000
G0Z10
X36Y57.999
G1Z-5.1F500
X-36F1000
G0Z10
X-57.999Y36
G1Z-5.1F500
Y-36F1000
G0Z10
X-36Y-57.999
G1Z-5.1F500
X36F1000
G0Z10
X60.999Y-31
G1Z-5.1F500
Y31F1000
G0Z10
X31Y60.999
G1Z-5.1F500
X-31F1000
G0Z10
X-60.999Y31
G1Z-5.1F500
Y-31F1000
G0Z10
X-31Y-60.999
G1Z-5.1F500
X31F1000
G0Z10
X63.999Y-24
G1Z-5.1F500
Y24F1000
G0Z10
X24Y63.999
G1Z-5.1F500
X-24F1000
G0Z10
X-63.999Y24
G1Z-5.1F500
Y-24F1000
G0Z10
X-24Y-63.999
G1Z-5.1F500
X24F1000
G0Z10
X67Y-14
G1Z-5.1F500
Y14F1000
G0Z10
X14Y67
G1Z-5.1F500
X-14F1000
G0Z10
X-67Y14
G1Z-5.1F500
Y-14F1000
G0Z10
X-14Y-67
G1Z-5.1F500
X14F1000
G0Z10
X0.999Y2
G1Z-6.912F500
Y0.999F1000
X-0.999
Y-0.999
X3.999
Y3.999
X-3.999
Y-3.999
X6.999
Y6.999
X-6.999
Y-6.999
X9.999
Y9.999
X-9.999
Y-9.999
X12.999
Y12.999
X-12.999
Y-12.999
X15.999
Y15.999
X-15.999
Y-15.999
X18.999
Y18.999
X-18.999
Y-18.999
X21.999
Y21.999
X-21.999
Y-21.999
X24.999
Y24.999
X-24.999
Y-24.999
X27.999
Y27.999
X-27.999
Y-27.999
X30.999
Y30.999
X-30.999
Y-30.999
X33.999
Y33.999
X-33.999
Y-33.999
X36.999
Y36.999
X-36.999
Y-36.999
X39.999
Y39.999
X-39.999
Y-39.999
X42.999
Y42.999
X-42.999
Y-42.999
X45.999
Y45.999
X-45.999
Y-45.999
X48.999
Y47.5
G0Z10
X47.5Y48.999
G1Z-6.912F500
X-47.5F1000
G0Z10
X-48.999Y47.5
G1Z-6.912F500
Y-47.5F1000
G0Z10
X-47.5Y-48.999
G1Z-6.912F500
X47.5F1000
G0Z10
X51.999Y-44.5
G1Z-6.912F500
Y44.5F1000
G0Z10
X44.5Y51.999
G1Z-6.912F500
X-44.5F1000
G0Z10
X-51.999Y44.5
G1Z-6.912F500
Y-44.5F1000
G0Z10
X-44.5Y-51.999
G1Z-6.912F500
X44.5F1000
G0Z10
X54.999Y-40.5
G1Z-6.912F500
Y40.5F1000
G0Z10
X40.5Y54.999
G1Z-6.912F500
X-40.5F1000
G0Z10
X-54.999Y40.5
G1Z-6.912F500
Y-40.5F1000
G0Z10
X-40.5Y-54.999
G1Z-6.912F500
X40.5F1000
G0Z10
X57.999Y-36
G1Z-6.912F500
Y36F1000
G0Z10
X36Y57.999
G1Z-6.912F500
X-36F1000
G0Z10
X-57.999Y36
G1Z-6.912F500
Y-36F1000
G0Z10
X-36Y-57.999
G1Z-6.912F500
X36F1000
G0Z10
X60.999Y-31
G1Z-6.912F500
Y31F1000
G0Z10
X31Y60.999
G1Z-6.912F500
X-31F1000
G0Z10
X-60.999Y31
G1Z-6.912F500
Y-31F1000
G0Z10
X-31Y-60.999
G1Z-6.912F500
X31F1000
G0Z10
X63.999Y-24
G1Z-6.912F500
Y24F1000
G0Z10
X24Y63.999
G1Z-6.912F500
X-24F1000
G0Z10
X-63.999Y24
G1Z-6.912F500
Y-24F1000
G0Z10
X-24Y-63.999
G1Z-6.912F500
X24F1000
G0Z10
X67Y-14
G1Z-6.912F500
Y14F1000
G0Z10
X14Y67
G1Z-6.912F500
X-14F1000
G0Z10
X-67Y14
G1Z-6.912F500
Y-14F1000
G0Z10
X-14Y-67
G1Z-6.912F500
X14F1000
G0Z10

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,727 @@
(Created with grbl post processor 2024/01/26 13:10)
G21
(G-code generated with BlenderCAM and NC library)
G17G90
(Tool: D = 3.0 mm type END flutes 2)
S12000M03
G00 Z10.0
G0X0Y0Z10
X100.999Y102
G1Z-0.1F500
Y100.999Z-0.677
X100.354Z-1.05F1000
X100.999Z-1.422
Y102Z-2F500
Y100.999F1000
X99
Y99
X103.999
Y103.999
X98.999
X98.733Z-5.1F500
X96F1000
Y98.246
Y98Z-2.501
Y97.5Z-2
Y96
X106.999
Y106.999
X98.999
X98.733Z-5.1F500
X93F1000
Y95.27
Y95Z-2
Y93
X109.999
Y109.999
X98.499
X98.233Z-5.1F500
X90F1000
Y94.273
Y94Z-2
Y90
X112.999
Y98
Y98.255Z-5.1F500
Y112.999F1000
X112.51
X111.999Z-2
X96.999
X96.732Z-5.1F500
X87F1000
Y93.775
Y93.5Z-2
Y87
X115.999
Y95.5
Y95.752Z-5.1F500
Y115.999F1000
X115.505
X115Z-2
X93.999
X93.729Z-5.1F500
X84F1000
Y93.778
Y93.5Z-2
Y84
X118.999
Y94
Y94.25Z-5.1F500
Y117.749F1000
Y118Z-2
Y118.999
X81
Y117.5
Y117.218Z-5.1F500
Y94.781F1000
Y94.5Z-2
Y81
X121.999
Y93.5
Y93.748Z-5.1F500
Y118.251F1000
Y118.5Z-2
Y121.999
X78
Y115.5
Y115.216Z-5.1F500
Y96.783F1000
Y96.5Z-2
Y78
X125
Y93.5
Y93.746Z-5.1F500
Y118.253F1000
Y118.5Z-2
Y125
X75
Y111.5
Y111.213Z-5.1F500
Y100.786F1000
Y100.5Z-2
Y75
X127.999
Y94.5
Y94.744Z-5.1F500
Y117.255F1000
Y117.5Z-2
Y127.999
X72
Y72
X130.999
Y96.5
Y96.742Z-5.1F500
Y115.257F1000
Y115.5Z-2
Y130.999
X69
Y69
X134
Y100
Y100.5Z-3.952F500
Y100.627Z-5.1
Y111.372F1000
Y111.5Z-3.951
Y112Z-2
Y134
X66
Y66
X136.999
Y136.999
X63
Y63
X140
Y140
X60
Y113.5
Y113.198Z-5.1F500
Y98.801F1000
Y98.5Z-2
Y60
X78.499
X78.782Z-5.1F500
X93.729F1000
X93.999Z-2
X115
X115.253Z-5.1F500
X130.256F1000
X130.5Z-2
X142.999
Y142.999
X57
Y116.5
Y116.195Z-5.1F500
Y95.804F1000
Y95.5Z-2
Y60.5
Y60.195Z-5.1F500
Y57F1000
X60.198
X60.499Z-2
X75.999
X76.285Z-5.1F500
X96.732F1000
X96.999Z-2
X111.999
X112.255Z-5.1F500
X133.258F1000
X133.499Z-2
X145.999
Y145.999
X54
Y118
Y117.692Z-5.1F500
Y94.307F1000
Y94Z-2
Y62
Y61.692Z-5.1F500
Y54F1000
X61.7
X61.999Z-2
X74.499
X74.786Z-5.1F500
X98.233F1000
X98.499Z-2
X110.5
X110.756Z-5.1F500
X134.759F1000
X135Z-2
X147
X147.232Z-5.1F500
X149F1000
Y57.268
Y57.5Z-2
Y98.5
Y98.731Z-5.1F500
Y113.268F1000
Y113.5Z-2
Y149
X51
Y118.5
Y118.189Z-5.1F500
Y93.81F1000
Y93.5Z-2
Y62.5
Y62.189Z-5.1F500
Y51F1000
X62.201
X62.499Z-2
X73.5
X73.787Z-5.1F500
X98.733F1000
X98.999Z-2
X109.999
X110.257Z-5.1F500
X135.26F1000
X135.499Z-2
X146.5
X146.732Z-5.1F500
X151.999F1000
Y60.27
Y60.5Z-2
Y95.5
Y95.729Z-5.1F500
Y116.27F1000
Y116.5Z-2
Y151.5
Y151.729Z-5.1F500
Y151.999F1000
X151.458
X151Z-2
X130.5
X130.256Z-5.1F500
X115.253F1000
X115Z-2
X93.999
X93.729Z-5.1F500
X78.782F1000
X78.499Z-2
X57.499
X57.195Z-5.1F500
X48F1000
Y149.813
Y149.5Z-2
Y118.5
Y118.186Z-5.1F500
Y93.813F1000
Y93.5Z-2
Y62.5
Y62.186Z-5.1F500
Y48F1000
X62.201
X62.499Z-2
X73.999
X74.286Z-5.1F500
X98.733F1000
X98.999Z-2
X109.999
X110.257Z-5.1F500
X135.26F1000
X135.499Z-2
X146.5
X146.732Z-5.1F500
X155F1000
Y61.772
Y62Z-2
Y94
Y94.227Z-5.1F500
Y117.772F1000
Y118Z-2
Y150
Y150.227Z-5.1F500
Y155F1000
X148.731
X148.499Z-2
X133.499
X133.258Z-5.1F500
X112.255F1000
X111.999Z-2
X96.999
X96.732Z-5.1F500
X76.285F1000
X75.999Z-2
X60.499
X60.198Z-5.1F500
X45F1000
Y150.817
Y150.5Z-2
Y117.5
Y117.182Z-5.1F500
Y94.817F1000
Y94.5Z-2
Y61.5
Y61.182Z-5.1F500
Y45F1000
X61.199
X61.499Z-2
X74.499
X74.786Z-5.1F500
X97.732F1000
X97.999Z-2
X111
X111.256Z-5.1F500
X134.259F1000
X134.499Z-2
X147.499
X147.731Z-5.1F500
X157.999F1000
Y62.274
Y62.5Z-2
Y93.5
Y93.725Z-5.1F500
Y118.274F1000
Y118.5Z-2
Y149.5
Y149.725Z-5.1F500
Y157.999F1000
X147.232
X147Z-2
X135
X134.759Z-5.1F500
X110.756F1000
X110.5Z-2
X98.499
X98.233Z-5.1F500
X74.786F1000
X74.499Z-2
X61.999
X61.7Z-5.1F500
X42F1000
Y152.32
Y152Z-2
Y116
Y115.679Z-5.1F500
Y96.32F1000
Y96Z-2
Y60
Y59.679Z-5.1F500
Y42F1000
X59.698
X59.999Z-2
X76.499
X76.784Z-5.1F500
X95.753F1000
X96Z-2.502
X96.499Z-2
X112.999
X113.254Z-5.1F500
X132.258F1000
X132.499Z-2
X149.499
X149.73Z-5.1F500
X160.999F1000
Y62.275
Y62.5Z-2
Y93.5
Y93.724Z-5.1F500
Y118.275F1000
Y118.5Z-2
Y149.5
Y149.724Z-5.1F500
Y160.999F1000
X146.732
X146.5Z-2
X135.499
X135.26Z-5.1F500
X110.257F1000
X109.999Z-2
X98.999
X98.733Z-5.1F500
X73.787F1000
X73.5Z-2
X62.499
X62.201Z-5.1F500
X39F1000
Y156.324
Y156Z-2
Y112
Y111.675Z-5.1F500
Y100.324F1000
Y100Z-2
Y56
Y55.675Z-5.1F500
Y44.324F1000
Y44Z-2
Y39
X43.999
X44.317Z-5.1F500
X55.694F1000
X55.999Z-2
X79.999
X80.281Z-5.1F500
X92.228F1000
X92.5Z-2
X116.499
X116.752Z-5.1F500
X128.755F1000
X128.999Z-2
X152.999
X153.228Z-5.1F500
X164F1000
Y61.752
Y62Z-2
Y94
Y94.247Z-5.1F500
Y117.752F1000
Y118Z-2
Y150
Y150.247Z-5.1F500
Y164F1000
X146.732
X146.5Z-2
X135.499
X135.26Z-5.1F500
X110.257F1000
X109.999Z-2
X98.999
X98.733Z-5.1F500
X74.286F1000
X73.999Z-2
X62.499
X62.201Z-5.1F500
X37.825F1000
X37.499Z-2
X36
Y36
X166.999
Y40
Y40.22Z-5.1F500
Y59.779F1000
Y60Z-2
Y96
Y96.22Z-5.1F500
Y115.779F1000
Y116Z-2
Y152
Y152.22Z-5.1F500
Y166.999F1000
X147.731
X147.499Z-2
X134.499
X134.259Z-5.1F500
X111.256F1000
X111Z-2
X97.999
X97.732Z-5.1F500
X74.786F1000
X74.499Z-2
X61.499
X61.199Z-5.1F500
X38.824F1000
X38.499Z-2
X33
Y33
X170
Y43.5
Y43.719Z-5.1F500
Y56.28F1000
Y56.5Z-2
Y99.5
Y99.719Z-5.1F500
Y112.28F1000
Y112.5Z-2
Y155.5
Y155.719Z-5.1F500
Y168.28F1000
Y168.5Z-2
Y170
X169
X168.779Z-5.1F500
X149.73F1000
X149.499Z-2
X132.499
X132.258Z-5.1F500
X113.254F1000
X112.999Z-2
X96.499
X96Z-2.502F500
X95.753Z-5.1
X76.784F1000
X76.499Z-2
X59.999
X59.698Z-5.1F500
X40.322F1000
X39.999Z-2
X30
Y30
X173
Y173
X165.5
X165.278Z-5.1F500
X153.228F1000
X152.999Z-2
X128.999
X128.755Z-5.1F500
X116.752F1000
X116.499Z-2
X92.5
X92.228Z-5.1F500
X80.281F1000
X79.999Z-2
X55.999
X55.694Z-5.1F500
X44.317F1000
X43.999Z-2
X27
Y27
X175.999
Y175.999
X24
Y24
X179
Y179
X21
Y21
X181.999
Y181.999
X18
Y18
X185
Y185
X15
Y15
X188
Y188
X12
Y12
X191
Y191
X9
Y9
X194
Y194
X6
Y6
X196.999
Y196.999
X3
Y3
X198.5
X196.854Z-1.05
X198.5Z-0.1
G0Z10
X96.732Y112.999
G1Z-5.1F500
X96.499Z-7.786
X87Z-7.62F1000
Y94
Y93.775Z-5.1
Y98.105Z-2.6
Y93.775Z-0.1
G0Z10
X125Y93.746
G1Z-5.1F500
Y94Z-8.283
Y118F1000
Y118.253Z-5.1
Y113.923Z-2.6
Y118.253Z-0.1
G0Z10
X134Y100.627
G1Z-5.1F500
Y101Z-8.44
Y111F1000
Y111.372Z-5.1
Y107.041Z-2.6
Y111.372Z-0.1
G0Z10
X60Y113.198
G1Z-5.1F500
Y113Z-7.149
Y99F1000
Y98.801Z-5.1
Y103.131Z-2.6
Y98.801Z-0.1
G0Z10
X76.285Y57
G1Z-5.1F500
X76.499Z-7.437
X96.499Z-7.786F1000
X96.732Z-5.1
X92.401Z-2.6
X96.732Z-0.1
G0Z10
X112.255
G1Z-5.1F500
X112.499Z-8.065
X133Z-8.423F1000
X133.258Z-5.1
X128.928Z-2.6
X133.258Z-0.1
G0Z10
X149Y98.731
G1Z-5.1F500
Y99Z-8.702
Y113F1000
Y113.268Z-5.1
Y108.938Z-2.6
Y113.268Z-0.1
G0Z10
X48Y118.186
G1Z-5.1F500
Y118Z-6.939
Y94F1000
Y93.813Z-5.1
Y98.143Z-2.6
Y93.813Z-0.1
G0Z10
Y62.186
G1Z-5.1F500
Y62Z-6.939
Y48F1000
X61.999Z-7.184
X62.201Z-5.1
X57.87Z-2.6
X62.201Z-0.1
G0Z10
X96.732Y155
G1Z-5.1F500
X96.499Z-7.786
X76.499Z-7.437F1000
X76.285Z-5.1
X80.615Z-2.6
X76.285Z-0.1
G0Z10
X60.198
G1Z-5.1F500
X59.999Z-7.149
X45Z-6.887F1000
Y151
Y150.817Z-5.1
Y155Z-2.685
X45.147Z-2.6
X45Z-2.514
Y150.817Z-0.1
G0Z10
Y117.182
G1Z-5.1F500
Y117Z-6.887
Y95F1000
Y94.817Z-5.1
Y99.147Z-2.6
Y94.817Z-0.1
G0Z10
Y61.182
G1Z-5.1F500
Y61Z-6.887
Y45F1000
X60.999Z-7.166
X61.199Z-5.1
X56.869Z-2.6
X61.199Z-0.1
G0Z10
X147.731
G1Z-5.1F500
X148Z-8.685
X157.999Z-8.859F1000
Y62
Y62.274Z-5.1
Y57.943Z-2.6
Y62.274Z-0.1
G0Z10
X80.281Y39
G1Z-5.1F500
X80.499Z-7.506
X92Z-7.707F1000
X92.228Z-5.1
X87.898Z-2.6
X92.228Z-0.1
G0Z10
X97.732Y166.999
G1Z-5.1F500
X97.499Z-7.803
X74.999Z-7.41F1000
X74.786Z-5.1
X79.116Z-2.6
X74.786Z-0.1
G0Z10
X61.199
G1Z-5.1F500
X60.999Z-7.166
X38.999Z-6.782F1000
X38.824Z-5.1
X43.154Z-2.6
X38.824Z-0.1
G0Z10
X170Y43.719
G1Z-5.1F500
Y44Z-9.069
Y56F1000
Y56.28Z-5.1
Y51.95Z-2.6
Y56.28Z-0.1
G0Z10
Y99.719
G1Z-5.1F500
Y100Z-9.069
Y112F1000
Y112.28Z-5.1
Y107.95Z-2.6
Y112.28Z-0.1
G0Z10
Y155.719
G1Z-5.1F500
Y156Z-9.069
Y168F1000
Y168.28Z-5.1
Y163.95Z-2.6
Y168.28Z-0.1
G0Z10
X132.258Y170
G1Z-5.1F500
X131.999Z-8.405
X113.499Z-8.082F1000
X113.254Z-5.1
X117.584Z-2.6
X113.254Z-0.1
G0Z10
X59.698
G1Z-5.1F500
X59.499Z-7.14
X40.499Z-6.808F1000
X40.322Z-5.1
X44.652Z-2.6
X40.322Z-0.1
G0Z10

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -1,4 +1,4 @@
(Created with grbl post processor 2024/01/13 06:54)
(Created with grbl post processor 2024/01/26 12:15)
G21
(G-code generated with BlenderCAM and NC library)
G17G90
@ -976,7 +976,6 @@ G0Z10
X-18.613Y-50.677
G1Z-5F500
X-18.091Y-50.524F1000
X-18.063Y-50.517
X-17.786Y-50.454
X-17.806Y-50.438
X-17.858Y-50.408
@ -1105,7 +1104,6 @@ G0Z10
X67.823Y80.439
G1Z-5F500
X69.398Y79.084F1000
X69.414Y79.07
X70.808Y77.65
X70.823Y77.632
X72.028Y76.153
@ -1129,7 +1127,7 @@ X74.3Y56.047
X73.85Y54.026
X73.341Y52.119
X72.773Y50.326
X72.15Y48.66
X72.144Y48.647
X71.463Y47.096
X71.457Y47.082
X71.45Y47.068
@ -1192,12 +1190,10 @@ X49.017Y-15.151
X49.005Y-15.166
X48.994Y-15.18
X46.245Y-18.424
X43.073Y-21.715
X43.052Y-21.735
X43.063Y-21.725
X39.491Y-25.031
X35.52Y-28.356
X31.149Y-31.698
X31.124Y-31.715
X29.598Y-32.7
X27.959Y-33.789
X26.202Y-34.986

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -51,7 +51,8 @@ class BlenderCAMTest(unittest.TestCase):
return ''.join(list(diff)[:num_lines])
def execute_blender(self, blend_file):
command = f'blender -b "{blend_file}" -P "{self.generator_path}"'
command = f'blender -noaudio -b "{blend_file}" -P "{self.generator_path}"'
print(f"Executing: {command}")
subprocess.run(command, shell=True, check=True)
def run_test_case(self, test_case):

Wyświetl plik

@ -164,7 +164,7 @@ class CAM_MOVEMENT_Panel(CAMButtonsPanel, bpy.types.Panel):
self.layout.prop(self.op.movement, 'ramp_in_angle')
self.layout.prop(self.op.movement, 'ramp_out')
if self.op.movement.ramp_out:
self.layout.prop(self.movement, 'ramp_out_angle')
self.layout.prop(self.op.movement, 'ramp_out_angle')
def draw_retract_tangential(self):
if not self.has_correct_level(): return

Wyświetl plik

@ -362,22 +362,22 @@ def getBoundsMultiple(operations):
def samplePathLow(o, ch1, ch2, dosample):
v1 = Vector(ch1.points[-1])
v2 = Vector(ch2.points[0])
v1 = Vector(ch1.get_point(-1))
v2 = Vector(ch2.get_point(0))
v = v2 - v1
d = v.length
v.normalize()
vref = Vector((0, 0, 0))
bpath = camPathChunk([])
bpath_points = []
i = 0
while vref.length < d:
i += 1
vref = v * o.dist_along_paths * i
if vref.length < d:
p = v1 + vref
bpath.points.append([p.x, p.y, p.z])
bpath_points.append([p.x, p.y, p.z])
# print('between path')
# print(len(bpath))
pixsize = o.optimisation.pixsize
@ -389,18 +389,18 @@ def samplePathLow(o, ch1, ch2, dosample):
o.update_bullet_collision_tag = False
cutterdepth = o.cutter_shape.dimensions.z / 2
for p in bpath.points:
for p in bpath_points:
z = getSampleBullet(o.cutter_shape, p[0], p[1], cutterdepth, 1, o.minz)
if z > p[2]:
p[2] = z
else:
for p in bpath.points:
for p in bpath_points:
xs = (p[0] - o.min.x) / pixsize + o.borderwidth + pixsize / 2 # -m
ys = (p[1] - o.min.y) / pixsize + o.borderwidth + pixsize / 2 # -m
z = getSampleImage((xs, ys), o.offset_image, o.minz) + o.skin
if z > p[2]:
p[2] = z
return bpath
return camPathChunk(bpath_points)
# def threadedSampling():#not really possible at all without running more blenders for same operation :( python!
@ -424,7 +424,7 @@ async def sampleChunks(o, pathSamples, layers):
cutterdepth = cutter.dimensions.z / 2
else:
if o.strategy != 'WATERLINE': # or prepare offset image, but not in some strategies.
prepareArea(o)
await prepareArea(o)
pixsize = o.optimisation.pixsize
@ -438,7 +438,7 @@ async def sampleChunks(o, pathSamples, layers):
totlen = 0 # total length of all chunks, to estimate sampling time.
for ch in pathSamples:
totlen += len(ch.points)
totlen += ch.count()
layerchunks = []
minz = o.minz - 0.000001 # correction for image method problems
layeractivechunks = []
@ -446,7 +446,7 @@ async def sampleChunks(o, pathSamples, layers):
for l in layers:
layerchunks.append([])
layeractivechunks.append(camPathChunk([]))
layeractivechunks.append(camPathChunkBuilder([]))
lastrunchunks.append([])
zinvert = 0
@ -473,7 +473,7 @@ async def sampleChunks(o, pathSamples, layers):
# for t in range(0,threads):
for s in patternchunk.points:
for s in patternchunk.get_points():
if o.strategy != 'WATERLINE' and int(100 * n / totlen) != last_percent:
last_percent = int(100 * n / totlen)
await progress_async('sampling paths ', last_percent)
@ -583,17 +583,20 @@ async def sampleChunks(o, pathSamples, layers):
if terminatechunk:
if len(ch.points) > 0:
layerchunks[i].append(ch)
thisrunchunks[i].append(ch)
layeractivechunks[i] = camPathChunk([])
as_chunk=ch.to_chunk()
layerchunks[i].append(as_chunk)
thisrunchunks[i].append(as_chunk)
layeractivechunks[i] = camPathChunkBuilder([])
lastsample = newsample
for i, l in enumerate(layers):
ch = layeractivechunks[i]
if len(ch.points) > 0:
layerchunks[i].append(ch)
thisrunchunks[i].append(ch)
layeractivechunks[i] = camPathChunk([])
as_chunk=ch.to_chunk()
layerchunks[i].append(as_chunk)
thisrunchunks[i].append(as_chunk)
layeractivechunks[i] = camPathChunkBuilder([])
# PARENTING
if o.strategy == 'PARALLEL' or o.strategy == 'CROSS' or o.strategy == 'OUTLINEFILL':
@ -664,7 +667,7 @@ async def sampleChunksNAxis(o, pathSamples, layers):
for l in layers:
layerchunks.append([])
layeractivechunks.append(camPathChunk([]))
layeractivechunks.append(camPathChunkBuilder([]))
lastrunchunks.append([])
n = 0
@ -826,12 +829,18 @@ async def sampleChunksNAxis(o, pathSamples, layers):
laststartpoint = startp
lastendpoint = endp
# convert everything to actual chunks
# rather than chunkBuilders
for i, l in enumerate(layers):
layeractivechunks[i]=layeractivechunks[i].to_chunk() if layeractivechunks[i] is not None else None
for i, l in enumerate(layers):
ch = layeractivechunks[i]
if len(ch.points) > 0:
layerchunks[i].append(ch)
thisrunchunks[i].append(ch)
layeractivechunks[i] = camPathChunk([])
layeractivechunks[i] = None
if o.strategy == 'PARALLEL' or o.strategy == 'CROSS' or o.strategy == 'OUTLINEFILL':
parentChildDist(thisrunchunks[i], lastrunchunks[i], o)
@ -1021,7 +1030,7 @@ async def connectChunksLow(chunks, o):
pos = (0, 0, 0)
for ch in chunks:
if len(ch.points) > 0:
if ch.count() > 0:
if lastch is not None and (ch.distStart(pos, o) < mergedist):
# CARVE should lift allways, when it goes below surface...
# print(mergedist,ch.dist(pos,o))
@ -1035,16 +1044,16 @@ async def connectChunksLow(chunks, o):
if o.optimisation.use_opencamlib and o.optimisation.use_exact and (
o.strategy == 'PARALLEL' or o.strategy == 'CROSS' or o.strategy == 'PENCIL'):
chunks_to_resample.append(
(connectedchunks[-1], len(connectedchunks[-1].points), len(between.points)))
(connectedchunks[-1], connectedchunks[-1].count(), between.count()))
connectedchunks[-1].points.extend(between.points)
connectedchunks[-1].points.extend(ch.points)
connectedchunks[-1].extend(between.get_points_np())
connectedchunks[-1].extend(ch.get_points_np())
else:
connectedchunks.append(ch)
lastch = ch
pos = lastch.points[-1]
pos = lastch.get_point(-1)
if o.optimisation.use_opencamlib and o.optimisation.use_exact and o.strategy != 'CUTOUT' and o.strategy != 'POCKET':
if o.optimisation.use_opencamlib and o.optimisation.use_exact and o.strategy != 'CUTOUT' and o.strategy != 'POCKET' and o.strategy != 'WATERLINE':
await oclResampleChunks(o, chunks_to_resample,use_cached_mesh=True)
return connectedchunks
@ -1069,7 +1078,7 @@ def getClosest(o, pos, chunks):
return ch
async def sortChunks(chunks, o):
async def sortChunks(chunks, o,last_pos=None):
if o.strategy != 'WATERLINE':
await progress_async('sorting paths')
sys.setrecursionlimit(100000) # the getNext() function of CamPathChunk was running out of recursion limits.
@ -1080,7 +1089,7 @@ async def sortChunks(chunks, o):
last_progress_time=time.time()
total= len(chunks)
i = len(chunks)
pos = (0, 0, 0)
pos = (0, 0, 0) if last_pos is None else last_pos
while len(chunks) > 0:
if o.strategy != 'WATERLINE' and time.time()-last_progress_time>0.1:
await progress_async("Sorting paths",100.0*(total-len(chunks))/total)
@ -1106,7 +1115,7 @@ async def sortChunks(chunks, o):
chunks.remove(ch)
sortedchunks.append(ch)
lastch = ch
pos = lastch.points[-1]
pos = lastch.get_point(-1)
# print(i, len(chunks))
# experimental fix for infinite loop problem
# else: