/*
RepRap
------
The Replicating Rapid Prototyper Project
Copyright (C) 2006
Adrian Bowyer & The University of Bath
http://reprap.org
Principal author:
Adrian Bowyer
Department of Mechanical Engineering
Faculty of Engineering and Design
University of Bath
Bath BA2 7AY
U.K.
e-mail: A.Bowyer@bath.ac.uk
RepRap is free; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
Licence as published by the Free Software Foundation; either
version 2 of the Licence, or (at your option) any later version.
RepRap 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
Library General Public Licence for more details.
For this purpose the words "software" and "library" in the GNU Library
General Public Licence are taken to mean any and all computer programs
computer files data results documents and other copyright information
available from the RepRap project.
You should have received a copy of the GNU Library General Public
Licence along with RepRap; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA,
or see
http://www.gnu.org/
=====================================================================
This program loads STL files of objects, orients them, and builds them
in the RepRap machine.
It is based on one of the open-source examples in Daniel Selman's excellent
Java3D book, and his notice is immediately below.
First version 2 April 2006
This version: 16 April 2006
*/
/*******************************************************************************
* VrmlPickingTest.java Copyright (C) 2001 Daniel Selman
*
* First distributed with the book "Java 3D Programming" by Daniel Selman and
* published by Manning Publications. http://manning.com/selman
*
* 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, version 2.
*
* 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.
*
* The license can be found on the WWW at: http://www.fsf.org/copyleft/gpl.html
*
* Or by writing to: Free Software Foundation, Inc., 59 Temple Place - Suite
* 330, Boston, MA 02111-1307, USA.
*
* Authors can be contacted at: Daniel Selman: daniel@selman.org
*
* If you make changes you think others would like, please contact one of the
* authors or someone at the www.j3d.org web site.
******************************************************************************/
package org.reprap.gui;
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.media.j3d.AmbientLight;
import javax.media.j3d.Background;
import javax.media.j3d.Bounds;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Group;
import javax.media.j3d.Node;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.ViewPlatform;
import javax.media.j3d.Transform3D;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingConstants;
import javax.vecmath.Color3f;
import javax.vecmath.Vector3d;
import com.sun.j3d.utils.picking.PickCanvas;
import com.sun.j3d.utils.picking.PickResult;
import com.sun.j3d.utils.picking.PickTool;
import org.reprap.Attributes;
import org.reprap.RFO;
import org.reprap.Preferences;
import org.reprap.geometry.polyhedra.AllSTLsToBuild;
import org.reprap.geometry.polyhedra.STLObject;
import org.reprap.utilities.Debug;
/**
* Little class to put up a radiobutton menu so you can set
* what material something is to be made from.
*
* @author adrian
*
*/
class MaterialRadioButtons extends JPanel {
private static final long serialVersionUID = 1L;
private static Attributes att;
private static JDialog dialog;
private static JTextField copies;
private static RepRapBuild rrb;
private static int stlIndex;
private MaterialRadioButtons(double volume)
{
super(new BorderLayout());
JPanel radioPanel;
ButtonGroup bGroup = new ButtonGroup();
String[] names;
radioPanel = new JPanel(new GridLayout(0, 1));
radioPanel.setSize(300,200);
JLabel jLabel0 = new JLabel();
radioPanel.add(jLabel0);
jLabel0.setText("Volume of object: " + Math.round(volume) + " mm^3");
jLabel0.setHorizontalAlignment(SwingConstants.CENTER);
JLabel jLabel2 = new JLabel();
radioPanel.add(jLabel2);
jLabel2.setText(" Number of copies of the object just loaded to print: ");
jLabel2.setHorizontalAlignment(SwingConstants.CENTER);
copies = new JTextField("1");
copies.setSize(20, 10);
copies.setHorizontalAlignment(SwingConstants.CENTER);
radioPanel.add(copies);
JLabel jLabel1 = new JLabel();
radioPanel.add(jLabel1);
jLabel1.setText(" Select the material for the object(s): ");
jLabel1.setHorizontalAlignment(SwingConstants.CENTER);
try
{
names = Preferences.allMaterials();
String matname = att.getMaterial();
if(matname == null)
matname = "";
int matnumber = -1;
for(int i = 0; i < names.length; i++)
{
if(matname.contentEquals(names[i]))
matnumber = i;
JRadioButton b = new JRadioButton(names[i]);
b.setActionCommand(names[i]);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
att.setMaterial(e.getActionCommand());
}});
if(i == matnumber)
b.setSelected(true);
bGroup.add(b);
radioPanel.add(b);
}
if(matnumber < 0)
{
att.setMaterial(names[0]);
JRadioButton b = (JRadioButton)bGroup.getElements().nextElement();
b.setSelected(true);
} else
copies.setEnabled(false); // If it's already loaded, don't make multiple copies (FUTURE: why not...?)
JButton okButton = new JButton();
radioPanel.add(okButton);
okButton.setText("OK");
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
OKHandler();
}
});
add(radioPanel, BorderLayout.LINE_START);
setBorder(BorderFactory.createEmptyBorder(20,20,20,20));
} catch (Exception ex)
{
Debug.e(ex.toString());
ex.printStackTrace();
}
}
public static void OKHandler()
{
//System.out.println("Copies: " + copies.getText());
int number = Integer.parseInt(copies.getText().trim()) - 1;
STLObject stl = rrb.getSTLs().get(stlIndex);
rrb.moreCopies(stl, att, number);
dialog.dispose();
}
public static void createAndShowGUI(Attributes a, RepRapBuild r, int index, double volume)
{
att = a;
rrb = r;
stlIndex = index;
//Create and set up the window.
JFrame f = new JFrame();
dialog = new JDialog(f, "Material selector");
dialog.setLocation(500, 400);
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
//Create and set up the content pane.
JComponent newContentPane = new MaterialRadioButtons(volume);
newContentPane.setOpaque(true); //content panes must be opaque
dialog.setContentPane(newContentPane);
//Display the window.
dialog.pack();
dialog.setModalityType(JDialog.DEFAULT_MODALITY_TYPE);
dialog.setVisible(true);
}
public static void createAndShowGUI(Attributes a, RepRapBuild r, STLObject lastPicked)
{
if(lastPicked == null)
return;
int index = -1;
for(int i = 0; i < r.getSTLs().size(); i++)
{
if(r.getSTLs().get(i) == lastPicked)
{
index = i;
break;
}
}
if (index >= 0)
createAndShowGUI(a, r, index, r.getSTLs().get(index).volume());
}
}
//************************************************************************
/**
* This is the main public class that creates a virtual world of the RepRap
* working volume, allows you to put STL-file objects in it, move them about
* to arrange them, and build them in the machine.
*/
public class RepRapBuild extends Panel3D implements MouseListener {
private static final long serialVersionUID = 1L;
private MouseObject mouse = null;
private PickCanvas pickCanvas = null; // The thing picked by a mouse click
private STLObject lastPicked = null; // The last thing picked
//private java.util.List<STLObject> stls = new ArrayList<STLObject>(); // All the STLObjects to be built
private AllSTLsToBuild stls;
//private int objectIndex = 0; // Counter for STLs as they are loaded
private boolean reordering;
// Constructors
public RepRapBuild() throws Exception {
initialise();
stls = new AllSTLsToBuild();
reordering = false;
}
public AllSTLsToBuild getSTLs()
{
return stls;
}
/**
* Set the material to make an STL object from.
* @param stl
*/
// private void getMaterialName(STLObject stl)
// {
// try {
// MaterialRadioButtons.createAndShowGUI(stl);
// }
// catch (Exception ex) {
// JOptionPane.showMessageDialog(null, "RepRapBuild material select exception: " + ex);
// ex.printStackTrace();
// }
// }
// Set bg light grey
protected Background createBackground() {
Background back = new Background(bgColour);
back.setApplicationBounds(createApplicationBounds());
return back;
}
protected BranchGroup createViewBranchGroup(TransformGroup[] tgArray,
ViewPlatform vp) {
BranchGroup vpBranchGroup = new BranchGroup();
if (tgArray != null && tgArray.length > 0) {
Group parentGroup = vpBranchGroup;
TransformGroup curTg = null;
for (int n = 0; n < tgArray.length; n++) {
curTg = tgArray[n];
parentGroup.addChild(curTg);
parentGroup = curTg;
}
tgArray[tgArray.length - 1].addChild(vp);
} else
vpBranchGroup.addChild(vp);
return vpBranchGroup;
}
// Set up the RepRap working volume
protected BranchGroup createSceneBranchGroup() throws Exception {
sceneBranchGroup = new BranchGroup();
BranchGroup objRoot = sceneBranchGroup;
Bounds lightBounds = getApplicationBounds();
AmbientLight ambLight = new AmbientLight(true, new Color3f(1.0f, 1.0f,
1.0f));
ambLight.setInfluencingBounds(lightBounds);
objRoot.addChild(ambLight);
DirectionalLight headLight = new DirectionalLight();
headLight.setInfluencingBounds(lightBounds);
objRoot.addChild(headLight);
mouse = new MouseObject(getApplicationBounds(), mouse_tf, mouse_zf);
wv_and_stls.setCapability(Group.ALLOW_CHILDREN_EXTEND);
wv_and_stls.setCapability(Group.ALLOW_CHILDREN_WRITE);
wv_and_stls.setCapability(Group.ALLOW_CHILDREN_READ);
// Load the STL file for the working volume
world = new STLObject(wv_and_stls, worldName);
String stlFile = getStlBackground();
workingVolume = new STLObject();
workingVolume.addSTL(stlFile, wv_offset, wv_app, null);
wv_and_stls.addChild(workingVolume.top());
// Set the mouse to move everything
mouse.move(world, false);
objRoot.addChild(world.top());
return objRoot;
}
// Action on mouse click
public void mouseClicked(MouseEvent e) {
pickCanvas.setShapeLocation(e);
PickResult pickResult = pickCanvas.pickClosest();
STLObject picked = null;
if (pickResult != null) // Got anything?
{
Node actualNode = pickResult.getObject();
Attributes att = (Attributes)actualNode.getUserData();
picked = att.getParent();
if (picked != null) // Really got something?
{
if (picked != workingVolume) // STL object picked?
{
//picked = findSTL(name);
if (picked != null) {
picked.setAppearance(picked_app); // Highlight it
if (lastPicked != null && !reordering)
lastPicked.restoreAppearance(); // lowlight
// the last
// one
if(!reordering)
mouse.move(picked, true); // Set the mouse to move it
lastPicked = picked; // Remember it
reorder();
}
} else { // Picked the working volume - deselect all and...
if(!reordering)
mouseToWorld();
}
}
}
}
public void mouseToWorld()
{
if (lastPicked != null)
lastPicked.restoreAppearance();
mouse.move(world, false); // ...switch the mouse to moving the world
lastPicked = null;
}
// Find the stl object in the scene with the given name
// protected STLObject findSTL(String name) {
// STLObject stl;
// for (int i = 0; i < stls.size(); i++) {
// stl = stls.get(i);
// if (stl.name == name)
// return stl;
// }
// return null;
// }
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
// Callback for when the user selects an STL file to load
public void anotherSTLFile(String s)
{
if (s == null)
return;
//objectIndex++;
STLObject stl = new STLObject();
Attributes att = stl.addSTL(s, null, Preferences.unselectedApp(), lastPicked);
if(att != null)
{
// New separate object, or just appended to lastPicked?
if(stl.numChildren() > 0)
{
wv_and_stls.addChild(stl.top());
stls.add(stl);
}
MaterialRadioButtons.createAndShowGUI(att, this, stls.size() - 1, stl.volume());
}
}
public void changeMaterial()
{
if(lastPicked == null)
return;
MaterialRadioButtons.createAndShowGUI(lastPicked.attributes(0), this, lastPicked);
}
// Callback for when the user selects an RFO file to load
public void addRFOFile(String s)
{
if (s == null)
return;
//deleteAllSTLs();
AllSTLsToBuild newStls = RFO.load(s);
for(int i = 0; i < newStls.size(); i++)
wv_and_stls.addChild(newStls.get(i).top());
stls.add(newStls);
}
public void saveRFOFile(String s)
{
RFO.save(s, stls);
}
public void saveSCADFile(String s)
{
stls.saveSCAD(s);
}
public void moreCopies(STLObject original, Attributes originalAttributes, int number)
{
if (number <= 0)
return;
String fileName = original.fileAndDirectioryItCameFrom(0);
Vector3d offset = new Vector3d();
offset.y = 0;
offset.z = 0;
double increment = original.extent().x + 5;
offset.x = increment;
for(int i = 0; i < number; i++)
{
STLObject stl = new STLObject();
Attributes newAtt = stl.addSTL(fileName, null, original.getAppearance(), null);
newAtt.setMaterial(originalAttributes.getMaterial());
if(newAtt != null)
{
Transform3D t3d1 = original.getTransform();
Transform3D t3d2 = new Transform3D();
t3d2.set(new Vector3d(offset));
t3d1.mul(t3d2);
stl.setTransform(t3d1);
// New separate object, or just appended to lastPicked?
if(stl.numChildren() > 0)
{
wv_and_stls.addChild(stl.top());
stls.add(stl);
}
}
offset.x += increment;
}
}
public void start() throws Exception {
if (pickCanvas == null)
initialise();
}
protected void addCanvas3D(Canvas3D c3d) {
setLayout(new BorderLayout());
add(c3d, BorderLayout.CENTER);
doLayout();
if (sceneBranchGroup != null) {
c3d.addMouseListener(this);
pickCanvas = new PickCanvas(c3d, sceneBranchGroup);
pickCanvas.setMode(PickTool.GEOMETRY_INTERSECT_INFO);
pickCanvas.setTolerance(4.0f);
}
c3d.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
// Callbacks for when the user rotates the selected object
public void xRotate() {
if (lastPicked != null)
lastPicked.xClick();
}
public void yRotate() {
if (lastPicked != null)
lastPicked.yClick();
}
public void zRotate(double angle) {
if (lastPicked != null)
lastPicked.zClick(angle);
}
// Callback for a request to convert units
public void inToMM() {
if (lastPicked != null)
lastPicked.inToMM();
}
public void doReorder()
{
if (lastPicked != null)
{
lastPicked.restoreAppearance();
mouseToWorld();
lastPicked = null;
}
reordering = true;
}
/**
* User is reordering the list
*/
private void reorder()
{
if(!reordering)
return;
if(stls.reorderAdd(lastPicked))
return;
for(int i = 0; i < stls.size(); i++)
stls.get(i).restoreAppearance();
//mouseToWorld();
lastPicked = null;
reordering = false;
}
// Move to the next one in the list
public void nextPicked()
{
if (lastPicked == null)
lastPicked = stls.get(0);
else
{
lastPicked.restoreAppearance();
lastPicked = stls.getNextOne(lastPicked);
}
lastPicked.setAppearance(picked_app);
mouse.move(lastPicked, true);
}
// public void materialSTL()
// {
// if (lastPicked == null)
// return;
// getMaterialName(lastPicked);
// mouseToWorld();
// }
// Callback to delete one of the loaded objects
public void deleteSTL()
{
if (lastPicked == null)
return;
int index = -1;
for(int i = 0; i < stls.size(); i++)
{
if(stls.get(i) == lastPicked)
{
index = i;
break;
}
}
if (index >= 0)
{
stls.remove(index);
index = wv_and_stls.indexOfChild(lastPicked.top());
mouseToWorld();
wv_and_stls.removeChild(index);
lastPicked = null;
}
}
public void deleteAllSTLs()
{
for(int i = 0; i < stls.size(); i++)
{
STLObject s = stls.get(i);
stls.remove(i);
int index = wv_and_stls.indexOfChild(s.top());
wv_and_stls.removeChild(index);
}
mouseToWorld();
lastPicked = null;
}
}