Java tutorial
/** * "Visolate" -- compute (Voronoi) PCB isolation routing toolpaths * * Copyright (C) 2004 Marsette A. Vona, III * 2012 Markus Hitter <mah@jump-ing.de> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ package visolate; import visolate.parser.*; import visolate.simulator.*; import visolate.model.*; import visolate.processor.*; import java.io.*; import java.text.NumberFormat; import java.text.ParseException; import java.util.*; import java.util.List; import java.net.*; import javax.swing.*; import javax.swing.filechooser.FileFilter; import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.security.*; import org.apache.commons.cli.CommandLine; import visolate.processor.GCodeFileWriter; public class Visolate extends JPanel implements SimulatorUI { private static final long serialVersionUID = 1L; public static final String DEMO_FILE = "example.grb"; /** * kept here so it is available in the invokeLater Runnable in Main */ public CommandLine commandline; public int processstatus; public boolean auto_mode; public Visolate() { this(null); } public Visolate(File file) { processstatus = 0; display = new Display(this); simulator = new Simulator(this); model = new Model(this); // TODO: get a toolpathsProcessor here, too, and get rid of myToolpathsProcessor. gCodeWriter = new GCodeFileWriter(); setBackground(Color.WHITE); setOpaque(true); Dimension d; Box processingBox = getProcessingBox(); Box box = Box.createVerticalBox(); box.add(getLoadFileBox()); box.add(display); box.add(model); box.add(getGCodeOptionsBox()); box.add(processingBox); setLayout(new BorderLayout()); add(box, "Center"); //make display take up max available space Dimension orig = getPreferredSize(); d = getLoadFileBox().getPreferredSize(); getLoadFileBox().setMaximumSize(new Dimension(Integer.MAX_VALUE, d.height)); d = model.getPreferredSize(); model.setMaximumSize(new Dimension(Integer.MAX_VALUE, d.height)); d = processingBox.getPreferredSize(); processingBox.setMaximumSize(new Dimension(Integer.MAX_VALUE, d.height)); display.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)); setPreferredSize(orig); if (file != null) loadFile(file); } private Box getLoadFileBox() { if (myLoadFileBox == null) { myLoadFileBox = Box.createHorizontalBox(); loadButton = new JButton("Load"); loadButton.setEnabled(false); loadButton.setBackground(Color.WHITE); loadButton.setVerticalAlignment(AbstractButton.CENTER); loadButton.setHorizontalAlignment(AbstractButton.CENTER); Dimension d = loadButton.getPreferredSize(); loadButton.setMaximumSize(new Dimension(d.width, d.height)); loadButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { loadFile(); } }); myLoadFileBox.add(loadButton); loadField = new JTextField(); d = loadField.getPreferredSize(); loadField.setMaximumSize(new Dimension(Integer.MAX_VALUE, d.height)); myLoadFileBox.add(loadField); browseButton = new JButton("Browse..."); browseButton.setBackground(Color.WHITE); browseButton.setVerticalAlignment(AbstractButton.CENTER); browseButton.setHorizontalAlignment(AbstractButton.CENTER); d = browseButton.getPreferredSize(); browseButton.setMaximumSize(new Dimension(d.width, d.height)); browseButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { File file = browse(); if (file != null) loadFile(file); loadButton.setEnabled(true); } }); myLoadFileBox.add(browseButton); myLoadFileBox.setBorder(BorderFactory.createTitledBorder("Input File")); } return myLoadFileBox; } private Box getGcodeBox() { if (myGcodeBox == null) { Dimension d; myGcodeBox = Box.createHorizontalBox(); gcodeButton = new JButton("Save G-Code"); gcodeButton.setEnabled(false); gcodeButton.setBackground(Color.WHITE); gcodeButton.setVerticalAlignment(AbstractButton.CENTER); gcodeButton.setHorizontalAlignment(AbstractButton.CENTER); d = gcodeButton.getPreferredSize(); gcodeButton.setMaximumSize(new Dimension(d.width, d.height)); gcodeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { saveGCode(); } }); myGcodeBox.add(gcodeButton); gcodeField = new JTextField(); gcodeField.setEnabled(false); d = gcodeField.getPreferredSize(); gcodeField.setMaximumSize(new Dimension(Integer.MAX_VALUE, d.height)); myGcodeBox.add(gcodeField); gcodeBrowseButton = new JButton("Browse..."); gcodeBrowseButton.setEnabled(false); gcodeBrowseButton.setBackground(Color.WHITE); gcodeBrowseButton.setVerticalAlignment(AbstractButton.CENTER); gcodeBrowseButton.setHorizontalAlignment(AbstractButton.CENTER); d = gcodeBrowseButton.getPreferredSize(); gcodeBrowseButton.setMaximumSize(new Dimension(d.width, d.height)); gcodeBrowseButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { File file = browse(); if (file != null) saveGCode(file); } }); myGcodeBox.add(gcodeBrowseButton); stopButton = new JButton("Stop"); stopButton.setBackground(Color.WHITE); // stopButton.setVerticalAlignment(AbstractButton.CENTER); // stopButton.setHorizontalAlignment(AbstractButton.CENTER); stopButton.setAlignmentX(0.5f); d = stopButton.getPreferredSize(); stopButton.setMaximumSize(new Dimension(Integer.MAX_VALUE, d.height)); stopButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { stopProcess(); } }); stopButton.setEnabled(false); progressBar = new JProgressBar(); progressBar.setBackground(Color.WHITE); } return myGcodeBox; } private Box getTopologyAndToolpathsBox() { Box topologyAndToolpathsBox = Box.createHorizontalBox(); topologyAndToolpathsBox.add(getTopologyBox()); topologyAndToolpathsBox.add(Box.createHorizontalGlue()); topologyAndToolpathsBox.add(getToolpathBox()); return topologyAndToolpathsBox; } private Box getToolpathBox() { Dimension d; Box toolpathsBox = Box.createHorizontalBox(); toolpathsButton = new JButton("Make Toolpaths"); toolpathsButton.setBackground(Color.WHITE); toolpathsButton.setVerticalAlignment(AbstractButton.CENTER); toolpathsButton.setHorizontalAlignment(AbstractButton.CENTER); d = toolpathsButton.getPreferredSize(); toolpathsButton.setMaximumSize(new Dimension(d.width, d.height)); toolpathsButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { computeToolpaths(); } }); toolpathsBox.add(toolpathsButton); // toolpathsBox.add(Box.createHorizontalStrut(16)); ButtonGroup modeGroup = new ButtonGroup(); voronoiButton = new JRadioButton("voronoi"); modeGroup.add(voronoiButton); voronoiButton.setBackground(Color.WHITE); // VORONOI_MODE is the default mode. voronoiButton.setSelected(true); toolpathsBox.add(voronoiButton); outlineButton = new JRadioButton("outline"); modeGroup.add(outlineButton); outlineButton.setBackground(Color.WHITE); // VORONOI_MODE is the default mode. outlineButton.setSelected(false); toolpathsBox.add(outlineButton); return toolpathsBox; } private Box getTopologyBox() { Dimension d; Box topologyBox = Box.createHorizontalBox(); topologyButton = new JButton("Fix Topology"); topologyButton.setBackground(Color.WHITE); topologyButton.setVerticalAlignment(AbstractButton.CENTER); topologyButton.setHorizontalAlignment(AbstractButton.CENTER); d = topologyButton.getPreferredSize(); topologyButton.setMaximumSize(new Dimension(d.width, d.height)); topologyButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { fixTopology(); } }); topologyBox.add(topologyButton); // topologyBox.add(Box.createHorizontalStrut(16)); manualTopology = new JCheckBox("manual"); manualTopology.setBackground(Color.WHITE); manualTopology.setSelected(false); topologyBox.add(manualTopology); return topologyBox; } private Box getMosaicBox() { Dimension d; if (myMosaicBox == null) { myMosaicBox = Box.createHorizontalBox(); mosaicButton = new JButton("Save High-Res"); mosaicButton.setBackground(Color.WHITE); mosaicButton.setVerticalAlignment(AbstractButton.CENTER); mosaicButton.setHorizontalAlignment(AbstractButton.CENTER); d = mosaicButton.getPreferredSize(); mosaicButton.setMaximumSize(new Dimension(d.width, d.height)); mosaicButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { saveMosaic(); } }); myMosaicBox.add(mosaicButton); mosaicField = new JTextField(); d = mosaicField.getPreferredSize(); mosaicField.setMaximumSize(new Dimension(Integer.MAX_VALUE, d.height)); mosaicField.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { saveMosaic(); } }); // mosaicField.addFocusListener(new FocusAdapter() { // public void focusLost(FocusEvent e) { saveMosaic(); } }); myMosaicBox.add(mosaicField); mosaicBrowseButton = new JButton("Browse..."); mosaicBrowseButton.setBackground(Color.WHITE); mosaicBrowseButton.setVerticalAlignment(AbstractButton.CENTER); mosaicBrowseButton.setHorizontalAlignment(AbstractButton.CENTER); d = mosaicBrowseButton.getPreferredSize(); mosaicBrowseButton.setMaximumSize(new Dimension(d.width, d.height)); mosaicBrowseButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { File file = browse(); if (file != null) saveMosaic(file); } }); myMosaicBox.add(mosaicBrowseButton); mosaicTilesButton = new JCheckBox("individual tiles"); mosaicTilesButton.setBackground(Color.WHITE); mosaicTilesButton.setSelected(false); myMosaicBox.add(mosaicTilesButton); } return myMosaicBox; } private Box getProcessingBox() { if (myProcessingBox == null) { myProcessingBox = Box.createVerticalBox(); myProcessingBox.setBorder(BorderFactory.createTitledBorder("Processing")); myProcessingBox.add(getMosaicBox()); myProcessingBox.add(getTopologyAndToolpathsBox()); myProcessingBox.add(getGcodeBox()); // processingBox.add(Box.createVerticalStrut(8)); myProcessingBox.add(stopButton); // processingBox.add(Box.createVerticalStrut(8)); myProcessingBox.add(progressBar); } return myProcessingBox; } private Box getGCodeOptionsBox() { if (myGCodeOptionsBox == null) { myGCodeOptionsBox = Box.createVerticalBox(); myGCodeOptionsBox.setBorder(BorderFactory.createTitledBorder("G-Code")); JPanel panel = new JPanel(); panel.setLayout(new GridLayout(3, 4)); panel.add(new JLabel("Coordinates")); panel.add(new JLabel("Z-coordinates")); panel.add(new JLabel("left upper coordinates")); panel.add(new JLabel("Metric")); panel.add(new JLabel("Feedrates")); panel.add(getRelativeCoordinatesButton()); panel.add(getZCuttingHeightPanel()); panel.add(getInitialXPanel()); panel.add(getMetricButton()); panel.add(getMillingSpeedPanel()); panel.add(getAbsoluteCoordinatesButton()); panel.add(getZDownMovementPanel()); panel.add(getInitialYPanel()); panel.add(getImperialButton()); panel.add(getPlungeSpeedPanel()); myGCodeOptionsBox.add(panel); } return myGCodeOptionsBox; } private JPanel getInitialXPanel() { if (myInitialXPanel == null) { myInitialXPanel = new JPanel(); myInitialXPanel.setLayout(new BorderLayout()); myInitialXPanel.add(new JLabel("X"), BorderLayout.WEST); myInitialXPanel.setToolTipText("Left side is at this coordinate (mm or inch)"); myInitialXPanel.setEnabled(gCodeWriter.getIsAbsolute()); final JTextField field = new JTextField(NumberFormat.getInstance().format(gCodeWriter.getXOffset())); myInitialXPanel.add(field, BorderLayout.CENTER); myInitialXPanel.addPropertyChangeListener("enabled", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { field.setEnabled(myInitialXPanel.isEnabled()); } }); field.setEnabled(myInitialXPanel.isEnabled()); field.addFocusListener(new FocusAdapter() { public void focusLost(FocusEvent evt) { try { gCodeWriter.setXOffset(NumberFormat.getInstance().parse(field.getText()).doubleValue()); } catch (ParseException e) { } field.setText(NumberFormat.getInstance().format(gCodeWriter.getXOffset())); } }); } return myInitialXPanel; } private JPanel getInitialYPanel() { if (myInitialYPanel == null) { myInitialYPanel = new JPanel(); myInitialYPanel.setLayout(new BorderLayout()); myInitialYPanel.add(new JLabel("Y"), BorderLayout.WEST); myInitialYPanel.setToolTipText("Upper side is at this coordinates (mm or inch)"); myInitialYPanel.setEnabled(gCodeWriter.getIsAbsolute()); final JTextField field = new JTextField(NumberFormat.getInstance().format(gCodeWriter.getYOffset())); myInitialYPanel.add(field, BorderLayout.CENTER); myInitialYPanel.addPropertyChangeListener("enabled", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { field.setEnabled(myInitialYPanel.isEnabled()); } }); field.setEnabled(myInitialYPanel.isEnabled()); field.addFocusListener(new FocusAdapter() { public void focusLost(FocusEvent evt) { try { gCodeWriter.setYOffset(NumberFormat.getInstance().parse(field.getText()).doubleValue()); } catch (ParseException e) { } field.setText(NumberFormat.getInstance().format(gCodeWriter.getYOffset())); } }); } return myInitialYPanel; } private Component getZDownMovementPanel() { if (myZDownMovementPanel == null) { myZDownMovementPanel = new JPanel(); myZDownMovementPanel.setLayout(new BorderLayout()); myZDownMovementPanel.add(new JLabel("travel clearance"), BorderLayout.WEST); myZDownMovementPanel.setToolTipText( "When not cutting, lift the cutter to this above origin, in mm or inch. Decimals in native language (point or comma)."); final JTextField field = new JTextField(NumberFormat.getInstance().format(gCodeWriter.getZClearance())); myZDownMovementPanel.add(field, BorderLayout.CENTER); field.addFocusListener(new FocusAdapter() { public void focusLost(FocusEvent evt) { try { gCodeWriter.setZClearance(NumberFormat.getInstance().parse(field.getText()).doubleValue()); } catch (ParseException e) { } field.setText(NumberFormat.getInstance().format(gCodeWriter.getZClearance())); } }); } return myZDownMovementPanel; } private JPanel getMillingSpeedPanel() { if (myMillingSpeedPanel == null) { myMillingSpeedPanel = new JPanel(); myMillingSpeedPanel.setLayout(new BorderLayout()); myMillingSpeedPanel.add(new JLabel("cutting feedrate"), BorderLayout.WEST); myMillingSpeedPanel.setToolTipText("Feedrate during cutting in mm or inch per minute."); final JTextField field = new JTextField( NumberFormat.getInstance().format(gCodeWriter.getMillingFeedrate())); myMillingSpeedPanel.add(field, BorderLayout.CENTER); field.addFocusListener(new FocusAdapter() { public void focusLost(FocusEvent evt) { try { gCodeWriter.setMillingFeedrate( NumberFormat.getInstance().parse(field.getText()).doubleValue()); } catch (ParseException e) { } field.setText(NumberFormat.getInstance().format(gCodeWriter.getMillingFeedrate())); } }); } return myMillingSpeedPanel; } private JPanel getPlungeSpeedPanel() { if (myPlungeSpeedPanel == null) { myPlungeSpeedPanel = new JPanel(); myPlungeSpeedPanel.setLayout(new BorderLayout()); myPlungeSpeedPanel.add(new JLabel("plunge feedrate"), BorderLayout.WEST); myPlungeSpeedPanel .setToolTipText("Feedrate when moving vertically into the workpiece in mm or inch per minute."); final JTextField field = new JTextField( NumberFormat.getInstance().format(gCodeWriter.getPlungeFeedrate())); myPlungeSpeedPanel.add(field, BorderLayout.CENTER); field.addFocusListener(new FocusAdapter() { public void focusLost(FocusEvent evt) { try { gCodeWriter .setPlungeFeedrate(NumberFormat.getInstance().parse(field.getText()).doubleValue()); } catch (ParseException e) { } field.setText(NumberFormat.getInstance().format(gCodeWriter.getPlungeFeedrate())); } }); } return myPlungeSpeedPanel; } private JPanel getZCuttingHeightPanel() { if (myZCuttingHeightPanel == null) { myZCuttingHeightPanel = new JPanel(); myZCuttingHeightPanel.setLayout(new BorderLayout()); myZCuttingHeightPanel.add(new JLabel("cutting height"), BorderLayout.WEST); myZCuttingHeightPanel.setToolTipText( "When cutting, the head should have this z-coordinate, in mm or inch. Likely a negative value, decimals in native language (point or comma)"); myZCuttingHeightPanel.setEnabled(gCodeWriter.getIsAbsolute()); final JTextField field = new JTextField( NumberFormat.getInstance().format(gCodeWriter.getZCuttingHeight())); myZCuttingHeightPanel.add(field, BorderLayout.CENTER); myZCuttingHeightPanel.addPropertyChangeListener("enabled", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { field.setEnabled(myZCuttingHeightPanel.isEnabled()); } }); field.setEnabled(myZCuttingHeightPanel.isEnabled()); field.addFocusListener(new FocusAdapter() { public void focusLost(FocusEvent evt) { try { gCodeWriter .setZCuttingHeight(NumberFormat.getInstance().parse(field.getText()).doubleValue()); } catch (ParseException e) { } field.setText(NumberFormat.getInstance().format(gCodeWriter.getZCuttingHeight())); } }); } return myZCuttingHeightPanel; } private JRadioButton getAbsoluteCoordinatesButton() { if (myAbsoluteCoordinatesButton == null) { myAbsoluteCoordinatesButton = new JRadioButton("absolute"); myAbsoluteCoordinatesButton.setSelected(gCodeWriter.getIsAbsolute()); myAbsoluteCoordinatesButton.setToolTipText( "Output absolute G-code coordinates. For the starting point, set X, Y and cutting height."); myAbsoluteCoordinatesButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { setAbsoluteCoordinates(myAbsoluteCoordinatesButton.isSelected()); } }); } return myAbsoluteCoordinatesButton; } private JRadioButton getRelativeCoordinatesButton() { if (myRelativeCoordinatesButton == null) { myRelativeCoordinatesButton = new JRadioButton("relative"); myRelativeCoordinatesButton.setSelected(!gCodeWriter.getIsAbsolute()); myRelativeCoordinatesButton .setToolTipText("Output relative G-code coordinates, starting at { 0.0, 0.0, 0.0 }."); myRelativeCoordinatesButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { setAbsoluteCoordinates(!myRelativeCoordinatesButton.isSelected()); } }); } return myRelativeCoordinatesButton; } public void setAbsoluteCoordinates(final boolean newValue) { getAbsoluteCoordinatesButton().setSelected(newValue); getRelativeCoordinatesButton().setSelected(!newValue); getZCuttingHeightPanel().setEnabled(newValue); getInitialXPanel().setEnabled(newValue); getInitialYPanel().setEnabled(newValue); gCodeWriter.setIsAbsolute(newValue); } private JRadioButton getMetricButton() { if (myMetricButton == null) { myMetricButton = new JRadioButton("metric"); myMetricButton.setSelected(gCodeWriter.getIsMetric()); myMetricButton.setToolTipText("Output G-code in mm. Coordinates entered here are in mm, too."); myMetricButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { if (myMetricButton.isSelected()) { getImperialButton().setSelected(false); gCodeWriter.setIsMetric(true); } } }); } return myMetricButton; } private JRadioButton getImperialButton() { if (myImperialButton == null) { myImperialButton = new JRadioButton("imperial"); myImperialButton.setSelected(!gCodeWriter.getIsMetric()); myImperialButton .setToolTipText("Output G-code in inches. Coordinates entered here are in inches, too."); myImperialButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { if (myImperialButton.isSelected()) { getMetricButton().setSelected(false); gCodeWriter.setIsMetric(false); } } }); } return myImperialButton; } public void saveMosaic() { saveMosaic(new File(mosaicField.getText().trim())); } public void saveMosaic(File file) { saveMosaic(file, mosaicTilesButton.isSelected()); } public void saveMosaic(File file, boolean tiles) { try { mosaicField.setText(file.toString()); if (file.exists() && !(JOptionPane.showConfirmDialog(this, "Overwrite existing mosaic file " + file + "?", "Overwrite?", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)) return; startProcess(new SaveMosaic(this, file, tiles)); } catch (AccessControlException e1) { accessControlError(); } } public void fixTopology() { startProcess(new TopologyProcessor(this)); } private void computeToolpaths() { int mode = -1; if (voronoiButton.isSelected()) mode = ToolpathsProcessor.VORONOI_MODE; else mode = ToolpathsProcessor.OUTLINE_MODE; myToolpathsProcessor = new ToolpathsProcessor(this, mode); startProcess(myToolpathsProcessor); } private File browse() { try { File input = null; String[] zenity = { "zenity", "--file-selection", "--title=Open", "--file-filter=Gerber file | *.gbr", "--file-filter=All | * " }; //FIXME: dirty workaround to get a fully functional and native file dialog on linux String filestring; try { Process p = Runtime.getRuntime().exec(zenity); BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); StringBuffer sb = new StringBuffer(); sb.append(br.readLine()); filestring = sb.toString(); if (filestring.equals("null")) { return null; } input = new File(filestring); } catch (IOException e1) { //fallback if zenity is not installed final JFileChooser fc = new JFileChooser(); fc.addChoosableFileFilter(new FileFilter() { @Override public String getDescription() { return "Gerber file"; } @Override public boolean accept(File f) { return f.getName().endsWith(".gbr"); } }); int returnVal = -1; returnVal = fc.showOpenDialog(fc); if (returnVal == JFileChooser.APPROVE_OPTION) { input = fc.getSelectedFile(); } } return input; } catch (AccessControlException e1) { accessControlError(); return null; } } private void accessControlError() { JOptionPane.showMessageDialog(this, "Cannot load or save from an applet. You must " + "either change your browser's security policy or " + "download the application and run it directly.", "Access Denied", JOptionPane.ERROR_MESSAGE); } public void destroy() { display.destroy(); } public void loadFile() { loadFile(new File(loadField.getText().trim())); } public void loadDemo() { loadURL(getClass().getResource(DEMO_FILE)); loadField.setText("[built-in demo]"); } public void loadURL(URL url) { try { load(url.openStream()); } catch (IOException e) { JOptionPane.showMessageDialog(this, "I/O Error: " + e.getMessage(), "I/O Error", JOptionPane.ERROR_MESSAGE); } } public void loadFile(File file) { currentFile = file; loadField.setText(file.toString()); setGcodeFile(file.toString() + ".ngc"); try { load(new FileInputStream(file)); } catch (FileNotFoundException e) { loadDemo(); JOptionPane.showMessageDialog(this, "File Not Found: " + e.getMessage() + "\nLoaded Demo file instead.", "File Not Found", JOptionPane.ERROR_MESSAGE); } } private void load(InputStream inputStream) { stopProcess(); enableControls(false); myToolpathsProcessor = null; simulator.reset(); try { Parser parser = new Parser(inputStream); parser.setSimulator(simulator); parser.Input(); } catch (visolate.parser.ParseException e) { JOptionPane.showMessageDialog(this, "Parse Error: " + e.getMessage(), "Parse Error", JOptionPane.ERROR_MESSAGE); } model.rebuild(); enableControls(true); } public void setGcodeFile(String filename) { gcodeField.setText(filename); } public void saveGCode() { saveGCode(new File(gcodeField.getText().trim())); } public void saveGCode(final File file) { gcodeField.setText(file.toString()); if (myToolpathsProcessor == null) { return; } try { if (file.exists() && (!auto_mode)) { int yesno = JOptionPane.showConfirmDialog(this, "Overwrite existing G-Code file " + file + "?", "Overwrite?", JOptionPane.YES_NO_OPTION); if (yesno != JOptionPane.YES_OPTION) { return; } } try { gCodeWriter.open(file); myToolpathsProcessor.writeGCode(gCodeWriter); gCodeWriter.close(); } catch (IOException e) { JOptionPane.showMessageDialog(this, "I/O Error writing G-Code: " + e.getMessage(), "I/O Error", JOptionPane.ERROR_MESSAGE); } } catch (AccessControlException e1) { accessControlError(); try { myToolpathsProcessor.writeGCode(null); } catch (IOException e2) { //nope } } } public void mouseClicked(double x, double y, int modifiers) { SortedSet<Net> clickedNets = new TreeSet<Net>(); model.getNetsAtPoint(x, y, 1.0 / display.getDPI(), clickedNets); if (manualTopology.isSelected()) { clearSelection(); TopologyProcessor.mergeNets(clickedNets); return; } if ((selectedNet != null) && clickedNets.contains(selectedNet)) { Iterator<Net> it = (clickedNets.tailSet(selectedNet)).iterator(); it.next(); if (it.hasNext()) { selectedNet = it.next(); } else { selectedNet = clickedNets.iterator().next(); } } else { selectedNet = null; if (!clickedNets.isEmpty()) { selectedNet = clickedNets.iterator().next(); } } Net selectedNetSave = selectedNet; if (!((modifiers & MouseEvent.CTRL_DOWN_MASK) != 0)) clearSelection(); selectedNet = selectedNetSave; if (selectedNet != null) { selectedNets.add(selectedNet); selectedNet.setHighlighted(true); } } public void clearSelection() { for (Iterator<Net> it = selectedNets.iterator(); it.hasNext();) { it.next().setHighlighted(false); } selectedNets.clear(); selectedNet = null; } public void keyReleased(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_F: startProcess(new FatnessProcessor(this)); break; case KeyEvent.VK_D: model.dump(); break; case KeyEvent.VK_I: { System.out.println(selectedNets.size() + " selected nets:"); for (Net net : selectedNets) { net.dump(); } break; } case KeyEvent.VK_DELETE: { for (Net net : selectedNets) { Set<Net> superNet = net.getSuperNet(); if (superNet == null) { model.deleteNet(net); } else { for (Iterator<Net> jt = superNet.iterator(); jt.hasNext();) { model.deleteNet(jt.next()); } } } undoHistory.add(0, new UndoDelete(selectedNets)); clearSelection(); break; } case KeyEvent.VK_U: { if (!undoHistory.isEmpty()) { Iterator<UndoTask> it = undoHistory.iterator(); UndoTask undoTask = it.next(); it.remove(); undoTask.undo(); } break; } } } public boolean askContinue(int line, int seq) { return JOptionPane.showConfirmDialog(this, "Continue after line " + line + ((seq >= 0) ? ("(sequence number " + seq + ")") : "") + "?", "Continue?", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION; } public Simulator getSimulator() { return simulator; } public Model getModel() { return model; } public Display getDisplay() { return display; } public void enableControls(boolean enable) { if (enable == true && loadField.getText() != null && !loadField.getText().endsWith("]")) { loadButton.setEnabled(true); } else { loadButton.setEnabled(false); } loadField.setEnabled(enable); browseButton.setEnabled(enable); mosaicButton.setEnabled(enable); mosaicField.setEnabled(enable); mosaicBrowseButton.setEnabled(enable); mosaicTilesButton.setEnabled(enable); topologyButton.setEnabled(enable); manualTopology.setEnabled(enable); toolpathsButton.setEnabled(enable); voronoiButton.setEnabled(enable); outlineButton.setEnabled(enable); if (myToolpathsProcessor != null) { gcodeButton.setEnabled(enable); gcodeField.setEnabled(enable); gcodeBrowseButton.setEnabled(enable); } } public void resetProgressBar(int numSteps) { progressBar.setMaximum(numSteps); progressBar.setValue(0); } public void tickProgressBar() { progressBar.setValue(progressBar.getValue() + 1); } public void startProcess(Processor processor) { stopProcess(); this.processor = processor; stopButton.setEnabled(true); processor.start(); } public void stopProcess() { if (processor == null) return; try { processor.stop(); } catch (InterruptedException e) { System.err.println("WARNING: interrupted while stopping process"); } processFinished(); } public void processFinished() { progressBar.setValue(0); stopButton.setEnabled(false); processor = null; if (processstatus == 1) { //returning from automated topology fixing processstatus = 2; computeToolpaths(); } else if (processstatus == 2) { //returning from automated toolpath creation System.out.println("Writing to gcode file: " + gcodeField.getText().trim()); saveGCode(); System.out.println("Exiting, all work done"); System.exit(0); } } public void addFrameTask(Runnable task) { if (display != null) display.addFrameTask(task); else task.run(); } private interface UndoTask { public void undo(); } private class UndoDelete implements UndoTask { private UndoDelete(Collection<Net> nets) { for (Net net : nets) { Set<Net> superNet = net.getSuperNet(); if (superNet == null) { this.nets.add(net); } else { this.nets.addAll(superNet); } } } public void undo() { // System.out.println("undeleting " + nets.size() + " nets"); for (Net net : nets) { model.undeleteNet(net); } } private Collection<Net> nets = new LinkedHashSet<Net>(); } private Simulator simulator = null; public Model model = null; private Display display = null; private Processor processor = null; private ToolpathsProcessor myToolpathsProcessor = null; private GCodeFileWriter gCodeWriter = null; private JButton loadButton; private JTextField loadField; private JButton browseButton; private File currentFile = null; private JButton mosaicButton; private JTextField mosaicField; private JButton mosaicBrowseButton; private JCheckBox mosaicTilesButton; private JProgressBar progressBar; private JButton stopButton; private JButton topologyButton; private JCheckBox manualTopology; private JButton toolpathsButton; private JRadioButton voronoiButton; private JRadioButton outlineButton; private JButton gcodeButton; private JTextField gcodeField; private JButton gcodeBrowseButton; /** * The ToolpathsProcessor generates the g-code that we write to a file. */ private Set<Net> selectedNets = new LinkedHashSet<Net>(); private Net selectedNet = null; private List<UndoTask> undoHistory = new LinkedList<UndoTask>(); private Box myProcessingBox; private Box myGCodeOptionsBox; private Box myMosaicBox; private Box myLoadFileBox; private Box myGcodeBox; private JRadioButton myRelativeCoordinatesButton; private JRadioButton myAbsoluteCoordinatesButton; private JRadioButton myImperialButton; private JRadioButton myMetricButton; private JPanel myZCuttingHeightPanel; private JPanel myZDownMovementPanel; private JPanel myMillingSpeedPanel; private JPanel myPlungeSpeedPanel; private JPanel myInitialXPanel; private JPanel myInitialYPanel; }