/** * Expand an arc into smaller sections. You can configure the length of each * section, and whether it is expanded with a bunch of smaller arcs, or with * line segments. */ /* Copywrite 2016 Will Winder This file is part of Universal Gcode Sender (UGS). UGS is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. UGS 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 General Public License for more details. You should have received a copy of the GNU General Public License along with UGS. If not, see <http://www.gnu.org/licenses/>. */ package com.willwinder.universalgcodesender.gcode.processors; import com.willwinder.universalgcodesender.gcode.GcodeParser; import com.willwinder.universalgcodesender.gcode.GcodeParser.GcodeMeta; import com.willwinder.universalgcodesender.gcode.GcodePreprocessorUtils; import com.willwinder.universalgcodesender.gcode.GcodeState; import com.willwinder.universalgcodesender.gcode.util.GcodeParserException; import com.willwinder.universalgcodesender.gcode.util.PlaneFormatter; import com.willwinder.universalgcodesender.i18n.Localization; import com.willwinder.universalgcodesender.types.PointSegment; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; import javax.vecmath.Point3d; /** * * @author wwinder */ public class ArcExpander implements ICommandProcessor { final private boolean convertToLines; final private double length; final private DecimalFormat df; @Override public String getHelp() { return Localization.getString("sender.help.arcs") + "\n" + Localization.getString("sender.arcs.length") + ": " + df.format(length); } /** * @param convertToLines toggles if smaller lines or arcs are returned. * @param length the length of each smaller segment. */ public ArcExpander(boolean convertToLines, double length) { this.convertToLines = convertToLines; this.length = length; // Setup decimal formatter df = new DecimalFormat("#.#########", Localization.dfs); } @Override public List<String> processCommand(String command, GcodeState state) throws GcodeParserException { if (state.currentPoint == null) throw new GcodeParserException(Localization.getString("parser.processor.arc.start-error")); List<String> results = new ArrayList<>(); List<GcodeMeta> commands = GcodeParser.processCommand(command, 0, state); // If this is not an arc, there is nothing to do. if (! hasArcCommand(commands)) { results.add(command); return results; } // Make sure there is just one command (the arc). // Note: This means "G17 G02 X5 Y5 R2" would be an error. if (commands.size() != 1) { throw new GcodeParserException(Localization.getString("parser.processor.arc.multiple-commands")); } GcodeMeta arcMeta = commands.get(0); PointSegment ps = arcMeta.point; Point3d start = state.currentPoint; Point3d end = arcMeta.point.point(); List<Point3d> points = GcodePreprocessorUtils.generatePointsAlongArcBDring( start, end, ps.center(), ps.isClockwise(), ps.getRadius(), 0, length, new PlaneFormatter(ps.getPlaneState())); // That function returns the first and last points. Exclude the first // point because the previous gcode command ends there already. points.remove(0); if (convertToLines) { // Tack the speed onto the first line segment in case the arc also // changed the feed value. String feed = "F" + arcMeta.point.getSpeed(); for (Point3d point : points) { results.add(GcodePreprocessorUtils.generateG1FromPoints(start, point, state.inAbsoluteMode, df) + feed); start = point; feed = ""; } } else { // TODO: Generate arc segments. throw new UnsupportedOperationException("I have not implemented this."); } return results; } private static boolean hasArcCommand(List<GcodeMeta> commands) { if (commands == null) return false; for (GcodeMeta meta : commands) { if (meta.point != null && meta.point.isArc()) { return true; } } return false; } }