Download src/Mod/CAM/Path/Base/Generator/rotation.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 7.07 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Base/Generator/rotation.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/CAM/Path/Base/Generator/rotation.py
-
curl -L -o rotation.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Base/Generator/rotation.py
7.07 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * Copyright (c) 2021 sliptonic <shopinthewoods@gmail.com> * | |
| # * * | |
| # * This program is free software; you can redistribute it and/or modify * | |
| # * it under the terms of the GNU Lesser General Public License (LGPL) * | |
| # * as published by the Free Software Foundation; either version 2 of * | |
| # * the License, or (at your option) any later version. * | |
| # * for detail see the LICENCE text file. * | |
| # * * | |
| # * 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 Library General Public License for more details. * | |
| # * * | |
| # * You should have received a copy of the GNU Library General Public * | |
| # * License along with this program; if not, write to the Free Software * | |
| # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * | |
| # * USA * | |
| # * * | |
| # *************************************************************************** | |
| # Technical Debt. This generator currently assumes 3+2 axis rotation of CA. | |
| # The main generator function should be extended to include other flavors of 3+2 | |
| import math | |
| import Path | |
| import FreeCAD | |
| from enum import Enum | |
| __title__ = "Rotation toolpath Generator" | |
| __author__ = "sliptonic (Brad Collette)" | |
| __url__ = "https://www.freecad.org" | |
| __doc__ = "Generates the rotation toolpath" | |
| if False: | |
| Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule()) | |
| Path.Log.trackModule(Path.Log.thisModule()) | |
| else: | |
| Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule()) | |
| class refAxis(Enum): | |
| x = FreeCAD.Vector(1, 0, 0) | |
| y = FreeCAD.Vector(0, 1, 0) | |
| z = FreeCAD.Vector(0, 0, 1) | |
| def relAngle(vec, ref): | |
| """ | |
| Takes a vector and a reference axis (refAxis) vector. Calculates the | |
| relative angle. The result is returned in degrees (plus or minus) | |
| """ | |
| Path.Log.debug("vec: {} ref: {}".format(vec, ref)) | |
| norm = vec * 1 # copy vec so we don't alter original | |
| if ref == refAxis.x: | |
| plane = refAxis.y.value | |
| elif ref == refAxis.y: | |
| plane = refAxis.z.value | |
| else: | |
| plane = refAxis.x.value | |
| norm.projectToPlane(FreeCAD.Vector(0, 0, 0), plane) | |
| ref = ref.value | |
| rot = FreeCAD.Rotation(norm, ref) | |
| ang = math.degrees(rot.Angle) | |
| angle = ang * plane.dot(rot.Axis) | |
| Path.Log.debug("relative ang: {}".format(angle)) | |
| return angle | |
| def __getCRotation(normalVector, cMin=-360, cMax=360): | |
| """ | |
| Calculate the valid C axis rotations component to align the normalVector | |
| with either the +y or -y axis. | |
| multiple poses may be possible. Returns a list of all valid poses | |
| """ | |
| Path.Log.debug("normalVector: {} cMin: {} cMax: {}".format(normalVector, cMin, cMax)) | |
| angle = relAngle(normalVector, refAxis.y) | |
| # Given an angle, there are four possibilities; rotating +- to each of the | |
| # two axes +y and -y | |
| candidates = [angle] | |
| if angle == 0: | |
| candidates.append(180) | |
| elif angle == 180: | |
| candidates.append(0) | |
| elif angle >= 0: | |
| candidates.append(angle - 180) | |
| candidates.append(180 + angle) | |
| candidates.append(angle - 360) | |
| else: | |
| candidates.append(angle + 180) | |
| candidates.append(-180 + angle) | |
| candidates.append(angle + 360) | |
| # final results are candidates that don't violate rotation limits | |
| results = [c for c in candidates if c >= cMin and c <= cMax] | |
| return results | |
| def __getARotation(normalVector, aMin=-360, aMax=360): | |
| """ | |
| Calculate the A axis rotation component. | |
| Final rotation is always assumed to be around +X. The sign of the returned | |
| value indicates direction of rotation. | |
| Returns None if rotation violates min/max constraints | |
| """ | |
| angle = relAngle(normalVector, refAxis.z) | |
| # only return a result if it doesn't violate rotation constraints | |
| if angle > aMin and angle <= aMax: | |
| return angle | |
| else: | |
| return None | |
| def generate(normalVector, aMin=-360, aMax=360, cMin=-360, cMax=360, compound=False): | |
| """ | |
| Generates Gcode rotation to align a vector (alignVector) with the positive Z axis. | |
| It first rotates around the Z axis (C rotation) | |
| to align the vector the positive Y axis. Then around the X axis | |
| (A rotation). | |
| The min and max arguments dictate the range of motion allowed rotation in | |
| the respective axis. | |
| Default assumes continuous rotation. | |
| Returns a list of path commands for the shortest valid solution | |
| If compound is False, axis moves will be broken out to individual commands | |
| The normalVector input from a typical face (f) can be obtained like this: | |
| u, v = f.ParameterRange[:2] | |
| n = f.normalAt(u,v) | |
| plm = obj.getGlobalPlacement() | |
| rot = plm.Rotation | |
| normalVector = rot.multVec(n | |
| """ | |
| Path.Log.track( | |
| "\n=============\n normalVector: {}\n aMin: {}\n aMax: {}\n cMin: {}\n cMax: {}".format( | |
| normalVector, aMin, aMax, cMin, cMax | |
| ) | |
| ) | |
| # Calculate C rotation | |
| cResults = __getCRotation(normalVector, cMin, cMax) | |
| Path.Log.debug("C Rotation results {}".format(cResults)) | |
| solutions = [] | |
| for result in cResults: | |
| # calculate a new vector based on the result | |
| rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), result) | |
| newvec = rot.multVec(normalVector) | |
| # Get the candidate A rotation for the new vector | |
| aResult = __getARotation(newvec, aMin, aMax) | |
| Path.Log.debug("\n=====\nFor C Rotation: {}\n Calculated A {}\n".format(result, aResult)) | |
| if aResult is not None: | |
| solutions.append({"A": aResult, "C": result}) | |
| if len(solutions) == 0: # No valid solution found | |
| raise ValueError("No valid rotation solution found") | |
| # find pose with the shortest transit length | |
| best = solutions[0] | |
| curlen = math.fabs(best["A"]) + math.fabs(best["C"]) | |
| for solution in solutions[1:]: | |
| testlen = math.fabs(solution["A"]) + math.fabs(solution["C"]) | |
| if testlen < curlen: | |
| best = solution | |
| curlen = testlen | |
| Path.Log.debug("best result: {}".format(best)) | |
| # format and return rotation commands | |
| commands = [] | |
| if compound: | |
| commands.append(Path.Command("G0", best)) | |
| else: | |
| for key, val in best.items(): | |
| print(key, val) | |
| commands.append(Path.Command("G0", {key: val})) | |
| return commands | |