// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2019 sliptonic * * * * 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 * * * ***************************************************************************/ #include #include #include #include #include "PathSegmentWalker.h" #define ARC_MIN_SEGMENTS 20.0 // minimum # segments to interpolate an arc namespace Path { Base::Vector3d compensateRotation( const Base::Vector3d& pt, const Base::Rotation& rot, const Base::Vector3d& center ) { Base::Vector3d ptRotated; rot.multVec(pt - center, ptRotated); return ptRotated + center; } Base::Rotation yawPitchRoll(double a, double b, double c) { Base::Rotation rot; rot.setYawPitchRoll(-c, -b, -a); return rot; } PathSegmentVisitor::~PathSegmentVisitor() {} void PathSegmentVisitor::setup(const Base::Vector3d& last) { (void)last; } void PathSegmentVisitor::g0( int id, const Base::Vector3d& last, const Base::Vector3d& next, const std::deque& pts ) { (void)id; (void)last; (void)next; (void)pts; } void PathSegmentVisitor::g1( int id, const Base::Vector3d& last, const Base::Vector3d& next, const std::deque& pts ) { (void)id; (void)last; (void)next; (void)pts; } void PathSegmentVisitor::g23( int id, const Base::Vector3d& last, const Base::Vector3d& next, const std::deque& pts, const Base::Vector3d& center ) { (void)id; (void)last; (void)next; (void)pts; (void)center; } void PathSegmentVisitor::g8x( int id, const Base::Vector3d& last, const Base::Vector3d& next, const std::deque& pts, const std::deque& p, const std::deque& q ) { (void)id; (void)last; (void)next; (void)pts; (void)p; (void)q; } void PathSegmentVisitor::g38(int id, const Base::Vector3d& last, const Base::Vector3d& next) { (void)id; (void)last; (void)next; } PathSegmentWalker::PathSegmentWalker(const Toolpath& tp_) : tp(tp_) , retract_mode(98) // Default to G98 (retract to initial Z) {} void PathSegmentWalker::walk(PathSegmentVisitor& cb, const Base::Vector3d& startPosition) { if (tp.getSize() == 0) { return; } ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Part" ); float deviation = hGrp->GetFloat("MeshDeviation", 0.2); Base::Vector3d rotCenter = tp.getCenter(); Base::Vector3d last(startPosition); Base::Rotation lrot; double A = 0.0; double B = 0.0; double C = 0.0; bool absolute = true; bool absolutecenter = false; // for mapping the coordinates to XY plane double Base::Vector3d::* pz = &Base::Vector3d::z; cb.setup(last); for (unsigned int i = 0; i < tp.getSize(); i++) { std::deque points; const Path::Command& cmd = tp.getCommand(i); const std::string& name = cmd.Name; Base::Vector3d next = cmd.getPlacement().getPosition(); double a = A; double b = B; double c = C; if (!absolute) { next = last + next; } if (!cmd.has("X")) { next.x = last.x; } if (!cmd.has("Y")) { next.y = last.y; } if (!cmd.has("Z")) { next.z = last.z; } if (cmd.has("A")) { a = cmd.getValue("A"); } if (cmd.has("B")) { b = cmd.getValue("B"); } if (cmd.has("C")) { c = cmd.getValue("C"); } Base::Rotation nrot = yawPitchRoll(a, b, c); Base::Vector3d rnext = compensateRotation(next, nrot, rotCenter); if ((name == "G0") || (name == "G00") || (name == "G1") || (name == "G01")) { // straight line if (nrot != lrot) { double amax = std::max( fmod(fabs(a - A), 360), std::max(fmod(fabs(b - B), 360), fmod(fabs(c - C), 360)) ); double angle = Base::toRadians(amax); int segments = std::max(ARC_MIN_SEGMENTS, 3.0 / (deviation / angle)); double da = (a - A) / segments; double db = (b - B) / segments; double dc = (c - C) / segments; Base::Vector3d dnext = (next - last) / segments; for (int j = 1; j < segments; j++) { Base::Vector3d inter = last + dnext * j; Base::Rotation rot = yawPitchRoll(A + da * j, B + db * j, C + dc * j); Base::Vector3d rinter = compensateRotation(inter, rot, rotCenter); points.push_back(rinter); } } if ("G0" == name || "G00" == name) { cb.g0(i, last, rnext, points); } else { cb.g1(i, last, rnext, points); } last = next; A = a; B = b; C = c; lrot = nrot; } else if ((name == "G2") || (name == "G02") || (name == "G3") || (name == "G03")) { // arc Base::Vector3d norm; Base::Vector3d center; if ((name == "G2") || (name == "G02")) { norm.*pz = -1.0; } else { norm.*pz = 1.0; } if (absolutecenter) { center = cmd.getCenter(); } else { center = (last + cmd.getCenter()); } Base::Vector3d next0(next); next0.*pz = 0.0; Base::Vector3d last0(last); last0.*pz = 0.0; Base::Vector3d center0(center); center0.*pz = 0.0; // double radius = (last - center).Length(); double angle = (next0 - center0).GetAngle(last0 - center0); // GetAngle will always return the minor angle. Switch if needed Base::Vector3d anorm = (last0 - center0) % (next0 - center0); if (anorm.*pz < 0) { if (name == "G3" || name == "G03") { angle = std::numbers::pi * 2 - angle; } } else if (anorm.*pz > 0) { if (name == "G2" || name == "G02") { angle = std::numbers::pi * 2 - angle; } } else if (angle == 0) { angle = std::numbers::pi * 2; } double amax = std::max( fmod(fabs(a - A), 360), std::max(fmod(fabs(b - B), 360), fmod(fabs(c - C), 360)) ); int segments = std::max( ARC_MIN_SEGMENTS, 3.0 / (deviation / std::max(angle, amax)) ); // we use a rather simple rule here, provisorily double dZ = (next.*pz - last.*pz) / segments; // How far each segment will helix in Z double dangle = angle / segments; double da = (a - A) / segments; double db = (b - B) / segments; double dc = (c - C) / segments; for (int j = 1; j < segments; j++) { Base::Vector3d inter; Base::Rotation rot(norm, dangle * j); rot.multVec((last0 - center0), inter); inter.*pz = last.*pz + dZ * j; // Enable displaying helices Base::Rotation arot = yawPitchRoll(A + da * j, B + db * j, C + dc * j); Base::Vector3d rinter = compensateRotation(center0 + inter, arot, rotCenter); points.push_back(rinter); } cb.g23(i, last, rnext, points, center); last = next; A = a; B = b; C = c; lrot = nrot; } else if (name == "G90") { // absolute mode absolute = true; } else if (name == "G91") { // relative mode absolute = false; } else if (name == "G90.1") { // absolute mode absolutecenter = true; } else if (name == "G91.1") { // relative mode absolutecenter = false; } else if ((name == "G73") || (name == "G74") || (name == "G81") || (name == "G82") || (name == "G83") || (name == "G84") || (name == "G85") || (name == "G86") || (name == "G89")) { // drill,tap,bore // Check for RetractMode annotation (G98 or G99) if (cmd.hasAnnotation("RetractMode")) { std::string mode = cmd.getAnnotationString("RetractMode"); if (mode == "G99") { retract_mode = 99; } else if (mode == "G98") { retract_mode = 98; } } double r = 0; if (cmd.has("R")) { r = cmd.getValue("R"); } std::deque plist; std::deque qlist; Base::Vector3d p1(next); p1.*pz = last.*pz; if (nrot != lrot) { double amax = std::max( fmod(fabs(a - A), 360), std::max(fmod(fabs(b - B), 360), fmod(fabs(c - C), 360)) ); double angle = Base::toRadians(amax); int segments = std::max(ARC_MIN_SEGMENTS, 3.0 / (deviation / angle)); double da = (a - A) / segments; double db = (b - B) / segments; double dc = (c - C) / segments; Base::Vector3d dnext = (p1 - last) / segments; for (int j = 1; j < segments; j++) { Base::Vector3d inter = last + dnext * j; Base::Rotation rot = yawPitchRoll(A + da * j, B + db * j, C + dc * j); Base::Vector3d rinter = compensateRotation(inter, rot, rotCenter); points.push_back(rinter); } } Base::Vector3d p1r = compensateRotation(p1, nrot, rotCenter); Base::Vector3d p2(next); p2.*pz = r; Base::Vector3d p2r = compensateRotation(p2, nrot, rotCenter); double q; if (cmd.has("Q")) { q = cmd.getValue("Q"); if (q > 0) { Base::Vector3d temp(next); for (temp.*pz = r; temp.*pz > next.*pz; temp.*pz -= q) { Base::Vector3d pr = compensateRotation(temp, nrot, rotCenter); qlist.push_back(pr); } } } Base::Vector3d p3(next); if (retract_mode == 99) { // G81,G83 need to account for G99 and retract to R only p3.*pz = p2.*pz; } else { p3.*pz = last.*pz; } Base::Vector3d p3r = compensateRotation(p3, nrot, rotCenter); plist.push_back(p1r); plist.push_back(p2r); plist.push_back(p3r); cb.g8x(i, last, next, points, plist, qlist); last = p3; A = a; B = b; C = c; lrot = nrot; } else if ((name == "G38.2") || (name == "G38.3") || (name == "G38.4") || (name == "G38.5")) { // Straight probe cb.g38(i, last, next); last = next; } else if (name == "G17") { pz = &Base::Vector3d::z; } else if (name == "G18") { pz = &Base::Vector3d::y; } else if (name == "G19") { pz = &Base::Vector3d::x; } else if (name == "G98") { retract_mode = 98; } else if (name == "G99") { retract_mode = 99; } } } } // namespace Path