From d7f722a91afa5a7c237d2f81a39ab2b101fad2ab Mon Sep 17 00:00:00 2001 From: tarman3 Date: Thu, 18 Dec 2025 15:07:34 +0200 Subject: [PATCH] CAM: Array - Refactor class PathArray --- src/Mod/CAM/Path/Op/Gui/Array.py | 228 +++++++++++++++---------------- 1 file changed, 107 insertions(+), 121 deletions(-) diff --git a/src/Mod/CAM/Path/Op/Gui/Array.py b/src/Mod/CAM/Path/Op/Gui/Array.py index c1801bd2bf..f72f9dfc06 100644 --- a/src/Mod/CAM/Path/Op/Gui/Array.py +++ b/src/Mod/CAM/Path/Op/Gui/Array.py @@ -299,7 +299,7 @@ class PathArray: def __init__( self, - baseList, + base, arrayType, copies, offsetVector, @@ -308,33 +308,44 @@ class PathArray: angle, centre, swapDirection, - jitterMagnitude=FreeCAD.Vector(0, 0, 0), - jitterPercent=0, - seed="FreeCAD", + jitterMagnitude, + jitterPercent, + seed, ): - self.baseList = list() + self.base = base self.arrayType = arrayType # ['Linear1D', 'Linear2D', 'Polar'] self.copies = copies self.offsetVector = offsetVector self.copiesX = copiesX self.copiesY = copiesY - self.angle = angle - self.centre = centre + self.polarAngle = angle + self.polarCentre = centre self.swapDirection = swapDirection self.jitterMagnitude = jitterMagnitude self.jitterPercent = jitterPercent self.seed = seed - if baseList: - if isinstance(baseList, list): - self.baseList = baseList - else: - self.baseList = [baseList] + def getPath(self): + """getPath() ... Call this method on an instance of the class to generate and return + path data for the requested path array.""" - # Private method - def _calculateJitter(self, pos): - """_calculateJitter(pos) ... - Returns the position argument with a random vector shift applied.""" + commands = [] + random.seed(self.seed) + + if self.arrayType == "Polar": + self.getPolarArray(commands) + elif self.arrayType == "Linear2D": + if self.swapDirection: + self.getLinear2DXYArray(commands) + else: + self.getLinear2DYXArray(commands) + else: + self.getLinear1DArray(commands) + + return Path.Path(commands) + + def calculateJitter(self, pos): + """Returns the position argument with a random vector shift applied.""" if self.jitterPercent == 0: pass elif random.randint(0, 100) < self.jitterPercent: @@ -343,117 +354,92 @@ class PathArray: pos.z = pos.z + random.uniform(-self.jitterMagnitude.z, self.jitterMagnitude.z) return pos - # Public method - def getPath(self): - """getPath() ... Call this method on an instance of the class to generate and return - path data for the requested path array.""" + def getLinear1DArray(self, commands): + """Array type Linear1D""" + for i in range(self.copies): + pos = FreeCAD.Vector( + self.offsetVector.x * (i + 1), + self.offsetVector.y * (i + 1), + self.offsetVector.z * (i + 1), + ) + pos = self.calculateJitter(pos) - base = self.baseList - for b in base: - if not b.isDerivedFrom("Path::Feature"): - return - if not b.Path: - return + for b in self.base: + pl = FreeCAD.Placement() + pl.move(pos) + path = PathUtils.getPathWithPlacement(b) + path = PathUtils.applyPlacementToPath(pl, path) + commands.extend(path.Commands) - b_tool_controller = toolController(b) - if not b_tool_controller: - return - - # build copies - output = "" - random.seed(self.seed) - - if self.arrayType == "Linear1D": - for i in range(self.copies): - pos = FreeCAD.Vector( - self.offsetVector.x * (i + 1), - self.offsetVector.y * (i + 1), - self.offsetVector.z * (i + 1), - ) - pos = self._calculateJitter(pos) - - for b in base: - pl = FreeCAD.Placement() - pl.move(pos) - np = Path.Path( - [cm.transform(pl) for cm in PathUtils.getPathWithPlacement(b).Commands] + def getLinear2DXYArray(self, commands): + """Array type Linear2D with initial X direction""" + for i in range(self.copiesY + 1): + for j in range(self.copiesX + 1): + if (i % 2) == 0: + pos = FreeCAD.Vector( + self.offsetVector.x * j, + self.offsetVector.y * i, + self.offsetVector.z * i, ) - output += np.toGCode() - - elif self.arrayType == "Linear2D": - if self.swapDirection: - for i in range(self.copiesY + 1): - for j in range(self.copiesX + 1): - if (i % 2) == 0: - pos = FreeCAD.Vector( - self.offsetVector.x * j, - self.offsetVector.y * i, - self.offsetVector.z * i, - ) - else: - pos = FreeCAD.Vector( - self.offsetVector.x * (self.copiesX - j), - self.offsetVector.y * i, - self.offsetVector.z * i, - ) - pos = self._calculateJitter(pos) - - for b in base: - pl = FreeCAD.Placement() - # do not process the index 0,0. It will be processed by the base Paths themselves - if not (i == 0 and j == 0): - pl.move(pos) - np = Path.Path( - [ - cm.transform(pl) - for cm in PathUtils.getPathWithPlacement(b).Commands - ] - ) - output += np.toGCode() - else: - for i in range(self.copiesX + 1): - for j in range(self.copiesY + 1): - if (i % 2) == 0: - pos = FreeCAD.Vector( - self.offsetVector.x * i, - self.offsetVector.y * j, - self.offsetVector.z * i, - ) - else: - pos = FreeCAD.Vector( - self.offsetVector.x * i, - self.offsetVector.y * (self.copiesY - j), - self.offsetVector.z * i, - ) - pos = self._calculateJitter(pos) - - for b in base: - pl = FreeCAD.Placement() - # do not process the index 0,0. It will be processed by the base Paths themselves - if not (i == 0 and j == 0): - pl.move(pos) - np = Path.Path( - [ - cm.transform(pl) - for cm in PathUtils.getPathWithPlacement(b).Commands - ] - ) - output += np.toGCode() - # Eif - else: - for i in range(self.copies): - for b in base: - ang = 360 - if self.copies > 0: - ang = self.angle / self.copies * (1 + i) + else: + pos = FreeCAD.Vector( + self.offsetVector.x * (self.copiesX - j), + self.offsetVector.y * i, + self.offsetVector.z * i, + ) + pos = self.calculateJitter(pos) + for b in self.base: pl = FreeCAD.Placement() - pl.rotate(self.centre, FreeCAD.Vector(0, 0, 1), ang) - np = PathUtils.applyPlacementToPath(pl, PathUtils.getPathWithPlacement(b)) - output += np.toGCode() + # index 0,0 will be processed by the base Paths themselves + if i != 0 or j != 0: + pl.move(pos) + path = PathUtils.getPathWithPlacement(b) + path = PathUtils.applyPlacementToPath(pl, path) + commands.extend(path.Commands) - # return output - return Path.Path(output) + def getLinear2DYXArray(self, commands): + """Array type Linear2D with initial Y direction""" + for i in range(self.copiesX + 1): + for j in range(self.copiesY + 1): + if (i % 2) == 0: + pos = FreeCAD.Vector( + self.offsetVector.x * i, + self.offsetVector.y * j, + self.offsetVector.z * i, + ) + else: + pos = FreeCAD.Vector( + self.offsetVector.x * i, + self.offsetVector.y * (self.copiesY - j), + self.offsetVector.z * i, + ) + pos = self.calculateJitter(pos) + + for b in self.base: + pl = FreeCAD.Placement() + # index 0,0 will be processed by the base Paths themselves + if i != 0 or j != 0: + pl.move(pos) + path = PathUtils.getPathWithPlacement(b) + path = PathUtils.applyPlacementToPath(pl, path) + commands.extend(path.Commands) + + def getPolarArray(self, commands): + """Array type Polar""" + for i in range(self.copies): + ang = 360 + if self.copies > 0: + ang = self.polarAngle / self.copies * (1 + i) + + # prepare placement for polar pattern + pl = FreeCAD.Placement() + pl.rotate(self.polarCentre, FreeCAD.Vector(0, 0, 1), ang) + + for b in self.base: + path = PathUtils.getPathWithPlacement(b) + path = PathUtils.applyPlacementToPath(pl, path) + commands.extend(path.Commands) class ViewProviderArray: