/* 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 javafx.application.Platform; import org.cirqwizard.geom.Circle; import org.cirqwizard.geom.Point; import org.cirqwizard.gerber.Flash; import org.cirqwizard.gerber.GerberPrimitive; import org.cirqwizard.logging.LoggerFactory; import org.cirqwizard.settings.SettingsFactory; 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.List; import java.util.Vector; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class AdditionalToolpathGenerator extends AbstractToolpathGenerator { private static final int MIN_LENGTH = 250; private int width; private int height; private int passes; private int overlap; private int toolDiameter; public AdditionalToolpathGenerator(int width, int height, int passes, int overlap, int toolDiameter, List<GerberPrimitive> primitives) { this.width = width; this.height = height; this.passes = passes; this.overlap = overlap; this.toolDiameter = toolDiameter; this.primitives = primitives; } public List<Toolpath> generate() { long tt = System.currentTimeMillis(); final Vector<Toolpath> segments = new Vector<>(); ExecutorService pool = Executors.newFixedThreadPool(SettingsFactory.getApplicationSettings().getProcessingThreads().getValue()); progressProperty.setValue(0); final double progressIncrement = 1.0 / primitives.size(); for (final GerberPrimitive primitive : primitives) { pool.submit(() -> { try { Platform.runLater(() -> progressProperty.setValue(progressProperty.getValue() + progressIncrement)); if (!(primitive instanceof Flash)) return null; Flash flash = (Flash) primitive; ArrayList<GerberPrimitive> primitivesCopy = new ArrayList<>(primitives); primitivesCopy.remove(flash); int windowSize = (flash.getAperture().getCircumRadius() + (inflation * (passes + 1) * overlap / 100)) * 2; int x = flash.getX() - windowSize; int y = flash.getY() - windowSize; x = Math.max(0, x); y = Math.max(0, y); Point windowOffset = new Point(x, y); int windowWidth = Math.min(windowSize * 2, width - x); int windowHeight = Math.min(windowSize * 2, height - y); for (int i = 0; i < passes; i++) { RasterWindow window = new RasterWindow(new Point(x, y), windowWidth, windowHeight); window.render(primitivesCopy, toolDiameter / 2); int inflation = toolDiameter / 2 + toolDiameter * (100 - overlap) / 100 * (1 + i); window.render(Arrays.asList((GerberPrimitive) flash), inflation); SimpleEdgeDetector detector = new SimpleEdgeDetector(window.getBufferedImage()); window = null; // Helping GC to reclaim memory consumed by rendered image detector.process(); if (detector.getOutput() != null) { List<Circle> knownCircles = translateKnownCircles(windowOffset, 1, getKnownCircles(inflation)); List<Toolpath> toolpaths = new Tracer(detector.getOutput(), windowWidth, windowHeight, toolDiameter, knownCircles).process(); detector = null; // Helping GC to reclaim memory consumed by processed image for (Toolpath t : toolpaths) { Point from = ((CuttingToolpath)t).getCurve().getFrom(); Point to = ((CuttingToolpath)t).getCurve().getTo(); if ((t instanceof LinearToolpath) && from.distanceTo(to) < MIN_LENGTH) continue; Point centerPoint = translateToWindowCoordinates(flash.getPoint(), windowOffset, 1); int threshold = flash.getAperture().getCircumRadius() + (int)Math.sqrt(inflation * inflation * 2) + 10; if (from.distanceTo(centerPoint) < threshold && to.distanceTo(centerPoint) < threshold) segments.addAll(translateToolpaths(Arrays.asList(t), windowOffset, 1)); } } } } catch (Throwable e) { LoggerFactory.logException("Exception caught while generating additional passes", e); } return null; }); } try { pool.shutdown(); pool.awaitTermination(10, TimeUnit.DAYS); } catch (InterruptedException e) {} tt = System.currentTimeMillis() - tt; System.out.println("Addtional passes generation time: " + tt); return segments; } }