/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.cirqwizard.generation; import org.cirqwizard.geom.Arc; import org.cirqwizard.geom.Curve; import org.cirqwizard.geom.Line; import org.cirqwizard.geom.Point; import org.cirqwizard.generation.toolpath.CircularToolpath; import org.cirqwizard.generation.toolpath.CuttingToolpath; import org.cirqwizard.generation.toolpath.LinearToolpath; import org.cirqwizard.generation.toolpath.Toolpath; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; public class ToolpathMerger { private List<Toolpath> toolpaths; private HashMap<Point, ArrayList<Toolpath>> verticesMap = new HashMap<>(); private int tolerance; private int roundingFactor; public ToolpathMerger(List<Toolpath> toolpaths, int tolerance) { this.toolpaths = toolpaths; this.tolerance = tolerance; this.roundingFactor = tolerance * 2; } public List<Toolpath> merge() { double comparisonThreshold = Math.PI / 60; // 3 degrees initVerticesMap(toolpaths); ArrayList<Toolpath> toBeRemoved = new ArrayList<>(); filterDots(toBeRemoved); for (Point p : verticesMap.keySet()) { ArrayList<Toolpath> toMerge = verticesMap.get(p); for (int i = 0; i < toMerge.size(); i++) { boolean merge = false; for (int j = i + 1; j < toMerge.size(); j++) { Toolpath t1 = toMerge.get(i); Toolpath t2 = toMerge.get(j); if (!t1.getClass().equals(t2.getClass())) continue; Curve c1 = ((CuttingToolpath)t1).getCurve(); Curve c2 = ((CuttingToolpath)t2).getCurve(); if (t1 == t2 || toBeRemoved.contains(t1) || toBeRemoved.contains(t2)) continue; boolean l1Inversed = false; if (!c1.getTo().equals(p, roundingFactor)) { c1 = c1.reverse(); l1Inversed = true; } if (!c2.getFrom().equals(p, roundingFactor)) c2 = c2.reverse(); if (t1 instanceof LinearToolpath && t2 instanceof LinearToolpath) { Line l1 = (Line) c1; Line l2 = (Line) c2; if (l2.getFrom().equals(l1.getTo(), tolerance)) { if (l1.getFrom().equals(l2.getTo())) // Removing duplicate segments { toBeRemoved.add(t2); removeVertices(c2.getTo(), t2); continue; } else if (Math.abs(Math.abs(l1.angleToX() - l2.angleToX()) - Math.PI) < comparisonThreshold) // Removing overlapping segments { if (l1.length() > l2.length()) { toBeRemoved.add(t2); removeVertices(c2.getTo(), t2); continue; } else { toBeRemoved.add(t1); removeVertices(c1.getFrom(), t1); break; } } double angleDifference = Math.abs(l1.angleToX() - l2.angleToX()); while (angleDifference >= Math.PI - comparisonThreshold) angleDifference -= Math.PI; angleDifference = Math.abs(angleDifference); if (angleDifference < comparisonThreshold) merge = true; } } else if (t1 instanceof CircularToolpath && t2 instanceof CircularToolpath) { Arc a1 = (Arc) c1; Arc a2 = (Arc) c2; if (a1.getTo().equals(a2.getFrom(), tolerance) && a1.isClockwise() == a2.isClockwise()) { if (a1.getCenter().equals(a2.getCenter()) && a1.getRadius() == a2.getRadius()) merge = true; } } if (merge) { Curve curve = ((CuttingToolpath) t1).getCurve(); if (l1Inversed) curve.setFrom(c2.getTo()); else curve.setTo(c2.getTo()); if (curve instanceof Arc && c2.getTo().equals(c1.getFrom(), tolerance)) curve.setTo(curve.getFrom()); toBeRemoved.add(t2); removeVertices(c2.getFrom(), t2); removeVertices(c2.getTo(), t2); addVertices(c2.getTo(), t1); break; } } if (merge) break; } } int lines = 0; int arcs = 0; ArrayList<Toolpath> result = new ArrayList<>(); for (Toolpath t : toolpaths) if (!toBeRemoved.contains(t)) { if (t instanceof LinearToolpath) lines++; if (t instanceof CircularToolpath) arcs++; result.add(t); } System.out.println("lines: " + lines + ", arcs: " + arcs); return result; } private void filterDots(List<Toolpath> toBeRemoved) { for (Point p : verticesMap.keySet()) { ArrayList<Toolpath> toMerge = verticesMap.get(p); for (int i = 0; i < toMerge.size(); i++) { for (int j = i + 1; j < toMerge.size(); j++) { Toolpath t1 = toMerge.get(i); Toolpath t2 = toMerge.get(j); if (t1 instanceof LinearToolpath) { if (((Line)((LinearToolpath) t1).getCurve()).length() < tolerance) toBeRemoved.add(t1); } else if (t2 instanceof LinearToolpath) { if (((Line)((LinearToolpath) t2).getCurve()).length() < tolerance) toBeRemoved.add(t2); } } } } } private void initVerticesMap(List<Toolpath> toolpaths) { for (Toolpath t : toolpaths) { Curve curve = ((CuttingToolpath) t).getCurve(); addVertices(curve.getFrom(), t); addVertices(curve.getTo(), t); } } private List<Point> getPointsForVertex(Point point) { Point p1 = new Point(point.getX() / roundingFactor * roundingFactor, point.getY() / roundingFactor * roundingFactor); Point p2 = p1.add(new Point(roundingFactor, 0)); Point p3 = p1.add(new Point(0, roundingFactor)); Point p4 = p1.add(new Point(roundingFactor, roundingFactor)); return Arrays.asList(p1, p2, p3, p4); } private void addVertices(Point point, Toolpath toolpath) { addVertex(getPointsForVertex(point), toolpath); } private void removeVertices(Point point, Toolpath toolpath) { removeVertex(getPointsForVertex(point), toolpath); } private void addVertex(List<Point> vertices, Toolpath toolpath) { for (Point p : vertices) { ArrayList<Toolpath> list = verticesMap.get(p); if (list == null) { list = new ArrayList<>(); verticesMap.put(p, list); } if (!list.contains(toolpath)) list.add(toolpath); } } private void removeVertex(List<Point> vertices, Toolpath toolpath) { for (Point p : vertices) { ArrayList<Toolpath> list = verticesMap.get(p); if (list != null) list.remove(toolpath); } } }