001    /*
002     * File $Workfile: PinView.java $
003     * $Archive: /Tower Of Hanoi/toh/src/net/pacbell/jfai/toh/ui/PinView.java $
004     * Last changed by $Author: Jürgen Failenschmid $
005     * Last modified   $Modtime: 12/08/05 6:42p $
006     * Last checked in $Date: 12/09/05 5:10p $
007     * $Revision: 5 $
008     *
009     *
010     * Copyright (C) 2001-2005 Jürgen Failenschmid. All rights reserved.
011     */
012    
013    package net.pacbell.jfai.toh.ui;
014    
015    import javax.media.j3d.Appearance;
016    import javax.media.j3d.BranchGroup;
017    import javax.media.j3d.Node;
018    import javax.media.j3d.Transform3D;
019    import javax.media.j3d.TransformGroup;
020    import javax.vecmath.Point3f;
021    import javax.vecmath.Vector3f;
022    
023    import com.sun.j3d.utils.geometry.Cylinder;
024    import com.sun.j3d.utils.geometry.Primitive;
025    
026    import net.pacbell.jfai.toh.domain.Disk;
027    import net.pacbell.jfai.toh.domain.Pin;
028    
029    /** Visualizes a pin of the Tower Of Hanoi puzzle. */
030    public class PinView {
031        private static final float EXTRA_HEIGHT = 0.05f;
032        private static final float HALF = 0.5f;
033        private static final float MAX_HEIGHT = 0.6f;
034        private static final float RADIUS = 0.01f;
035        private Appearance appearance;
036        private Point3f baseLocation;
037        private BranchGroup group;
038        private Pin model;
039    
040        /**
041         * Create a new instance given a Pin as the model and the location of the
042         * base cap of the pin.
043         * 
044         * @param aModel a Pin
045         * @param baseLocation the absolute position where the pin meets the base
046         * @param anAppearance the Appearance
047         */
048        public PinView(Pin aModel, Point3f baseLocation, Appearance anAppearance) {
049            setModel(aModel);
050            setGroup(new BranchGroup());
051            setAppearance(anAppearance);
052            setBaseLocation(baseLocation);
053            final Cylinder pin = new Cylinder(RADIUS, height(),
054                Primitive.GENERATE_NORMALS | Primitive.ENABLE_GEOMETRY_PICKING,
055                anAppearance);
056            final Vector3f centerLocation = new Vector3f(getBaseLocation());
057            centerLocation.y += height() / 2;
058            final Transform3D translate = new Transform3D();
059            translate.set(centerLocation);
060            final TransformGroup transform = new TransformGroup(translate);
061            transform.addChild(pin);
062            transform.setCapability(Node.ENABLE_PICK_REPORTING);
063            transform.setUserData(aModel); // Used for picking
064            getGroup().addChild(transform);
065            getGroup().compile();
066        }
067    
068        /**
069         * Answers the pin height.
070         * 
071         * @return the total height of the pin
072         */
073        public static final float height() {
074            return MAX_HEIGHT + EXTRA_HEIGHT;
075        }
076    
077        /**
078         * Answers the maximum height of a stack of disks.
079         * 
080         * @return the maximum height
081         */
082        public static final float stackHeight() {
083            return MAX_HEIGHT;
084        }
085    
086        /**
087         * Answer a point for the center of the disk view of the given disk. If the
088         * disk is not in the receiver's tower, the location above the top of the
089         * tower is returned.
090         * 
091         * @param disk a Disk
092         * @return The absolute coordinates of the disk's center point
093         *         corresponding to the given position.
094         */
095        public Point3f diskLocation(Disk disk) {
096            return diskLocation(getModel().diskLocation(disk));
097        }
098    
099        /**
100         * Answer a point for the center of the disk view that represents the disk
101         * in the tower with the given 1-based position. The position is relative
102         * to the top of the tower.
103         * 
104         * @param topPosition This is 1 for the disk at the top. It increases by 1
105         *            for each disk in the stack (if any).
106         * @return The absolute coordinates of the disk's center point
107         *         corresponding to the given position.
108         */
109        public Point3f diskLocation(int topPosition) {
110            /*
111             * If topPosition is less than 0, use 0. If steps is less than 0, then
112             * either there are no disks or the specified distance exceeds the
113             * number of disks in the tower. In that case, answer the location at
114             * the bottom of the pin.
115             */
116            final int steps = Math.max(0, getModel().getNumberOfDisks()
117                - Math.max(0, topPosition));
118            final Point3f center = getBaseLocation();
119            center.y += (steps + HALF) * DiskView.getHeight();
120            return center;
121        }
122    
123        /**
124         * Gets the appearance.
125         * 
126         * @return the Appearance
127         */
128        public Appearance getAppearance() {
129            return appearance;
130        }
131    
132        /**
133         * Gets the point where the pin and the base meet.
134         * 
135         * @return the absolute position of the pin's bottom
136         */
137        public Point3f getBaseLocation() {
138            return new Point3f(baseLocation);
139        }
140    
141        /**
142         * Gets the branch group.
143         * 
144         * @return the BranchGroup
145         */
146        public BranchGroup getGroup() {
147            return group;
148        }
149    
150        /**
151         * Gets the model.
152         * 
153         * @return the Pin
154         */
155        public Pin getModel() {
156            return model;
157        }
158    
159        /**
160         * Gets the position of the pin's top.
161         * 
162         * @return the absolute position of the top
163         */
164        public Point3f getTopLocation() {
165            final Point3f topLocation = getBaseLocation();
166            topLocation.y += height();
167            return topLocation;
168        }
169    
170        private void setAppearance(Appearance appearance) {
171            this.appearance = appearance;
172        }
173    
174        private void setBaseLocation(Point3f baseLocation) {
175            this.baseLocation = baseLocation;
176        }
177    
178        private void setGroup(BranchGroup group) {
179            this.group = group;
180        }
181    
182        private void setModel(Pin model) {
183            this.model = model;
184        }
185    }