/**
* ORIPA - Origami Pattern Editor
* Copyright (C) 2005-2009 Jun Mitani http://mitani.cs.tsukuba.ac.jp/
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 oripa.view.model;
import java.awt.Color;
import java.awt.GraphicsConfiguration;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.ArrayList;
import java.util.List;
import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Bounds;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Geometry;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.LineArray;
import javax.media.j3d.LineAttributes;
import javax.media.j3d.Node;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.Shape3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TriangleArray;
import javax.vecmath.Color3f;
import javax.vecmath.Point2d;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;
import oripa.ORIPA;
import oripa.doc.Doc;
import oripa.fold.OriFace;
import oripa.fold.OriHalfedge;
import oripa.fold.OrigamiModel;
import com.sun.j3d.utils.behaviors.mouse.MouseRotate;
import com.sun.j3d.utils.behaviors.mouse.MouseTranslate;
import com.sun.j3d.utils.behaviors.mouse.MouseZoom;
import com.sun.j3d.utils.picking.PickCanvas;
import com.sun.j3d.utils.picking.PickResult;
import com.sun.j3d.utils.picking.behaviors.PickMouseBehavior;
import com.sun.j3d.utils.universe.SimpleUniverse;
class J3DFace {
OriFace oriFace;
Shape3D frontShape3D;
Shape3D backShape3D;
boolean bSelected;
J3DFace(OriFace f) {
oriFace = f;
bSelected = false;
}
}
public class ModelViewScreen3D extends Canvas3D implements MouseListener, MouseMotionListener, MouseWheelListener,
ComponentListener {
ArrayList<J3DFace> faces = new ArrayList<>();
TransformGroup objTrans = new TransformGroup();
Appearance mainAppearance = new Appearance();
SimpleUniverse universe;
BranchGroup objRoot = new BranchGroup();
BranchGroup scene;
Color3f faceColorFront = new Color3f(1.0f, 0.8f, 0.8f);
Color3f faceColorBack = new Color3f(0.8f, 0.8f, 1.0f);
Color3f faceColorSelected = new Color3f(Color.RED);
public ModelViewScreen3D(GraphicsConfiguration config) {
super(config);
scene = createSceneGraph();
PolygonAttributes pa = new PolygonAttributes(PolygonAttributes.POLYGON_FILL,
PolygonAttributes.CULL_BACK, // Types of culling
0.1f); // Polygon offset
mainAppearance.setCapability(Appearance.ALLOW_POLYGON_ATTRIBUTES_WRITE);
mainAppearance.setPolygonAttributes(pa);
universe = new SimpleUniverse(this);
universe.getViewingPlatform().setNominalViewingTransform();
}
public void setModel() {
faces.clear();
Doc document = ORIPA.doc;
OrigamiModel origamiModel = document.getOrigamiModel();
List<OriFace> sortedFaces = origamiModel.getSortedFaces();
BranchGroup faceBG = new BranchGroup();
BranchGroup lineBG = new BranchGroup();
objTrans.addChild(faceBG);
objTrans.addChild(lineBG);
int fCount = 0;
Point2d maxPoint = new Point2d(-Double.MAX_VALUE, -Double.MAX_VALUE);
Point2d minPoint = new Point2d(Double.MAX_VALUE, Double.MAX_VALUE);
for (OriFace face : sortedFaces) {
for (OriHalfedge he : face.halfedges) {
maxPoint.x = Math.max(maxPoint.x, he.positionForDisplay.x);
maxPoint.y = Math.max(maxPoint.y, he.positionForDisplay.y);
minPoint.x = Math.min(minPoint.x, he.positionForDisplay.x);
minPoint.y = Math.min(minPoint.y, he.positionForDisplay.y);
}
}
Point3d centerP = new Point3d();
centerP.set((maxPoint.x + minPoint.x) * 0.5, (maxPoint.y + minPoint.y) * 0.5, 0);
double size = maxPoint.distance(minPoint);
LineAttributes lineAttributes =
new LineAttributes(1.0f, // Line thickness
LineAttributes.PATTERN_SOLID, // Line type
false); // Whether to handle anti-aliasing
LineAttributes lineAttributesBold =
new LineAttributes(3.0f, // Line thickness
LineAttributes.PATTERN_SOLID, // Line type
false); // Whether to handle anti-aliasing
for (OriFace face : sortedFaces) {
J3DFace j3dFace = new J3DFace(face);
faces.add(j3dFace);
double z = (fCount - sortedFaces.size() * 0.5) * (-10);
OriHalfedge startHe = face.halfedges.get(0);
Point3d[] p = new Point3d[3];
p[0] = new Point3d(startHe.positionForDisplay.x, startHe.positionForDisplay.y, z);
p[0].sub(centerP);
p[0].scale(1.0 / size);
TriangleArray faceGeometryFront = new TriangleArray(3 * (face.halfedges.size() - 2),
GeometryArray.COORDINATES | GeometryArray.COLOR_3);
TriangleArray faceGeometryBack = new TriangleArray(3 * (face.halfedges.size() - 2),
GeometryArray.COORDINATES | GeometryArray.COLOR_3);
for (int i = 0; i < face.halfedges.size(); i++) {
OriHalfedge he = face.halfedges.get(i);
p[1] = new Point3d(he.positionForDisplay.x, he.positionForDisplay.y, z);
p[2] = new Point3d(he.next.positionForDisplay.x, he.next.positionForDisplay.y, z);
p[1].sub(centerP);
p[1].scale(1.0 / size);
p[2].sub(centerP);
p[2].scale(1.0 / size);
// Outline
LineArray lineGeometry = new LineArray(2, GeometryArray.COORDINATES |
GeometryArray.COLOR_3);
lineGeometry.setCoordinate(0, p[1]);
lineGeometry.setCoordinate(1, p[2]);
Appearance lineAppearance = new Appearance();
lineAppearance.setCapability(Appearance.ALLOW_LINE_ATTRIBUTES_WRITE);
if (he.pair == null) {
lineAppearance.setLineAttributes(lineAttributesBold);
lineGeometry.setColor(0, new Color3f(Color.RED));
lineGeometry.setColor(1, new Color3f(Color.RED));
} else {
lineAppearance.setLineAttributes(lineAttributes);
lineGeometry.setColor(0, new Color3f(Color.BLACK));
lineGeometry.setColor(1, new Color3f(Color.BLACK));
}
Shape3D lines = new Shape3D(lineGeometry, lineAppearance);
lines.setUserData("line" + fCount);
lineBG.addChild(lines);
if (i == 0 || i == face.halfedges.size() - 1) {
continue;
}
for (int j = 0; j < 3; j++) {
int k = (i - 1) * 3 + j;
faceGeometryFront.setCoordinate(k, p[j]);
faceGeometryBack.setCoordinate(k, p[2 - j]);
faceGeometryFront.setColor(k, faceColorFront);
faceGeometryBack.setColor(k, faceColorBack);
}
}
faceGeometryFront.setCapability(Geometry.ALLOW_INTERSECT);
faceGeometryBack.setCapability(Geometry.ALLOW_INTERSECT);
faceGeometryFront.setCapability(GeometryArray.ALLOW_COLOR_WRITE);
faceGeometryBack.setCapability(GeometryArray.ALLOW_COLOR_WRITE);
faceGeometryFront.setCapability(GeometryArray.ALLOW_COUNT_READ);
faceGeometryBack.setCapability(GeometryArray.ALLOW_COUNT_READ);
Shape3D shapeFront = new Shape3D(faceGeometryFront, mainAppearance);
Shape3D shapeBack = new Shape3D(faceGeometryBack, mainAppearance);
j3dFace.frontShape3D = shapeFront;
j3dFace.backShape3D = shapeBack;
shapeFront.setUserData(j3dFace);
shapeBack.setUserData(j3dFace);
shapeFront.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
shapeBack.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
faceBG.addChild(shapeFront);
faceBG.addChild(shapeBack);
fCount++;
}
BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
SimplePicking picker =
new SimplePicking(faceBG/*
* objRoot
*/, this, bounds);
picker.setupCallback(new SimplePickingCallback() {
@Override
public void picked(int type, Node node) {
if (node == null) {
return;
}
Shape3D sp = (Shape3D) node;
J3DFace face = (J3DFace) sp.getUserData();
setSelected(face);
}
});
objRoot.addChild(picker);
universe.addBranchGraph(scene);
}
private void setSelected(J3DFace face) {
for (J3DFace f : faces) {
setColor(f.frontShape3D, faceColorFront);
setColor(f.backShape3D, faceColorBack);
}
setColor(face.backShape3D, faceColorSelected);
setColor(face.frontShape3D, faceColorSelected);
for (OriHalfedge he : face.oriFace.halfedges) {
if (he.pair == null) {
continue;
}
for (J3DFace f : faces) {
if (f == face) {
continue;
}
if (f.oriFace == he.pair.face) {
setColor(f.backShape3D, new Color3f(0.0f, 0.5f, 0.0f));
setColor(f.frontShape3D, new Color3f(0.0f, 0.5f, 0.0f));
}
}
}
}
private void setColor(Shape3D sp, Color3f color) {
for (int i = 0; i < sp.numGeometries(); i++) {
TriangleArray ta = (TriangleArray) sp.getGeometry(i);
int vNum = ta.getVertexCount();
for (int j = 0; j < vNum; j++) {
ta.setColor(j, color);
}
}
}
private BranchGroup createSceneGraph() {
Background background = new Background();
background.setColor(new Color3f(1.0f, 1.0f, 1.0f));
background.setApplicationBounds(new BoundingSphere(new Point3d(), 10000.0));
objRoot.addChild(background);
{
BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
DirectionalLight dlight =
new DirectionalLight(new Color3f(1.0f, 0.0f, 0.0f),
new Vector3f(0.87f, 0.0f, -0.5f));
dlight.setInfluencingBounds(bounds);
objRoot.addChild(dlight);
AmbientLight alight = new AmbientLight();
alight.setInfluencingBounds(bounds);
objRoot.addChild(alight);
}
objRoot.addChild(objTrans);
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
MouseRotate rotat = new MouseRotate(objTrans);
MouseTranslate trans = new MouseTranslate(objTrans);
MouseZoom zoom = new MouseZoom(objTrans);
BoundingSphere bounds = new BoundingSphere();
bounds.setRadius(1.0);
rotat.setSchedulingBounds(bounds);
trans.setSchedulingBounds(bounds);
zoom.setSchedulingBounds(bounds);
objTrans.addChild(rotat);
objTrans.addChild(trans);
objTrans.addChild(zoom);
return objRoot;
}
@Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseDragged(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseMoved(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseWheelMoved(MouseWheelEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void componentResized(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void componentMoved(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void componentShown(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void componentHidden(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
}
interface SimplePickingCallback {
void picked(int nodeType, Node node);
}
class SimplePicking extends PickMouseBehavior {
protected SimplePickingCallback callback = null;
public SimplePicking(BranchGroup root, Canvas3D canvas, Bounds bounds) {
super(canvas, root, bounds);
this.setSchedulingBounds(bounds);
System.out.println("tolerance" + getTolerance());
this.setTolerance(0.0f);
setMode(PickCanvas.GEOMETRY);
}
public void setupCallback(SimplePickingCallback callback) {
this.callback = callback;
}
@Override
public void updateScene(int xpos, int ypos) {
pickCanvas.setShapeLocation(xpos, ypos);
PickResult res = pickCanvas.pickClosest();
if (res != null) {
callback.picked(PickResult.SHAPE3D, res.getNode(PickResult.SHAPE3D));
}
}
}