import com.sun.j3d.utils.picking.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.applet.JMainFrame;
import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;

import javax.swing.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.*;
import java.awt.event.*;


public class Test extends JApplet implements ActionListener {

        BranchGroup root = null;
        Shape3D sh = null;

        public Test() {
                makeMenu();
                makeJ3DScene();
        }

        public void actionPerformed(ActionEvent e) {
                if (e.getActionCommand()=="Pick!") {
                        doPicking();
                }
        }

        public static void main(String[] args) {
        JMainFrame app = new JMainFrame(new Test(), 800, 600);
        }

        private void makeMenu() {

                JPanel jp = new JPanel();
                JButton jb = new JButton();
                jb.setText("Pick!");
                jb.addActionListener(this);
                jp.add(jb);

                this.getContentPane().setLayout(new BorderLayout());
                this.getContentPane().add(jp,BorderLayout.SOUTH);
        }

        private void makeJ3DScene() {

                JPanel jpJ3D = new JPanel();
                jpJ3D.setLayout(new BorderLayout());

                GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
                Canvas3D canvas3d = new Canvas3D(config);
                jpJ3D.add(canvas3d,BorderLayout.CENTER);

                SimpleUniverse s = new SimpleUniverse(canvas3d);
                s.getViewingPlatform().setNominalViewingTransform();

                OrbitBehavior myOrbit = new OrbitBehavior(canvas3d,OrbitBehavior.REVERSE_ALL);
                BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 1.0);
                myOrbit.setSchedulingBounds(bounds);
                myOrbit.setRotationCenter(new Point3d(0.0,0.0,0.0));
                s.getViewingPlatform().setViewPlatformBehavior(myOrbit);

                root = makeBranch();
                root.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
                root.compile();
                s.addBranchGraph(root);

                this.getContentPane().add(jpJ3D,BorderLayout.CENTER);

        }

        private BranchGroup makeBranch() {

                BranchGroup BG = new BranchGroup();

                // Sphere1 -> target
                Sphere sp1 = new Sphere(0.5f,Sphere.GEOMETRY_NOT_SHARED,new Appearance());
                PickTool.setCapabilities(sp1.getShape(Sphere.BODY),PickTool.INTERSECT_FULL);
        sh = sp1.getShape();

                Appearance app = new Appearance();
                app.setColoringAttributes(new ColoringAttributes(new Color3f(Color.lightGray),ColoringAttributes.FASTEST));
                PolygonAttributes pa = new PolygonAttributes();
                pa.setPolygonMode(PolygonAttributes.POLYGON_LINE);
                app.setPolygonAttributes(pa);
                sp1.setAppearance(app);

                BG.addChild(sp1);

                // Sphere2 -> focal point
                Transform3D T3D = new Transform3D();
                T3D.setTranslation(new Vector3f(0.75f,0.0f,0.0f));
                TransformGroup TG = new TransformGroup(T3D);
                TG.setPickable(false);

                Sphere sp2 = new Sphere(0.025f);
                Appearance app2 = new Appearance();
                app2.setColoringAttributes(new ColoringAttributes(new Color3f(Color.green),ColoringAttributes.FASTEST));
                sp2.setAppearance(app2);

                TG.addChild(sp2);
                BG.addChild(TG);

                return BG;
        }

        private void doPicking() {

                // Picking

                GeometryArray ga = (GeometryArray) sh.getGeometry();

                PickTool picking = new PickTool(root);
                picking.setMode(PickTool.GEOMETRY_INTERSECT_INFO);

                Point3d point = new Point3d();
                Point3d point2 = new Point3d();
                Point3d focus = new Point3d(0.75,0.0,0.0);

                for (int i = 0; i<ga.getVertexCount();i++) {

                        ga.getCoordinate(i,point);      // vertex
                        Vector3d v = new Vector3d(point);    // vector
                        v.sub(focus);

                        picking.setShapeRay(focus,v);
                        PickResult pick = picking.pickClosest();

                        System.out.println("\nfocal point: "+focus+"  -   aiming point: "+point+"  -  vector: "+v);

                        // 3 cases
                        if (pick!=null) {
                                PickIntersection pi = pick.getClosestIntersection(focus);
                                point2 = pi.getClosestVertexCoordinatesVW();

                                // case 1: correct hit -> red
                                if (point2.equals(point)) {

                                        System.out.println("Hit: vertex "+i+"  -   coordinate: "+point);

                                        Transform3D T3D = new Transform3D();
                                        T3D.setTranslation(new Vector3d(point));
                                        BranchGroup BG = new BranchGroup();
                                        TransformGroup TG = new TransformGroup(T3D);

                                        Sphere sp3 = new Sphere(0.015f);
                                        Appearance app = new Appearance();
                                        ColoringAttributes ca = new ColoringAttributes();
                                        ca.setColor(new Color3f(Color.red));
                                        app.setColoringAttributes(ca);
                                        sp3.setAppearance(app);

                                        BG.setPickable(false);
                                        TG.addChild(sp3);
                                        BG.addChild(TG);
                                        root.addChild(BG);
                                } else {     // case 2: hit, but not closest point (back face) -> blue
                                        System.out.println("shadowing effect: vertex "+i+"  -   coordinate: "+point2);

                                        Transform3D T3D = new Transform3D();
                                        T3D.setTranslation(new Vector3d(point));
                                        BranchGroup BG = new BranchGroup();
                                        TransformGroup TG = new TransformGroup(T3D);

                                        Sphere sp2 = new Sphere(0.015f);
                                        Appearance app = new Appearance();
                                        ColoringAttributes ca = new ColoringAttributes();
                                        ca.setColor(new Color3f(Color.blue));
                                        app.setColoringAttributes(ca);
                                        sp2.setAppearance(app);

                                        BG.setPickable(false);
                                        TG.addChild(sp2);
                                        BG.addChild(TG);
                                        root.addChild(BG);
                                }
                        }  else {  // case 3: no hit -> yellow
                                System.out.println("No Hit: vertex "+i+"  -   coordinate: "+point);

                                Transform3D T3D = new Transform3D();
                                T3D.setTranslation(new Vector3d(point));
                                BranchGroup BG = new BranchGroup();
                                TransformGroup TG = new TransformGroup(T3D);

                                Sphere sp2 = new Sphere(0.015f);
                                Appearance app = new Appearance();
                                ColoringAttributes ca = new ColoringAttributes();
                                ca.setColor(new Color3f(Color.yellow));
                                app.setColoringAttributes(ca);
                                sp2.setAppearance(app);

                                BG.setPickable(false);
                                TG.addChild(sp2);
                                BG.addChild(TG);
                                root.addChild(BG);
                        }
                }
        }
}
