kopia lustrzana https://github.com/vilemduha/blendercam
614 wiersze
22 KiB
Python
614 wiersze
22 KiB
Python
![]() |
"""Fabex 'pattern.py' © 2012 Vilem Novak
|
||
![]() |
|
||
|
Functions to read CAM path patterns and return CAM path chunks.
|
||
|
"""
|
||
|
|
||
![]() |
from math import (
|
||
|
ceil,
|
||
|
floor,
|
||
|
pi,
|
||
|
sqrt,
|
||
|
)
|
||
![]() |
import time
|
||
|
|
||
![]() |
import numpy
|
||
|
|
||
![]() |
import bpy
|
||
![]() |
from mathutils import Euler, Vector
|
||
![]() |
|
||
|
from .cam_chunk import (
|
||
![]() |
CamPathChunk,
|
||
|
CamPathChunkBuilder,
|
||
![]() |
shapely_to_chunks,
|
||
|
)
|
||
![]() |
|
||
![]() |
from .utilities.chunk_utils import (
|
||
![]() |
chunks_refine,
|
||
|
parent_child_distance,
|
||
![]() |
)
|
||
![]() |
from .utilities.simple_utils import progress
|
||
![]() |
|
||
![]() |
|
||
![]() |
def get_path_pattern_parallel(o, angle):
|
||
![]() |
"""Generate path chunks for parallel movement based on object dimensions
|
||
|
and angle.
|
||
|
|
||
|
This function calculates a series of path chunks for a given object,
|
||
|
taking into account its dimensions and the specified angle. It utilizes
|
||
|
both a traditional method and an alternative algorithm (currently
|
||
|
disabled) to generate these paths. The paths are constructed by
|
||
|
iterating over calculated vectors and applying transformations based on
|
||
|
the object's properties. The resulting path chunks can be used for
|
||
|
various movement types, including conventional and climb movements.
|
||
|
|
||
|
Args:
|
||
|
o (object): An object containing properties such as dimensions and movement type.
|
||
|
angle (float): The angle to rotate the path generation.
|
||
|
|
||
|
Returns:
|
||
|
list: A list of path chunks generated based on the object's dimensions and
|
||
|
angle.
|
||
|
"""
|
||
|
|
||
![]() |
zlevel = 1
|
||
![]() |
pathd = o.distance_between_paths
|
||
|
pathstep = o.distance_along_paths
|
||
![]() |
pathchunks = []
|
||
|
|
||
|
xm = (o.max.x + o.min.x) / 2
|
||
|
ym = (o.max.y + o.min.y) / 2
|
||
|
vm = Vector((xm, ym, 0))
|
||
|
xdim = o.max.x - o.min.x
|
||
|
ydim = o.max.y - o.min.y
|
||
|
dim = (xdim + ydim) / 2.0
|
||
|
e = Euler((0, 0, angle))
|
||
|
reverse = False
|
||
|
if bpy.app.debug_value == 0: # by default off
|
||
|
# this is the original pattern method, slower, but well tested:
|
||
|
dirvect = Vector((0, 1, 0))
|
||
|
dirvect.rotate(e)
|
||
|
dirvect.normalize()
|
||
|
dirvect *= pathstep
|
||
![]() |
for a in range(
|
||
|
int(-dim / pathd), int(dim / pathd)
|
||
|
): # this is highly ineffective, computes path2x the area needed...
|
||
![]() |
chunk = CamPathChunkBuilder([])
|
||
![]() |
v = Vector((a * pathd, int(-dim / pathstep) * pathstep, 0))
|
||
|
v.rotate(e)
|
||
![]() |
# shifting for the rotation, so pattern rotates around middle...
|
||
|
v += vm
|
||
![]() |
for b in range(int(-dim / pathstep), int(dim / pathstep)):
|
||
|
v += dirvect
|
||
|
|
||
|
if v.x > o.min.x and v.x < o.max.x and v.y > o.min.y and v.y < o.max.y:
|
||
|
chunk.points.append((v.x, v.y, zlevel))
|
||
![]() |
if (
|
||
|
(reverse and o.movement.type == "MEANDER")
|
||
|
or (o.movement.type == "CONVENTIONAL" and o.movement.spindle_rotation == "CW")
|
||
|
or (o.movement.type == "CLIMB" and o.movement.spindle_rotation == "CCW")
|
||
|
):
|
||
![]() |
chunk.points.reverse()
|
||
|
|
||
|
if len(chunk.points) > 0:
|
||
![]() |
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].reverse()
|
||
![]() |
changechunk = pathchunks[-1]
|
||
|
pathchunks[-1] = pathchunks[-2]
|
||
|
pathchunks[-2] = changechunk
|
||
|
|
||
|
reverse = not reverse
|
||
|
# print (chunk.points)
|
||
|
else: # alternative algorithm with numpy, didn't work as should so blocked now...
|
||
|
v = Vector((0, 1, 0))
|
||
|
v.rotate(e)
|
||
![]() |
e1 = Euler((0, 0, -pi / 2))
|
||
![]() |
v1 = v.copy()
|
||
|
v1.rotate(e1)
|
||
|
|
||
![]() |
axis_across_paths = numpy.array(
|
||
|
(
|
||
|
numpy.arange(int(-dim / pathd), int(dim / pathd)) * pathd * v1.x + xm,
|
||
|
numpy.arange(int(-dim / pathd), int(dim / pathd)) * pathd * v1.y + ym,
|
||
|
numpy.arange(int(-dim / pathd), int(dim / pathd)) * 0,
|
||
|
)
|
||
|
)
|
||
|
|
||
|
axis_along_paths = numpy.array(
|
||
|
(
|
||
|
numpy.arange(int(-dim / pathstep), int(dim / pathstep)) * pathstep * v.x,
|
||
|
numpy.arange(int(-dim / pathstep), int(dim / pathstep)) * pathstep * v.y,
|
||
|
numpy.arange(int(-dim / pathstep), int(dim / pathstep)) * 0 + zlevel,
|
||
|
)
|
||
|
) # rotate this first
|
||
![]() |
progress(axis_along_paths)
|
||
|
chunks = []
|
||
|
for a in range(0, len(axis_across_paths[0])):
|
||
|
# progress(chunks[a,...,...].shape)
|
||
|
# progress(axis_along_paths.shape)
|
||
|
nax = axis_along_paths.copy()
|
||
|
# progress(nax.shape)
|
||
|
nax[0] += axis_across_paths[0][a]
|
||
|
nax[1] += axis_across_paths[1][a]
|
||
|
# progress(a)
|
||
|
# progress(nax.shape)
|
||
|
# progress(chunks.shape)
|
||
|
# progress(chunks[...,a,...].shape)
|
||
|
xfitmin = nax[0] > o.min.x
|
||
|
xfitmax = nax[0] < o.max.x
|
||
|
xfit = xfitmin & xfitmax
|
||
|
# print(xfit,nax)
|
||
|
nax = numpy.array([nax[0][xfit], nax[1][xfit], nax[2][xfit]])
|
||
|
yfitmin = nax[1] > o.min.y
|
||
|
yfitmax = nax[1] < o.max.y
|
||
|
yfit = yfitmin & yfitmax
|
||
|
nax = numpy.array([nax[0][yfit], nax[1][yfit], nax[2][yfit]])
|
||
|
chunks.append(nax.swapaxes(0, 1))
|
||
|
# chunks
|
||
|
pathchunks = []
|
||
|
for ch in chunks:
|
||
|
ch = ch.tolist()
|
||
![]() |
pathchunks.append(CamPathChunk(ch))
|
||
![]() |
# print (ch)
|
||
|
return pathchunks
|
||
![]() |
|
||
|
|
||
![]() |
def get_path_pattern(operation):
|
||
![]() |
"""Generate a path pattern based on the specified operation strategy.
|
||
|
|
||
|
This function constructs a path pattern for a given operation by
|
||
|
analyzing its parameters and applying different strategies such as
|
||
|
'PARALLEL', 'CROSS', 'BLOCK', 'SPIRAL', 'CIRCLES', and 'OUTLINEFILL'.
|
||
|
Each strategy dictates how the path is built, utilizing various
|
||
|
geometric calculations and conditions to ensure the path adheres to the
|
||
|
specified operational constraints. The function also handles the
|
||
|
orientation and direction of the path based on the movement settings
|
||
|
provided in the operation.
|
||
|
|
||
|
Args:
|
||
|
operation (object): An object containing parameters for path generation,
|
||
|
including strategy, movement type, and geometric bounds.
|
||
|
|
||
|
Returns:
|
||
|
list: A list of path chunks representing the generated path pattern.
|
||
|
"""
|
||
|
|
||
![]() |
o = operation
|
||
|
t = time.time()
|
||
![]() |
progress("Building Path Pattern")
|
||
![]() |
minx, miny, minz, maxx, maxy, maxz = o.min.x, o.min.y, o.min.z, o.max.x, o.max.y, o.max.z
|
||
|
|
||
|
pathchunks = []
|
||
|
|
||
|
zlevel = 1 # minz#this should do layers...
|
||
![]() |
if o.strategy == "PARALLEL":
|
||
![]() |
pathchunks = get_path_pattern_parallel(o, o.parallel_angle)
|
||
![]() |
elif o.strategy == "CROSS":
|
||
![]() |
pathchunks.extend(get_path_pattern_parallel(o, o.parallel_angle))
|
||
|
pathchunks.extend(get_path_pattern_parallel(o, o.parallel_angle - pi / 2.0))
|
||
![]() |
|
||
![]() |
elif o.strategy == "BLOCK":
|
||
![]() |
pathd = o.distance_between_paths
|
||
|
pathstep = o.distance_along_paths
|
||
![]() |
maxxp = maxx
|
||
|
maxyp = maxy
|
||
|
minxp = minx
|
||
|
minyp = miny
|
||
|
x = 0.0
|
||
|
y = 0.0
|
||
|
incx = 1
|
||
|
incy = 0
|
||
![]() |
chunk = CamPathChunkBuilder([])
|
||
![]() |
i = 0
|
||
|
while maxxp - minxp > 0 and maxyp - minyp > 0:
|
||
|
y = minyp
|
||
|
for a in range(ceil(minxp / pathstep), ceil(maxxp / pathstep), 1):
|
||
|
x = a * pathstep
|
||
|
chunk.points.append((x, y, zlevel))
|
||
|
|
||
|
if i > 0:
|
||
|
minxp += pathd
|
||
|
chunk.points.append((maxxp, minyp, zlevel))
|
||
|
|
||
|
x = maxxp
|
||
|
|
||
|
for a in range(ceil(minyp / pathstep), ceil(maxyp / pathstep), 1):
|
||
|
y = a * pathstep
|
||
|
chunk.points.append((x, y, zlevel))
|
||
|
|
||
|
minyp += pathd
|
||
|
chunk.points.append((maxxp, maxyp, zlevel))
|
||
|
|
||
|
y = maxyp
|
||
|
for a in range(floor(maxxp / pathstep), ceil(minxp / pathstep), -1):
|
||
|
x = a * pathstep
|
||
|
chunk.points.append((x, y, zlevel))
|
||
|
|
||
|
maxxp -= pathd
|
||
|
chunk.points.append((minxp, maxyp, zlevel))
|
||
|
|
||
|
x = minxp
|
||
|
for a in range(floor(maxyp / pathstep), ceil(minyp / pathstep), -1):
|
||
|
y = a * pathstep
|
||
|
chunk.points.append((x, y, zlevel))
|
||
|
chunk.points.append((minxp, minyp, zlevel))
|
||
|
|
||
|
maxyp -= pathd
|
||
|
|
||
|
i += 1
|
||
![]() |
if o.movement.insideout == "INSIDEOUT":
|
||
![]() |
chunk.points.reverse()
|
||
![]() |
if (o.movement.type == "CLIMB" and o.movement.spindle_rotation == "CW") or (
|
||
|
o.movement.type == "CONVENTIONAL" 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])
|
||
![]() |
pathchunks = [chunk.to_chunk()]
|
||
![]() |
|
||
![]() |
elif o.strategy == "SPIRAL":
|
||
![]() |
chunk = CamPathChunkBuilder([])
|
||
|
pathd = o.distance_between_paths
|
||
|
pathstep = o.distance_along_paths
|
||
![]() |
midx = (o.max.x + o.min.x) / 2
|
||
|
midy = (o.max.y + o.min.y) / 2
|
||
|
x = pathd / 4
|
||
|
y = pathd / 4
|
||
|
v = Vector((pathd / 4, 0, 0))
|
||
|
|
||
|
# progress(x,y,midx,midy)
|
||
|
e = Euler((0, 0, 0))
|
||
![]() |
# pi = pi
|
||
![]() |
chunk.points.append((midx + v.x, midy + v.y, zlevel))
|
||
|
while midx + v.x > o.min.x or midy + v.y > o.min.y:
|
||
|
# v.x=x-midx
|
||
|
# v.y=y-midy
|
||
|
offset = 2 * v.length * pi
|
||
|
e.z = 2 * pi * (pathstep / offset)
|
||
|
v.rotate(e)
|
||
|
|
||
![]() |
v.length = v.length + pathd / (offset / pathstep)
|
||
![]() |
# progress(v.x,v.y)
|
||
|
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.to_chunk())
|
||
![]() |
chunk = CamPathChunkBuilder([])
|
||
![]() |
if len(chunk.points) > 0:
|
||
![]() |
pathchunks.append(chunk.to_chunk())
|
||
![]() |
if o.movement.insideout == "OUTSIDEIN":
|
||
![]() |
pathchunks.reverse()
|
||
|
for chunk in pathchunks:
|
||
![]() |
if o.movement.insideout == "OUTSIDEIN":
|
||
![]() |
chunk.reverse()
|
||
![]() |
|
||
![]() |
if (o.movement.type == "CONVENTIONAL" and o.movement.spindle_rotation == "CW") or (
|
||
|
o.movement.type == "CLIMB" and o.movement.spindle_rotation == "CCW"
|
||
|
):
|
||
![]() |
# TODO
|
||
![]() |
chunk.flip_x(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":
|
||
![]() |
pathd = o.distance_between_paths
|
||
|
pathstep = o.distance_along_paths
|
||
![]() |
midx = (o.max.x + o.min.x) / 2
|
||
|
midy = (o.max.y + o.min.y) / 2
|
||
|
rx = o.max.x - o.min.x
|
||
|
ry = o.max.y - o.min.y
|
||
![]() |
maxr = sqrt(rx * rx + ry * ry)
|
||
![]() |
|
||
|
# progress(x,y,midx,midy)
|
||
|
e = Euler((0, 0, 0))
|
||
![]() |
# pi = pi
|
||
![]() |
chunk = CamPathChunkBuilder([])
|
||
![]() |
chunk.points.append((midx, midy, zlevel))
|
||
![]() |
pathchunks.append(chunk.to_chunk())
|
||
![]() |
r = 0
|
||
|
|
||
|
while r < maxr:
|
||
|
r += pathd
|
||
![]() |
chunk = CamPathChunkBuilder([])
|
||
![]() |
firstchunk = chunk
|
||
|
v = Vector((-r, 0, 0))
|
||
|
steps = 2 * pi * r / pathstep
|
||
|
e.z = 2 * pi / steps
|
||
|
laststepchunks = []
|
||
|
currentstepchunks = []
|
||
|
for a in range(0, int(steps)):
|
||
|
laststepchunks = currentstepchunks
|
||
|
currentstepchunks = []
|
||
|
|
||
|
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:
|
||
|
if len(chunk.points) > 0:
|
||
|
chunk.closed = False
|
||
![]() |
chunk = chunk.to_chunk()
|
||
![]() |
pathchunks.append(chunk)
|
||
|
currentstepchunks.append(chunk)
|
||
![]() |
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 = CamPathChunkBuilder([])
|
||
![]() |
for ch in laststepchunks:
|
||
|
for p in currentstepchunks:
|
||
![]() |
parent_child_distance(p, ch, o)
|
||
![]() |
|
||
![]() |
if o.movement.insideout == "OUTSIDEIN":
|
||
![]() |
pathchunks.reverse()
|
||
|
for chunk in pathchunks:
|
||
![]() |
if o.movement.insideout == "OUTSIDEIN":
|
||
![]() |
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.reverse()
|
||
![]() |
# pathchunks=sort_chunks(pathchunks,o)not until they get hierarchy parents!
|
||
![]() |
elif o.strategy == "OUTLINEFILL":
|
||
![]() |
polys = o.silhouette.geoms
|
||
![]() |
pathchunks = []
|
||
|
chunks = []
|
||
|
for p in polys:
|
||
![]() |
p = p.buffer(-o.distance_between_paths / 10, o.optimisation.circle_detail)
|
||
![]() |
# first, move a bit inside, because otherwise the border samples go crazy very often changin between
|
||
|
# hit/non hit and making too many jumps in the path.
|
||
![]() |
chunks.extend(shapely_to_chunks(p, 0))
|
||
![]() |
|
||
|
pathchunks.extend(chunks)
|
||
|
lastchunks = chunks
|
||
|
firstchunks = chunks
|
||
|
|
||
![]() |
approxn = (min(maxx - minx, maxy - miny) / o.distance_between_paths) / 2
|
||
![]() |
i = 0
|
||
|
|
||
|
for porig in polys:
|
||
|
p = porig
|
||
![]() |
while not p.is_empty:
|
||
![]() |
p = p.buffer(-o.distance_between_paths, o.optimisation.circle_detail)
|
||
![]() |
if not p.is_empty:
|
||
![]() |
nchunks = shapely_to_chunks(p, zlevel)
|
||
![]() |
|
||
![]() |
if o.movement.insideout == "INSIDEOUT":
|
||
![]() |
parent_child_distance(lastchunks, nchunks, o)
|
||
![]() |
else:
|
||
![]() |
parent_child_distance(nchunks, lastchunks, o)
|
||
![]() |
pathchunks.extend(nchunks)
|
||
|
lastchunks = nchunks
|
||
|
percent = int(i / approxn * 100)
|
||
![]() |
progress("Outlining Polygons ", percent)
|
||
![]() |
i += 1
|
||
|
pathchunks.reverse()
|
||
|
if not o.inverse: # dont do ambient for inverse milling
|
||
|
lastchunks = firstchunks
|
||
|
for p in polys:
|
||
![]() |
d = o.distance_between_paths
|
||
|
steps = o.ambient_radius / o.distance_between_paths
|
||
![]() |
for a in range(0, int(steps)):
|
||
|
dist = d
|
||
![]() |
if a == int(o.cutter_diameter / 2 / o.distance_between_paths):
|
||
![]() |
if o.optimisation.use_exact:
|
||
|
dist += o.optimisation.pixsize * 0.85
|
||
![]() |
# this is here only because silhouette is still done with zbuffer method,
|
||
|
# even if we use bullet collisions.
|
||
![]() |
else:
|
||
![]() |
dist += o.optimisation.pixsize * 2.5
|
||
|
p = p.buffer(dist, o.optimisation.circle_detail)
|
||
![]() |
if not p.is_empty:
|
||
![]() |
nchunks = shapely_to_chunks(p, zlevel)
|
||
![]() |
if o.movement.insideout == "INSIDEOUT":
|
||
![]() |
parent_child_distance(nchunks, lastchunks, o)
|
||
![]() |
else:
|
||
![]() |
parent_child_distance(lastchunks, nchunks, o)
|
||
![]() |
pathchunks.extend(nchunks)
|
||
|
lastchunks = nchunks
|
||
|
|
||
![]() |
if o.movement.insideout == "OUTSIDEIN":
|
||
![]() |
pathchunks.reverse()
|
||
|
|
||
|
for chunk in pathchunks:
|
||
![]() |
if o.movement.insideout == "OUTSIDEIN":
|
||
![]() |
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.reverse()
|
||
![]() |
|
||
![]() |
chunks_refine(pathchunks, o)
|
||
![]() |
progress(time.time() - t)
|
||
|
return pathchunks
|
||
![]() |
|
||
|
|
||
![]() |
def get_path_pattern_4_axis(operation):
|
||
![]() |
"""Generate path patterns for a specified operation along a rotary axis.
|
||
|
|
||
|
This function constructs a series of path chunks based on the provided
|
||
|
operation's parameters, including the rotary axis, strategy, and
|
||
|
dimensions. It calculates the necessary angles and positions for the
|
||
|
cutter based on the specified strategy (PARALLELR, PARALLEL, or HELIX)
|
||
|
and generates the corresponding path chunks for machining operations.
|
||
|
|
||
|
Args:
|
||
|
operation (object): An object containing parameters for the machining operation,
|
||
|
including min and max coordinates, rotary axis configuration,
|
||
|
distance settings, and movement strategy.
|
||
|
|
||
|
Returns:
|
||
|
list: A list of path chunks generated for the specified operation.
|
||
|
"""
|
||
|
|
||
![]() |
o = operation
|
||
|
t = time.time()
|
||
![]() |
progress("Building Path Pattern")
|
||
![]() |
minx, miny, minz, maxx, maxy, maxz = o.min.x, o.min.y, o.min.z, o.max.x, o.max.y, o.max.z
|
||
|
pathchunks = []
|
||
|
zlevel = 1 # minz#this should do layers...
|
||
|
|
||
|
# set axes for various options, Z option is obvious nonsense now.
|
||
![]() |
if o.rotary_axis_1 == "X":
|
||
![]() |
a1 = 0
|
||
|
a2 = 1
|
||
|
a3 = 2
|
||
![]() |
if o.rotary_axis_1 == "Y":
|
||
![]() |
a1 = 1
|
||
|
a2 = 0
|
||
|
a3 = 2
|
||
![]() |
if o.rotary_axis_1 == "Z":
|
||
![]() |
a1 = 2
|
||
|
a2 = 0
|
||
|
a3 = 1
|
||
|
|
||
![]() |
o.max.z = o.max_z
|
||
![]() |
# set radius for all types of operation
|
||
![]() |
radius = max(o.max.z, 0.0001)
|
||
![]() |
radiusend = o.min.z
|
||
|
|
||
|
mradius = max(radius, radiusend)
|
||
![]() |
circlesteps = (mradius * pi * 2) / o.distance_along_paths
|
||
![]() |
circlesteps = max(4, circlesteps)
|
||
|
anglestep = 2 * pi / circlesteps
|
||
|
# generalized rotation
|
||
|
e = Euler((0, 0, 0))
|
||
|
e[a1] = anglestep
|
||
|
|
||
|
# generalized length of the operation
|
||
|
maxl = o.max[a1]
|
||
|
minl = o.min[a1]
|
||
![]() |
steps = (maxl - minl) / o.distance_between_paths
|
||
![]() |
|
||
|
# set starting positions for cutter e.t.c.
|
||
|
cutterstart = Vector((0, 0, 0))
|
||
|
cutterend = Vector((0, 0, 0)) # end point for casting
|
||
|
|
||
![]() |
if o.strategy_4_axis == "PARALLELR":
|
||
![]() |
for a in range(0, floor(steps) + 1):
|
||
![]() |
chunk = CamPathChunkBuilder([])
|
||
![]() |
|
||
![]() |
cutterstart[a1] = o.min[a1] + a * o.distance_between_paths
|
||
![]() |
cutterend[a1] = cutterstart[a1]
|
||
|
|
||
|
cutterstart[a2] = 0 # radius
|
||
|
cutterend[a2] = 0 # radiusend
|
||
|
|
||
|
cutterstart[a3] = radius
|
||
|
cutterend[a3] = radiusend
|
||
|
|
||
|
for b in range(0, floor(circlesteps) + 1):
|
||
|
# print(cutterstart,cutterend)
|
||
|
chunk.startpoints.append(cutterstart.to_tuple())
|
||
|
chunk.endpoints.append(cutterend.to_tuple())
|
||
|
rot = [0, 0, 0]
|
||
|
rot[a1] = a * 2 * pi + b * anglestep
|
||
|
|
||
|
chunk.rotations.append(rot)
|
||
|
cutterstart.rotate(e)
|
||
|
cutterend.rotate(e)
|
||
|
|
||
|
chunk.depth = radiusend - radius
|
||
|
# last point = first
|
||
|
chunk.startpoints.append(chunk.startpoints[0])
|
||
|
chunk.endpoints.append(chunk.endpoints[0])
|
||
|
chunk.rotations.append(chunk.rotations[0])
|
||
|
|
||
![]() |
pathchunks.append(chunk.to_chunk())
|
||
![]() |
|
||
![]() |
if o.strategy_4_axis == "PARALLEL":
|
||
|
circlesteps = (mradius * pi * 2) / o.distance_between_paths
|
||
|
steps = (maxl - minl) / o.distance_along_paths
|
||
![]() |
|
||
|
anglestep = 2 * pi / circlesteps
|
||
|
# generalized rotation
|
||
|
e = Euler((0, 0, 0))
|
||
|
e[a1] = anglestep
|
||
|
|
||
|
reverse = False
|
||
|
|
||
|
for b in range(0, floor(circlesteps) + 1):
|
||
![]() |
chunk = CamPathChunkBuilder([])
|
||
![]() |
cutterstart[a2] = 0
|
||
|
cutterstart[a3] = radius
|
||
|
|
||
|
cutterend[a2] = 0
|
||
|
cutterend[a3] = radiusend
|
||
|
|
||
|
e[a1] = anglestep * b
|
||
|
|
||
|
cutterstart.rotate(e)
|
||
|
cutterend.rotate(e)
|
||
|
|
||
|
for a in range(0, floor(steps) + 1):
|
||
![]() |
cutterstart[a1] = o.min[a1] + a * o.distance_along_paths
|
||
![]() |
cutterend[a1] = cutterstart[a1]
|
||
|
chunk.startpoints.append(cutterstart.to_tuple())
|
||
|
chunk.endpoints.append(cutterend.to_tuple())
|
||
|
rot = [0, 0, 0]
|
||
|
rot[a1] = b * anglestep
|
||
|
chunk.rotations.append(rot)
|
||
|
|
||
![]() |
chunk = chunk.to_chunk()
|
||
![]() |
chunk.depth = radiusend - radius
|
||
![]() |
pathchunks.append(chunk)
|
||
![]() |
|
||
![]() |
if (
|
||
|
(reverse and o.movement.type == "MEANDER")
|
||
|
or (o.movement.type == "CONVENTIONAL" and o.movement.spindle_rotation == "CW")
|
||
|
or (o.movement.type == "CLIMB" and o.movement.spindle_rotation == "CCW")
|
||
|
):
|
||
![]() |
chunk.reverse()
|
||
![]() |
|
||
![]() |
reverse = not reverse
|
||
![]() |
|
||
![]() |
if o.strategy_4_axis == "HELIX":
|
||
![]() |
print("Helix")
|
||
![]() |
|
||
![]() |
a1step = o.distance_between_paths / circlesteps
|
||
![]() |
|
||
![]() |
chunk = CamPathChunkBuilder([]) # only one chunk, init here
|
||
![]() |
|
||
![]() |
for a in range(0, floor(steps) + 1):
|
||
![]() |
cutterstart[a1] = o.min[a1] + a * o.distance_between_paths
|
||
![]() |
cutterend[a1] = cutterstart[a1]
|
||
|
cutterstart[a2] = 0
|
||
|
cutterstart[a3] = radius
|
||
|
cutterend[a3] = radiusend
|
||
![]() |
|
||
![]() |
for b in range(0, floor(circlesteps) + 1):
|
||
|
# print(cutterstart,cutterend)
|
||
|
cutterstart[a1] += a1step
|
||
|
cutterend[a1] += a1step
|
||
|
chunk.startpoints.append(cutterstart.to_tuple())
|
||
|
chunk.endpoints.append(cutterend.to_tuple())
|
||
![]() |
|
||
![]() |
rot = [0, 0, 0]
|
||
|
rot[a1] = a * 2 * pi + b * anglestep
|
||
|
chunk.rotations.append(rot)
|
||
![]() |
|
||
![]() |
cutterstart.rotate(e)
|
||
|
cutterend.rotate(e)
|
||
![]() |
|
||
![]() |
chunk = chunk.to_chunk()
|
||
![]() |
chunk.depth = radiusend - radius
|
||
![]() |
|
||
![]() |
pathchunks.append(chunk)
|
||
![]() |
|
||
![]() |
return pathchunks
|