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 }