Package bs.bs2d.gui.views

Source Code of bs.bs2d.gui.views.GCodeView

package bs.bs2d.gui.views;

import bs.bs2d.geom.AreaSet;
import bs.bs2d.gui.BSConstants;
import bs.bs2d.gui.BusyStateListener;
import bs.bs2d.gui.UserMessage;
import bs.bs2d.slicer.PrintObject;
import bs.bs2d.slicer.Slicer;
import bs.properties.BSProperties;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.TopologyException;
import com.vividsolutions.jts.geom.util.AffineTransformation;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.vecmath.Point2d;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;

/**
*
* @author Djen
*/
public class GCodeView implements BSGUIView{
   
    private static final String NAME = "G-Code";
   
    private static final boolean OPTIMIZE_PACKING = true;
   
    // gui
    private final JComponent content;
    private final JTextArea gCodeArea;
    private final GCodeViewControls controls;
    private final JMenu[] menus;
   
    // data
    private AreaSet areaSet;
    private PrintObject optimized, uniform;
    private float[] densities;
    private Slicer slicer;
    private BSProperties bsp;
    private Point2d[] positions;
   
   
    private File gcodeFile;
   
    BusyStateListener bsl;
   
    public GCodeView(){
        gCodeArea = new JTextArea();
        content = createContent();
       
        ActionListener alUpdate = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
               
                bsl.setBusy(true);
                new Thread(new Runnable() {

                    @Override
                    public void run() {
                        try{
                            updatePrintSettings();
                        } catch (TopologyException ex){
                            UserMessage.showError("Geometry Error", "A geometry error occurred during slicing. Try smoothing infill areas!");
                            ex.printStackTrace();
                        }
                        bsl.setBusy(false);
                    }
                }).start();
            }
        };
        ActionListener alGCode = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
               
                bsl.setBusy(true);
                new Thread(new Runnable() {

                    @Override
                    public void run() {
                        try{
                            generateGcode();
                        } catch (TopologyException ex){
                            UserMessage.showError("Geometry Error", "A geometry error occurred during slicing. Try smoothing infill areas!");
                            ex.printStackTrace();
                        }
                        bsl.setBusy(false);
                    }
                }).start();
            }
        };
        ActionListener alSave = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                saveGCode();
            }
        };
       
        controls = new GCodeViewControls(alUpdate, alGCode, alSave,
                                    getSettings(BSConstants.PRINT_DIR_NAME),
                                    getSettings(BSConstants.PRINTER_DIR_NAME),
                                    getSettings(BSConstants.FILAMENT_DIR_NAME));
       
        menus = buildMenus();
    }

    private void saveGCode() {
        File f;
        if(gcodeFile != null)
            f = gcodeFile;
        else
            f = BSConstants.workFile;
       
        String path = f.getPath();
        String base = FilenameUtils.getFullPath(path);
        String name = FilenameUtils.getBaseName(path);
       
        if(controls.getPrintPairs())
            name += "_pairs";
        else if(!controls.getPrintOptimized())
            name += "_uniform_equivalent";
       
        int count = controls.getPartCount();
        name += "x" + count;
       
        name += ".gcode";
        f = FileUtils.getFile(base, name);
       
       
        JFileChooser fc = new JFileChooser();
        fc.setSelectedFile(f);
        fc.setFileFilter(new FileNameExtensionFilter("*.gcode", "gcode"));
       
        int result = fc.showSaveDialog(null);
        if(result == JFileChooser.APPROVE_OPTION){
            try {
                slicer.writeToFile(fc.getSelectedFile());
            } catch (IOException ex) {
                UserMessage.showError("Error writing file!", "The gcode file could not be written!");
            }
        }
    }
   
    private void updatePrintSettings() throws TopologyException{
        bsp = new BSProperties();
       
        try {
            File fPrint = controls.getPrint();
            File fPrinter = controls.getPrinter();
            File fFilament = controls.getFilament();
            bsp.loadProperties(fPrint, fFilament, fPrinter);
        } catch (IOException ex) {
            UserMessage.showError("Error Loading Settings",
                    "An error occurred while loading the selected settings "
                            + "files. Default settings will be used.");
        }
       
        Envelope bb = areaSet.getEnvelope();// bounding box
        final double margin = bsp.skirt_distance + 1; // min 1 mm distance to print bed boundaries
       
        int x = (int)((bsp.bed_size[0] - 2*margin + bsp.gap) / (bb.getWidth() + bsp.gap)); // x num of tiles
        int y = (int)((bsp.bed_size[1] - 2*margin + bsp.gap) / (bb.getHeight() + bsp.gap)); // y num of tiles
       
        positions = getTilePositions(areaSet, x, y, bsp);
       
        if(OPTIMIZE_PACKING){
            for (int i = y; i > y/2; i--) {
                Point2d[] newPos = optimizePacking(areaSet, i, bsp);
                if(newPos.length > positions.length){
                    positions = newPos;
                    System.out.println("successful packing!");
                }
            }
        }
       
        controls.setMaxTiles(positions.length/y, y);
       
       
        if(x == 0 || y == 0){
            UserMessage.showError("Error!", "Object does not fit on print bed!");
        }
       
        optimized = new PrintObject(areaSet, toDouble(densities), controls.getObjectHeight());
        uniform = optimized.getUniform();
       
        double filament = slice(optimized);
        double eqDensity = getEquivalentDensity(uniform, filament, 0.0005);
       
        uniform.setDensity(eqDensity);
        controls.setResultingDensity(eqDensity);
    }
   
    private Point2d[] optimizePacking(AreaSet as, int lines, BSProperties bsp){
        double lineHeight = (bsp.bed_size[1] - 2*(bsp.extrusion_width + bsp.skirt_distance) - (lines-1)*bsp.gap)/lines;
        Geometry shell0 = (Geometry)as.getShell().clone();
        Envelope env0 = shell0.getEnvelopeInternal();
       
        AffineTransformation t;
        t = AffineTransformation.translationInstance(-env0.getMinX(), -env0.getMinY());
        shell0 = t.transform(shell0);
        env0 = shell0.getEnvelopeInternal();
       
        Geometry shell1 = (Geometry)shell0.clone();
        t = AffineTransformation.translationInstance(env0.getWidth(), lineHeight - env0.getHeight());
        shell1 = t.transform(shell1);
       
        double dist = shell0.distance(shell1);
        double error = dist-2.5;
        while(Math.abs(error) > 0.5){
            t = AffineTransformation.translationInstance(-error/2, 0);
            shell1 = t.transform(shell1);
            dist = shell0.distance(shell1);
            System.out.println("shell 1 distance: " + dist);
            error = dist-2.5;
        }
        Envelope env1 = shell1.getEnvelopeInternal();
       
        Geometry shell2 = (Geometry)shell1.clone();
        t = AffineTransformation.translationInstance(env1.getWidth(), -env1.getMinY());
        shell2 = t.transform(shell2);
       
        dist = shell1.distance(shell2);
        error = dist-2.5;
        while(Math.abs(error) > 0.5){
            t = AffineTransformation.translationInstance(-error/3, 0);
            shell2 = t.transform(shell2);
            dist = shell1.distance(shell2);
            System.out.println("shell 2 distance: " + dist);
            error = dist-2.5;
        }
        Envelope env2 = shell2.getEnvelopeInternal();
       
        Coordinate c0 = env0.centre();
        Coordinate c1 = env1.centre();
        Coordinate c2 = env2.centre();
       
        double[] dx = new double[]{c2.x - c1.x, c1.x - c0.x};
        double gap = bsp.extrusion_width + bsp.skirt_distance;
        double[] py = new double[]{c0.y + gap, c1.y + gap};
       
        double minX = gap + env0.getWidth()/2;
        double maxX = bsp.bed_size[0] - minX;
       
        ArrayList<Point2d> centers = new ArrayList<>();
       
        double curX = minX + env0.getWidth()/2;
        centers.add(new Point2d(curX, py[0]));
        for (int i = 1; true; i++) {
            curX += dx[i%2];
            if(curX > maxX)
                break;
            centers.add(new Point2d(curX, py[i%2]));
        }
       
        int xCount = centers.size();
        double dy = lineHeight + bsp.gap;
        for (int i = 1; i < lines; i++) {
            for (int j = 0; j < xCount; j++) {
                Point2d pj = centers.get(j);
                centers.add(new Point2d(pj.x, pj.y + i*dy));
            }
        }
       
        return centers.toArray(new Point2d[centers.size()]);
    }
   
    private double slice(List<PrintObject> objects) throws TopologyException{
        slicer = new Slicer(bsp);
        slicer.slice(objects);
        return slicer.getFilamentUsed();
    }
   
    private double slice(PrintObject obj) throws TopologyException{
        List<PrintObject> o = new ArrayList<>(1);
        o.add(obj);
        return slice(o);
    }
   
    /**
     * Calculates the density for a uniform infill print to use exactly the
     * specified amount of filament.
     * @param obj the Object to print
     * @param filament the amount (length) of filament to be used
     * @param maxError maximum relative error (0.01 = 1% deviation)
     * @return the density for the uniform infill part
     */
    private double getEquivalentDensity(PrintObject obj, double filament, double maxError) throws TopologyException{
       
        double filamentUsed, error = Double.MAX_VALUE;
        double minD = 0, maxD = 1; //min and max density
        double eqD = 0.5; // equivalent density (to be determined)
       
        while(Math.abs(error) > (filament * maxError)) {
            eqD = (minD + maxD) / 2;
            obj.setDensity(eqD);
            filamentUsed = slice(obj);
           
            error = filament - filamentUsed;
            System.out.println("error: " + error + " at density: " + eqD);
           
            if(error > 0) // using too little filament
                minD = eqD;
            else // using too much filament
                maxD = eqD;
           
            if(maxD-minD < 0.0000000001){
                System.out.println("best possible approximation reached!");
                break;
            }
        }

        return eqD;
    }
   
    private void generateGcode() throws TopologyException{
        int count = controls.getPartCount();
        boolean pairs = controls.getPrintPairs();
        boolean printOptimized = controls.getPrintOptimized();
       
//        List<PrintObject> objects = getTiles(x, y, pairs, printOptimized);
        List<PrintObject> objects = getPrintObjects(count, pairs, printOptimized);
        slice(objects);
       
        gCodeArea.setText(slicer.getGCode());
    }
   
    private Point2d[] getTilePositions(AreaSet as, int x, int y, BSProperties bsp){
        Envelope bb = as.getEnvelope();// bounding box
       
        // determine center for first tile and distance to next tile
        double widthX = x * (bb.getWidth() + bsp.gap) - bsp.gap;
        double t1x = bsp.print_center[0] - (widthX - bb.getWidth())/2;
        double distx = bb.getWidth() + bsp.gap;
        double widthY = y * (bb.getHeight() + bsp.gap) - bsp.gap;
        double t1y = bsp.print_center[1] - (widthY - bb.getHeight())/2;
        double disty = bb.getHeight() + bsp.gap;
       
        Point2d[] positions = new Point2d[x*y];
        for (int ix = 0; ix < x; ix++) {
            for (int iy = 0; iy < y; iy++) {
                positions[ix*y + iy] = new Point2d(t1x + ix*distx, t1y + iy*disty);
            }
        }
       
        return positions;
    }
   
    private List<PrintObject> getPrintObjects(int count, boolean pairs, boolean printOptimized){
        ArrayList<PrintObject> tiles = new ArrayList<>(count);
        PrintObject obj;
       
        for (int i = 0; i < count; i++) {

            if(pairs){
                if(i % 2 == 0)
                    obj = optimized;
                else
                    obj = uniform;
            } else {
                if(printOptimized)
                    obj = optimized;
                else
                    obj = uniform;
            }
            tiles.add(new PrintObject(obj, positions[i]));
        }
       
        return tiles;
    }
   
    private List<PrintObject> getTiles(int x, int y, boolean pairs, boolean printOptimized){
       
        Envelope bb = optimized.getAreaSet().getEnvelope();// bounding box
       
        // determine center for first tile and distance to next tile
        double widthX = x * (bb.getWidth() + bsp.gap) - bsp.gap;
        double t1x = bsp.print_center[0] - (widthX - bb.getWidth())/2;
        double distx = bb.getWidth() + bsp.gap;
        double widthY = y * (bb.getHeight() + bsp.gap) - bsp.gap;
        double t1y = bsp.print_center[1] - (widthY - bb.getHeight())/2;
        double disty = bb.getHeight() + bsp.gap;
       
        ArrayList<PrintObject> tiles = new ArrayList<>(x * y);
        int counter = 0;
        PrintObject obj;
        for (int ix = 0; ix < x; ix++) {
            for (int iy = 0; iy < y; iy++) {
                if(pairs){
                    if(counter % 2 == 0)
                        obj = optimized;
                    else
                        obj = uniform;
                } else {
                    if(printOptimized)
                        obj = optimized;
                    else
                        obj = uniform;
                }
                Point2d pos = new Point2d(t1x + ix*distx, t1y + iy*disty);
                tiles.add(new PrintObject(obj, pos));
               
                counter++;
            }
        }
       
        if(pairs && tiles.size() % 2 == 1){
            tiles.remove(tiles.size()-1);
            tiles.trimToSize();
        }
       
        return tiles;
    }
   
    private static double getAverage(double[] f){
        double a = 0;
       
        for (double g : f)
            a += g;
       
        return a / f.length;
    }
   
    private static double[] toDouble(float[] f){
        double[] d = new double[f.length];
        for (int i = 0; i < f.length; i++)
            d[i] = f[i];
        return d;
    }
   
    private JComponent createContent(){
        gCodeArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
        gCodeArea.setEditable(false);
       
        return new JScrollPane(gCodeArea);
    }
   
    private JMenu[] buildMenus(){
        JMenu[] ms = new JMenu[0];
        return ms;
    }
   
    private File[] getSettings(String settings){
        ArrayList<File> files = new ArrayList<>();
       
        File f = FileUtils.getFile(BSConstants.SETTINGS_DIR, settings);
        File[] list = f.listFiles(BSConstants.INI_EXTENSION_FILTER);
        if(list != null)
            files.addAll(Arrays.asList(list));
       
        f = FileUtils.getFile(BSConstants.SLIC3R_DIR, settings);
        list = f.listFiles(BSConstants.INI_EXTENSION_FILTER);
        if(list != null)
            files.addAll(Arrays.asList(list));
       
        return files.toArray(new File[files.size()]);
    }
   
    @Override
    public JComponent getContent() {
        return content;
    }

    @Override
    public JComponent getControls() {
        return controls;
    }

    @Override
    public JMenu[] getMenus() {
        return menus;
    }

    @Override
    public void init(Object data) {
        if(data instanceof Object[]){
            Object[] odata = (Object[]) data;
           
            if(odata[0] instanceof AreaSet &&
                    odata[1] instanceof float[]){
               
                areaSet = (AreaSet) odata[0];
                densities = (float[]) odata[1];
               
                return;
            }
        }
       
        // invalid data
        areaSet = null;
        gCodeArea.setText("");
    }
 
    @Override
    public Object getData() {
        return null;
    }

    @Override
    public String getName() {
        return NAME;
    }
   
    @Override
    public void setBusyStateListener(BusyStateListener bsl) {
        this.bsl = bsl;
    }
   
}
TOP

Related Classes of bs.bs2d.gui.views.GCodeView

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.