package vgp.tutor.linear; import java.awt.Color; import jv.object.PsDebug; import jv.geom.PgPolygon; import jv.geom.PgPolygonSet; import jv.project.PvCameraIf; import jv.project.PvDisplayIf; import jv.project.PjProject; import jv.vecmath.PdVector; import jv.vecmath.PiVector; /** * Simple interactive linear algebra applet performs vector calculations * on two vectors and shows the result vector. *
* The result vector has 3D coordinates because of the possible cross product mode.
* Nevertheless, the dimension of the two argument vectors has been chosen 2D
* since then the vectors always lie in the xy-plane in the display, even
* if the camera is in perspective mode. This makes the dragging of the argument
* vectors in cross product mode more intuitive. Note, the method computeResult()
* may be simplified a little if one chooses 3D coordinates for all vectors.
*
* @author Konrad Polthier
* @version 05.11.02, 1.30 revised (ep) Use perspective camera for cross product.
* 04.03.01, 1.20 revised (kp) Converted into a tutorial, and moved to vgp.tutor.
* 26.11.00, 1.10 revised (kp) Use global instead of local polygon colors.
* 22.10.00, 1.00 created (kp)
*/
public class PjLinear extends PjProject {
/** Summation mode, result is sum of two argument vectors. */
public static final int MODE_ADD = 0;
/** Difference mode, result is difference of two argument vectors. */
public static final int MODE_SUB = 1;
/** Cross product mode, result is cross product of two argument vectors. */
public static final int MODE_CROSS = 2;
/**
* Two argument vectors are represented as two lines of a polygon set
* determined by their endpoints. The base point of both vectors is
* identical, namely, the first vertex of the polygon set.
* The second and third vertex of the polygon set are the endpoints
* of both vertices.
*/
protected PgPolygonSet m_vectors;
/**
* The result vector is a single polygon. The base point of this vector
* is kept equal to the base point of the two argument vectors, and
* its endpoint is computed from the two argument vectors depending
* on the current mode, i.e. either the sum, difference or cross product.
*/
protected PgPolygon m_result;
/** Computation mode, possibly values are MODE_... constants. */
protected int m_mode;
/**
* Constructors creates two argument vectors and a result vector. Initializations
* of instance variables should be performed within the init() method
* to allow resetting the project later by simply calling the init() method.
*/
public PjLinear() {
super("Interactive Linear Algebra"); // Super class constructors assigns name of this project.
// Create a new polygon set in 2D space to store the two argument vectors.
m_vectors = new PgPolygonSet(2);
m_vectors.setName("2 Vectors"); // Set unique name of polygon set.
// Create a new polygon in 3D space to store the result vector.
m_result = new PgPolygon(3);
m_result.setName("Result"); // Set unique name of polygon
if (getClass() == PjLinear.class) // Invoke the init() method. This construct refuses to call
init(); // the init method if this constructor is invoked by a subclass.
}
/**
* Initialize all instance variables of this project. This method
* is invoked during the construction of this project and whenever
* this project is resetted. Therefore, allocations of instance variables
* should be performed in the constructor of the project.
*/
public void init() {
super.init();
m_vectors.init(); // Initialize the argument vectors.
m_vectors.setNumVertices(3); // Allocate three vertices for basepoint and two endpoints
m_vectors.setVertex(0, 0., 0.); // Base point (index = 0) of both vectors
m_vectors.setVertex(1, .5, 1.); // Endpoint (index = 1) of first vector
m_vectors.setVertex(2, -2., 2.); // Endpoint (index = 2) of second vector
m_vectors.setNumPolygons(2); // Allocate two lines being the two argument vectors
// Determine the index of the basepoint (0) and endpoint (1) of the first vector
m_vectors.setPolygon(0, new PiVector(0, 1));
// Determine the index of the basepoint (0) and endpoint (2) of the second vector
m_vectors.setPolygon(1, new PiVector(0, 2));
m_vectors.getPolygon(0).setName("v"); // Set name of first argument vector
m_vectors.getPolygon(1).setName("w"); // Set name of second argument vector
m_vectors.showPolygonLabels(true); // Show name of argument vectors at tip
m_vectors.setGlobalPolygonColor(Color.blue); // Set color of argument vectors
m_vectors.setGlobalPolygonSize(2.); // Set thickness of argument vectors
m_vectors.showPolygonEndArrow(true);
m_vectors.setParent(this); // Register the argument vectors as children
// allows to catch their update events in update(Object)
// and initiate recomputation of the result vector.
m_result.setNumVertices(2); // Allocate place for both endpoints of result vector.
m_result.setGlobalEdgeColor(Color.red); // Set color of result vector
m_result.setGlobalEdgeSize(2.); // Set thickness of result vector
m_result.showPolygonEndArrow(true); // Enable drawing of arrow of result vector
setMode(MODE_ADD); // Set initial computation mode.
}
public void start() {
computeResult(m_mode); // Compute result vector
addGeometry(m_result); // Register result vector in display
addGeometry(m_vectors); // Register two argument vectors in display
selectGeometry(m_vectors); // Set two vectors as the active geometry in display
// to be able to pick their vertices.
PvDisplayIf disp = getDisplay(); // Get the display from this project
if (disp != null) { // and set some display options.
disp.showGrid(true); // Show grid in the background
disp.selectCamera(PvCameraIf.CAMERA_ORTHO_XY); // Initially, project onto xy-plane
disp.setMajorMode(PvDisplayIf.MODE_PICK); // Force picking of polygon vertices
}
super.start();
}
/** Get the current computation mode, possibly values are MODE_... constants. */
public int getMode() { return m_mode; }
/**
* Set the current computation mode, possibly values are MODE_... constants.
* Method also recomputes the result vector and initiates a redraw.
*/
public int setMode(int mode) {
m_mode = mode;
PvDisplayIf disp = getDisplay(); // Get the display from this project
if (m_mode == MODE_CROSS && disp != null) // and if we are showing cross product
disp.selectCamera(PvCameraIf.CAMERA_PERSPECTIVE);// then set camera to perspective mode
else if (disp != null) // otherwise
disp.selectCamera(PvCameraIf.CAMERA_ORTHO_XY); // we want xy-projection mode
computeResult(m_mode);
m_result.update(m_result);
return m_mode;
}
/**
* Perform vector arithmetic on two argument vectors to compute the result vector.
* This method could be made simpler if one uses 3D coordinates for
* argument vectors too.
*/
public void computeResult(int mode) {
// Get base point of all vectors (creating a copy allows to resize base below)
PdVector base = PdVector.copyNew(m_vectors.getVertex(0));
// Extract first argument vector from vertices of polygon set
PdVector v = PdVector.subNew(m_vectors.getVertex(1), base);
// Extract second argument vector from vertices of polygon set
PdVector w = PdVector.subNew(m_vectors.getVertex(2), base);
// Resize base and argument vectors to have same dimension 3 as result vector
// since vector arithmetic methods often require same length of arguments.
base.setSize(3);
v.setSize(3);
w.setSize(3);
PdVector vec = new PdVector(3);
switch (m_mode) {
case MODE_ADD:
vec.add(v, w);
break;
case MODE_SUB:
vec.sub(v, w);
break;
case MODE_CROSS:
vec.cross(v, w);
break;
default:
if (PsDebug.WARNING) PsDebug.warning("unknown mode = "+m_mode);
return;
}
// Set base point of result vector
m_result.setVertex(0, base);
// Set endpoint of result vector as sum of base and computed vector
vec.add(base);
m_result.setVertex(1, vec);
}
/**
* Perform recomputations whenever a child has changed. Here the two argument
* vectors have registered this class as parent, therefore, we are able to catch
* their update events and recompute the result vectors if they have changed.
* Method is usually invoked from the children.
*/
public boolean update(Object event) {
if (event == m_vectors) { // If the two argument vector have changed...
computeResult(m_mode); // Recompute the result vector based on the current mode.
m_result.update(m_result); // Redraw result vector in display.
return true; // Event successfully handled, just return.
}
// If we do not know about the event then just forward it to the superclass.
return super.update(event);
}
}