├── G2C.jar ├── .gitattributes ├── lib ├── jgoodies-forms-1.8.0.jar ├── jgoodies-common-1.8.0.jar └── jgoodies-forms-1.8.0-sources.jar ├── src ├── g2c │ ├── preview │ │ ├── ScaledPane.java │ │ ├── GerberColors.java │ │ └── RealScaleImage.java │ ├── gerber │ │ ├── contents │ │ │ ├── Macro.java │ │ │ ├── DrawItem.java │ │ │ └── Aperture.java │ │ └── loader │ │ │ ├── PositionManager.java │ │ │ └── GerberExtensionHandler.java │ ├── printers │ │ ├── Printer.java │ │ └── ElegooMars.java │ └── application │ │ ├── LayerListObject.java │ │ ├── PreviewPane.java │ │ ├── LayerSettings.java │ │ ├── Main.java │ │ ├── CalibrationWizard.java │ │ └── Exporter.java └── photon │ └── file │ ├── parts │ ├── IPhotonProgress.java │ ├── PhotonRow.java │ ├── PhotonLine.java │ ├── PhotonProjectType.java │ ├── PhotonDot.java │ ├── IFileHeader.java │ ├── PhotonMatix.java │ ├── PhotonAaMatrix.java │ ├── PhotonFileMachineInfo.java │ ├── PhotonOutputStream.java │ ├── PhotonFilePrintParameters.java │ ├── photons │ │ └── PhotonsFileHeader.java │ ├── PhotonInputStream.java │ ├── PhotonFilePreview.java │ ├── photon │ │ └── PhotonFileHeader.java │ ├── PhotonLayer.java │ └── PhotonFileLayer.java │ ├── ui │ ├── Text.java │ ├── ScrollPosition.java │ ├── PhotonAALevel.java │ ├── PhotonPreviewImage.java │ ├── PhotonEditPanel.java │ ├── ScrollUtil.java │ ├── PhotonAaPanel.java │ └── PhotonLayerImage.java │ └── PhotonFile.java ├── .gitignore ├── .project ├── .classpath ├── .settings └── org.eclipse.jdt.core.prefs └── README.md /G2C.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TMaxElectronics/Gerber2Chitubox/HEAD/G2C.jar -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /lib/jgoodies-forms-1.8.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TMaxElectronics/Gerber2Chitubox/HEAD/lib/jgoodies-forms-1.8.0.jar -------------------------------------------------------------------------------- /lib/jgoodies-common-1.8.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TMaxElectronics/Gerber2Chitubox/HEAD/lib/jgoodies-common-1.8.0.jar -------------------------------------------------------------------------------- /lib/jgoodies-forms-1.8.0-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TMaxElectronics/Gerber2Chitubox/HEAD/lib/jgoodies-forms-1.8.0-sources.jar -------------------------------------------------------------------------------- /src/g2c/preview/ScaledPane.java: -------------------------------------------------------------------------------- 1 | package g2c.preview; 2 | 3 | import javax.swing.JPanel; 4 | 5 | public class ScaledPane extends JPanel { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/g2c/gerber/contents/Macro.java: -------------------------------------------------------------------------------- 1 | package g2c.gerber.contents; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | 6 | public class Macro { 7 | public List cmds = new LinkedList<>(); 8 | 9 | public void addCmd (String cmd) { 10 | cmds.add(cmd); 11 | } 12 | } -------------------------------------------------------------------------------- /src/g2c/gerber/contents/DrawItem.java: -------------------------------------------------------------------------------- 1 | package g2c.gerber.contents; 2 | import java.awt.Shape; 3 | 4 | public class DrawItem { 5 | public boolean drawCopper; 6 | public Shape shape; 7 | 8 | public DrawItem (Shape shape, boolean drawCopper) { 9 | this.shape = shape; 10 | this.drawCopper = drawCopper; 11 | } 12 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.war 15 | *.nar 16 | *.ear 17 | *.tar.gz 18 | *.rar 19 | 20 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 21 | hs_err_pid* 22 | -------------------------------------------------------------------------------- /src/g2c/preview/GerberColors.java: -------------------------------------------------------------------------------- 1 | package g2c.preview; 2 | 3 | import java.awt.Color; 4 | 5 | public class GerberColors { 6 | public enum PixelValue {blank, outline, primitive, error}; 7 | static final Color Blank = Color.black; 8 | static final Color Outline = Color.pink; 9 | static final Color Primitive = Color.blue; 10 | static final Color Error = Color.red; 11 | } 12 | -------------------------------------------------------------------------------- /src/g2c/printers/Printer.java: -------------------------------------------------------------------------------- 1 | package g2c.printers; 2 | 3 | import java.awt.Dimension; 4 | 5 | public interface Printer { 6 | public Dimension getScreenResolution(); 7 | public Dimension getScreenSizeMM(); 8 | 9 | public double getBedXmm(); 10 | public double getBedYmm(); 11 | public double getBedZmm(); 12 | Dimension getScreenPPI(); 13 | Dimension getBezelMargin(); 14 | String getName();; 15 | } 16 | -------------------------------------------------------------------------------- /src/g2c/gerber/contents/Aperture.java: -------------------------------------------------------------------------------- 1 | package g2c.gerber.contents; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | 6 | public class Aperture { 7 | public int type; 8 | public List parms = new LinkedList<>(); 9 | 10 | public Aperture (int type) { 11 | this.type = type; 12 | } 13 | 14 | public void addParm (double parm) { 15 | parms.add(parm); 16 | } 17 | } -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | Gerber2Chitubox 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=13 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=13 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning 13 | org.eclipse.jdt.core.compiler.release=enabled 14 | org.eclipse.jdt.core.compiler.source=13 15 | -------------------------------------------------------------------------------- /src/g2c/gerber/loader/PositionManager.java: -------------------------------------------------------------------------------- 1 | package g2c.gerber.loader; 2 | 3 | import java.awt.Rectangle; 4 | import java.awt.Shape; 5 | 6 | public class PositionManager { 7 | private static Rectangle.Double bounds = new Rectangle.Double(); // Computed bounding box for PCB layer 8 | private static double minX = 100000.0, minY = 100000.0; 9 | 10 | public static void reset() { 11 | bounds = new Rectangle.Double(); 12 | minX = 100000.0; 13 | minY = 100000.0; 14 | } 15 | 16 | public static void registerShape(Shape shape) { 17 | if(shape.getBounds2D().getMinX() < minX) minX = shape.getBounds2D().getMinX(); 18 | if(shape.getBounds2D().getMinY() < minY) minY = shape.getBounds2D().getMinY(); 19 | bounds.add(shape.getBounds2D()); 20 | } 21 | 22 | public static double getMinX() { 23 | return minX; 24 | } 25 | 26 | public static double getHeight() { 27 | return bounds.getHeight(); 28 | } 29 | 30 | public static double getMinY() { 31 | return minY; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/g2c/printers/ElegooMars.java: -------------------------------------------------------------------------------- 1 | package g2c.printers; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.Rectangle; 5 | 6 | public class ElegooMars implements Printer { 7 | 8 | @Override 9 | public Dimension getScreenResolution() { 10 | Dimension ret = new Dimension(); 11 | ret.setSize(2560, 1440); 12 | return ret; 13 | } 14 | 15 | @Override 16 | public Dimension getBezelMargin() { 17 | Dimension ret = new Dimension(); 18 | ret.setSize(10, 10); 19 | return ret; 20 | } 21 | 22 | @Override 23 | public Dimension getScreenSizeMM() { 24 | Dimension ret = new Dimension(); 25 | ret.setSize(120.96, 68.04); 26 | return ret; 27 | } 28 | 29 | @Override 30 | public Dimension getScreenPPI() { 31 | Dimension res = getScreenResolution(); 32 | Dimension siz = getScreenSizeMM(); 33 | Dimension ret = new Dimension(); 34 | 35 | ret.setSize((res.getWidth() * 25.4) / siz.getWidth(), (res.getHeight() * 25.4) / siz.getHeight()); 36 | return ret; 37 | } 38 | 39 | @Override 40 | public double getBedXmm() { 41 | return getScreenSizeMM().getWidth(); 42 | } 43 | 44 | @Override 45 | public double getBedYmm() { 46 | return getScreenSizeMM().getHeight(); 47 | } 48 | 49 | @Override 50 | public double getBedZmm() { 51 | return 150.0; 52 | } 53 | 54 | @Override 55 | public String getName() { 56 | return "Elegoo Mars"; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/photon/file/parts/IPhotonProgress.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | /** 28 | * by bn on 05/07/2018. 29 | */ 30 | public interface IPhotonProgress { 31 | void showInfo(String str); 32 | } 33 | -------------------------------------------------------------------------------- /src/photon/file/parts/PhotonRow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | import java.util.ArrayList; 28 | 29 | /** 30 | * by bn on 10/07/2018. 31 | */ 32 | public class PhotonRow { 33 | public ArrayList lines = new ArrayList<>(); 34 | } 35 | -------------------------------------------------------------------------------- /src/g2c/application/LayerListObject.java: -------------------------------------------------------------------------------- 1 | package g2c.application; 2 | 3 | import java.awt.Color; 4 | import java.awt.geom.AffineTransform; 5 | import java.awt.image.BufferedImage; 6 | 7 | import javax.swing.JCheckBox; 8 | 9 | import g2c.gerber.loader.GerberLoader; 10 | 11 | public class LayerListObject extends JCheckBox{ 12 | GerberLoader gerber; 13 | String name = "layer"; 14 | Color color = Color.pink; 15 | 16 | public LayerListObject(GerberLoader gerber, Color c, String name) { 17 | color = c; 18 | this.gerber = gerber; 19 | this.name = name; 20 | gerber.readFile(); 21 | gerber.renderImage(c); 22 | } 23 | 24 | public LayerListObject(String name) { 25 | this.name = name; 26 | } 27 | 28 | public BufferedImage getImage() { 29 | return gerber.getImage(); 30 | } 31 | 32 | public BufferedImage getCorrectedImage() { 33 | return gerber.getCorrectedImage(); 34 | } 35 | 36 | public void setColor(Color c) { 37 | color = c; 38 | gerber.renderImage(c); 39 | } 40 | 41 | public String toString() { 42 | return name; 43 | } 44 | 45 | public String getText() { 46 | return name; 47 | } 48 | 49 | public void forceRender() { 50 | gerber.renderImage(); 51 | } 52 | 53 | public Color getColor() { 54 | return color; 55 | } 56 | 57 | public String getName() { 58 | return name; 59 | } 60 | 61 | public void setName(String newName) { 62 | this.name = newName; 63 | } 64 | 65 | public void applyCorrection(double xOffset, double yOffset, int angle) { 66 | gerber.applyCorrection(xOffset, yOffset, angle); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/photon/file/ui/Text.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.ui; 26 | 27 | public class Text { 28 | 29 | public static String formatSeconds(float time) { 30 | if (time % 1 == 0) { 31 | return String.format("%.0fs", time); 32 | } else { 33 | return String.format("%.1fs", time); 34 | } 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/photon/file/parts/PhotonLine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | import java.awt.*; 28 | 29 | /** 30 | * by bn on 10/07/2018. 31 | */ 32 | public class PhotonLine { 33 | public Color color; 34 | public int length; 35 | 36 | public PhotonLine(Color color, int length) { 37 | this.color = color; 38 | this.length = length; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/g2c/preview/RealScaleImage.java: -------------------------------------------------------------------------------- 1 | package g2c.preview; 2 | 3 | import java.awt.Color; 4 | import java.awt.image.BufferedImage; 5 | import java.io.ByteArrayInputStream; 6 | import java.io.InputStream; 7 | 8 | import javax.imageio.ImageIO; 9 | 10 | import g2c.preview.GerberColors.PixelValue; 11 | 12 | public class RealScaleImage { 13 | double ppmX, ppmY; 14 | boolean renderIsUpToDate = false; 15 | PixelValue[][] renderedImageData; 16 | Color background = Color.black, foreground = Color.red; 17 | 18 | public RealScaleImage(double ppmX, double ppmY, int width, int heigt) { 19 | this.ppmX = ppmX; this.ppmY = ppmY; 20 | renderedImageData = new PixelValue[heigt][width]; 21 | } 22 | 23 | public BufferedImage getRenderedImage() { 24 | // convert byte[] back to a BufferedImage 25 | BufferedImage newBi = new BufferedImage(renderedImageData[0].length, renderedImageData.length, BufferedImage.TYPE_3BYTE_BGR); 26 | int x,y; 27 | for(x = 0; x < newBi.getWidth(); x ++) { 28 | for(y = 0; y < newBi.getHeight(); y ++) { 29 | 30 | switch (renderedImageData[y][x]) { 31 | case blank: 32 | newBi.setRGB(x, y, GerberColors.Blank.getRGB()); 33 | break; 34 | case outline: 35 | newBi.setRGB(x, y, GerberColors.Outline.getRGB()); 36 | break; 37 | case primitive: 38 | newBi.setRGB(x, y, GerberColors.Primitive.getRGB()); 39 | break; 40 | case error: 41 | newBi.setRGB(x, y, GerberColors.Error.getRGB()); 42 | break; 43 | default: 44 | System.out.println("Unexpected value: " + renderedImageData[y][x]); 45 | } 46 | } 47 | } 48 | return newBi; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/photon/file/parts/PhotonProjectType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | /** 28 | * by bn on 30/06/2018. 29 | */ 30 | public enum PhotonProjectType { 31 | cast (0), 32 | lcdMirror (1), 33 | ; 34 | 35 | int projectID; 36 | 37 | PhotonProjectType(int projectID) { 38 | this.projectID = projectID; 39 | } 40 | 41 | public static PhotonProjectType find(int typeID) { 42 | for(PhotonProjectType photonProjectType : values()) { 43 | if (photonProjectType.projectID == typeID) { 44 | return photonProjectType; 45 | } 46 | } 47 | return lcdMirror; 48 | } 49 | 50 | public int getProjectID() { 51 | return projectID; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/photon/file/ui/ScrollPosition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.ui; 26 | 27 | /** 28 | * by bn on 02/07/2018. 29 | */ 30 | public enum ScrollPosition { 31 | 32 | None (0), 33 | Top (1), 34 | VerticalCenter (2), 35 | Bottom (4), 36 | Left (8), 37 | HorizontalCenter (16), 38 | Right (32); 39 | 40 | public int scrollPos; 41 | 42 | ScrollPosition(int scrollPos) { 43 | this.scrollPos = scrollPos; 44 | } 45 | 46 | 47 | public static ScrollPosition contains(int vertical) { 48 | for(ScrollPosition scrollPosition : values()) { 49 | if ((scrollPosition.scrollPos & vertical)>0) { 50 | return scrollPosition; 51 | } 52 | } 53 | return None; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/g2c/application/PreviewPane.java: -------------------------------------------------------------------------------- 1 | package g2c.application; 2 | 3 | import java.awt.BasicStroke; 4 | import java.awt.Color; 5 | import java.awt.Graphics; 6 | import java.awt.Graphics2D; 7 | import java.awt.Toolkit; 8 | 9 | import javax.swing.DefaultListModel; 10 | import javax.swing.JList; 11 | import javax.swing.JPanel; 12 | 13 | public class PreviewPane extends JPanel{ 14 | JList list; 15 | DefaultListModel objects; 16 | LayerListObject layer; 17 | 18 | boolean singleLayer = false; 19 | 20 | public PreviewPane(JList list) { 21 | this.list = list; 22 | objects = (DefaultListModel) list.getModel(); 23 | } 24 | 25 | public PreviewPane(LayerListObject layer) { 26 | this.layer = layer; 27 | singleLayer = true; 28 | } 29 | 30 | 31 | 32 | @Override 33 | public void paint (Graphics g) { 34 | double scaleX = (double) (getWidth() - 6) / Main.currPrinter.getScreenResolution().getWidth(); 35 | double scaleY = (double) (getHeight() - 6) / Main.currPrinter.getScreenResolution().getHeight(); 36 | double scale = Math.min(scaleX, scaleY); 37 | int width = (int) (scale * Main.currPrinter.getScreenResolution().getWidth()); 38 | int height = (int) (scale * Main.currPrinter.getScreenResolution().getHeight()); 39 | 40 | if(singleLayer) { 41 | g.drawImage(layer.getImage(), 3, 3, width, height, null); 42 | 43 | }else { 44 | 45 | if(list.getSelectedValue() == null) { 46 | for(int i = 0; i < list.getModel().getSize(); i++) { 47 | System.out.println("now"); 48 | g.drawImage(objects.getElementAt(i).getImage(), 3, 3, width, height, null); 49 | } 50 | }else { 51 | g.drawImage(list.getSelectedValue().getImage(), 3, 3, width, height, null); 52 | } 53 | } 54 | if(objects.size() > 0) { 55 | ((Graphics2D) g).setColor(Color.gray); 56 | ((Graphics2D) g).setStroke(new BasicStroke(2)); 57 | ((Graphics2D) g).drawRect(3, 3, width, height); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/photon/file/parts/PhotonDot.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | /** 28 | * by bn on 02/07/2018. 29 | */ 30 | public class PhotonDot { 31 | public int x; 32 | public int y; 33 | 34 | public PhotonDot(int x, int y) { 35 | this.x = x; 36 | this.y = y; 37 | } 38 | 39 | @Override 40 | public boolean equals(Object o) { 41 | if (this == o) return true; 42 | if (!(o instanceof PhotonDot)) return false; 43 | 44 | PhotonDot photonDot = (PhotonDot) o; 45 | 46 | return x == photonDot.x && y == photonDot.y; 47 | 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | int result = x; 53 | result = 31 * result + y; 54 | return result; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/g2c/gerber/loader/GerberExtensionHandler.java: -------------------------------------------------------------------------------- 1 | package g2c.gerber.loader; 2 | 3 | import java.awt.Color; 4 | 5 | public class GerberExtensionHandler { 6 | public static Color getDefaultLayerColor(String extension) { 7 | switch (extension.toLowerCase()) { 8 | case ".gbl": 9 | return Color.blue; 10 | case ".gtl": 11 | return Color.red; 12 | case ".gbs": 13 | return new Color(0xff00ff); 14 | case ".gts": 15 | return new Color(0x800080); 16 | case ".gbo": 17 | return new Color(0x808000); 18 | case ".gto": 19 | return new Color(0xFFFF00); 20 | case ".gbp": 21 | return new Color(0x800000); 22 | case ".gtp": 23 | return new Color(0x808080); 24 | case ".g1": 25 | return new Color(0xBC8E00); 26 | case ".g2": 27 | return new Color(0x70DBFA); 28 | case ".gm8": //altium outline 29 | case ".gko": 30 | return new Color(0xFF9900); 31 | case ".xln": 32 | case ".txt": 33 | return new Color(0xC0C0C0); 34 | case ".gm9": 35 | return new Color(0x008000); 36 | } 37 | return Color.CYAN; 38 | } 39 | 40 | public static String getDefaultLayerName(String extension) { 41 | switch (extension.toLowerCase()) { 42 | case ".gbl": 43 | return "Copper bottom"; 44 | case ".gtl": 45 | return "Copper top"; 46 | case ".gbs": 47 | return "Soldermask bottom"; 48 | case ".gts": 49 | return "Soldermask top"; 50 | case ".gbo": 51 | return "Silkscreen bottom"; 52 | case ".gto": 53 | return "Silkscreen top"; 54 | case ".gbp": 55 | return "Paste bottom"; 56 | case ".gtp": 57 | return "Paste top"; 58 | case ".g1": 59 | return "Copper inner 1"; 60 | case ".g2": 61 | return "Copper inner 2"; 62 | case ".g3": 63 | return "Copper inner 3"; 64 | case ".g4": 65 | return "Copper inner 4"; 66 | case ".gm8": //altium outline 67 | case ".gko": 68 | return "Outline"; 69 | case ".xln": 70 | case ".txt": 71 | return "Drills"; 72 | case ".gm9": 73 | return "Milling"; 74 | } 75 | return "layer"; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/g2c/application/LayerSettings.java: -------------------------------------------------------------------------------- 1 | package g2c.application; 2 | 3 | import java.awt.EventQueue; 4 | 5 | import javax.swing.JFrame; 6 | import javax.swing.JLabel; 7 | import javax.swing.JButton; 8 | import javax.swing.JColorChooser; 9 | import javax.swing.JPanel; 10 | import javax.swing.JTextField; 11 | import javax.swing.event.ChangeEvent; 12 | import javax.swing.event.ChangeListener; 13 | 14 | import java.awt.event.ActionListener; 15 | import java.awt.event.ActionEvent; 16 | 17 | public class LayerSettings { 18 | 19 | JFrame frame; 20 | private JTextField textField; 21 | JPanel panel = new JPanel(); 22 | 23 | /** 24 | * Initialize the contents of the frame. 25 | */ 26 | public LayerSettings(LayerListObject obj) { 27 | frame = new JFrame(); 28 | frame.setBounds(100, 100, 450, 118); 29 | frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); 30 | frame.getContentPane().setLayout(null); 31 | 32 | JButton btnNewButton = new JButton("Choose"); 33 | btnNewButton.addActionListener(new ActionListener() { 34 | public void actionPerformed(ActionEvent e) { 35 | obj.setColor(JColorChooser.showDialog(frame, "Choose a new color for " + obj.getName(), obj.getColor())); 36 | panel.setBackground(obj.getColor()); 37 | Main.frame.repaint(); 38 | } 39 | }); 40 | btnNewButton.setBounds(335, 45, 89, 23); 41 | frame.getContentPane().add(btnNewButton); 42 | 43 | panel.setBackground(obj.getColor()); 44 | panel.setBounds(300, 45, 25, 23); 45 | frame.getContentPane().add(panel); 46 | 47 | JLabel lblNewLabel = new JLabel("Color"); 48 | lblNewLabel.setBounds(10, 45, 280, 23); 49 | frame.getContentPane().add(lblNewLabel); 50 | 51 | JLabel lblName = new JLabel("Name"); 52 | lblName.setBounds(10, 11, 89, 23); 53 | frame.getContentPane().add(lblName); 54 | 55 | textField = new JTextField(obj.getName()); 56 | textField.addActionListener(new ActionListener() { 57 | public void actionPerformed(ActionEvent e) { 58 | obj.setName(textField.getText()); 59 | Main.frame.repaint(); 60 | } 61 | }); 62 | textField.setBounds(109, 11, 315, 23); 63 | frame.getContentPane().add(textField); 64 | textField.setColumns(10); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/photon/file/ui/PhotonAALevel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.ui; 26 | 27 | public enum PhotonAALevel { 28 | NoAntiAliase (1,1, "No Anti Aliase"), 29 | AA2 (2, 2, "2 Layers AA"), 30 | AA4 (3, 4, "4 Layers AA"), 31 | AA8 (4, 8, "8 Layers AA"), 32 | AA16 (5, 16, "16 Layers AA"), 33 | 34 | ; 35 | 36 | public int index; 37 | public int levels; 38 | public String name; 39 | 40 | PhotonAALevel(int index, int levels, String name) { 41 | this.index = index; 42 | this.levels = levels; 43 | this.name = name; 44 | } 45 | 46 | public static PhotonAALevel find(int id) { 47 | for(PhotonAALevel photonAALevel : values()) { 48 | if (photonAALevel.index==id) { 49 | return photonAALevel; 50 | } 51 | } 52 | return NoAntiAliase; 53 | } 54 | 55 | public static PhotonAALevel findByLevel(int id) { 56 | for(PhotonAALevel photonAALevel : values()) { 57 | if (photonAALevel.levels==id) { 58 | return photonAALevel; 59 | } 60 | } 61 | return NoAntiAliase; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/photon/file/parts/IFileHeader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | import java.util.List; 28 | 29 | public interface IFileHeader { 30 | 31 | String getInformation(); 32 | int getNumberOfLayers(); 33 | 34 | int getResolutionY(); 35 | int getResolutionX(); 36 | 37 | float getBuildAreaX(); 38 | float getBuildAreaY(); 39 | 40 | int getBottomLayers(); 41 | void setBottomLayers(int bottomLayers); 42 | 43 | float getLayerHeight(); 44 | 45 | float getExposureTimeSeconds(); 46 | 47 | float getBottomExposureTimeSeconds(); 48 | void setExposureBottomTimeSeconds(float exposureBottomTimeSeconds); 49 | 50 | void setExposureTimeSeconds(float exposureTimeSeconds); 51 | float getNormalExposure(); 52 | 53 | float getOffTimeSeconds(); 54 | void setOffTimeSeconds(float offTimeSeconds); 55 | 56 | int getPrintTimeSeconds(); 57 | boolean isMirrored(); 58 | 59 | boolean hasAA(); 60 | int getAALevels(); 61 | void setAALevels(int levels, List layers); 62 | 63 | int getVersion(); 64 | void setFileVersion(int i); 65 | 66 | int getByteSize(); 67 | void unLink(); 68 | void setPrintTimeSeconds(int time); 69 | } 70 | -------------------------------------------------------------------------------- /src/photon/file/ui/PhotonPreviewImage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.ui; 26 | 27 | import photon.file.parts.PhotonFilePreview; 28 | 29 | import javax.swing.*; 30 | import java.awt.*; 31 | import java.awt.image.BufferedImage; 32 | 33 | /** 34 | * by bn on 03/07/2018. 35 | */ 36 | public class PhotonPreviewImage extends JPanel { 37 | private BufferedImage image; 38 | 39 | public PhotonPreviewImage(int width, int height) { 40 | if (width>0 && height>0) { 41 | image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 42 | setPreferredSize(new Dimension(width, height)); 43 | } 44 | } 45 | 46 | @Override 47 | protected void paintComponent(Graphics g) { 48 | if (image!=null) { 49 | g.drawImage(image, 0, 0, null); 50 | } 51 | } 52 | 53 | public void reInit(int width, int height) { 54 | if (width>0 && height>0) { 55 | image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 56 | setPreferredSize(new Dimension(width, height)); 57 | } 58 | } 59 | 60 | public void drawImage(PhotonFilePreview preview) { 61 | if (image!=null && preview.getResolutionX()>0 && preview.getResolutionY()>0) { 62 | image.getRaster().setDataElements(0, 0, preview.getResolutionX(), preview.getResolutionY(), preview.getImageData()); 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/photon/file/parts/PhotonMatix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | /** 28 | * by bn on 14/07/2018. 29 | */ 30 | public class PhotonMatix { 31 | 32 | public Integer[][] calcMatrix = new Integer[5][5]; 33 | 34 | public void clear() { 35 | for (int y = 0; y < 5; y++) { 36 | for (int x = 0; x < 5; x++) { 37 | calcMatrix[y][x] = 0; 38 | } 39 | } 40 | } 41 | 42 | 43 | public int set(int x, int y, byte[][] iArray, int width, int height) { 44 | int blanks = 0; 45 | int x0 = x - 2; 46 | int y0 = y - 2; 47 | for (int yi = 0; yi < 5; yi++) { 48 | for (int xi = 0; xi < 5; xi++) { 49 | int y2 = y0 + yi; 50 | int x2 = x0 + xi; 51 | if (y2 >= 0 && y2 < height && x2 >= 0 && x2 < width) { 52 | switch (iArray[y2][x2]) { 53 | case PhotonLayer.SUPPORTED: 54 | case PhotonLayer.CONNECTED: 55 | calcMatrix[yi][xi] = 16; 56 | break; 57 | 58 | case PhotonLayer.ISLAND: 59 | calcMatrix[yi][xi] = 4; 60 | break; 61 | 62 | case PhotonLayer.OFF: 63 | if (yi > 0 && yi < 4 && xi > 0 && xi < 4) { 64 | blanks++; 65 | } 66 | break; 67 | } 68 | } 69 | } 70 | } 71 | return blanks; 72 | } 73 | 74 | 75 | public void calc() { 76 | Integer[][] temp = new Integer[3][3]; 77 | for (int yi = 0; yi < 3; yi++) { 78 | for (int xi = 0; xi < 3; xi++) { 79 | if (calcMatrix[yi+1][xi+1]==PhotonLayer.OFF) { 80 | temp[yi][xi] = calc(xi+1, yi+1); 81 | } 82 | } 83 | } 84 | for (int yi = 0; yi < 3; yi++) { 85 | for (int xi = 0; xi < 3; xi++) { 86 | if (calcMatrix[yi+1][xi+1]==PhotonLayer.OFF) { 87 | calcMatrix[yi+1][xi+1] = temp[yi][xi]; 88 | } 89 | } 90 | } 91 | } 92 | 93 | private int calc(int x, int y) { 94 | return (calcMatrix[y-1][x] / 4) + (calcMatrix[y][x-1] / 4) + (calcMatrix[y][x+1] / 4) + (calcMatrix[y+1][x] / 4); 95 | } 96 | 97 | public void level() { 98 | for (int yi = 0; yi < 5; yi++) { 99 | for (int xi = 0; xi < 5; xi++) { 100 | if (calcMatrix[yi][xi]<4) calcMatrix[yi][xi] = 0; 101 | } 102 | } 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/photon/file/parts/PhotonAaMatrix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | public class PhotonAaMatrix { 28 | 29 | public Integer[][] aaMatrix = new Integer[5][5]; 30 | private Boolean[] hasDivisor = new Boolean[5]; 31 | 32 | public void clear() { 33 | for (int y = 0; y < 5; y++) { 34 | for (int x = 0; x < 5; x++) { 35 | aaMatrix[y][x] = 0; 36 | } 37 | } 38 | } 39 | 40 | public void set(int x, int y, int val) { 41 | aaMatrix[y-1][x-1] = val; 42 | } 43 | 44 | public int[][] calc(int[][] source) { 45 | int[][] target = null; 46 | 47 | if (source!=null) { 48 | target = source.clone(); 49 | 50 | int divisor = 0; 51 | for (int y = 0; y < 5; y++) { 52 | int rowDivistor = 0; 53 | for (int x = 0; x < 5; x++) { 54 | rowDivistor += aaMatrix[y][x]; 55 | } 56 | hasDivisor[y] = (rowDivistor>0); 57 | divisor += rowDivistor; 58 | } 59 | 60 | if (divisor >0) { 61 | int height = source.length; 62 | if (height > 0) { 63 | int width = source[0].length; 64 | if (width > 0) { 65 | int sum;; 66 | int dy; 67 | int dx; 68 | for (int y = 0; y < height; y++) { 69 | for (int x = 0; x < width; x++) { 70 | sum = 0; 71 | for (int cy = -2; cy <= 2; cy++) { 72 | if (hasDivisor[2+cy]) 73 | for (int cx = -2; cx <= 2; cx++) { 74 | dy = y+cy; 75 | dx = x+cx; 76 | if (dy>=0 && dy< height) { 77 | if (dx>=0 && dx< width) { 78 | sum += source[dy][dx] * aaMatrix[2+cy][2+cx]; 79 | } else { 80 | sum += source[y][x] * aaMatrix[2+cy][2+cx]; 81 | } 82 | } else { 83 | sum += source[y][x] * aaMatrix[2+cy][2+cx]; 84 | } 85 | } 86 | } 87 | target[y][x] = sum / divisor; 88 | } 89 | } 90 | } 91 | } 92 | } 93 | } 94 | return target; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/photon/file/parts/PhotonFileMachineInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | import java.io.ByteArrayInputStream; 28 | import java.util.Arrays; 29 | 30 | /** 31 | * by bn on 01/07/2018. 32 | */ 33 | public class PhotonFileMachineInfo { 34 | private int u1, u2, u3, u4, u5, u6, u7; 35 | 36 | private int machineNameAddress; 37 | private int machineNameSize; 38 | private byte[] machineName = {}; 39 | 40 | private int u8, u9, u10, u11, u12, u13, u14, u15, u16, u17; 41 | 42 | private int infoByteSize; 43 | 44 | public PhotonFileMachineInfo() { 45 | machineName = "ELEGOO MARS".getBytes(); 46 | this.infoByteSize = 76; 47 | } 48 | 49 | public PhotonFileMachineInfo(int address, int byteSize, byte[] file) throws Exception { 50 | 51 | this.infoByteSize = byteSize; 52 | 53 | if (byteSize > 0) { 54 | byte[] data = Arrays.copyOfRange(file, address, address + byteSize); 55 | 56 | try (PhotonInputStream ds = new PhotonInputStream(new ByteArrayInputStream(data))) { 57 | u1 = ds.readInt(); 58 | u2 = ds.readInt(); 59 | u3 = ds.readInt(); 60 | u4 = ds.readInt(); 61 | u5 = ds.readInt(); 62 | u6 = ds.readInt(); 63 | u7 = ds.readInt(); 64 | 65 | machineNameAddress = ds.readInt(); 66 | machineNameSize = ds.readInt(); 67 | 68 | u8 = ds.readInt(); 69 | u9 = ds.readInt(); 70 | u10 = ds.readInt(); 71 | u11 = ds.readInt(); 72 | u12 = ds.readInt(); 73 | u13 = ds.readInt(); 74 | u14 = ds.readInt(); 75 | u15 = ds.readInt(); 76 | u16 = ds.readInt(); 77 | u17 = ds.readInt(); 78 | 79 | } 80 | 81 | machineName = Arrays.copyOfRange(file, machineNameAddress, machineNameAddress + machineNameSize); 82 | } 83 | } 84 | 85 | public void save(PhotonOutputStream os, int startAddress) throws Exception { 86 | if (infoByteSize > 0) { 87 | os.writeInt(u1); 88 | os.writeInt(u2); 89 | os.writeInt(u3); 90 | os.writeInt(u4); 91 | os.writeInt(u5); 92 | os.writeInt(u6); 93 | os.writeInt(u7); 94 | os.writeInt(startAddress + infoByteSize); 95 | os.writeInt(machineName.length); 96 | os.writeInt(u8); 97 | os.writeInt(u9); 98 | os.writeInt(u10); 99 | os.writeInt(u11); 100 | os.writeInt(u12); 101 | os.writeInt(u13); 102 | os.writeInt(u14); 103 | os.writeInt(u15); 104 | os.writeInt(u16); 105 | os.writeInt(u17); 106 | os.write(machineName); 107 | } 108 | } 109 | 110 | public int getByteSize() { 111 | return infoByteSize + machineName.length; 112 | } 113 | 114 | 115 | public void unLink() { 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gerber2Chitubox 2 | This is a tool that allows you to expose photosensitive PCBs with lcd masked resin printers with a chitubox controller. 3 | It loads gerbers and generates the file that the printer wants including any mirroring, offsets, drill guides etc. 4 | 5 | THIS IS STILL ONLY IN THE CONCEPT STAGE! The code is working, but pretty much just hacked together to test out the idea. More updates will follow but use the software at your own risk until now. 6 | It currently only supports the scaling for the elegoo mars (infact only MY printer since I calibrated it with that...), I'll add support for custom printer presets at some point. 7 | 8 | # IT'S ALIIIVE :D 9 | After initial testing suggested this project was dead, I spent some more time and who would have thought... it actually does work out very well :) 10 | 11 | The G2C.jar binary a runnable jar file of the software if you want to give it a shot :) 12 | 13 | # Required printer modifications 14 | By default the printers lcd has a glass pane infront of it for protection and likely flatness too. This however reduces exposure accuracy a lot (more like 0.4mm/15mil process) because of the lamp geometry. To fix this you will have to remove the peice of glass. 15 | 16 | **DO THIS AT YOUR OWN RISK, YOU CAN VERY EASILY RUIN YOUR PRINTER WITH THIS** 17 | 18 | First (carefully!) remove the display assembly from the printer. unfortunately it is glued in quite well (at least it was in my printer) which made it impossible to do without damage.You can buy new ones on amazon for ~20€ or other places though so don't sweat it if you break yours (I broke two trying this :P). 19 | 20 | Once you have the screen out you can start seperating the glass pane. The two parts are glued together very well so put a drop or two of isopropanol in between the actual lcd and the glass, then slowly and carefully start seperating the screen with a plastic spudger. It shouldn't take much force at all, if it does add a bit more IPA. 21 | 22 | Then install only the LCD in the printer and put the thinnest non-transparent tape you can find around the outside of it so it looks like this: 23 | 24 | [image](https://no_link_yet) 25 | 26 | This is to prevent the massive light leakage around the display exposing the pcb where it shouldn't. 27 | 28 | I also recommend making some alignment markers like you see in the picture. The ones I made register in the space for the resin tank to make using them repeatable. 29 | This is less critical fo doing single layer exposures, but absolutely essential for more than that. You can make the "alignmen test" pcb to measure the offset you have between top and bottom layer for example, and then compensate for that during the file export. 30 | I'll show how to do this in a video at some point if I find the time. 31 | 32 | **Keep in mind that the printer cannot be used for 3d printing like this**. If you want to go back place the glass pane back on the display, and tape it down around the outside a little. Make sure to als re-level the build plate. It is probably a good idea to clean the glue off the back too, but I haven't found a solvent that works for that yet. 33 | 34 | # Performance 35 | I'm running an old V1 Elegoo mars, with the hack described above. Tested with Bungard pre-sensitised base material at 250s exposure time. 36 | 37 | min. copper spacing: 0.1mm (4mil) 38 | 39 | min. trace width: 0.15mm (6mil) (with some better exposure adjustment 0.1mm is likely possible) 40 | 41 | Convenience wise it is good too. I can go from gerber export to pcb in hand (without soldermask, single layer) in ~30min, which took me more like 1h or more before. 42 | 43 | # fix for gerbers with arcs 44 | At the moment the gerber loader still can't load arcs correctly all the time (tested with altium). 45 | You can go around this issue by un-selecting "use arcs" upon file export. 46 | 47 | # Big thanks to the following projects: 48 | [GerberPlot by wholder](https://github.com/wholder/GerberPlot) ; Heavily modified and used to read the input files. I'll contribute back to the original project once my modifications are proven 49 | 50 | [PhotonFileValidator by Photonsters](https://github.com/Photonsters/PhotonFileValidator) ; Modified to allow for creation of new files, and to allow layer creation from a pixelbuffer 51 | -------------------------------------------------------------------------------- /src/photon/file/ui/PhotonEditPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.ui; 26 | 27 | import photon.file.parts.PhotonLayer; 28 | 29 | import javax.swing.*; 30 | import java.awt.*; 31 | import java.awt.geom.AffineTransform; 32 | import java.awt.image.BufferedImage; 33 | 34 | /** 35 | * by bn on 16/07/2018. 36 | */ 37 | public class PhotonEditPanel extends JPanel { 38 | private int width; 39 | private int height; 40 | 41 | private BufferedImage image; 42 | 43 | private boolean mirrored; 44 | 45 | public PhotonEditPanel(int width, int height) { 46 | this.width = width; 47 | this.height = height; 48 | image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 49 | setPreferredSize(new Dimension(width, height)); 50 | } 51 | 52 | public void setMirrored(boolean mirrored) { 53 | this.mirrored = mirrored; 54 | } 55 | 56 | @Override 57 | protected void paintComponent(Graphics g) { 58 | g.drawImage(image, 0, 0, null); 59 | } 60 | 61 | private Graphics2D createGraphics() { 62 | Graphics2D g = image.createGraphics(); 63 | if (mirrored) { 64 | AffineTransform transform = new AffineTransform(); 65 | transform.setToScale(1, -1); 66 | transform.translate(0, -image.getHeight()); 67 | g.setTransform(transform); 68 | } 69 | return g; 70 | } 71 | 72 | public void drawLayer(int layerX, int layerY, PhotonLayer layer) { 73 | Graphics2D g = createGraphics(); 74 | g.setBackground(Color.decode("#999999")); 75 | 76 | g.clearRect(0, 0, width, height); 77 | 78 | 79 | for (int y = 0; y < 45; y++) { 80 | for (int x = 0; x < 75; x++) { 81 | int x1 = 15 + x * 10; 82 | int y1 = 15 + y * 10; 83 | 84 | switch (layer.get(layerY + y, layerX + x)) { 85 | case PhotonLayer.SUPPORTED: 86 | g.setColor(Color.decode("#008800")); 87 | break; 88 | 89 | case PhotonLayer.CONNECTED: 90 | g.setColor(Color.decode("#FFFF00")); 91 | break; 92 | 93 | case PhotonLayer.ISLAND: 94 | g.setColor(Color.decode("#FF0000")); 95 | break; 96 | 97 | default: 98 | g.setColor(Color.black); 99 | 100 | } 101 | g.fillRect(x1, y1, 9, 9); 102 | } 103 | } 104 | 105 | g.dispose(); 106 | } 107 | 108 | public void drawDot(int layerX, int layerY, PhotonLayer layer, Color color) { 109 | Graphics2D g = createGraphics(); 110 | g.setColor(color); 111 | g.fillRect(15+layerX*10, 15+layerY*10, 9, 9); 112 | g.dispose(); 113 | } 114 | 115 | public void drawRect(Rectangle r, Color color) { 116 | Graphics2D g = createGraphics(); 117 | g.setColor(color); 118 | g.drawRect(15+r.x*10, 15+r.y*10, (r.width + 1) * 10-1, (r.height + 1) * 10-1); 119 | g.dispose(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/photon/file/parts/PhotonOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | import java.io.DataOutput; 28 | import java.io.DataOutputStream; 29 | import java.io.IOException; 30 | import java.io.OutputStream; 31 | 32 | 33 | /** 34 | * by bn on 06/07/2018. 35 | */ 36 | public class PhotonOutputStream extends OutputStream implements DataOutput { 37 | private OutputStream outputStream; 38 | private DataOutputStream dataOutputStream; 39 | 40 | 41 | public PhotonOutputStream(OutputStream outputStream) { 42 | this.outputStream = outputStream; 43 | dataOutputStream = new DataOutputStream(outputStream); 44 | } 45 | 46 | @Override 47 | public void write(byte[] b, int off, int len) throws IOException { 48 | outputStream.write(b, off, len); 49 | } 50 | 51 | @Override 52 | public void writeBoolean(boolean v) throws IOException { 53 | dataOutputStream.writeBoolean(v); 54 | } 55 | 56 | @Override 57 | public void writeByte(int v) throws IOException { 58 | dataOutputStream.writeByte(v); 59 | } 60 | 61 | @Deprecated 62 | @Override 63 | public void writeBytes(String s) throws IOException { 64 | dataOutputStream.writeBytes(s); 65 | } 66 | 67 | @Override 68 | public void writeChar(int v) throws IOException { 69 | writeShort(v); 70 | } 71 | 72 | @Override 73 | public void writeChars(String s) throws IOException { 74 | for (int i = 0; i < s.length(); i++) { 75 | writeChar(s.charAt(i)); 76 | } 77 | } 78 | 79 | @Override 80 | public void writeDouble(double v) throws IOException { 81 | writeLong(Double.doubleToLongBits(v)); 82 | } 83 | 84 | @Override 85 | public void writeFloat(float v) throws IOException { 86 | writeInt(Float.floatToIntBits(v)); 87 | } 88 | 89 | @Override 90 | public void write(int b) throws IOException { 91 | writeInt(b); 92 | } 93 | 94 | @Override 95 | public void writeInt(int v) throws IOException { 96 | outputStream.write(0xFF & v); 97 | outputStream.write(0xFF & (v >> 8)); 98 | outputStream.write(0xFF & (v >> 16)); 99 | outputStream.write(0xFF & (v >> 24)); 100 | } 101 | 102 | @Override 103 | public void writeLong(long v) throws IOException { 104 | byte[] bytes = longToBytes(Long.reverseBytes(v)); 105 | write(bytes, 0, bytes.length); 106 | } 107 | 108 | private byte[] longToBytes(long l) { 109 | byte[] result = new byte[8]; 110 | for (int i = 7; i >= 0; i--) { 111 | result[i] = (byte)(l & 0xFF); 112 | l >>= 8; 113 | } 114 | return result; 115 | } 116 | 117 | @Override 118 | public void writeShort(int v) throws IOException { 119 | outputStream.write(0xFF & v); 120 | outputStream.write(0xFF & (v >> 8)); 121 | } 122 | 123 | @Override 124 | public void writeUTF(String str) throws IOException { 125 | dataOutputStream.writeUTF(str); 126 | } 127 | 128 | @Override 129 | public void close() throws IOException { 130 | outputStream.close(); 131 | } 132 | 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/photon/file/parts/PhotonFilePrintParameters.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | import java.io.ByteArrayInputStream; 28 | import java.util.Arrays; 29 | 30 | public class PhotonFilePrintParameters { 31 | public float bottomLiftDistance = 5.0f; 32 | public float bottomLiftSpeed = 300.0f; 33 | 34 | public float liftingDistance = 5.0f; 35 | public float liftingSpeed = 300.0f; 36 | public float retractSpeed = 300.0f; 37 | 38 | public float volumeMl = 0; 39 | public float weightG = 0; 40 | public float costDollars = 0; 41 | 42 | public float bottomLightOffDelay = 0.0f; 43 | public float lightOffDelay = 0.0f; 44 | public int bottomLayerCount; 45 | 46 | public int p1; 47 | public int p2; 48 | public int p3; 49 | public int p4; 50 | 51 | 52 | public PhotonFilePrintParameters() { 53 | bottomLiftDistance = 0; 54 | bottomLiftSpeed = 0; 55 | 56 | liftingDistance = 0; 57 | liftingSpeed = 100; 58 | retractSpeed = 100; 59 | 60 | volumeMl = 1; 61 | weightG = 1; 62 | costDollars = 1000000; //ONE MILLION DOLLARRRRSSS 63 | 64 | bottomLightOffDelay = 0; 65 | lightOffDelay = 0; 66 | bottomLayerCount = 0; 67 | 68 | p1 = 0; 69 | p2 = 461870400; 70 | p3 = 357; 71 | p4 = 0; 72 | } 73 | 74 | public PhotonFilePrintParameters(int bottomLayerCount) { 75 | this.bottomLayerCount = bottomLayerCount; 76 | } 77 | 78 | public PhotonFilePrintParameters(int parametersPos, byte[] file) throws Exception { 79 | byte[] data = Arrays.copyOfRange(file, parametersPos, parametersPos + getByteSize()); 80 | PhotonInputStream ds = new PhotonInputStream(new ByteArrayInputStream(data)); 81 | 82 | bottomLiftDistance = ds.readFloat(); 83 | bottomLiftSpeed = ds.readFloat(); 84 | 85 | liftingDistance = ds.readFloat(); 86 | liftingSpeed = ds.readFloat(); 87 | retractSpeed = ds.readFloat(); 88 | 89 | volumeMl = ds.readFloat(); 90 | weightG = ds.readFloat(); 91 | costDollars = ds.readFloat(); 92 | 93 | bottomLightOffDelay = ds.readFloat(); 94 | lightOffDelay = ds.readFloat(); 95 | bottomLayerCount = ds.readInt(); 96 | 97 | p1 = ds.readInt(); 98 | p2 = ds.readInt(); 99 | p3 = ds.readInt(); 100 | p4 = ds.readInt(); 101 | } 102 | 103 | public void save(PhotonOutputStream os) throws Exception { 104 | os.writeFloat(bottomLiftDistance); 105 | os.writeFloat(bottomLiftSpeed); 106 | 107 | os.writeFloat(liftingDistance); 108 | os.writeFloat(liftingSpeed); 109 | os.writeFloat(retractSpeed); 110 | 111 | os.writeFloat(volumeMl); 112 | os.writeFloat(weightG); 113 | os.writeFloat(costDollars); 114 | 115 | os.writeFloat(bottomLightOffDelay); 116 | os.writeFloat(lightOffDelay); 117 | os.writeInt(bottomLayerCount); 118 | 119 | os.writeInt(p1); 120 | os.writeInt(p2); 121 | os.writeInt(p3); 122 | os.writeInt(p4); 123 | } 124 | 125 | public int getByteSize() { 126 | return 4+4 +4+4+4 +4+4+4 +4+4+4 +4+4+4+4; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/photon/file/ui/ScrollUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.ui; 26 | 27 | import javax.swing.*; 28 | 29 | /** 30 | * by bn on 02/07/2018. 31 | */ 32 | public class ScrollUtil { 33 | 34 | private ScrollUtil() { 35 | } 36 | 37 | 38 | public static void scrollTo(JScrollPane c, ScrollPosition scrollPosition) { 39 | switch (scrollPosition) { 40 | case Top: 41 | c.getVerticalScrollBar().setValue(0); 42 | break; 43 | case VerticalCenter: 44 | if (c.getVerticalScrollBar()!=null) { 45 | c.getVerticalScrollBar().setValue(c.getVerticalScrollBar().getMaximum()); 46 | c.getVerticalScrollBar().setValue(c.getVerticalScrollBar().getValue() / 2); 47 | c.getVerticalScrollBar().setUnitIncrement(16); 48 | } 49 | break; 50 | case Bottom: 51 | c.getVerticalScrollBar().setValue(c.getVerticalScrollBar().getMaximum()); 52 | break; 53 | 54 | case Left: 55 | c.getHorizontalScrollBar().setValue(0); 56 | break; 57 | case HorizontalCenter: 58 | if (c.getHorizontalScrollBar()!=null) { 59 | c.getHorizontalScrollBar().setValue(c.getHorizontalScrollBar().getMaximum()); 60 | c.getHorizontalScrollBar().setValue(c.getHorizontalScrollBar().getValue() / 2); 61 | c.getHorizontalScrollBar().setUnitIncrement(16); 62 | } 63 | break; 64 | case Right: 65 | c.getHorizontalScrollBar().setValue(c.getHorizontalScrollBar().getMaximum()); 66 | break; 67 | } 68 | } 69 | 70 | public static void scroll(JScrollPane c, ScrollPosition scrollPosition) { 71 | switch (scrollPosition) { 72 | case Top: 73 | int val = c.getVerticalScrollBar().getValue(); 74 | if (val>18) { 75 | c.getVerticalScrollBar().setValue(val - 18); 76 | } else { 77 | c.getVerticalScrollBar().setValue(0); 78 | } 79 | break; 80 | case Bottom: 81 | val = c.getVerticalScrollBar().getValue(); 82 | if (val < (c.getVerticalScrollBar().getMaximum() - 18)) { 83 | c.getVerticalScrollBar().setValue(val + 18); 84 | } else { 85 | c.getVerticalScrollBar().setValue(c.getVerticalScrollBar().getMaximum()); 86 | } 87 | break; 88 | 89 | case Left: 90 | val = c.getHorizontalScrollBar().getValue(); 91 | if (val>18) { 92 | c.getHorizontalScrollBar().setValue(val - 18); 93 | } else { 94 | c.getHorizontalScrollBar().setValue(0); 95 | } 96 | break; 97 | case Right: 98 | val = c.getHorizontalScrollBar().getValue(); 99 | if (val< (c.getHorizontalScrollBar().getMaximum() - 18)) { 100 | c.getHorizontalScrollBar().setValue(val + 18); 101 | } else { 102 | c.getHorizontalScrollBar().setValue(c.getHorizontalScrollBar().getMaximum()); 103 | } 104 | break; 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/photon/file/ui/PhotonAaPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.ui; 26 | 27 | import photon.file.parts.PhotonFileLayer; 28 | import photon.file.parts.PhotonLayer; 29 | 30 | import javax.swing.*; 31 | import java.awt.*; 32 | import java.awt.geom.AffineTransform; 33 | import java.awt.image.BufferedImage; 34 | import java.util.ArrayList; 35 | 36 | public class PhotonAaPanel extends JPanel { 37 | private int width; 38 | private int height; 39 | 40 | private BufferedImage image; 41 | 42 | private boolean mirrored; 43 | 44 | public PhotonAaPanel(int width, int height) { 45 | this.width = width; 46 | this.height = height; 47 | image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 48 | setPreferredSize(new Dimension(width, height)); 49 | } 50 | 51 | public void setMirrored(boolean mirrored) { 52 | this.mirrored = mirrored; 53 | } 54 | 55 | @Override 56 | protected void paintComponent(Graphics g) { 57 | g.drawImage(image, 0, 0, null); 58 | } 59 | 60 | private Graphics2D createGraphics() { 61 | Graphics2D g = image.createGraphics(); 62 | if (mirrored) { 63 | AffineTransform transform = new AffineTransform(); 64 | transform.setToScale(1, -1); 65 | transform.translate(0, -image.getHeight()); 66 | g.setTransform(transform); 67 | } 68 | return g; 69 | } 70 | 71 | public void drawLayer(int layerX, int layerY, PhotonFileLayer layer, int aaLevel) { 72 | 73 | ArrayList photonLayers = new ArrayList<>(); 74 | 75 | if (aaLevel<1) { 76 | photonLayers.add(layer.getLayer()); 77 | for(PhotonFileLayer photonFileLayer: layer.getAntiAlias()) { 78 | photonLayers.add(photonFileLayer.getLayer()); 79 | } 80 | } else if (aaLevel==1) { 81 | photonLayers.add(layer.getLayer()); 82 | } else { 83 | photonLayers.add(layer.getAntiAlias(aaLevel-2).getLayer()); 84 | } 85 | 86 | Graphics2D g = createGraphics(); 87 | 88 | g.setBackground(Color.decode("#999999")); 89 | g.clearRect(0, 0, width, height); 90 | 91 | for (int y = 0; y < 45; y++) { 92 | for (int x = 0; x < 75; x++) { 93 | int x1 = 15 + x * 10; 94 | int y1 = 15 + y * 10; 95 | 96 | if (aaLevel == 0) { 97 | int colorDiv = 170 / photonLayers.size(); 98 | int color = 0; 99 | 100 | for(PhotonLayer photonLayer : photonLayers) { 101 | switch (photonLayer.get(layerY + y, layerX + x)) { 102 | case PhotonLayer.ISLAND: 103 | case PhotonLayer.CONNECTED: 104 | case PhotonLayer.SUPPORTED: 105 | color += colorDiv; 106 | break; 107 | } 108 | } 109 | 110 | if (color>0) color += 75; 111 | if (color>=235) color = 255; 112 | 113 | g.setColor(new Color( color, color, color)); 114 | 115 | } else { 116 | 117 | switch (photonLayers.get(0).get(layerY + y, layerX + x)) { 118 | case PhotonLayer.ISLAND: 119 | case PhotonLayer.CONNECTED: 120 | case PhotonLayer.SUPPORTED: 121 | g.setColor(Color.decode("#FFFFFF")); 122 | break; 123 | 124 | default: 125 | g.setColor(Color.black); 126 | 127 | } 128 | } 129 | g.fillRect(x1, y1, 9, 9); 130 | 131 | } 132 | } 133 | 134 | g.dispose(); 135 | } 136 | 137 | public void drawDot(int layerX, int layerY, PhotonLayer layer, Color color) { 138 | Graphics2D g = createGraphics(); 139 | g.setColor(color); 140 | g.fillRect(15 + layerX * 10, 15 + layerY * 10, 9, 9); 141 | g.dispose(); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/photon/file/parts/photons/PhotonsFileHeader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts.photons; 26 | 27 | import photon.file.parts.IFileHeader; 28 | import photon.file.parts.PhotonFileLayer; 29 | import photon.file.parts.PhotonInputStream; 30 | 31 | import java.io.ByteArrayInputStream; 32 | import java.io.DataInputStream; 33 | import java.util.List; 34 | 35 | public class PhotonsFileHeader implements IFileHeader { 36 | 37 | private int fileVersion; 38 | 39 | private int fileMark0; 40 | private int fileMark1; 41 | private int fileMark2; 42 | private int fileMark3; 43 | private int fileMark4; 44 | 45 | 46 | private int areaNum; 47 | 48 | private int headerAddress; 49 | private int backup0; 50 | private int previewAddress; 51 | private int backup1; 52 | private int layersDefAddress; 53 | private int backup2; 54 | private int layersImageAddress; 55 | 56 | 57 | public PhotonsFileHeader(byte[] file) throws Exception { 58 | DataInputStream ds = new DataInputStream(new ByteArrayInputStream(file)); 59 | 60 | fileVersion = ds.readInt(); 61 | 62 | fileMark0 = ds.readInt(); 63 | fileMark1 = ds.readInt(); 64 | fileMark2 = ds.readInt(); 65 | fileMark3 = ds.readInt(); 66 | fileMark4 = ds.readInt(); 67 | 68 | 69 | areaNum = ds.readInt(); 70 | 71 | headerAddress = ds.readInt(); 72 | backup0 = ds.readInt(); 73 | previewAddress = ds.readInt(); 74 | backup1 = ds.readInt(); 75 | layersDefAddress = ds.readInt(); 76 | backup2 = ds.readInt(); 77 | layersImageAddress = ds.readInt(); 78 | 79 | } 80 | 81 | 82 | 83 | @Override 84 | public String getInformation() { 85 | return null; 86 | } 87 | 88 | @Override 89 | public int getNumberOfLayers() { 90 | return 0; 91 | } 92 | 93 | @Override 94 | public int getResolutionY() { 95 | return 0; 96 | } 97 | 98 | @Override 99 | public int getResolutionX() { 100 | return 0; 101 | } 102 | 103 | @Override 104 | public float getBuildAreaX() { 105 | return 0; 106 | } 107 | 108 | @Override 109 | public float getBuildAreaY() { 110 | return 0; 111 | } 112 | 113 | @Override 114 | public int getBottomLayers() { 115 | return 0; 116 | } 117 | 118 | @Override 119 | public void setBottomLayers(int bottomLayers) { 120 | 121 | } 122 | 123 | @Override 124 | public float getLayerHeight() { 125 | return 0; 126 | } 127 | 128 | @Override 129 | public float getExposureTimeSeconds() { 130 | return 0; 131 | } 132 | 133 | @Override 134 | public float getBottomExposureTimeSeconds() { 135 | return 0; 136 | } 137 | 138 | @Override 139 | public void setExposureBottomTimeSeconds(float exposureBottomTimeSeconds) { 140 | 141 | } 142 | 143 | @Override 144 | public void setExposureTimeSeconds(float exposureTimeSeconds) { 145 | 146 | } 147 | 148 | @Override 149 | public float getNormalExposure() { 150 | return 0; 151 | } 152 | 153 | @Override 154 | public float getOffTimeSeconds() { 155 | return 0; 156 | } 157 | 158 | @Override 159 | public void setOffTimeSeconds(float offTimeSeconds) { 160 | 161 | } 162 | 163 | @Override 164 | public int getPrintTimeSeconds() { 165 | return 0; 166 | } 167 | 168 | @Override 169 | public boolean isMirrored() { return false; } 170 | 171 | @Override 172 | public boolean hasAA() { 173 | return false; 174 | } 175 | 176 | @Override 177 | public int getAALevels() { 178 | return 0; 179 | } 180 | 181 | @Override 182 | public void setAALevels(int levels, List layers) { 183 | 184 | } 185 | 186 | @Override 187 | public int getVersion() { 188 | return 0; 189 | } 190 | 191 | @Override 192 | public void setFileVersion(int i) { 193 | 194 | } 195 | 196 | @Override 197 | public int getByteSize() { 198 | return 0; 199 | } 200 | 201 | @Override 202 | public void unLink() { 203 | 204 | } 205 | 206 | 207 | 208 | @Override 209 | public void setPrintTimeSeconds(int time) { 210 | // TODO Auto-generated method stub 211 | 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/photon/file/parts/PhotonInputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | /** 28 | * by bn on 01/07/2018. 29 | */ 30 | 31 | import java.io.DataInput; 32 | import java.io.DataInputStream; 33 | import java.io.IOException; 34 | import java.io.InputStream; 35 | 36 | public class PhotonInputStream extends InputStream implements DataInput { 37 | private DataInputStream dataInputStream; 38 | private InputStream inputStream; 39 | private byte byteBuffer[]; 40 | 41 | public PhotonInputStream(InputStream inputStream) { 42 | this.inputStream = inputStream; 43 | dataInputStream = new DataInputStream(inputStream); 44 | byteBuffer = new byte[8]; // Largest data type is 64-bits (8 bytes) 45 | } 46 | 47 | @Override 48 | public int read() throws IOException { 49 | return inputStream.read(); 50 | } 51 | 52 | @Override 53 | public final int read(byte[] bytes, int offset, int readLen) throws IOException { 54 | return inputStream.read(bytes, offset, readLen); 55 | } 56 | 57 | @Override 58 | public final void readFully(byte[] bytes) throws IOException { 59 | dataInputStream.readFully(bytes, 0, bytes.length); 60 | } 61 | 62 | @Override 63 | public final void readFully(byte[] bytes, int offset, int readLen) throws IOException { 64 | dataInputStream.readFully(bytes, offset, readLen); 65 | } 66 | 67 | @Override 68 | public final int skipBytes(int n) throws IOException { 69 | return dataInputStream.skipBytes(n); 70 | } 71 | 72 | @Override 73 | public final boolean readBoolean() throws IOException { 74 | return dataInputStream.readBoolean(); 75 | } 76 | 77 | @Override 78 | public final byte readByte() throws IOException { 79 | return dataInputStream.readByte(); 80 | } 81 | 82 | @Override 83 | public final int readUnsignedByte() throws IOException { 84 | return dataInputStream.readUnsignedByte(); 85 | } 86 | 87 | @Override 88 | public final short readShort() throws IOException { 89 | dataInputStream.readFully(byteBuffer, 0, 2); 90 | return (short)((byteBuffer[1] & 0xff) << 8 | (byteBuffer[0] & 0xff)); 91 | } 92 | 93 | @Override 94 | public final int readUnsignedShort() throws IOException { 95 | dataInputStream.readFully(byteBuffer, 0, 2); 96 | return ((byteBuffer[1] & 0xff) << 8 | (byteBuffer[0] & 0xff)); 97 | } 98 | 99 | @Override 100 | public final char readChar() throws IOException { 101 | dataInputStream.readFully(byteBuffer, 0, 2); 102 | return (char)((byteBuffer[1] & 0xff) << 8 | (byteBuffer[0] & 0xff)); 103 | } 104 | 105 | @Override 106 | public final int readInt() throws IOException { 107 | dataInputStream.readFully(byteBuffer, 0, 4); 108 | return (byteBuffer[3]) << 24 | (byteBuffer[2] & 0xff) << 16 | 109 | (byteBuffer[1] & 0xff) << 8 | (byteBuffer[0] & 0xff); 110 | } 111 | 112 | @Override 113 | public final long readLong() throws IOException { 114 | dataInputStream.readFully(byteBuffer, 0, 8); 115 | return (long)(byteBuffer[7]) << 56 | (long)(byteBuffer[6]&0xff) << 48 | 116 | (long)(byteBuffer[5] & 0xff) << 40 | (long)(byteBuffer[4] & 0xff) << 32 | 117 | (long)(byteBuffer[3] & 0xff) << 24 | (long)(byteBuffer[2] & 0xff) << 16 | 118 | (long)(byteBuffer[1] & 0xff) << 8 | (long)(byteBuffer[0] & 0xff); 119 | } 120 | 121 | @Override 122 | public final float readFloat() throws IOException { 123 | return Float.intBitsToFloat(readInt()); 124 | } 125 | 126 | @Override 127 | public final double readDouble() throws IOException { 128 | return Double.longBitsToDouble(readLong()); 129 | } 130 | 131 | @Deprecated 132 | @Override 133 | public final String readLine() throws IOException { 134 | return dataInputStream.readLine(); 135 | } 136 | 137 | @Override 138 | public final String readUTF() throws IOException { 139 | return dataInputStream.readUTF(); 140 | } 141 | 142 | @Override 143 | public int available() throws IOException { 144 | return dataInputStream.available(); 145 | } 146 | 147 | @Override 148 | public final void close() throws IOException { 149 | dataInputStream.close(); 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/photon/file/parts/PhotonFilePreview.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | import java.awt.image.BufferedImage; 28 | import java.io.ByteArrayInputStream; 29 | import java.util.ArrayList; 30 | import java.util.Arrays; 31 | 32 | /** 33 | * by bn on 01/07/2018. 34 | */ 35 | public class PhotonFilePreview { 36 | private int resolutionX; 37 | private int resolutionY; 38 | private int imageAddress; 39 | private int dataSize; 40 | 41 | private byte[] rawImageData; 42 | 43 | private int[] imageData; 44 | 45 | private int p1; 46 | private int p2; 47 | private int p3; 48 | private int p4; 49 | 50 | public PhotonFilePreview(int previewAddress, byte[] file) throws Exception { 51 | byte[] data = Arrays.copyOfRange(file, previewAddress, previewAddress + 32); 52 | PhotonInputStream ds = new PhotonInputStream(new ByteArrayInputStream(data)); 53 | 54 | resolutionX = ds.readInt(); 55 | resolutionY = ds.readInt(); 56 | imageAddress = ds.readInt(); 57 | dataSize = ds.readInt(); 58 | p1 = ds.readInt(); 59 | p2 = ds.readInt(); 60 | p3 = ds.readInt(); 61 | p4 = ds.readInt(); 62 | 63 | rawImageData = Arrays.copyOfRange(file, imageAddress, imageAddress + dataSize); 64 | 65 | decodeImageData(); 66 | } 67 | 68 | public PhotonFilePreview(BufferedImage image) { 69 | imageData = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth()); 70 | resolutionX = image.getWidth(); 71 | resolutionY = image.getHeight(); 72 | encodeImageData(); 73 | } 74 | 75 | private void encodeImageData() { 76 | ArrayList data = new ArrayList(); 77 | 78 | int currSegLength = 1; 79 | int currColor = imageData[0]; 80 | for(int currPos = 1 ; currPos < imageData.length ; currPos++) { 81 | if(imageData[currPos] != currColor) { 82 | int dot = (((currColor & 0x0000ff) >> 3) & 0x1f) | (((currColor & 0xff00) >> 5) & 0x7C0) | (((currColor & 0xff0000) >> 8) & 0xf800) | ((currSegLength > 1) ? 0x0020 : 0); 83 | data.add((byte) (dot & 0xff)); data.add((byte) ((dot >> 8) & 0xff)); 84 | 85 | if(currSegLength > 1) { 86 | currSegLength --; 87 | data.add((byte) (currSegLength & 0xff)); data.add((byte) ((currSegLength >> 8) & 0x0f)); 88 | } 89 | 90 | currSegLength = 1; 91 | currColor = imageData[currPos]; 92 | }else { 93 | currSegLength ++; 94 | } 95 | } 96 | 97 | rawImageData = new byte[data.size()]; 98 | int currPos = 0; 99 | for(Byte b : data) { 100 | rawImageData[currPos++] = b; 101 | } 102 | dataSize = rawImageData.length; 103 | } 104 | 105 | public void save(PhotonOutputStream os, int startAddress) throws Exception { 106 | os.writeInt(resolutionX); 107 | os.writeInt(resolutionY); 108 | os.writeInt(startAddress + 4+4+4+4 + 4+4+4+4); 109 | os.writeInt(dataSize); 110 | os.writeInt(p1); 111 | os.writeInt(p2); 112 | os.writeInt(p3); 113 | os.writeInt(p4); 114 | os.write(rawImageData, 0, dataSize); 115 | } 116 | 117 | public int getByteSize() { 118 | return 4+4+4+4 + 4+4+4+4 + dataSize; 119 | } 120 | 121 | private void decodeImageData() { 122 | imageData = new int[resolutionX * resolutionY]; 123 | int d = 0; 124 | for (int i = 0; i < dataSize; i++) { 125 | int dot = rawImageData[i] & 0xFF | ((rawImageData[++i] & 0xFF) << 8); 126 | 127 | int color = ((dot & 0xF800) << 8) | ((dot & 0x07C0) << 5) | ((dot & 0x001F) << 3); 128 | 129 | // int red = ((dot >> 11) & 0x1F) << 3; 130 | // int green = ((dot >> 6) & 0x1F) << 3; 131 | // int blue = (dot & 0x1F) << 3; 132 | // color = red<<16 | green<<8 | blue; 133 | 134 | int repeat = 1; 135 | if ((dot & 0x0020) == 0x0020) { 136 | repeat += rawImageData[++i] & 0xFF | ((rawImageData[++i] & 0x0F) << 8); 137 | } 138 | 139 | while (repeat > 0) { 140 | imageData[d++] = color; 141 | repeat--; 142 | } 143 | } 144 | 145 | } 146 | 147 | public int getResolutionX() { 148 | return resolutionX; 149 | } 150 | 151 | public int getResolutionY() { 152 | return resolutionY; 153 | } 154 | 155 | public int[] getImageData() { 156 | return imageData; 157 | } 158 | 159 | public void unLink() { 160 | rawImageData = null; 161 | imageData = null; 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /src/photon/file/ui/PhotonLayerImage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.ui; 26 | 27 | import photon.file.parts.PhotonFileLayer; 28 | import photon.file.parts.PhotonLine; 29 | import photon.file.parts.PhotonRow; 30 | 31 | import javax.swing.*; 32 | 33 | import g2c.application.Main; 34 | 35 | import java.awt.*; 36 | import java.awt.geom.AffineTransform; 37 | import java.awt.image.BufferedImage; 38 | import java.util.ArrayList; 39 | import java.util.BitSet; 40 | 41 | /** 42 | * by bn on 02/07/2018. 43 | */ 44 | public class PhotonLayerImage extends JPanel { 45 | private int width; 46 | private int height; 47 | private float scale = 1f; 48 | private BufferedImage image; 49 | private boolean mirrored; 50 | 51 | public PhotonLayerImage(int width, int height) { 52 | this.width = width; 53 | this.height = height; 54 | image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 55 | setPreferredSize(new Dimension(width, height)); 56 | } 57 | 58 | public void setMirrored(boolean mirrored) { 59 | this.mirrored = mirrored; 60 | } 61 | 62 | @Override 63 | protected void paintComponent(Graphics g) { 64 | double scaleX = (double) getVisibleRect().getWidth() / width; 65 | double scaleY = (double) (getVisibleRect().getHeight() - 45) / height; 66 | System.out.println("bounds = " + getVisibleRect()); 67 | double scale = Math.min(scaleX, scaleY); 68 | int drawWidth = (int) (scale * width); 69 | int drawHeight = (int) (scale * height); 70 | g.drawImage(image, 0, 0, drawWidth, drawHeight, null); 71 | } 72 | 73 | public void setSize(int width, int height) { 74 | reScale(scale, width, height); 75 | } 76 | 77 | public void reScale(int width, int height) { 78 | reScale(scale, width, height); 79 | } 80 | 81 | public void reScale(float scale, int width, int height) { 82 | this.scale = scale; 83 | this.width = width; 84 | this.height = height; 85 | 86 | image = new BufferedImage((int) (width * scale), (int) (height * scale), BufferedImage.TYPE_INT_RGB); 87 | setPreferredSize(new Dimension((int) (width * scale), (int) (height * scale))); 88 | } 89 | 90 | public void drawLayer(PhotonFileLayer layer, int margin) { 91 | if (layer != null) { 92 | Graphics2D g = image.createGraphics(); 93 | 94 | if (mirrored) { 95 | AffineTransform transform = new AffineTransform(); 96 | transform.setToScale(1, -1); 97 | transform.translate(0, -image.getHeight()); 98 | g.setTransform(transform); 99 | } 100 | 101 | g.scale(scale, scale); 102 | 103 | g.clearRect(0, 0, width, height); 104 | 105 | if (margin > 0) { 106 | int x2 = (width - 1) - margin; 107 | int y2 = (height - 1) - margin; 108 | g.setColor(Color.decode("#009999")); 109 | g.drawLine(margin, margin, x2, margin); 110 | g.drawLine(margin, margin, margin, y2); 111 | g.drawLine(margin, y2, x2, y2); 112 | g.drawLine(x2, margin, x2, y2); 113 | } 114 | 115 | if (layer.isCalculated) { 116 | 117 | ArrayList rows = layer.getRows(); 118 | if (rows != null) { 119 | int columnNumber = 0; 120 | for (PhotonRow row : rows) { 121 | int i = 0; 122 | for (PhotonLine line : row.lines) { 123 | int end = i + line.length; 124 | if (i == 0) System.out.println("startLength = " + line.length); 125 | if (line.color != Color.black) { 126 | g.setColor(line.color); 127 | g.drawLine(columnNumber + margin, i + margin, columnNumber + margin, end + margin); 128 | } 129 | i = end; 130 | } 131 | columnNumber++; 132 | } 133 | } 134 | 135 | } else { 136 | g.setColor(Color.decode("#008888")); 137 | int columnNumber = 0; 138 | for (BitSet column : layer.getUnknownRows()) { 139 | drawDot(g, columnNumber, column); 140 | columnNumber++; 141 | } 142 | } 143 | 144 | g.dispose(); 145 | } 146 | } 147 | 148 | private void drawDot(Graphics2D g, int columnNumber, BitSet column) { 149 | if (!column.isEmpty()) { 150 | for (int i = column.nextSetBit(0); i >= 0; i = column.nextSetBit(i + 1)) { 151 | g.drawLine(columnNumber, i, columnNumber, i); 152 | } 153 | } 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /src/g2c/application/Main.java: -------------------------------------------------------------------------------- 1 | package g2c.application; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.EventQueue; 5 | import java.awt.Graphics; 6 | 7 | import javax.swing.JFrame; 8 | import javax.swing.JPanel; 9 | import javax.swing.border.EmptyBorder; 10 | 11 | import g2c.gerber.loader.GerberExtensionHandler; 12 | import g2c.gerber.loader.GerberLoader; 13 | import g2c.preview.GerberColors; 14 | import g2c.printers.ElegooMars; 15 | import g2c.printers.Printer; 16 | 17 | import javax.swing.JMenuBar; 18 | import javax.swing.JMenu; 19 | import javax.swing.JToggleButton; 20 | import javax.swing.DefaultListModel; 21 | import javax.swing.JButton; 22 | import javax.swing.JFileChooser; 23 | 24 | import java.awt.event.ActionListener; 25 | import java.awt.image.BufferedImage; 26 | import java.io.File; 27 | import java.util.prefs.Preferences; 28 | import java.awt.event.ActionEvent; 29 | import javax.swing.JList; 30 | import javax.swing.event.ListSelectionListener; 31 | import javax.swing.event.ListSelectionEvent; 32 | import java.awt.event.MouseAdapter; 33 | import java.awt.event.MouseEvent; 34 | import com.jgoodies.forms.layout.FormLayout; 35 | import com.jgoodies.forms.layout.ColumnSpec; 36 | import com.jgoodies.forms.layout.FormSpecs; 37 | import com.jgoodies.forms.layout.RowSpec; 38 | import javax.swing.JMenuItem; 39 | 40 | public class Main extends JFrame { 41 | private static Preferences prefs = Preferences.userRoot().node(Main.class.getName()); 42 | private JPanel contentPane; 43 | static Main frame; 44 | JList list = new JList(); 45 | public static Printer currPrinter = new ElegooMars(); 46 | private LayerSettings layerSettings = null; 47 | 48 | /** 49 | * Launch the application. 50 | */ 51 | public static void main(String[] args) { 52 | EventQueue.invokeLater(new Runnable() { 53 | public void run() { 54 | try { 55 | frame = new Main(); 56 | frame.setVisible(true); 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | } 60 | } 61 | }); 62 | } 63 | /** 64 | * Create the frame. 65 | */ 66 | public Main() { 67 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 68 | setBounds(100, 100, 1171, 796); 69 | 70 | JMenuBar menuBar = new JMenuBar(); 71 | setJMenuBar(menuBar); 72 | 73 | JMenu mnNewMenu = new JMenu("File"); 74 | menuBar.add(mnNewMenu); 75 | 76 | JMenu mnNewMenu_1 = new JMenu("Tools"); 77 | menuBar.add(mnNewMenu_1); 78 | 79 | JMenuItem mntmNewMenuItem = new JMenuItem("Calibration Wizard"); 80 | mntmNewMenuItem.addActionListener(new ActionListener() { 81 | public void actionPerformed(ActionEvent e) { 82 | new CalibrationWizard().setVisible(true); 83 | } 84 | }); 85 | mnNewMenu_1.add(mntmNewMenuItem); 86 | contentPane = new JPanel(); 87 | contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); 88 | setContentPane(contentPane); 89 | list.addMouseListener(new MouseAdapter() { 90 | @Override 91 | public void mouseClicked(MouseEvent e) { 92 | if(e.getButton() == 2) list.clearSelection(); 93 | if(e.getButton() == 3) { 94 | if(list.getSelectedIndex() != -1) { 95 | if(layerSettings != null) layerSettings.frame.dispose(); 96 | layerSettings = new LayerSettings(list.getSelectedValue()); 97 | 98 | layerSettings.frame.setVisible(true); 99 | } 100 | } 101 | } 102 | }); 103 | list.addListSelectionListener(new ListSelectionListener() { 104 | public void valueChanged(ListSelectionEvent e) { 105 | repaint(); 106 | } 107 | }); 108 | contentPane.setLayout(new FormLayout(new ColumnSpec[] { 109 | FormSpecs.UNRELATED_GAP_COLSPEC, 110 | FormSpecs.MIN_COLSPEC, 111 | FormSpecs.RELATED_GAP_COLSPEC, 112 | FormSpecs.MIN_COLSPEC, 113 | FormSpecs.RELATED_GAP_COLSPEC, 114 | ColumnSpec.decode("max(100dlu;default)"), 115 | FormSpecs.UNRELATED_GAP_COLSPEC, 116 | FormSpecs.GLUE_COLSPEC, 117 | FormSpecs.UNRELATED_GAP_COLSPEC,}, 118 | new RowSpec[] { 119 | FormSpecs.UNRELATED_GAP_ROWSPEC, 120 | FormSpecs.GLUE_ROWSPEC, 121 | FormSpecs.UNRELATED_GAP_ROWSPEC, 122 | RowSpec.decode("23px"), 123 | FormSpecs.UNRELATED_GAP_ROWSPEC,})); 124 | 125 | list.setModel(new DefaultListModel<>()); 126 | contentPane.add(list, "2, 2, 5, 1, fill, fill"); 127 | 128 | JButton btnNewButton = new JButton("+"); 129 | btnNewButton.addActionListener(new ActionListener() { 130 | public void actionPerformed(ActionEvent e) { 131 | JFileChooser ch = new JFileChooser(); 132 | ch.setMultiSelectionEnabled(true); 133 | ch.setSelectedFile(new File(prefs.get("default.dir", "/"))); 134 | ch.showDialog(frame, "ye"); 135 | File[] newLayerFiles = ch.getSelectedFiles(); 136 | if(newLayerFiles.length > 0) { 137 | for(File f : newLayerFiles) { 138 | String ext = (f.getName().indexOf('.') == -1) ? null : f.getName().substring(f.getName().indexOf('.')); 139 | System.out.println("ext = " + f.length()); 140 | if(ext.hashCode() == ".rar".hashCode() || ext.hashCode() == ".zip".hashCode() || ext.hashCode() == ".log".hashCode() || f.length() > 10000000) continue; 141 | GerberLoader load = new GerberLoader(f); 142 | LayerListObject newObj = new LayerListObject(load, GerberExtensionHandler.getDefaultLayerColor(ext), GerberExtensionHandler.getDefaultLayerName(ext)); 143 | ((DefaultListModel) list.getModel()).addElement(newObj); 144 | } 145 | prefs.put("default.dir", newLayerFiles[0].getAbsolutePath()); 146 | layerAddedHandler(); 147 | } 148 | } 149 | }); 150 | contentPane.add(btnNewButton, "2, 4, right, top"); 151 | 152 | JPanel panel = new PreviewPane(list); 153 | contentPane.add(panel, "8, 2, 1, 3, fill, fill"); 154 | 155 | JButton btnNewButton_1 = new JButton("-"); 156 | btnNewButton_1.addActionListener(new ActionListener() { 157 | public void actionPerformed(ActionEvent e) { 158 | if(list.getSelectedIndex() != -1) ((DefaultListModel) list.getModel()).remove(list.getSelectedIndex()); 159 | list.clearSelection(); 160 | repaint(); 161 | } 162 | }); 163 | contentPane.add(btnNewButton_1, "4, 4"); 164 | 165 | JButton btnNewButton_2 = new JButton("Export"); 166 | btnNewButton_2.addActionListener(new ActionListener() { 167 | public void actionPerformed(ActionEvent e) { 168 | Exporter exp = new Exporter(list.getSelectedValue()); 169 | exp.frame.setVisible(true); 170 | } 171 | }); 172 | contentPane.add(btnNewButton_2, "6, 4"); 173 | } 174 | 175 | public void layerAddedHandler() { 176 | DefaultListModel objects = (DefaultListModel) list.getModel(); 177 | for(int i = 0; i < list.getModel().getSize(); i++) { 178 | objects.getElementAt(i).forceRender(); 179 | } 180 | repaint(); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/g2c/application/CalibrationWizard.java: -------------------------------------------------------------------------------- 1 | package g2c.application; 2 | 3 | import java.awt.BasicStroke; 4 | import java.awt.BorderLayout; 5 | import java.awt.Color; 6 | import java.awt.EventQueue; 7 | import java.awt.Font; 8 | import java.awt.Graphics2D; 9 | import java.awt.geom.AffineTransform; 10 | import java.awt.geom.Arc2D; 11 | import java.awt.image.BufferedImage; 12 | import java.io.File; 13 | import java.util.prefs.Preferences; 14 | 15 | import javax.swing.JFrame; 16 | import javax.swing.JPanel; 17 | import javax.swing.border.EmptyBorder; 18 | 19 | import photon.file.PhotonFile; 20 | import photon.file.parts.PhotonFileLayer; 21 | import photon.file.parts.PhotonFilePreview; 22 | 23 | import javax.swing.JButton; 24 | import javax.swing.JFileChooser; 25 | import javax.swing.JSpinner; 26 | import javax.swing.JLabel; 27 | import javax.swing.SwingConstants; 28 | import java.awt.event.ActionListener; 29 | import java.awt.event.ActionEvent; 30 | import javax.swing.SpinnerNumberModel; 31 | import javax.swing.event.ChangeListener; 32 | import javax.swing.event.ChangeEvent; 33 | import javax.swing.JCheckBox; 34 | 35 | public class CalibrationWizard extends JFrame { 36 | private static Preferences prefs = Preferences.userRoot().node(CalibrationWizard.class.getName()); 37 | private JPanel contentPane; 38 | BufferedImage preview; 39 | JSpinner fromSpinner = new JSpinner(); 40 | JSpinner toSpinner = new JSpinner(); 41 | JSpinner stepSpinner = new JSpinner(); 42 | JFrame frame; 43 | JPanel panel = new JPanel(); 44 | JCheckBox invertBox = new JCheckBox("invert"); 45 | 46 | /** 47 | * Create the frame. 48 | */ 49 | public CalibrationWizard() { 50 | frame = this; 51 | preview = new BufferedImage(Main.currPrinter.getScreenResolution().width, Main.currPrinter.getScreenResolution().height, BufferedImage.TYPE_INT_ARGB); 52 | 53 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 54 | setBounds(100, 100, 1008, 640); 55 | contentPane = new JPanel(); 56 | contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); 57 | setContentPane(contentPane); 58 | contentPane.setLayout(null); 59 | 60 | JButton btnNewButton = new JButton("Export"); 61 | btnNewButton.addActionListener(new ActionListener() { 62 | public void actionPerformed(ActionEvent e) { 63 | JFileChooser ch = new JFileChooser(); 64 | ch.setMultiSelectionEnabled(true); 65 | ch.setSelectedFile(new File(prefs.get("default.dir", "/"))); 66 | ch.showDialog(frame, "ye"); 67 | File exportFile = ch.getSelectedFile(); 68 | if(exportFile != null) { 69 | prefs.put("default.dir", exportFile.getAbsolutePath()); 70 | generateFile(exportFile); 71 | } 72 | } 73 | }); 74 | btnNewButton.setBounds(893, 569, 89, 23); 75 | contentPane.add(btnNewButton); 76 | 77 | panel.setBounds(10, 42, 972, 516); 78 | contentPane.add(panel); 79 | 80 | fromSpinner.setModel(new SpinnerNumberModel(10, 10, 10000, 10)); 81 | fromSpinner.setBounds(10, 11, 89, 20); 82 | fromSpinner.addChangeListener(new ChangeListener() { 83 | public void stateChanged(ChangeEvent e) { 84 | drawPattern(); 85 | } 86 | }); 87 | contentPane.add(fromSpinner); 88 | 89 | toSpinner.setModel(new SpinnerNumberModel(10, 10, 10000, 10)); 90 | toSpinner.setBounds(148, 11, 89, 20); 91 | toSpinner.addChangeListener(new ChangeListener() { 92 | public void stateChanged(ChangeEvent e) { 93 | drawPattern(); 94 | } 95 | }); 96 | contentPane.add(toSpinner); 97 | 98 | JLabel lblNewLabel = new JLabel("to"); 99 | lblNewLabel.setHorizontalAlignment(SwingConstants.CENTER); 100 | lblNewLabel.setBounds(109, 14, 29, 14); 101 | contentPane.add(lblNewLabel); 102 | 103 | JLabel lblIn = new JLabel("in"); 104 | lblIn.setHorizontalAlignment(SwingConstants.CENTER); 105 | lblIn.setBounds(247, 14, 29, 14); 106 | contentPane.add(lblIn); 107 | stepSpinner.addChangeListener(new ChangeListener() { 108 | public void stateChanged(ChangeEvent e) { 109 | drawPattern(); 110 | } 111 | }); 112 | stepSpinner.setModel(new SpinnerNumberModel(1, 1, 8, 1)); 113 | 114 | stepSpinner.setBounds(286, 11, 89, 20); 115 | contentPane.add(stepSpinner); 116 | 117 | JLabel lblSteps = new JLabel("Steps"); 118 | lblSteps.setHorizontalAlignment(SwingConstants.CENTER); 119 | lblSteps.setBounds(385, 14, 62, 14); 120 | contentPane.add(lblSteps); 121 | 122 | invertBox.setBounds(790, 569, 97, 23); 123 | contentPane.add(invertBox); 124 | } 125 | 126 | private void drawPattern() { 127 | int segCount = (int) stepSpinner.getValue(); 128 | int segWidth = preview.getWidth() / segCount; 129 | preview = new BufferedImage(Main.currPrinter.getScreenResolution().width, Main.currPrinter.getScreenResolution().height, BufferedImage.TYPE_INT_ARGB); 130 | Graphics2D g = preview.createGraphics(); 131 | g.setColor(Color.BLACK); 132 | double secIncr = (double) ((int) toSpinner.getValue() - (int) fromSpinner.getValue()) / (double) (segCount - 1); 133 | 134 | g.setStroke(new BasicStroke(3)); 135 | g.setFont(new Font("Arial", Font.BOLD, 175)); 136 | 137 | for(int i = 0; i < segCount; i++) { 138 | int baseX = i * segWidth; 139 | g.drawRect(baseX + 1, 1, segWidth - 1, preview.getHeight() - 1); 140 | 141 | String text = "Test @ " + Integer.toString((int) fromSpinner.getValue() + (int) ((double) i * secIncr)) + " sec"; 142 | AffineTransform orig = g.getTransform(); 143 | g.rotate(Math.PI/2); 144 | int stringHeight = (int) g.getFontMetrics().getStringBounds(text, g).getHeight(); 145 | int stringWidth = (int) g.getFontMetrics().getStringBounds(text, g).getWidth(); 146 | g.drawString(text, preview.getHeight() / 2 - stringWidth / 2, - baseX - segWidth / 2 + stringHeight / 2 - 25); 147 | g.setTransform(orig); 148 | 149 | } 150 | 151 | g.dispose(); 152 | g = (Graphics2D) panel.getGraphics(); 153 | g.setColor(panel.getBackground()); 154 | g.fillRect(0, 0, panel.getWidth(), panel.getHeight()); 155 | 156 | if(invertBox.isSelected()) { 157 | BufferedImage temp = new BufferedImage(preview.getWidth(), preview.getHeight(), BufferedImage.TYPE_INT_ARGB); 158 | for (int x = 0; x < preview.getWidth(); x++) { 159 | for (int y = 0; y < preview.getHeight(); y++) { 160 | int rgba = preview.getRGB(x, y); 161 | Color col = new Color(rgba, true); 162 | col = new Color(255 - col.getRed(), 163 | 255 - col.getGreen(), 164 | 255 - col.getBlue()); 165 | temp.setRGB(x, y, col.getRGB()); 166 | } 167 | } 168 | g.drawImage(temp, 10, 30, 1024, 576, null); 169 | }else { 170 | g.drawImage(preview, 10, 30, 1024, 576, null); 171 | } 172 | g.setColor(Color.RED); 173 | g.drawArc(10, 10, 80, 80, 184, 149); 174 | g.setColor(Color.BLUE); 175 | g.drawArc(100, 10, 80, 80, -10, 180); 176 | 177 | g.dispose(); 178 | } 179 | 180 | void generateFile(File f) { 181 | int segCount = (int) stepSpinner.getValue(); 182 | int segWidth = preview.getWidth() / segCount; 183 | double secIncr = (double) ((int) toSpinner.getValue() - (int) fromSpinner.getValue()) / (double) (segCount - 1); 184 | 185 | PhotonFile exp = new PhotonFile(Main.currPrinter); 186 | 187 | for (int currLayer = 0; currLayer < segCount; currLayer++) { 188 | BufferedImage layerImage = new BufferedImage(Main.currPrinter.getScreenResolution().width, Main.currPrinter.getScreenResolution().height, BufferedImage.TYPE_INT_ARGB); 189 | Graphics2D g = layerImage.createGraphics(); 190 | g.setColor(Color.BLACK); 191 | g.setStroke(new BasicStroke(3)); 192 | g.setFont(new Font("Arial", Font.BOLD, 175)); 193 | 194 | for(int i = 0; i < segCount - currLayer; i++) { 195 | int baseX = i * segWidth; 196 | g.drawRect(baseX + 1, 1, segWidth - 1, preview.getHeight() - 1); 197 | 198 | String text = "Test @ " + Integer.toString((int) toSpinner.getValue() - (int) ((double) i * secIncr)) + " sec"; 199 | AffineTransform orig = g.getTransform(); 200 | g.rotate(Math.PI/2); 201 | int stringHeight = (int) g.getFontMetrics().getStringBounds(text, g).getHeight(); 202 | int stringWidth = (int) g.getFontMetrics().getStringBounds(text, g).getWidth(); 203 | g.drawString(text, preview.getHeight() / 2 - stringWidth / 2, - baseX - segWidth / 2 + stringHeight / 2 - 25); 204 | g.setTransform(orig); 205 | 206 | } 207 | g.dispose(); 208 | 209 | PhotonFileLayer layer = new PhotonFileLayer(layerImage, invertBox.isSelected(), exp.getPhotonFileHeader()); 210 | layer.setLayerExposure((float) ((currLayer == 0) ? ((int) fromSpinner.getValue()) : secIncr)); 211 | exp.addLayer(layer); 212 | try { 213 | exp.calculate(currLayer); 214 | } catch (Exception e) { 215 | // TODO Auto-generated catch block 216 | e.printStackTrace(); 217 | } 218 | } 219 | 220 | exp.getPhotonFileHeader().setExposureBottomTimeSeconds((float) (int) fromSpinner.getValue()); 221 | exp.getPhotonFileHeader().setExposureTimeSeconds((float) secIncr); 222 | exp.getPhotonFileHeader().setBottomLayers(1); 223 | exp.adjustLayerSettings(); 224 | 225 | PhotonFilePreview newPreview = new PhotonFilePreview(preview); 226 | exp.setPreviewOne(newPreview); exp.setPreviewTwo(newPreview); 227 | try { 228 | exp.saveFile(f); 229 | } catch (Exception e) { 230 | // TODO Auto-generated catch block 231 | e.printStackTrace(); 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/g2c/application/Exporter.java: -------------------------------------------------------------------------------- 1 | package g2c.application; 2 | 3 | import java.awt.BasicStroke; 4 | import java.awt.Color; 5 | import java.awt.Dimension; 6 | import java.awt.EventQueue; 7 | import java.awt.Font; 8 | import java.awt.Graphics2D; 9 | import java.awt.image.AffineTransformOp; 10 | import java.awt.image.BufferedImage; 11 | import java.awt.image.ColorModel; 12 | import java.awt.image.WritableRaster; 13 | import java.io.File; 14 | import java.util.ArrayList; 15 | import java.util.prefs.Preferences; 16 | 17 | import javax.swing.JFrame; 18 | import javax.swing.JComboBox; 19 | import javax.swing.JFileChooser; 20 | import javax.swing.JLabel; 21 | import javax.swing.JButton; 22 | import javax.swing.JSpinner; 23 | import javax.swing.JPanel; 24 | import com.jgoodies.forms.layout.FormLayout; 25 | import com.jgoodies.forms.layout.ColumnSpec; 26 | import com.jgoodies.forms.layout.FormSpecs; 27 | import com.jgoodies.forms.layout.RowSpec; 28 | 29 | import g2c.gerber.loader.GerberLoader; 30 | import photon.file.PhotonFile; 31 | import photon.file.parts.PhotonFileLayer; 32 | import photon.file.parts.PhotonFilePreview; 33 | import photon.file.parts.PhotonLayer; 34 | import photon.file.ui.PhotonLayerImage; 35 | import java.awt.event.ActionListener; 36 | import java.awt.geom.AffineTransform; 37 | import java.awt.event.ActionEvent; 38 | import javax.swing.JCheckBox; 39 | import javax.swing.SwingConstants; 40 | import javax.swing.SpinnerNumberModel; 41 | import javax.swing.event.ChangeListener; 42 | import javax.swing.event.ChangeEvent; 43 | import javax.swing.DefaultComboBoxModel; 44 | import java.awt.event.ComponentAdapter; 45 | import java.awt.event.ComponentEvent; 46 | 47 | public class Exporter { 48 | private static Preferences prefs = Preferences.userRoot().node(Exporter.class.getName()); 49 | JFrame frame; 50 | LayerListObject layer; 51 | JSpinner expTime = new JSpinner(); 52 | PhotonFile exp = new PhotonFile(Main.currPrinter); 53 | PhotonLayerImage panel; 54 | JCheckBox mirrorLayer = new JCheckBox("Mirror Layer"); 55 | JComboBox typeBox = new JComboBox(); 56 | JComboBox drillOLBox = new JComboBox(); 57 | JCheckBox chckbxOverrideHoleDiameter = new JCheckBox("Override Hole diameter"); 58 | JSpinner newHoleDiam = new JSpinner(); 59 | JLabel newHoleDiamMMLBL = new JLabel("mm"); 60 | JSpinner rotation = new JSpinner(); 61 | JSpinner offsetY = new JSpinner(); 62 | JSpinner offsetX = new JSpinner(); 63 | 64 | /** 65 | * Initialize the contents of the frame. 66 | */ 67 | public Exporter(LayerListObject layer) { 68 | this.layer = layer; 69 | frame = new JFrame(); 70 | frame.setBounds(100, 100, 824, 467); 71 | frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 72 | frame.getContentPane().setLayout(new FormLayout(new ColumnSpec[] { 73 | FormSpecs.UNRELATED_GAP_COLSPEC, 74 | ColumnSpec.decode("163px"), 75 | ColumnSpec.decode("100px:grow(5)"), 76 | ColumnSpec.decode("174px"), 77 | FormSpecs.UNRELATED_GAP_COLSPEC, 78 | ColumnSpec.decode("43px"), 79 | FormSpecs.UNRELATED_GAP_COLSPEC, 80 | ColumnSpec.decode("24px"), 81 | FormSpecs.UNRELATED_GAP_COLSPEC, 82 | ColumnSpec.decode("16px"), 83 | FormSpecs.UNRELATED_GAP_COLSPEC, 84 | ColumnSpec.decode("35px:grow"), 85 | FormSpecs.RELATED_GAP_COLSPEC, 86 | ColumnSpec.decode("17px"), 87 | FormSpecs.RELATED_GAP_COLSPEC, 88 | ColumnSpec.decode("43px"), 89 | FormSpecs.UNRELATED_GAP_COLSPEC, 90 | ColumnSpec.decode("31px"), 91 | FormSpecs.UNRELATED_GAP_COLSPEC,}, 92 | new RowSpec[] { 93 | FormSpecs.UNRELATED_GAP_ROWSPEC, 94 | RowSpec.decode("23px"), 95 | FormSpecs.UNRELATED_GAP_ROWSPEC, 96 | RowSpec.decode("22px"), 97 | FormSpecs.UNRELATED_GAP_ROWSPEC, 98 | RowSpec.decode("22px"), 99 | FormSpecs.UNRELATED_GAP_ROWSPEC, 100 | RowSpec.decode("23px"), 101 | FormSpecs.UNRELATED_GAP_ROWSPEC, 102 | FormSpecs.DEFAULT_ROWSPEC, 103 | FormSpecs.UNRELATED_GAP_ROWSPEC, 104 | FormSpecs.DEFAULT_ROWSPEC, 105 | RowSpec.decode("161px:grow"), 106 | RowSpec.decode("22px"), 107 | FormSpecs.UNRELATED_GAP_ROWSPEC, 108 | RowSpec.decode("23px"), 109 | FormSpecs.UNRELATED_GAP_ROWSPEC,})); 110 | 111 | JLabel lblNewLabel = new JLabel("Exposure Time:"); 112 | frame.getContentPane().add(lblNewLabel, "6, 4, 7, 1, fill, fill"); 113 | expTime.setModel(new SpinnerNumberModel(250, 10, 1000, 10)); 114 | 115 | frame.getContentPane().add(expTime, "14, 4, 3, 1, fill, fill"); 116 | 117 | 118 | JButton btnSave = new JButton("Save As"); 119 | btnSave.addActionListener(new ActionListener() { 120 | public void actionPerformed(ActionEvent e) { 121 | JFileChooser ch = new JFileChooser(); 122 | ch.setMultiSelectionEnabled(true); 123 | ch.setSelectedFile(new File(prefs.get("default.dir", "/"))); 124 | ch.showDialog(frame, "ye"); 125 | File exportFile = ch.getSelectedFile(); 126 | if(exportFile != null) { 127 | prefs.put("default.dir", exportFile.getAbsolutePath()); 128 | exportFile(exportFile); 129 | } 130 | } 131 | }); 132 | 133 | JLabel lblNewLabel_1_1_2 = new JLabel("Offsets (mm):"); 134 | frame.getContentPane().add(lblNewLabel_1_1_2, "6, 10, 3, 1"); 135 | 136 | JLabel lblNewLabel_1_1_3 = new JLabel("x"); 137 | lblNewLabel_1_1_3.setHorizontalAlignment(SwingConstants.TRAILING); 138 | frame.getContentPane().add(lblNewLabel_1_1_3, "10, 10"); 139 | offsetX.setModel(new SpinnerNumberModel(new Double(1.45), new Double(0), null, new Double(1))); 140 | offsetX.addChangeListener(new ChangeListener() { 141 | public void stateChanged(ChangeEvent e) { 142 | updatePreview(); 143 | } 144 | }); 145 | 146 | frame.getContentPane().add(offsetX, "12, 10"); 147 | 148 | JLabel lblNewLabel_1_1_3_1 = new JLabel("y"); 149 | lblNewLabel_1_1_3_1.setHorizontalAlignment(SwingConstants.TRAILING); 150 | frame.getContentPane().add(lblNewLabel_1_1_3_1, "14, 10"); 151 | offsetY.setModel(new SpinnerNumberModel(new Double(2.1), new Double(0), null, new Double(1))); 152 | offsetY.addChangeListener(new ChangeListener() { 153 | public void stateChanged(ChangeEvent e) { 154 | updatePreview(); 155 | } 156 | }); 157 | 158 | frame.getContentPane().add(offsetY, "16, 10"); 159 | 160 | JLabel lblNewLabel_1_1_2_1 = new JLabel("Rotation (°):"); 161 | frame.getContentPane().add(lblNewLabel_1_1_2_1, "6, 12, 3, 1"); 162 | rotation.addChangeListener(new ChangeListener() { 163 | public void stateChanged(ChangeEvent e) { 164 | updatePreview(); 165 | } 166 | }); 167 | 168 | frame.getContentPane().add(rotation, "10, 12, 7, 1"); 169 | frame.getContentPane().add(btnSave, "4, 16, fill, top"); 170 | 171 | panel = new PhotonLayerImage(layer.getImage().getWidth(), layer.getImage().getHeight()); 172 | 173 | frame.getContentPane().add(panel, "2, 2, 3, 12, fill, top"); 174 | 175 | JButton btnCancel = new JButton("Cancel"); 176 | frame.getContentPane().add(btnCancel, "2, 16, fill, top"); 177 | 178 | JLabel lblNewLabel_1 = new JLabel("Preset"); 179 | frame.getContentPane().add(lblNewLabel_1, "6, 2, fill, fill"); 180 | 181 | JComboBox comboBox = new JComboBox(); 182 | comboBox.setEditable(true); 183 | frame.getContentPane().add(comboBox, "8, 2, 7, 1, fill, fill"); 184 | mirrorLayer.addActionListener(new ActionListener() { 185 | public void actionPerformed(ActionEvent e) { 186 | updatePreview(); 187 | } 188 | }); 189 | frame.getContentPane().add(mirrorLayer, "12, 8, 7, 1, fill, top"); 190 | 191 | JLabel lblNewLabel_1_1 = new JLabel("Material Type"); 192 | frame.getContentPane().add(lblNewLabel_1_1, "6, 6, 3, 1, fill, fill"); 193 | typeBox.addActionListener(new ActionListener() { 194 | public void actionPerformed(ActionEvent e) { 195 | updatePreview(); 196 | } 197 | }); 198 | 199 | typeBox.setModel(new DefaultComboBoxModel(new String[] {"Positive", "Negative"})); 200 | frame.getContentPane().add(typeBox, "10, 6, 9, 1, fill, fill"); 201 | 202 | DefaultComboBoxModel temp = new DefaultComboBoxModel(); 203 | drillOLBox.addActionListener(new ActionListener() { 204 | public void actionPerformed(ActionEvent e) { 205 | updatePreview(); 206 | } 207 | }); 208 | drillOLBox.setModel(temp); 209 | temp.addElement(new LayerListObject("None")); 210 | for(int i = 0; i< Main.frame.list.getModel().getSize();i++){ 211 | temp.addElement(Main.frame.list.getModel().getElementAt(i)); 212 | } 213 | frame.getContentPane().add(drillOLBox, "12, 14, 7, 1, fill, top"); 214 | 215 | JLabel lblNewLabel_1_1_1 = new JLabel("Add drill guide"); 216 | frame.getContentPane().add(lblNewLabel_1_1_1, "6, 14, 5, 1, fill, fill"); 217 | chckbxOverrideHoleDiameter.addActionListener(new ActionListener() { 218 | public void actionPerformed(ActionEvent e) { 219 | updatePreview(); 220 | newHoleDiam.setEnabled(chckbxOverrideHoleDiameter.isSelected()); 221 | newHoleDiamMMLBL.setEnabled(chckbxOverrideHoleDiameter.isSelected()); 222 | } 223 | }); 224 | frame.getContentPane().add(chckbxOverrideHoleDiameter, "6, 16, 7, 1, fill, top"); 225 | newHoleDiam.addChangeListener(new ChangeListener() { 226 | public void stateChanged(ChangeEvent e) { 227 | updatePreview(); 228 | } 229 | }); 230 | newHoleDiam.setModel(new SpinnerNumberModel(0.5, 0.1, 100.0, 0.1)); 231 | 232 | 233 | newHoleDiam.setEnabled(false); 234 | frame.getContentPane().add(newHoleDiam, "14, 16, 3, 1, fill, center"); 235 | 236 | newHoleDiamMMLBL.setEnabled(false); 237 | frame.getContentPane().add(newHoleDiamMMLBL, "18, 16, fill, fill"); 238 | 239 | JButton btnNewButton = new JButton("+"); 240 | frame.getContentPane().add(btnNewButton, "16, 2, 3, 1, right, top"); 241 | 242 | JButton btnNewButton_1 = new JButton("-"); 243 | frame.getContentPane().add(btnNewButton_1, "16, 2, fill, top"); 244 | 245 | JLabel lblS = new JLabel("s"); 246 | frame.getContentPane().add(lblS, "18, 4, fill, fill"); 247 | updatePreview(); 248 | } 249 | 250 | public void updatePreview() { 251 | layer.applyCorrection((double) offsetX.getValue(), (double) offsetY.getValue(), (int) rotation.getValue()); 252 | if(drillOLBox.getSelectedIndex() > 0) { 253 | Main.frame.list.getModel().getElementAt(drillOLBox.getSelectedIndex() - 1).gerber.applyCorrection((double) offsetX.getValue(), (double) offsetY.getValue(), (int) rotation.getValue()); 254 | } 255 | setLayer(layer.getCorrectedImage()); 256 | } 257 | 258 | public void setLayer(BufferedImage data) { 259 | exp = new PhotonFile(Main.currPrinter); 260 | BufferedImage flipped = deepCopyImage(data); 261 | AffineTransform flipper = new AffineTransform(); 262 | 263 | if(drillOLBox.getSelectedIndex() > 0) { 264 | double newHoleSize = (double) newHoleDiam.getValue() / 25.4; //gerber lib talks imperial :( 265 | flipped = Main.frame.list.getModel().getElementAt(drillOLBox.getSelectedIndex() - 1).gerber.overlayDrills(flipped, chckbxOverrideHoleDiameter.isSelected() ? newHoleSize : -1, Main.currPrinter.getScreenPPI().getWidth(), Main.currPrinter.getScreenPPI().getHeight(), Main.currPrinter.getScreenResolution().width, Main.currPrinter.getScreenResolution().height, true); 266 | } 267 | 268 | if(mirrorLayer.isSelected()) { 269 | flipper.scale(-1, 1); flipper.translate(-flipped.getWidth(), 0); 270 | } 271 | 272 | AffineTransformOp dolphin = new AffineTransformOp(flipper, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); 273 | flipped = dolphin.filter(flipped, null); 274 | boolean invert = typeBox.getSelectedIndex() == 0; 275 | 276 | BufferedImage out = new BufferedImage(data.getWidth(), data.getHeight(), data.getType()); 277 | Graphics2D outDrawer = out.createGraphics(); 278 | Dimension margin = Main.currPrinter.getBezelMargin(); 279 | outDrawer.drawImage(flipped, (int) (mirrorLayer.isSelected() ? -margin.getWidth() : margin.getWidth()), (int) margin.getHeight(), null); 280 | outDrawer.dispose(); 281 | 282 | PhotonFileLayer layer = new PhotonFileLayer(out, invert, exp.getPhotonFileHeader()); 283 | exp.addLayer(layer); 284 | 285 | try { 286 | exp.calculate(0); 287 | } catch (Exception e) { 288 | // TODO Auto-generated catch block 289 | e.printStackTrace(); 290 | } 291 | panel.drawLayer(exp.getLayer(0), 0); 292 | frame.update(frame.getGraphics()); 293 | } 294 | 295 | private void exportFile(File f) { 296 | exp.setExposure((int) expTime.getValue()); 297 | PhotonFilePreview newPreview = null; 298 | 299 | AffineTransform flipper = new AffineTransform(); 300 | 301 | //flipper.translate((int) xMargin.getValue(), (int) yMargin.getValue()); 302 | if(mirrorLayer.isSelected()) { 303 | flipper.scale(-1, 1); flipper.translate(flipper.getTranslateX() - layer.getImage().getWidth(), flipper.getTranslateY()); 304 | } 305 | 306 | AffineTransformOp dolphin = new AffineTransformOp(flipper, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); 307 | 308 | newPreview = new PhotonFilePreview(dolphin.filter(layer.getImage(), null)); 309 | exp.setPreviewOne(newPreview); exp.setPreviewTwo(newPreview); 310 | try { 311 | exp.saveFile(f); 312 | } catch (Exception e) { 313 | // TODO Auto-generated catch block 314 | e.printStackTrace(); 315 | } 316 | } 317 | 318 | static BufferedImage deepCopyImage(BufferedImage bi) { 319 | ColorModel cm = bi.getColorModel(); 320 | boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); 321 | WritableRaster raster = bi.copyData(null); 322 | return new BufferedImage(cm, raster, isAlphaPremultiplied, null); 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /src/photon/file/parts/photon/PhotonFileHeader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts.photon; 26 | 27 | import photon.file.parts.*; 28 | import photon.file.ui.Text; 29 | 30 | import java.io.ByteArrayInputStream; 31 | import java.io.File; 32 | import java.util.List; 33 | 34 | import g2c.printers.Printer; 35 | 36 | /** 37 | * by bn on 30/06/2018. 38 | */ 39 | public class PhotonFileHeader implements IFileHeader { 40 | private int header1; 41 | private int version; 42 | private float bedXmm; 43 | private float bedYmm; 44 | private float bedZmm; 45 | private int unknown1; 46 | private int unknown2; 47 | private int unknown3; 48 | private float layerHeightMilimeter; 49 | private float exposureTimeSeconds; 50 | private float exposureBottomTimeSeconds; 51 | private float offTimeSeconds; 52 | private int bottomLayers; 53 | private int resolutionX; 54 | private int resolutionY; 55 | 56 | private int previewOneOffsetAddress; 57 | private int layersDefinitionOffsetAddress; 58 | private int numberOfLayers; 59 | 60 | private int previewTwoOffsetAddress; 61 | 62 | public int printTimeSeconds; 63 | private PhotonProjectType projectType; 64 | 65 | private int printParametersOffsetAddress; 66 | private int printParametersSize; 67 | private int antiAliasingLevel; 68 | 69 | private short lightPWM; 70 | private short bottomLightPWM; 71 | 72 | private int unknown4; 73 | private int machineInfoOffsetAddress; 74 | private int machineInfoSize; 75 | 76 | 77 | public PhotonFilePrintParameters photonFilePrintParameters; 78 | public PhotonFileMachineInfo photonFileMachineInfo; 79 | 80 | 81 | public PhotonFileHeader(Printer targetDevice) { 82 | 83 | header1 = 318570521; 84 | version = 2; 85 | 86 | bedXmm = (float) targetDevice.getBedXmm(); 87 | bedYmm = (float) targetDevice.getBedYmm(); 88 | bedZmm = (float) targetDevice.getBedZmm(); 89 | 90 | unknown1 = 0; //what even is this? copied from one of my exported files 91 | unknown2 = 0; 92 | unknown3 = 1097859072; 93 | 94 | layerHeightMilimeter = 0.05f; 95 | exposureTimeSeconds = 10.0f; 96 | exposureBottomTimeSeconds = 10.0f; 97 | 98 | offTimeSeconds = 0; 99 | bottomLayers = 1; 100 | 101 | resolutionX = targetDevice.getScreenResolution().height; 102 | resolutionY = targetDevice.getScreenResolution().width; 103 | 104 | setNumberOfLayers(0); 105 | 106 | printTimeSeconds = 0; 107 | 108 | projectType = PhotonProjectType.lcdMirror; 109 | 110 | printParametersSize = 60; //is this actually fixed? 111 | antiAliasingLevel = 1; 112 | 113 | lightPWM = 255; 114 | bottomLightPWM = 255; 115 | 116 | unknown4 = 0; 117 | machineInfoSize = 76; //is this actually fixed? 118 | 119 | photonFilePrintParameters = new PhotonFilePrintParameters(); 120 | photonFileMachineInfo = new PhotonFileMachineInfo(); 121 | } 122 | 123 | public PhotonFileHeader(byte[] file) throws Exception { 124 | PhotonInputStream ds = new PhotonInputStream(new ByteArrayInputStream(file)); 125 | 126 | header1 = ds.readInt(); 127 | version = ds.readInt(); 128 | 129 | bedXmm = ds.readFloat(); 130 | bedYmm = ds.readFloat(); 131 | bedZmm = ds.readFloat(); 132 | 133 | unknown1 = ds.readInt(); 134 | unknown2 = ds.readInt(); 135 | unknown3 = ds.readInt(); 136 | 137 | layerHeightMilimeter = ds.readFloat(); 138 | exposureTimeSeconds = ds.readFloat(); 139 | exposureBottomTimeSeconds = ds.readFloat(); 140 | 141 | offTimeSeconds = ds.readFloat(); 142 | bottomLayers = ds.readInt(); 143 | 144 | resolutionX = ds.readInt(); 145 | resolutionY = ds.readInt(); 146 | 147 | previewOneOffsetAddress = ds.readInt(); 148 | layersDefinitionOffsetAddress = ds.readInt(); 149 | 150 | setNumberOfLayers(ds.readInt()); 151 | 152 | previewTwoOffsetAddress = ds.readInt(); 153 | printTimeSeconds = ds.readInt(); 154 | 155 | projectType = PhotonProjectType.find(ds.readInt()); 156 | 157 | printParametersOffsetAddress = ds.readInt(); 158 | printParametersSize = ds.readInt(); 159 | antiAliasingLevel = ds.readInt(); 160 | 161 | lightPWM = ds.readShort(); 162 | bottomLightPWM = ds.readShort(); 163 | 164 | unknown4 = ds.readInt(); 165 | machineInfoOffsetAddress = ds.readInt(); 166 | if (version>1) { 167 | machineInfoSize = ds.readInt(); 168 | } 169 | } 170 | 171 | public int getByteSize() { 172 | return 4+4 + 4+4+4 + 4+4+4 + 4+4+4 + 4+4 + 4+4 + 4+4 + 4 + 4+4 + 4 + 4+4+4 +2+2 +4+4+ (version>1?4:0); 173 | } 174 | 175 | public void save(PhotonOutputStream os, int previewOnePos, int previewTwoPos, int layerDefinitionPos, int parametersPos, int machineInfoPos) throws Exception { 176 | previewOneOffsetAddress = previewOnePos; 177 | previewTwoOffsetAddress = previewTwoPos; 178 | layersDefinitionOffsetAddress = layerDefinitionPos; 179 | printParametersOffsetAddress = parametersPos; 180 | machineInfoOffsetAddress = machineInfoPos; 181 | 182 | os.writeInt(header1); 183 | os.writeInt(version); 184 | 185 | os.writeFloat(bedXmm); 186 | os.writeFloat(bedYmm); 187 | os.writeFloat(bedZmm); 188 | 189 | os.writeInt(unknown1); 190 | os.writeInt(unknown2); 191 | os.writeInt(unknown3); 192 | 193 | os.writeFloat(layerHeightMilimeter); 194 | os.writeFloat(exposureTimeSeconds); 195 | os.writeFloat(exposureBottomTimeSeconds); 196 | 197 | os.writeFloat(offTimeSeconds); 198 | os.writeInt(bottomLayers); 199 | 200 | os.writeInt(resolutionX); 201 | os.writeInt(resolutionY); 202 | 203 | os.writeInt(previewOneOffsetAddress); 204 | os.writeInt(layersDefinitionOffsetAddress); 205 | 206 | os.writeInt(getNumberOfLayers()); 207 | 208 | os.writeInt(previewTwoOffsetAddress); 209 | os.writeInt(printTimeSeconds); 210 | 211 | os.writeInt(projectType.getProjectID()); 212 | 213 | os.writeInt(printParametersOffsetAddress); 214 | os.writeInt(printParametersSize); 215 | os.writeInt(antiAliasingLevel); 216 | 217 | os.writeShort(lightPWM); 218 | os.writeShort(bottomLightPWM); 219 | 220 | os.writeInt(unknown4); 221 | os.writeInt(machineInfoOffsetAddress); 222 | if (version>1) { 223 | os.writeInt(machineInfoSize); 224 | } 225 | } 226 | 227 | 228 | public int getPreviewOneOffsetAddress() { 229 | return previewOneOffsetAddress; 230 | } 231 | 232 | public int getPreviewTwoOffsetAddress() { 233 | return previewTwoOffsetAddress; 234 | } 235 | 236 | public int getNumberOfLayers() { 237 | return numberOfLayers; 238 | } 239 | 240 | public int getLayersDefinitionOffsetAddress() { 241 | return layersDefinitionOffsetAddress; 242 | } 243 | 244 | public float getNormalExposure() { 245 | return exposureTimeSeconds; 246 | } 247 | 248 | public float getOffTime() { 249 | return offTimeSeconds; 250 | } 251 | 252 | public int getResolutionX() { 253 | return resolutionX; 254 | } 255 | 256 | public int getResolutionY() { 257 | return resolutionY; 258 | } 259 | 260 | public float getBuildAreaX() { 261 | return bedXmm; 262 | } 263 | 264 | public float getBuildAreaY() { 265 | return bedYmm; 266 | } 267 | 268 | public float getLayerHeight() { 269 | return layerHeightMilimeter; 270 | } 271 | 272 | public int getBottomLayers() { 273 | return bottomLayers; 274 | } 275 | 276 | public float getBottomExposureTimeSeconds() { 277 | return exposureBottomTimeSeconds; 278 | } 279 | 280 | public float getOffTimeSeconds() { 281 | return offTimeSeconds; 282 | } 283 | 284 | public float getExposureTimeSeconds() { 285 | return exposureTimeSeconds; 286 | } 287 | 288 | public void unLink() { 289 | } 290 | 291 | public void setExposureTimeSeconds(float exposureTimeSeconds) { 292 | this.exposureTimeSeconds = exposureTimeSeconds; 293 | } 294 | 295 | public void setExposureBottomTimeSeconds(float exposureBottomTimeSeconds) { 296 | this.exposureBottomTimeSeconds = exposureBottomTimeSeconds; 297 | } 298 | 299 | public void setOffTimeSeconds(float offTimeSeconds) { 300 | this.offTimeSeconds = offTimeSeconds; 301 | } 302 | 303 | public void setBottomLayers(int bottomLayers) { 304 | this.bottomLayers = bottomLayers; 305 | } 306 | 307 | public int getVersion() { 308 | return version; 309 | } 310 | 311 | public int getPrintParametersOffsetAddress() { 312 | return printParametersOffsetAddress; 313 | } 314 | 315 | public int getPrintParametersSize() { 316 | return printParametersSize; 317 | } 318 | 319 | public int getMachineInfoOffsetAddress() { 320 | return machineInfoOffsetAddress; 321 | } 322 | 323 | public int getMachineInfoSize() { 324 | return machineInfoSize; 325 | } 326 | 327 | public int getAntiAliasingLevel() { 328 | return antiAliasingLevel; 329 | } 330 | 331 | public void setAntiAliasingLevel(int antiAliasingLevel) { 332 | this.antiAliasingLevel = antiAliasingLevel; 333 | } 334 | 335 | public void setFileVersion(int i) { 336 | version = i; 337 | antiAliasingLevel = 1; 338 | lightPWM = 255; 339 | bottomLightPWM = 255; 340 | 341 | photonFilePrintParameters = new PhotonFilePrintParameters(getBottomLayers()); 342 | } 343 | 344 | public String getInformation() { 345 | return String.format("T: %.3f", layerHeightMilimeter) + 346 | ", E: " + Text.formatSeconds(exposureTimeSeconds) + 347 | ", O: " + Text.formatSeconds(offTimeSeconds) + 348 | ", BE: " + Text.formatSeconds(exposureBottomTimeSeconds) + 349 | String.format(", BL: %d", bottomLayers); 350 | } 351 | 352 | public boolean hasAA() { 353 | return (getVersion()>1 && getAntiAliasingLevel()>1); 354 | } 355 | 356 | public int getAALevels() { 357 | if (getVersion()>1) { 358 | return getAntiAliasingLevel(); 359 | } 360 | return 1; 361 | } 362 | 363 | public void setAALevels(int levels, List layers) { 364 | if (getVersion()>1) { 365 | if (levels < getAntiAliasingLevel()) { 366 | reduceAaLevels(levels, layers); 367 | } 368 | if (levels > getAntiAliasingLevel()) { 369 | increaseAaLevels(levels, layers); 370 | } 371 | } 372 | } 373 | 374 | private void increaseAaLevels(int levels, List layers) { 375 | // insert base layer to the correct count, as we are to recalc the AA anyway 376 | for(PhotonFileLayer photonFileLayer : layers) { 377 | while (photonFileLayer.getAntiAlias().size()<(levels-1)) { 378 | photonFileLayer.getAntiAlias().add(new PhotonFileLayer(photonFileLayer, this)); 379 | } 380 | } 381 | setAntiAliasingLevel(levels); 382 | } 383 | 384 | private void reduceAaLevels(int levels, List layers) { 385 | // delete any layers to the correct count, as we are to recalc the AA anyway 386 | for(PhotonFileLayer photonFileLayer : layers) { 387 | while (photonFileLayer.getAntiAlias().size()>(levels-1)) { 388 | photonFileLayer.getAntiAlias().remove(0); 389 | } 390 | } 391 | setAntiAliasingLevel(levels); 392 | } 393 | 394 | 395 | public int getPrintTimeSeconds() { 396 | return printTimeSeconds; 397 | } 398 | 399 | public boolean isMirrored() { 400 | return projectType == PhotonProjectType.lcdMirror; 401 | } 402 | 403 | public void readParameters(byte[] file) throws Exception { 404 | photonFilePrintParameters = new PhotonFilePrintParameters(getPrintParametersOffsetAddress(), file); 405 | photonFileMachineInfo = new PhotonFileMachineInfo(getMachineInfoOffsetAddress(), getMachineInfoSize(), file); 406 | } 407 | 408 | public void setNumberOfLayers(int numberOfLayers) { 409 | this.numberOfLayers = numberOfLayers; 410 | } 411 | 412 | public void setPrintTimeSeconds(int time) { 413 | printTimeSeconds = time; 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /src/photon/file/parts/PhotonLayer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | import java.awt.*; 28 | import java.awt.geom.AffineTransform; 29 | import java.awt.image.BufferedImage; 30 | import java.io.IOException; 31 | import java.util.ArrayList; 32 | import java.util.Arrays; 33 | import java.util.BitSet; 34 | import java.util.Hashtable; 35 | import java.util.Random; 36 | 37 | /** 38 | * by bn on 02/07/2018. 39 | */ 40 | public class PhotonLayer { 41 | public final static byte OFF = 0x00; 42 | public final static byte SUPPORTED = 0x01; 43 | public final static byte ISLAND = 0x02; 44 | public final static byte CONNECTED = 0x03; 45 | 46 | private int width; 47 | private int height; 48 | private int islandCount = 0; 49 | 50 | private byte[][] iArray; 51 | private int[] pixels; 52 | private int[] rowIslands; 53 | 54 | private static byte[] emptyRow; 55 | private static int[] emptyCol; 56 | 57 | private static byte[] scratchPad; 58 | 59 | public PhotonLayer(int width, int height) { 60 | this.width = width; 61 | this.height = height; 62 | 63 | iArray = new byte[height][width]; 64 | pixels = new int[height]; 65 | rowIslands = new int[height]; 66 | 67 | if (emptyRow == null || emptyRow.length < width) { 68 | emptyRow = new byte[width]; 69 | } 70 | 71 | if (emptyCol == null || emptyCol.length < height) { 72 | emptyCol = new int[height]; 73 | } 74 | 75 | if (scratchPad == null || scratchPad.length < width * height) { 76 | scratchPad = new byte[width * height]; 77 | } 78 | } 79 | 80 | public PhotonLayer(BufferedImage data, boolean invert) { 81 | width = data.getHeight(); 82 | height = data.getWidth(); 83 | 84 | Random r = new Random(); 85 | 86 | iArray = new byte[height][width]; 87 | pixels = new int[height]; 88 | rowIslands = new int[height]; 89 | 90 | if (emptyRow == null || emptyRow.length < width) { 91 | emptyRow = new byte[width]; 92 | } 93 | 94 | if (emptyCol == null || emptyCol.length < height) { 95 | emptyCol = new int[height]; 96 | } 97 | 98 | if (scratchPad == null || scratchPad.length < width * height) { 99 | scratchPad = new byte[width * height]; 100 | } 101 | 102 | 103 | for (int y = 0; y < height; y++) { 104 | int pix = 0; 105 | for (int x = 0; x < width; x++) { 106 | if(invert) { 107 | iArray[y][x] = ((data.getRGB(y, x) >> 24) != 0) ? OFF : CONNECTED; 108 | }else { 109 | iArray[y][x] = ((data.getRGB(y, x) >> 24) != 0) ? CONNECTED : OFF; 110 | } 111 | if(iArray[y][x] != OFF) pix++;//System.out.println("[" + x + "," + y + "] = " + iArray[y][x]); 112 | } 113 | pixels[y] = pix; 114 | rowIslands[y] = 0; 115 | } 116 | } 117 | 118 | 119 | 120 | public void clear() { 121 | for (int y = 0; y < height; y++) { 122 | System.arraycopy(emptyRow, 0, iArray[y], 0, width); 123 | } 124 | System.arraycopy(emptyCol, 0, pixels, 0, height); 125 | System.arraycopy(emptyCol, 0, rowIslands, 0, height); 126 | } 127 | 128 | public void supported(int x, int y) { 129 | iArray[y][x] = SUPPORTED; 130 | pixels[y]++; 131 | } 132 | 133 | public void unSupported(int x, int y) { 134 | iArray[y][x] = CONNECTED; 135 | pixels[y]++; 136 | } 137 | 138 | public void island(int x, int y) { 139 | iArray[y][x] = ISLAND; 140 | rowIslands[y]++; 141 | islandCount++; 142 | pixels[y]++; 143 | } 144 | 145 | public void remove(int x, int y, byte type) { 146 | iArray[y][x] = OFF; 147 | switch (type) { 148 | case ISLAND: 149 | rowIslands[y]--; 150 | islandCount--; 151 | break; 152 | } 153 | pixels[y]--; 154 | } 155 | 156 | 157 | public void reduce() { 158 | // Double reduce to handle single line connections. 159 | for (int i = 0; i < 2; i++) { 160 | if (islandCount > 0) { 161 | for (int y = 0; y < height; y++) { 162 | if (rowIslands[y] > 0) { 163 | for (int x = 0; x < width; x++) { 164 | if (iArray[y][x] == ISLAND) { 165 | if (connected(x, y)) { 166 | makeConnected(x, y); 167 | checkUp(x, y); 168 | if (rowIslands[y] == 0) { 169 | break; 170 | } 171 | } 172 | } 173 | } 174 | } 175 | } 176 | } 177 | } 178 | } 179 | 180 | private void checkUp(int x, int y) { 181 | if (y > 0 && rowIslands[y - 1] > 0 && iArray[y - 1][x] == ISLAND) { 182 | makeConnected(x, y - 1); 183 | checkUp(x, y - 1); 184 | } 185 | if (x > 0 && rowIslands[y] > 0 && iArray[y][x - 1] == ISLAND) { 186 | makeConnected(x - 1, y); 187 | checkBackUp(x - 1, y); 188 | } 189 | if (x < (width-1) && rowIslands[y] > 0 && iArray[y][x + 1] == ISLAND) { 190 | makeConnected(x + 1, y); 191 | checkFrontUp(x + 1, y); 192 | } 193 | } 194 | 195 | private void checkBackUp(int x, int y) { 196 | if (y > 0 && rowIslands[y - 1] > 0 && iArray[y - 1][x] == ISLAND) { 197 | makeConnected(x, y - 1); 198 | checkBackUp(x, y - 1); 199 | } 200 | if (x > 0 && rowIslands[y] > 0 && iArray[y][x - 1] == ISLAND) { 201 | makeConnected(x - 1, y); 202 | checkBackUp(x - 1, y); 203 | } 204 | } 205 | 206 | private void checkFrontUp(int x, int y) { 207 | if (y > 0 && rowIslands[y - 1] > 0 && iArray[y - 1][x] == ISLAND) { 208 | makeConnected(x, y - 1); 209 | checkFrontUp(x, y - 1); 210 | } 211 | if (x < (width-1) && rowIslands[y] > 0 && iArray[y][x + 1] == ISLAND) { 212 | makeConnected(x + 1, y); 213 | checkFrontUp(x + 1, y); 214 | } 215 | } 216 | 217 | private void makeConnected(int x, int y) { 218 | iArray[y][x] = CONNECTED; 219 | rowIslands[y]--; 220 | islandCount--; 221 | } 222 | 223 | private boolean connected(int x, int y) { 224 | return x > 0 && (iArray[y][x - 1] & 0x01) == SUPPORTED 225 | || x < (width - 1) && (iArray[y][x + 1] & 0x01) == SUPPORTED 226 | || y > 0 && (iArray[y - 1][x] & 0x01) == SUPPORTED 227 | || (y < (height - 1) && (iArray[y + 1][x] & 0x01) == SUPPORTED); 228 | } 229 | 230 | public int setIslands(ArrayList islandRows) { 231 | int islands = 0; 232 | for (int y = 0; y < height; y++) { 233 | BitSet bitSet = new BitSet(); 234 | if (rowIslands[y] > 0) { 235 | for (int x = 0; x < width; x++) { 236 | if (iArray[y][x] == ISLAND) { 237 | bitSet.set(x); 238 | } 239 | } 240 | } 241 | islandRows.add(bitSet); 242 | islands += rowIslands[y]; 243 | } 244 | return islands; 245 | } 246 | 247 | public void unLink() { 248 | iArray = null; 249 | pixels = null; 250 | rowIslands = null; 251 | } 252 | 253 | public byte[] packLayerImage() { 254 | int ptr = 0; 255 | for (int y = 0; y < height; y++) { 256 | if (pixels[y] == 0) { 257 | ptr = add(ptr, OFF, width); 258 | } else { 259 | byte current = OFF; 260 | int length = 0; 261 | for (int x = 0; x < width; x++) { 262 | byte next = iArray[y][x]; 263 | if (next != current) { 264 | if (length > 0) { 265 | ptr = add(ptr, current, length); 266 | } 267 | current = next; 268 | length = 1; 269 | } else { 270 | length++; 271 | } 272 | } 273 | if (length > 0) { 274 | ptr = add(ptr, current, length); 275 | } 276 | } 277 | } 278 | byte[] img = new byte[ptr]; 279 | System.arraycopy(scratchPad, 0, img, 0, ptr); 280 | return img; 281 | } 282 | 283 | public void unpackLayerImage(byte[] packedLayerImage) { 284 | clear(); 285 | int x = 0; 286 | int y = 0; 287 | int imageLength = packedLayerImage.length; 288 | for (int i = 0; i < imageLength; i++) { 289 | byte rle = packedLayerImage[i]; 290 | byte colorCode = (byte) ((rle & 0x60) >> 5); 291 | 292 | boolean extended = (rle & 0x80) == 0x80; 293 | int length = rle & 0x1F; 294 | if (extended) { 295 | i++; 296 | length = (length << 8) | packedLayerImage[i] & 0x00ff; 297 | } 298 | 299 | Arrays.fill(iArray[y], x, x + length, colorCode); 300 | 301 | switch (colorCode) { 302 | case SUPPORTED: 303 | pixels[y]+=length; 304 | break; 305 | case CONNECTED: 306 | pixels[y]+=length; 307 | break; 308 | case ISLAND: 309 | rowIslands[y]+= length; 310 | islandCount+=length; 311 | pixels[y]+=length; 312 | break; 313 | 314 | } 315 | 316 | 317 | x += length; 318 | if (x >= width) { 319 | y++; 320 | x = 0; 321 | } 322 | } 323 | 324 | } 325 | 326 | 327 | private int add(int ptr, byte current, int length) { 328 | if (length < 32) { 329 | scratchPad[ptr++] = (byte) ((current << 5) | (length & 0x1f)); 330 | } else { 331 | scratchPad[ptr++] = (byte) (0x80 | (current << 5) | (length >> 8 & 0x00FF)); 332 | scratchPad[ptr++] = (byte) (length & 0x00FF); 333 | } 334 | return ptr; 335 | } 336 | 337 | /** 338 | * Get a layer image for drawing. 339 | *

340 | * This will decode the RLE packed layer information and return a list of rows, with color and length information 341 | * 342 | * @param packedLayerImage The packed layer image information 343 | * @param width The width of the current layer, used to change rows 344 | * @return A list with the 345 | */ 346 | public static ArrayList getRows(byte[] packedLayerImage, int width, boolean isCalculated) { 347 | Hashtable colors = new Hashtable<>(); 348 | colors.put(OFF, Color.black); 349 | if (isCalculated) { 350 | colors.put(SUPPORTED, Color.decode("#008800")); 351 | } else { 352 | colors.put(SUPPORTED, Color.decode("#000088")); 353 | } 354 | colors.put(CONNECTED, Color.decode("#FFFF00")); 355 | colors.put(ISLAND, Color.decode("#FF0000")); 356 | ArrayList rows = new ArrayList<>(); 357 | int resolutionX = width - 1; 358 | PhotonRow currentRow = new PhotonRow(); 359 | rows.add(currentRow); 360 | int x = 0; 361 | if (packedLayerImage!=null) { // when user tries to show a layer before its calculated 362 | for (int i = 0; i < packedLayerImage.length; i++) { 363 | byte rle = packedLayerImage[i]; 364 | byte colorCode = (byte) ((rle & 0x60) >> 5); 365 | Color color = colors.get(colorCode); 366 | boolean extended = (rle & 0x80) == 0x80; 367 | int length = rle & 0x1F; 368 | if (extended) { 369 | length = (length << 8) | (packedLayerImage[++i] & 0x00ff); 370 | } 371 | currentRow.lines.add(new PhotonLine(color, length)); 372 | x += length; 373 | 374 | if (x >= resolutionX) { 375 | currentRow = new PhotonRow(); 376 | rows.add(currentRow); 377 | x = 0; 378 | } 379 | } 380 | } 381 | return rows; 382 | } 383 | 384 | public int removeIslands() { 385 | int count = 0; 386 | if (islandCount > 0) { 387 | for (int y = 0; y < height; y++) { 388 | if (rowIslands[y] > 0) { 389 | for (int x = 0; x < width; x++) { 390 | if (iArray[y][x] == ISLAND) { 391 | remove(x, y, ISLAND); 392 | ++count; 393 | } 394 | } 395 | } 396 | } 397 | } 398 | return count; 399 | } 400 | 401 | public int fixlayer() { 402 | PhotonMatix photonMatix = new PhotonMatix(); 403 | ArrayList dots = new ArrayList<>(); 404 | if (islandCount > 0) { 405 | for (int y = 0; y < height; y++) { 406 | if (rowIslands[y] > 0) { 407 | for (int x = 0; x < width; x++) { 408 | if (iArray[y][x] == ISLAND) { 409 | photonMatix.clear(); 410 | int blanks = photonMatix.set(x, y, iArray, width, height); 411 | if (blanks>0) { // one or more neighbour pixels are OFF 412 | photonMatix.calc(); 413 | photonMatix.level(); 414 | photonMatix.calc(); 415 | 416 | for(int ry=0; ry<3; ry++) { 417 | for (int rx = 0; rx < 3; rx++) { 418 | int iy = y-1+ry; 419 | int ix = x-1+rx; 420 | if (iArray[iy][ix] == OFF) { 421 | if (photonMatix.calcMatrix[1+ry][1+rx]>3) { 422 | dots.add(new PhotonDot(ix, iy)); 423 | } 424 | } 425 | } 426 | } 427 | } 428 | } 429 | } 430 | } 431 | } 432 | } 433 | for(PhotonDot dot : dots) { 434 | island(dot.x, dot.y); 435 | } 436 | return dots.size(); 437 | } 438 | 439 | public byte[] packImageData() { 440 | 441 | int ptr = 0; 442 | 443 | for (int y = 0; y < height; y++) { 444 | if (pixels[y] == 0) { 445 | ptr = addPhotonRLE(ptr, true, width); 446 | } else { 447 | byte current = OFF; 448 | int length = 0; 449 | for (int x = 0; x < width; x++) { 450 | byte next = iArray[y][x]; 451 | if (next != current) { 452 | if (length > 0) { 453 | ptr = addPhotonRLE(ptr, current==OFF, length); 454 | } 455 | current = next; 456 | length = 1; 457 | } else { 458 | length++; 459 | } 460 | } 461 | if (length > 0) { 462 | ptr = addPhotonRLE(ptr, current==OFF, length); 463 | } 464 | } 465 | } 466 | byte[] img = new byte[ptr]; 467 | System.arraycopy(scratchPad, 0, img, 0, ptr); 468 | return img; 469 | } 470 | 471 | private int addPhotonRLE(int ptr, boolean off, int length) { 472 | 473 | while (length > 0) { 474 | int lineLength = length < 125 ? length : 125; // max storage length of 0x7D (125) ?? Why not 127? 475 | scratchPad[ptr++] = (byte) ((off ? 0x00: 0x80) | (lineLength & 0x7f)); 476 | length -= lineLength; 477 | } 478 | 479 | return ptr; 480 | } 481 | 482 | public byte get(int x, int y) { 483 | return iArray[y][x]; 484 | } 485 | 486 | } 487 | -------------------------------------------------------------------------------- /src/photon/file/parts/PhotonFileLayer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file.parts; 26 | 27 | import photon.file.parts.photon.PhotonFileHeader; 28 | 29 | import java.awt.image.BufferedImage; 30 | import java.io.ByteArrayInputStream; 31 | import java.util.*; 32 | 33 | /** 34 | * by bn on 01/07/2018. 35 | */ 36 | public class PhotonFileLayer { 37 | private float layerPositionZ; 38 | private float layerExposure; 39 | private float layerOffTimeSeconds; 40 | private int dataAddress; 41 | private int dataSize; 42 | private int unknown1; 43 | private int unknown2; 44 | private int unknown3; 45 | private int unknown4; 46 | 47 | private byte[] imageData; 48 | 49 | private byte[] packedLayerImage; 50 | 51 | private ArrayList islandRows; 52 | private int isLandsCount; 53 | private long pixels; 54 | 55 | private ArrayList antiAliasLayers = new ArrayList<>(); 56 | 57 | private boolean extendsMargin; 58 | private PhotonFileHeader photonFileHeader; 59 | public boolean isCalculated; 60 | 61 | private PhotonFileLayer(PhotonInputStream ds) throws Exception { 62 | layerPositionZ = ds.readFloat(); 63 | layerExposure = ds.readFloat(); 64 | layerOffTimeSeconds = ds.readFloat(); 65 | 66 | dataAddress = ds.readInt(); 67 | dataSize = ds.readInt(); 68 | 69 | unknown1 = ds.readInt(); 70 | unknown2 = ds.readInt(); 71 | unknown3 = ds.readInt(); 72 | unknown4 = ds.readInt(); 73 | } 74 | 75 | public PhotonFileLayer(PhotonFileLayer photonFileLayer, PhotonFileHeader photonFileHeader) { 76 | layerPositionZ = photonFileLayer.layerPositionZ; 77 | layerExposure = photonFileLayer.layerExposure; 78 | layerOffTimeSeconds = photonFileLayer.layerOffTimeSeconds; 79 | dataAddress = photonFileLayer.dataAddress; 80 | dataAddress = photonFileLayer.dataSize; 81 | 82 | this.photonFileHeader = photonFileHeader; 83 | 84 | // Dont copy data, we are building new AA layers anyway 85 | //this.imageData = copy(); 86 | //this.packedLayerImage = copy(); 87 | } 88 | 89 | public PhotonFileLayer(BufferedImage data, boolean invert, IFileHeader photonFileHeader) { 90 | layerPositionZ = 0.05f; 91 | layerExposure = 10.0f; 92 | layerOffTimeSeconds = 0; 93 | 94 | this.photonFileHeader = (PhotonFileHeader) photonFileHeader; 95 | 96 | PhotonLayer layer = new PhotonLayer(data, invert); 97 | try { 98 | saveLayer(layer); 99 | } catch (Exception e) { 100 | // TODO Auto-generated catch block 101 | e.printStackTrace(); 102 | } 103 | } 104 | 105 | public int savePos(int dataPosition) throws Exception { 106 | dataAddress = dataPosition; 107 | return dataPosition + dataSize; 108 | } 109 | 110 | public void save(PhotonOutputStream os) throws Exception { 111 | System.out.println("posZ = " + layerPositionZ); 112 | os.writeFloat(layerPositionZ); 113 | System.out.println("layerExposure = " + layerExposure); 114 | os.writeFloat(layerExposure); 115 | System.out.println("layerOffTimeSeconds = " + layerOffTimeSeconds); 116 | os.writeFloat(layerOffTimeSeconds); 117 | 118 | os.writeInt(dataAddress); 119 | os.writeInt(dataSize); 120 | 121 | os.writeInt(unknown1); 122 | os.writeInt(unknown2); 123 | os.writeInt(unknown3); 124 | os.writeInt(unknown4); 125 | } 126 | 127 | public void saveData(PhotonOutputStream os) throws Exception { 128 | os.write(imageData, 0, dataSize); 129 | } 130 | 131 | public static int getByteSize() { 132 | return 4 + 4 + 4 + 4 + 4 + 4 + 4 + 4 + 4; 133 | } 134 | 135 | public ArrayList unpackImage(int resolutionX, int resolutionY) { 136 | pixels = 0; 137 | resolutionX = resolutionX - 1; 138 | ArrayList unpackedImage = new ArrayList<>(resolutionY); 139 | BitSet currentRow = new BitSet(resolutionX); 140 | unpackedImage.add(currentRow); 141 | int x = 0; 142 | for (byte rle : imageData) { 143 | int length = rle & 0x7F; 144 | boolean color = (rle & 0x80) == 0x80; 145 | if (color) { 146 | pixels += length; 147 | } 148 | int endPosition = x + (length - 1); 149 | int lineEnd = Integer.min(endPosition, resolutionX); 150 | if (color) { 151 | currentRow.set(x, 1 + lineEnd); 152 | } 153 | if (endPosition > resolutionX) { 154 | currentRow = new BitSet(); 155 | unpackedImage.add(currentRow); 156 | lineEnd = endPosition - (resolutionX + 1); 157 | if (color ) { 158 | currentRow.set(0, 1 + lineEnd); 159 | } 160 | } 161 | x = lineEnd + 1; 162 | if (x > resolutionX) { 163 | currentRow = new BitSet(); 164 | unpackedImage.add(currentRow); 165 | x = 0; 166 | } 167 | } 168 | return unpackedImage; 169 | } 170 | 171 | private void aaPixels(ArrayList unpackedImage, PhotonLayer photonLayer) { 172 | photonLayer.clear(); 173 | 174 | for (int y = 0; y < unpackedImage.size(); y++) { 175 | BitSet currentRow = unpackedImage.get(y); 176 | if (currentRow != null) { 177 | for (int x = 0; x < currentRow.length(); x++) { 178 | if (currentRow.get(x)) { 179 | photonLayer.unSupported(x, y); 180 | } 181 | } 182 | } 183 | } 184 | } 185 | 186 | private void unknownPixels(ArrayList unpackedImage, PhotonLayer photonLayer) { 187 | photonLayer.clear(); 188 | 189 | for (int y = 0; y < unpackedImage.size(); y++) { 190 | BitSet currentRow = unpackedImage.get(y); 191 | if (currentRow != null) { 192 | int x = 0; 193 | while ((x = currentRow.nextSetBit(x)) >= 0) { 194 | photonLayer.supported(x, y); 195 | ++x; 196 | } 197 | } 198 | } 199 | } 200 | 201 | private void calculate(ArrayList unpackedImage, ArrayList previousUnpackedImage, PhotonLayer photonLayer) { 202 | islandRows = new ArrayList<>(); 203 | isLandsCount = 0; 204 | 205 | photonLayer.clear(); 206 | 207 | for (int y = 0; y < unpackedImage.size(); y++) { 208 | BitSet currentRow = unpackedImage.get(y); 209 | BitSet prevRow = previousUnpackedImage != null ? previousUnpackedImage.get(y) : null; 210 | if (currentRow != null) { 211 | int x = 0; 212 | while ((x = currentRow.nextSetBit(x)) >= 0) { 213 | if (prevRow == null || prevRow.get(x)) { 214 | photonLayer.supported(x, y); 215 | } else { 216 | photonLayer.island(x, y); 217 | } 218 | ++x; 219 | } 220 | } 221 | } 222 | 223 | photonLayer.reduce(); 224 | 225 | isLandsCount = photonLayer.setIslands(islandRows); 226 | } 227 | 228 | 229 | public static List readLayers(PhotonFileHeader photonFileHeader, byte[] file, int margin, IPhotonProgress iPhotonProgress) throws Exception { 230 | //PhotonLayer photonLayer = new PhotonLayer(photonFileHeader.getResolutionX(), photonFileHeader.getResolutionY()); 231 | 232 | List layers = new ArrayList<>(); 233 | 234 | int antiAliasLevel = 1; 235 | if (photonFileHeader.getVersion() > 1) { 236 | antiAliasLevel = photonFileHeader.getAntiAliasingLevel(); 237 | } 238 | 239 | int layerCount = photonFileHeader.getNumberOfLayers(); 240 | 241 | try (PhotonInputStream ds = new PhotonInputStream(new ByteArrayInputStream(file, photonFileHeader.getLayersDefinitionOffsetAddress(), file.length))) { 242 | Hashtable layerMap = new Hashtable<>(); 243 | for (int i = 0; i < layerCount; i++) { 244 | 245 | iPhotonProgress.showInfo("Reading photon file layer " + (i + 1) + "/" + photonFileHeader.getNumberOfLayers()); 246 | 247 | PhotonFileLayer layer = new PhotonFileLayer(ds); 248 | layer.photonFileHeader = photonFileHeader; 249 | layer.imageData = Arrays.copyOfRange(file, layer.dataAddress, layer.dataAddress + layer.dataSize); 250 | layers.add(layer); 251 | layerMap.put(i, layer); 252 | } 253 | 254 | if (antiAliasLevel > 1) { 255 | for (int a = 0; a < (antiAliasLevel - 1); a++) { 256 | for (int i = 0; i < layerCount; i++) { 257 | iPhotonProgress.showInfo("Reading photon file AA " + (2 + a) + "/" + antiAliasLevel + " layer " + (i + 1) + "/" + photonFileHeader.getNumberOfLayers()); 258 | 259 | PhotonFileLayer layer = new PhotonFileLayer(ds); 260 | layer.photonFileHeader = photonFileHeader; 261 | layer.imageData = Arrays.copyOfRange(file, layer.dataAddress, layer.dataAddress + layer.dataSize); 262 | 263 | layerMap.get(i).addAntiAliasLayer(layer); 264 | 265 | } 266 | } 267 | } 268 | } 269 | 270 | //photonLayer.unLink(); 271 | System.gc(); 272 | 273 | return layers; 274 | } 275 | 276 | private void addAntiAliasLayer(PhotonFileLayer layer) { 277 | antiAliasLayers.add(layer); 278 | } 279 | 280 | public static void calculateAALayers(PhotonFileHeader photonFileHeader, List layers, PhotonAaMatrix photonAaMatrix, IPhotonProgress iPhotonProgress) throws Exception { 281 | PhotonLayer photonLayer = new PhotonLayer(photonFileHeader.getResolutionX(), photonFileHeader.getResolutionY()); 282 | int[][] source = new int[photonFileHeader.getResolutionY()][photonFileHeader.getResolutionX()]; 283 | 284 | int i = 0; 285 | for (PhotonFileLayer layer : layers) { 286 | ArrayList unpackedImage = layer.unpackImage(photonFileHeader.getResolutionX(), photonFileHeader.getResolutionY()); 287 | 288 | iPhotonProgress.showInfo("Calculating AA for photon file layer " + i + "/" + photonFileHeader.getNumberOfLayers()); 289 | 290 | 291 | for (int y = 0; y < photonFileHeader.getResolutionY(); y++) { 292 | for (int x = 0; x < photonFileHeader.getResolutionX(); x++) { 293 | source[y][x] = 0; 294 | } 295 | } 296 | 297 | for (int y = 0; y < unpackedImage.size(); y++) { 298 | BitSet currentRow = unpackedImage.get(y); 299 | if (currentRow != null) { 300 | for (int x = 0; x < currentRow.length(); x++) { 301 | if (currentRow.get(x)) { 302 | source[y][x] = 255; 303 | } 304 | } 305 | } 306 | } 307 | 308 | // Calc 309 | int[][] target = photonAaMatrix.calc(source); 310 | 311 | int aaTresholdDiff = 255 / photonFileHeader.getAntiAliasingLevel(); 312 | int aaTreshold = 0; 313 | for (PhotonFileLayer aaFileLayer : layer.antiAliasLayers) { 314 | photonLayer.clear(); 315 | aaTreshold += aaTresholdDiff; 316 | 317 | for (int y = 0; y < photonFileHeader.getResolutionY(); y++) { 318 | for (int x = 0; x < photonFileHeader.getResolutionX(); x++) { 319 | if (target[y][x] >= aaTreshold) { 320 | photonLayer.supported(x, y); 321 | } 322 | } 323 | } 324 | 325 | aaFileLayer.saveLayer(photonLayer); 326 | } 327 | 328 | i++; 329 | } 330 | photonLayer.unLink(); 331 | System.gc(); 332 | 333 | } 334 | 335 | public static void calculateLayers(PhotonFileHeader photonFileHeader, List layers, int margin, IPhotonProgress iPhotonProgress) throws Exception { 336 | PhotonLayer photonLayer = new PhotonLayer(photonFileHeader.getResolutionX(), photonFileHeader.getResolutionY()); 337 | ArrayList previousUnpackedImage = null; 338 | int i = 0; 339 | for (PhotonFileLayer layer : layers) { 340 | ArrayList unpackedImage = layer.unpackImage(photonFileHeader.getResolutionX(), photonFileHeader.getResolutionY()); 341 | 342 | iPhotonProgress.showInfo("Calculating photon file layer " + i + "/" + photonFileHeader.getNumberOfLayers()); 343 | 344 | if (margin > 0) { 345 | layer.extendsMargin = layer.checkMargin(unpackedImage, margin); 346 | } 347 | 348 | layer.unknownPixels(unpackedImage, photonLayer); 349 | 350 | layer.calculate(unpackedImage, previousUnpackedImage, photonLayer); 351 | 352 | if (previousUnpackedImage != null) { 353 | previousUnpackedImage.clear(); 354 | } 355 | previousUnpackedImage = unpackedImage; 356 | 357 | layer.packedLayerImage = photonLayer.packLayerImage(); 358 | layer.isCalculated = true; 359 | 360 | if (photonFileHeader.getVersion() > 1) { 361 | for (PhotonFileLayer aaFileLayer : layer.antiAliasLayers) { 362 | ArrayList aaUnpackedImage = aaFileLayer.unpackImage(photonFileHeader.getResolutionX(), photonFileHeader.getResolutionY()); 363 | PhotonLayer aaPhotonLayer = new PhotonLayer(photonFileHeader.getResolutionX(), photonFileHeader.getResolutionY()); 364 | aaFileLayer.unknownPixels(aaUnpackedImage, aaPhotonLayer); 365 | aaFileLayer.packedLayerImage = aaPhotonLayer.packLayerImage(); 366 | aaFileLayer.isCalculated = false; 367 | } 368 | } 369 | 370 | i++; 371 | } 372 | photonLayer.unLink(); 373 | System.gc(); 374 | } 375 | 376 | public static void calculateLayers(PhotonFileHeader photonFileHeader, List layers, int margin, int layerNo) throws Exception { 377 | PhotonLayer photonLayer = new PhotonLayer(photonFileHeader.getResolutionX(), photonFileHeader.getResolutionY()); 378 | ArrayList previousUnpackedImage = null; 379 | 380 | if (layerNo > 0) { 381 | previousUnpackedImage = layers.get(layerNo - 1).unpackImage(photonFileHeader.getResolutionX(), photonFileHeader.getResolutionY()); 382 | } 383 | 384 | for (int i = 0; i < 2; i++) { 385 | PhotonFileLayer layer = layers.get(layerNo + i); 386 | ArrayList unpackedImage = layer.unpackImage(photonFileHeader.getResolutionX(), photonFileHeader.getResolutionY()); 387 | 388 | if (margin > 0) { 389 | layer.extendsMargin = layer.checkMargin(unpackedImage, margin); 390 | } 391 | 392 | layer.unknownPixels(unpackedImage, photonLayer); 393 | 394 | layer.calculate(unpackedImage, previousUnpackedImage, photonLayer); 395 | 396 | if (previousUnpackedImage != null) { 397 | previousUnpackedImage.clear(); 398 | } 399 | previousUnpackedImage = unpackedImage; 400 | 401 | layer.packedLayerImage = photonLayer.packLayerImage(); 402 | layer.isCalculated = true; 403 | 404 | i++; 405 | } 406 | photonLayer.unLink(); 407 | System.gc(); 408 | } 409 | 410 | public ArrayList getRows() { 411 | return PhotonLayer.getRows(packedLayerImage, photonFileHeader.getResolutionX(), isCalculated); 412 | } 413 | 414 | public ArrayList getIslandRows() { 415 | return islandRows; 416 | } 417 | 418 | public int getIsLandsCount() { 419 | return isLandsCount; 420 | } 421 | 422 | public long getPixels() { 423 | return pixels; 424 | } 425 | 426 | public float getLayerPositionZ() { 427 | return layerPositionZ; 428 | } 429 | 430 | public void setLayerPositionZ(float layerPositionZ) { 431 | this.layerPositionZ = layerPositionZ; 432 | } 433 | 434 | public float getLayerExposure() { 435 | return layerExposure; 436 | } 437 | 438 | public float getLayerOffTime() { 439 | return layerOffTimeSeconds; 440 | } 441 | 442 | public void setLayerExposure(float layerExposure) { 443 | this.layerExposure = layerExposure; 444 | } 445 | 446 | public void setLayerOffTimeSeconds(float layerOffTimeSeconds) { 447 | this.layerOffTimeSeconds = layerOffTimeSeconds; 448 | } 449 | 450 | public void unLink() { 451 | imageData = null; 452 | packedLayerImage = null; 453 | if (islandRows != null) { 454 | islandRows.clear(); 455 | } 456 | photonFileHeader = null; 457 | } 458 | 459 | public boolean doExtendMargin() { 460 | return extendsMargin; 461 | } 462 | 463 | private boolean checkMargin(ArrayList unpackedImage, int margin) { 464 | if (unpackedImage.size() > margin) { 465 | // check top margin rows 466 | for (int i = 0; i < margin; i++) { 467 | if (!unpackedImage.get(i).isEmpty()) { 468 | return true; 469 | } 470 | } 471 | // check bottom margin rows 472 | for (int i = unpackedImage.size() - margin; i < unpackedImage.size(); i++) { 473 | if (!unpackedImage.get(i).isEmpty()) { 474 | return true; 475 | } 476 | } 477 | 478 | for (int i = margin; i < unpackedImage.size() - margin; i++) { 479 | BitSet row = unpackedImage.get(i); 480 | int nextBit = row.nextSetBit(0); 481 | if (nextBit >= 0 && nextBit < margin) { 482 | return true; 483 | } 484 | nextBit = row.nextSetBit(photonFileHeader.getResolutionX() - margin); 485 | if (nextBit > photonFileHeader.getResolutionX() - margin) { 486 | return true; 487 | } 488 | } 489 | 490 | } 491 | return false; 492 | } 493 | 494 | public PhotonLayer getLayer() { 495 | PhotonLayer photonLayer = new PhotonLayer(photonFileHeader.getResolutionX(), photonFileHeader.getResolutionY()); 496 | photonLayer.unpackLayerImage(packedLayerImage); 497 | return photonLayer; 498 | } 499 | 500 | public void getUpdateLayer(PhotonLayer photonLayer) { 501 | photonLayer.unpackLayerImage(packedLayerImage); 502 | } 503 | 504 | public void updateLayerIslands(PhotonLayer photonLayer) { 505 | islandRows = new ArrayList<>(); 506 | isLandsCount = photonLayer.setIslands(islandRows); 507 | } 508 | 509 | public void saveLayer(PhotonLayer photonLayer) throws Exception { 510 | this.packedLayerImage = photonLayer.packLayerImage(); 511 | this.imageData = photonLayer.packImageData(); 512 | this.dataSize = imageData.length; 513 | islandRows = new ArrayList<>(); 514 | isLandsCount = photonLayer.setIslands(islandRows); 515 | } 516 | 517 | public ArrayList getUnknownRows() { 518 | return unpackImage(photonFileHeader.getResolutionX(), photonFileHeader.getResolutionY()); 519 | } 520 | 521 | public PhotonFileLayer getAntiAlias(int a) { 522 | if (antiAliasLayers.size() > a) { 523 | return antiAliasLayers.get(a); 524 | } 525 | return null; 526 | } 527 | 528 | public ArrayList getAntiAlias() { 529 | return antiAliasLayers; 530 | } 531 | } 532 | -------------------------------------------------------------------------------- /src/photon/file/PhotonFile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Bonosoft 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package photon.file; 26 | 27 | import photon.file.parts.*; 28 | import photon.file.parts.photon.PhotonFileHeader; 29 | import photon.file.parts.photons.PhotonsFileHeader; 30 | 31 | import java.io.*; 32 | import java.util.ArrayList; 33 | import java.util.List; 34 | 35 | import g2c.printers.Printer; 36 | 37 | /** 38 | * by bn on 30/06/2018. 39 | */ 40 | public class PhotonFile { 41 | private IFileHeader iFileHeader; 42 | private PhotonFilePreview previewOne; 43 | private PhotonFilePreview previewTwo; 44 | private List layers; 45 | 46 | private StringBuilder islandList; 47 | private int islandLayerCount; 48 | private ArrayList islandLayers; 49 | 50 | private int margin; 51 | private ArrayList marginLayers; 52 | 53 | public PhotonFile(Printer target) { 54 | iFileHeader = new PhotonFileHeader(target); 55 | layers = new ArrayList<>(); 56 | resetMarginAndIslandInfo(); 57 | } 58 | 59 | public PhotonFile readFile(File file, IPhotonProgress iPhotonProgress) throws Exception { 60 | if (file.getName().toLowerCase().endsWith(".photons")) { 61 | return readPhotonsFile(getBinaryData(file), iPhotonProgress); 62 | } 63 | return readFile(getBinaryData(file), iPhotonProgress); 64 | } 65 | 66 | private PhotonFile readPhotonsFile(byte[] file, IPhotonProgress iPhotonProgress) throws Exception { 67 | iPhotonProgress.showInfo("Reading Photon S file header information..."); 68 | 69 | PhotonsFileHeader photonsFileHeader = new PhotonsFileHeader(file); 70 | iFileHeader = photonsFileHeader; 71 | 72 | 73 | 74 | return this; 75 | } 76 | 77 | private PhotonFile readFile(byte[] file, IPhotonProgress iPhotonProgress) throws Exception { 78 | iPhotonProgress.showInfo("Reading Photon file header information..."); 79 | PhotonFileHeader photonFileHeader = new PhotonFileHeader(file); 80 | iFileHeader = photonFileHeader; 81 | 82 | iPhotonProgress.showInfo("Reading photon large preview image information..."); 83 | previewOne = new PhotonFilePreview(photonFileHeader.getPreviewOneOffsetAddress(), file); 84 | iPhotonProgress.showInfo("Reading photon small preview image information..."); 85 | previewTwo = new PhotonFilePreview(photonFileHeader.getPreviewTwoOffsetAddress(), file); 86 | if (photonFileHeader.getVersion() > 1) { 87 | iPhotonProgress.showInfo("Reading Print parameters information..."); 88 | photonFileHeader.readParameters(file); 89 | } 90 | iPhotonProgress.showInfo("Reading photon layers information..."); 91 | layers = PhotonFileLayer.readLayers(photonFileHeader, file, margin, iPhotonProgress); 92 | resetMarginAndIslandInfo(); 93 | 94 | return this; 95 | } 96 | 97 | public void saveFile(File file) throws Exception { 98 | FileOutputStream fileOutputStream = new FileOutputStream(file); 99 | writeFile(fileOutputStream); 100 | fileOutputStream.flush(); 101 | fileOutputStream.close(); 102 | } 103 | 104 | private void writeFile(OutputStream outputStream) throws Exception { 105 | int antiAliasLevel = iFileHeader.getAALevels(); 106 | 107 | int headerPos = 0; 108 | int previewOnePos = headerPos + iFileHeader.getByteSize(); 109 | int previewTwoPos = previewOnePos + previewOne.getByteSize(); 110 | int layerDefinitionPos = previewTwoPos + previewTwo.getByteSize(); 111 | 112 | int parametersPos = 0; 113 | int machineInfoPos = 0; 114 | if (iFileHeader.getVersion() > 1) { 115 | parametersPos = layerDefinitionPos; 116 | if (((PhotonFileHeader)iFileHeader).photonFileMachineInfo.getByteSize() > 0) { 117 | machineInfoPos = parametersPos + ((PhotonFileHeader)iFileHeader).photonFilePrintParameters.getByteSize(); 118 | layerDefinitionPos = machineInfoPos + ((PhotonFileHeader)iFileHeader).photonFileMachineInfo.getByteSize(); 119 | } else { 120 | layerDefinitionPos = parametersPos + ((PhotonFileHeader)iFileHeader).photonFilePrintParameters.getByteSize(); 121 | } 122 | } 123 | 124 | int dataPosition = layerDefinitionPos + (PhotonFileLayer.getByteSize() * iFileHeader.getNumberOfLayers() * antiAliasLevel); 125 | 126 | 127 | PhotonOutputStream os = new PhotonOutputStream(outputStream); 128 | 129 | ((PhotonFileHeader)iFileHeader).save(os, previewOnePos, previewTwoPos, layerDefinitionPos, parametersPos, machineInfoPos); 130 | previewOne.save(os, previewOnePos); 131 | previewTwo.save(os, previewTwoPos); 132 | 133 | if (iFileHeader.getVersion() > 1) { 134 | ((PhotonFileHeader)iFileHeader).photonFilePrintParameters.save(os); 135 | ((PhotonFileHeader)iFileHeader).photonFileMachineInfo.save(os, machineInfoPos); 136 | } 137 | 138 | // Optimize order for speed read on photon 139 | for (int i = 0; i < iFileHeader.getNumberOfLayers(); i++) { 140 | PhotonFileLayer layer = layers.get(i); 141 | dataPosition = layer.savePos(dataPosition); 142 | if (antiAliasLevel > 1) { 143 | for (int a = 0; a < (antiAliasLevel - 1); a++) { 144 | dataPosition = layer.getAntiAlias(a).savePos(dataPosition); 145 | } 146 | } 147 | } 148 | 149 | // Order for backward compatibility with photon/cbddlp version 1 150 | for (int i = 0; i < iFileHeader.getNumberOfLayers(); i++) { 151 | layers.get(i).save(os); 152 | } 153 | 154 | if (antiAliasLevel > 1) { 155 | for (int a = 0; a < (antiAliasLevel - 1); a++) { 156 | for (int i = 0; i < iFileHeader.getNumberOfLayers(); i++) { 157 | layers.get(i).getAntiAlias(a).save(os); 158 | } 159 | } 160 | } 161 | 162 | // Optimize order for speed read on photon 163 | for (int i = 0; i < iFileHeader.getNumberOfLayers(); i++) { 164 | PhotonFileLayer layer = layers.get(i); 165 | layer.saveData(os); 166 | if (antiAliasLevel > 1) { 167 | for (int a = 0; a < (antiAliasLevel - 1); a++) { 168 | layer.getAntiAlias(a).saveData(os); 169 | } 170 | } 171 | } 172 | } 173 | 174 | 175 | private byte[] getBinaryData(File entry) throws Exception { 176 | if (entry.isFile()) { 177 | int fileSize = (int) entry.length(); 178 | byte[] fileData = new byte[fileSize]; 179 | 180 | InputStream stream = new FileInputStream(entry); 181 | int bytesRead = 0; 182 | while (bytesRead < fileSize) { 183 | int readCount = stream.read(fileData, bytesRead, fileSize - bytesRead); 184 | if (readCount < 0) { 185 | throw new IOException("Could not read all bytes of the file"); 186 | } 187 | bytesRead += readCount; 188 | } 189 | 190 | return fileData; 191 | } 192 | return null; 193 | } 194 | 195 | public String getInformation() { 196 | if (iFileHeader == null) return ""; 197 | return iFileHeader.getInformation(); 198 | } 199 | 200 | 201 | public int getIslandLayerCount() { 202 | if (islandList == null) { 203 | findIslands(); 204 | } 205 | return islandLayerCount; 206 | } 207 | 208 | public ArrayList getIslandLayers() { 209 | if (islandList == null) { 210 | findIslands(); 211 | } 212 | return islandLayers; 213 | } 214 | 215 | public void setMargin(int margin) { 216 | this.margin = margin; 217 | } 218 | 219 | public ArrayList getMarginLayers() { 220 | if (marginLayers == null) { 221 | return new ArrayList<>(); 222 | } 223 | return marginLayers; 224 | } 225 | 226 | public String getMarginInformation() { 227 | if (marginLayers == null) { 228 | return "No safety margin set, printing to the border."; 229 | } else { 230 | if (marginLayers.size() == 0) { 231 | return "The model is within the defined safety margin (" + this.margin + " pixels)."; 232 | } else if (marginLayers.size() == 1) { 233 | return "The layer " + marginLayers.get(0) + " contains model parts that extend beyond the margin."; 234 | } 235 | StringBuilder marginList = new StringBuilder(); 236 | int count = 0; 237 | for (int layer : marginLayers) { 238 | if (count > 10) { 239 | marginList.append(", ..."); 240 | break; 241 | } else { 242 | if (marginList.length() > 0) marginList.append(", "); 243 | marginList.append(layer); 244 | } 245 | count++; 246 | } 247 | return "The layers " + marginList.toString() + " contains model parts that extend beyond the margin."; 248 | } 249 | } 250 | 251 | public String getLayerInformation() { 252 | if (islandList == null) { 253 | findIslands(); 254 | } 255 | if (islandLayerCount == 0) { 256 | return "Whoopee, all is good, no unsupported areas"; 257 | } else if (islandLayerCount == 1) { 258 | return "Unsupported islands found in layer " + islandList.toString(); 259 | } 260 | return "Unsupported islands found in layers " + islandList.toString(); 261 | } 262 | 263 | private void findIslands() { 264 | if (islandLayers != null) { 265 | islandLayers.clear(); 266 | islandList = new StringBuilder(); 267 | islandLayerCount = 0; 268 | if (layers != null) { 269 | for (int i = 0; i < iFileHeader.getNumberOfLayers(); i++) { 270 | PhotonFileLayer layer = layers.get(i); 271 | if (layer.getIsLandsCount() > 0) { 272 | if (islandLayerCount < 11) { 273 | if (islandLayerCount == 10) { 274 | islandList.append(", ..."); 275 | } else { 276 | if (islandList.length() > 0) islandList.append(", "); 277 | islandList.append(i); 278 | } 279 | } 280 | islandLayerCount++; 281 | islandLayers.add(i); 282 | } 283 | } 284 | } 285 | } 286 | } 287 | 288 | public int getWidth() { 289 | return iFileHeader.getResolutionY(); 290 | } 291 | 292 | public int getHeight() { 293 | return iFileHeader.getResolutionX(); 294 | } 295 | 296 | public int getLayerCount() { 297 | return iFileHeader.getNumberOfLayers(); 298 | } 299 | 300 | public PhotonFileLayer getLayer(int i) { 301 | if (layers != null && layers.size() > i) { 302 | return layers.get(i); 303 | } 304 | return null; 305 | } 306 | 307 | public long getPixels() { 308 | long total = 0; 309 | if (layers != null) { 310 | for (PhotonFileLayer layer : layers) { 311 | total += layer.getPixels(); 312 | } 313 | } 314 | return total; 315 | } 316 | 317 | public IFileHeader getPhotonFileHeader() { 318 | return iFileHeader; 319 | } 320 | 321 | public void setPreviewOne(PhotonFilePreview newPreview) { 322 | previewOne = newPreview; 323 | } 324 | 325 | public void setPreviewTwo(PhotonFilePreview newPreview) { 326 | previewTwo = newPreview; 327 | } 328 | 329 | public PhotonFilePreview getPreviewOne() { 330 | return previewOne; 331 | } 332 | 333 | public PhotonFilePreview getPreviewTwo() { 334 | return previewTwo; 335 | } 336 | 337 | 338 | public void unLink() { 339 | while (!layers.isEmpty()) { 340 | PhotonFileLayer layer = layers.remove(0); 341 | layer.unLink(); 342 | } 343 | if (islandLayers != null) { 344 | islandLayers.clear(); 345 | } 346 | if (marginLayers != null) { 347 | marginLayers.clear(); 348 | } 349 | iFileHeader.unLink(); 350 | iFileHeader = null; 351 | previewOne.unLink(); 352 | previewOne = null; 353 | previewTwo.unLink(); 354 | previewTwo = null; 355 | System.gc(); 356 | } 357 | 358 | 359 | public void adjustLayerSettings() { 360 | double totalTime = 0; 361 | for (int i = 0; i < layers.size(); i++) { 362 | PhotonFileLayer layer = layers.get(i); 363 | if (i < iFileHeader.getBottomLayers()) { 364 | layer.setLayerExposure(iFileHeader.getBottomExposureTimeSeconds()); 365 | totalTime += iFileHeader.getBottomExposureTimeSeconds(); 366 | } else { 367 | layer.setLayerExposure(iFileHeader.getNormalExposure()); 368 | totalTime += iFileHeader.getBottomExposureTimeSeconds(); 369 | } 370 | layer.setLayerOffTimeSeconds(iFileHeader.getOffTimeSeconds()); 371 | totalTime += iFileHeader.getOffTimeSeconds(); 372 | } 373 | iFileHeader.setPrintTimeSeconds((int) totalTime); 374 | } 375 | 376 | public void fixAll(IPhotonProgress progres) throws Exception { 377 | boolean layerWasFixed = false; 378 | do { 379 | do { 380 | // Repeatedly fix layers until none are possible to fix 381 | // Fixing some layers can make other layers auto-fixable 382 | layerWasFixed = fixLayers(progres); 383 | } while(layerWasFixed); 384 | if(islandLayers.size() > 0) { 385 | // Nothing can be done further, just remove all layers left 386 | layerWasFixed = removeAllIslands(progres) || layerWasFixed; 387 | } 388 | if(layerWasFixed && islandLayers.size() > 0) { 389 | // We could've created new islands by removing islands, repeat fixing process 390 | // until everything is fixed or nothing can be done 391 | progres.showInfo("
Some layers were fixed, but " + islandLayers.size() + " still unsupported, repeating...
"); 392 | } 393 | } while(layerWasFixed); 394 | } 395 | 396 | public boolean removeAllIslands(IPhotonProgress progres) throws Exception { 397 | boolean layersFixed = false; 398 | progres.showInfo("Removing islands from " + islandLayers.size() + " layers...
"); 399 | PhotonLayer layer = null; 400 | for (int layerNo : islandLayers) { 401 | PhotonFileLayer fileLayer = layers.get(layerNo); 402 | if (layer == null) { 403 | layer = fileLayer.getLayer(); 404 | } else { 405 | fileLayer.getUpdateLayer(layer); 406 | } 407 | progres.showInfo("Removing islands from layer " + layerNo); 408 | 409 | int removed = layer.removeIslands(); 410 | if(removed == 0) { 411 | progres.showInfo(", but nothing could be done."); 412 | } else { 413 | progres.showInfo(", " + removed + " islands removed"); 414 | fileLayer.saveLayer(layer); 415 | calculate(layerNo); 416 | if (layerNo < getLayerCount() - 1) { 417 | calculate(layerNo + 1); 418 | } 419 | layersFixed = true; 420 | } 421 | progres.showInfo("
"); 422 | } 423 | findIslands(); 424 | return layersFixed; 425 | } 426 | 427 | public boolean fixLayers(IPhotonProgress progres) throws Exception { 428 | boolean layersFixed = false; 429 | PhotonLayer layer = null; 430 | for (int layerNo : islandLayers) { 431 | progres.showInfo("Checking layer " + layerNo); 432 | 433 | // Unpack the layer data to the layer utility class 434 | PhotonFileLayer fileLayer = layers.get(layerNo); 435 | if (layer == null) { 436 | layer = fileLayer.getLayer(); 437 | } else { 438 | fileLayer.getUpdateLayer(layer); 439 | } 440 | 441 | int changed = fixit(progres, layer, fileLayer, 10); 442 | if (changed == 0) { 443 | progres.showInfo(", but nothing could be done."); 444 | } else { 445 | fileLayer.saveLayer(layer); 446 | calculate(layerNo); 447 | if (layerNo < getLayerCount() - 1) { 448 | calculate(layerNo + 1); 449 | } 450 | layersFixed = true; 451 | } 452 | 453 | progres.showInfo("
"); 454 | 455 | } 456 | findIslands(); 457 | return layersFixed; 458 | } 459 | 460 | private int fixit(IPhotonProgress progres, PhotonLayer layer, PhotonFileLayer fileLayer, int loops) throws Exception { 461 | int changed = layer.fixlayer(); 462 | if (changed > 0) { 463 | layer.reduce(); 464 | fileLayer.updateLayerIslands(layer); 465 | progres.showInfo(", " + changed + " pixels changed"); 466 | if (loops > 0) { 467 | changed += fixit(progres, layer, fileLayer, loops - 1); 468 | } 469 | } 470 | return changed; 471 | } 472 | 473 | public void calculateAaLayers(IPhotonProgress progres, PhotonAaMatrix photonAaMatrix) throws Exception { 474 | PhotonFileLayer.calculateAALayers((PhotonFileHeader) iFileHeader, layers, photonAaMatrix, progres); 475 | } 476 | 477 | public void calculate(IPhotonProgress progres) throws Exception { 478 | PhotonFileLayer.calculateLayers((PhotonFileHeader)iFileHeader, layers, margin, progres); 479 | resetMarginAndIslandInfo(); 480 | } 481 | 482 | public void calculate(int layerNo) throws Exception { 483 | PhotonFileLayer.calculateLayers((PhotonFileHeader)iFileHeader, layers, margin, layerNo); 484 | resetMarginAndIslandInfo(); 485 | } 486 | 487 | private void resetMarginAndIslandInfo() { 488 | islandList = null; 489 | islandLayerCount = 0; 490 | islandLayers = new ArrayList<>(); 491 | 492 | if (margin > 0) { 493 | marginLayers = new ArrayList<>(); 494 | int i = 0; 495 | for (PhotonFileLayer layer : layers) { 496 | if (layer.doExtendMargin()) { 497 | marginLayers.add(i); 498 | } 499 | i++; 500 | } 501 | } 502 | } 503 | 504 | public float getZdrift() { 505 | float expectedHeight = iFileHeader.getLayerHeight() * (iFileHeader.getNumberOfLayers() - 1); 506 | float actualHeight = layers.get(layers.size() - 1).getLayerPositionZ(); 507 | return expectedHeight - actualHeight; 508 | 509 | } 510 | 511 | public void fixLayerHeights() { 512 | int index = 0; 513 | for (PhotonFileLayer layer : layers) { 514 | layer.setLayerPositionZ(index * iFileHeader.getLayerHeight()); 515 | index++; 516 | } 517 | } 518 | 519 | public int getVersion() { 520 | return iFileHeader.getVersion(); 521 | } 522 | 523 | public boolean hasAA() { 524 | return iFileHeader.hasAA(); 525 | } 526 | 527 | public int getAALevels() { 528 | return iFileHeader.getAALevels(); 529 | } 530 | 531 | public void changeToVersion2() { 532 | iFileHeader.setFileVersion(2); 533 | } 534 | 535 | // only call this when recalculating AA levels 536 | public void setAALevels(int levels) { 537 | iFileHeader.setAALevels(levels, layers); 538 | } 539 | 540 | public void addLayer(PhotonLayer photonLayer) { 541 | // TODO Auto-generated method stub 542 | 543 | } 544 | 545 | public void addLayer(PhotonFileLayer photonFileLayer) { 546 | layers.add(photonFileLayer); 547 | ((PhotonFileHeader) iFileHeader).printTimeSeconds += (int) photonFileLayer.getLayerExposure(); 548 | ((PhotonFileHeader) iFileHeader).setNumberOfLayers(((PhotonFileHeader) iFileHeader).getNumberOfLayers() + 1); 549 | } 550 | 551 | public void setExposure(int value) { 552 | ((PhotonFileHeader) iFileHeader).setExposureTimeSeconds((float) value); 553 | ((PhotonFileHeader) iFileHeader).setExposureBottomTimeSeconds((float) value); 554 | adjustLayerSettings(); 555 | } 556 | } 557 | 558 | --------------------------------------------------------------------------------