home *** CD-ROM | disk | FTP | other *** search
Java Source | 1997-01-29 | 23.6 KB | 759 lines |
- /*
- * Copyright (c) 1997 ORC Incorporated. All Rights Reserved.
- *
- * Permission to use, copy, modify, and distribute this software
- * and its documentation for NON-COMMERCIAL purposes and without
- * fee is hereby granted provided that this copyright notice
- * appears in all copies. Please contact <info@ocnus.com> for
- * further information regarding copyright and licensing.
- * This code is provided without warranty, either expressed or implied.
- */
- /*
- * Portions of this code appear in the book
- * "Late Night VRML 2.0 with Java", Ziff-Davis Press, 1997
- *
- * Nurbs.java
- *
- * author Timothy F. Rohaly
- * version 1.0, 01/15/97
- */
-
- package nurbs;
-
-
- /**
- * NURBS curve and surface
- * @author Timothy F. Rohaly
- * @version 1.0, 1/15/97
- */
- public class Nurbs implements Cloneable {
-
- protected Knot u;
- protected Knot v;
- protected ControlNet controlNet;
-
-
- /**
- * NURBS Constructor.
- */
- Nurbs() {
- // Use of this form not recommended since it
- // bypasses consistency checks.
- // It is left here solely as an ease to development.
- }
-
-
- /**
- * NURBS Constructor.
- * @param u the U knot sequence
- * @param v the V knot sequence
- * @param controlNet the array of Point4 control points
- * @exception java.lang.IllegalArgumentException when the
- */
- public Nurbs(Knot u, Knot v, ControlNet controlNet) {
- //
- // Check for consistency
- //
- String errorString = getClass().getName() + ": " +
- "Number of knots in array must equal " +
- "order + number of control points";
- if (u.numControlPoints != controlNet.numUControlPoints ||
- v.numControlPoints != controlNet.numVControlPoints )
- throw new IllegalArgumentException(errorString);
-
- this.u = u;
- this.v = v;
- this.controlNet = controlNet;
- }
-
-
- /**
- * Performs a deep copy of this NURBS.
- * @return a new instance of Nurbs
- */
- public Object clone() {
- try {
- Nurbs n = (Nurbs) super.clone();
- n.u = (Knot) u.clone();
- n.v = (Knot) v.clone();
- n.controlNet = (ControlNet) controlNet.clone();
- return n;
- }
- catch (CloneNotSupportedException e) {
- String errorString = getClass().getName() + ": " +
- "CloneNotSupportedException " +
- "for a Cloneable class";
- throw new InternalError(errorString); // this should never happen
- }
- }
-
-
- /**
- * Access the number of U knots.
- * @return the number of U knots
- */
- public int getNumUKnots() {
- return u.numKnots;
- }
-
-
- /**
- * Access the number of V knots.
- * @return the number of V knots
- */
- public int getNumVKnots() {
- return v.numKnots;
- }
-
-
- /**
- * Access the NURB order in U.
- * @return the order in U of the NURB
- */
- public int getUOrder() {
- return u.order;
- }
-
-
- /**
- * Access the NURB order in V.
- * @return the order in V of the NURB
- */
- public int getVOrder() {
- return v.order;
- }
-
-
- /**
- * Access the number of U control points.
- * @return the number of U control points
- */
- public int getNumUControlPoints() {
- return controlNet.numUControlPoints;
- }
-
-
- /**
- * Access the number of V control points.
- * @return the number of V control points
- */
- public int getNumVControlPoints() {
- return controlNet.numVControlPoints;
- }
-
-
- /**
- * Access the U knot sequence.
- * @return the U knot sequence
- */
- public Knot getUKnot() {
- return u;
- }
-
-
- /**
- * Access the V knot sequence.
- * @return the V knot sequence
- */
- public Knot getVKnot() {
- return v;
- }
-
-
- /**
- * Set the U knot sequence.
- * @param u the new U knot sequence
- */
- public void setUKnot(Knot u) {
- this.u = u;
- }
-
-
- /**
- * Set the V knot sequence.
- * @param v the new V knot sequence
- */
- public void setVKnot(Knot v) {
- this.v = v;
- }
-
-
- /**
- * Set the ControlNet.
- * @param controlNet the new ControlNet
- */
- public void setControlNet(ControlNet controlNet) {
- this.controlNet = controlNet;
- }
-
-
- /**
- * Scale this NURBS uniformly.
- * @param scale the scale
- */
- public void scale(float scale) {
- controlNet.scale(scale);
- }
-
-
- /**
- * Scale this NURBS non-uniformly.
- * @param scale the scale
- */
- public void scale(float xscale, float yscale, float zscale) {
- controlNet.scale(xscale, yscale, zscale);
- }
-
-
- /**
- * Translate this NURBS.
- * @param x the translation in X
- * @param y the translation in Y
- * @param z the translation in Z
- */
- public void translate(float x, float y, float z) {
- controlNet.translate(x, y, z);
- }
-
-
- /**
- * Rotate this NURBS.
- * @param x the X component of the rotation axis
- * @param y the Y component of the rotation axis
- * @param z the Z component of the rotation axis
- * @param theta the rotation in radians
- */
- public void rotate(float x, float y, float z, float theta) {
- controlNet.rotate(x, y, z, theta);
- }
-
-
- /**
- * Transpose this NURBS by swapping U and V.
- */
- public void transpose() {
- Knot temp;
-
- temp = u;
- u = v;
- v = temp;
-
- controlNet.transpose();
- }
-
-
- /**
- * Refine this NURBS in the U and V directions by inserting
- * new knots and control points.
- * Employs recursive subdivision using the Oslo algorithm.
- * Refinement is done through recursive subdivision by adding
- * uSegments knots in the U direction and vSegments knots in
- * the V direction. (This also adds uSegments*vSegments
- * control points since the order remains constant.)
- * @param uSegments the number of new control points
- * @param vSegments the number of new control points
- * @return the refined Nurbs
- */
- public Nurbs tessellate(int uSegments, int vSegments) {
- //
- // Declare the return Nurbs. It initially refers
- // to "this" to account for the case where
- // uSegments == vSegments == 0
- //
- Nurbs temp = this;
-
- //
- // Tesselate in U direction first
- //
- if (uSegments > 0) {
- temp = this.tessellateU(uSegments);
- // System.err.println("After Tesselate: " + temp);
- }
-
- if (vSegments > 0) {
- //
- // If this is a surface, transpose (swap U, V),
- // then tessellate in new U direction (= old V direction)
- //
- if (uSegments <=0)
- temp = (Nurbs) this.clone();
- temp.transpose();
-
- Nurbs temp2 = temp.tessellateU(vSegments);
-
- //
- // Transpose back to its original configuration
- // and return
- //
- temp2.transpose();
- return temp2;
- }
- else {
- //
- // This is a curve, so we're done
- //
- return temp;
- }
- }
-
-
- /**
- * Refine this NURBS in the U direction by inserting new knots
- * and control points.
- * Refinement is done via recursive subdivision by adding
- * segments new knots.
- * @param uSegments the number of new control points
- * @return the refined Nurbs
- */
- private Nurbs tessellateU(int uSegments) {
- Nurbs temp = new Nurbs();
-
- //
- // First add new knots to U knot sequence
- //
- int uOrder = this.getUOrder();
- int numUKnots = Math.max(uSegments, this.getUOrder()*2 +1);
- int numUControlPoints = numUKnots - this.getUOrder();
-
- temp.u = new Knot(uOrder, numUControlPoints);
- temp.u.makeKnots(u.knot[0], u.knot[u.numKnots-1]);
- temp.u = temp.u.unionKnots(this.u);
-
- //
- // Copy the V knot sequence
- //
- temp.v = (Knot) v.clone();
-
- // System.err.println("tessellate: New U Knot Vector = " +
- // temp.u.toString());
- // System.err.println("tessellate: New V Knot Vector = " +
- // temp.v.toString());
-
- //
- // Second create the new ControlNet
- //
- Point4[][] tmppoints = new Point4[temp.u.numControlPoints]
- [temp.v.numControlPoints];
- for (int i=0; i<temp.u.numControlPoints; i++) {
- for (int j=0; j<temp.v.numControlPoints; j++) {
- // Creates the Point4 objects and initializes
- // them to zero
- tmppoints[i][j] = new Point4();
- }
- }
- ControlNet tmpcontrol = new ControlNet(temp.u.numControlPoints,
- temp.v.numControlPoints,
- tmppoints);
- temp.setControlNet(tmpcontrol);
-
- //
- // Now refine in U direction
- //
- // System.err.println("loop over " + temp.u.numControlPoints);
- //
- // should this be over numKnots? no, since
- // numControlPoints = numKnots - order, and since we
- // know that the last order knots are unchanged, we don't
- // have to deal with those.
- //
- for (int i=0; i<temp.u.numControlPoints; i++) {
- // System.err.println("in refine " + i);
- // knotIndex tells us where this particular new knot lies
- // in the old knot series
- // System.err.println("new knot=" + temp.u.knot[i]);
- int knotIndex = Knot.interval(this.u.knot, temp.u.knot[i]);
- // System.err.println("Index knotIndex = " + knotIndex +
- // " out of " + getNumUKnots());
- this.subdivide(temp, knotIndex, i);
- }
- return temp;
- }
-
-
- /**
- * Calculates the control points corresponding to new knots.
- * This is called once for each new knot and creates
- * numVControlPoints new control points.
- * @param temp the Nurbs which will contain the new control points
- * @param oldKnotIndex the index of the old knot
- * @param newKnotIndex the index of the new knot
- */
- protected void subdivide(Nurbs temp, int oldKnotIndex, int newKnotIndex) {
-
- int ttsize = getUOrder();
- Point4[][] tmppoints = new Point4[getUOrder()][oldKnotIndex+1];
-
- //
- // Refine U
- //
- // System.err.println("subdivide: kk loop over " + getNumVControlPoints());
- for (int kk=0; kk<getNumVControlPoints(); kk++) {
- // System.err.println("subdivide: ii loop over mu=" +
- // oldKnotIndex + " nrb1.k=" + getUOrder());
- for (int j=oldKnotIndex-getUOrder()+1; j<oldKnotIndex+1; j++) {
- tmppoints[0][j] = this.controlNet.controlPoints[j][kk];
- // System.err.println("subdivide: ttpts=" + tmppoints[0][j]);
- }
-
- // System.err.println("subdivide: ir loop over " + this.u.order);
- for (int i=1; i<getUOrder(); i++) {
- // System.err.println("subdivide: ii(2) loop over mu=" +
- // oldKnotIndex + " nrb1.k=" + u.order + " ir=" + i);
- for (int j=oldKnotIndex-getUOrder()+1+i; j<oldKnotIndex+1; j++) {
- float t1 = temp.u.knot[newKnotIndex + getUOrder() - i] -
- this.u.knot[j];
- float t2 = this.u.knot[j + getUOrder() - i] -
- temp.u.knot[newKnotIndex + getUOrder() - i];
- float tmul = 1.0f / (t1 + t2);
- // System.err.println("subdivide: t1=" + t1 +
- // " t2=" + t2 + " tmul=" + tmul);
- // interpolate new control points using old
- // control points
- // System.err.println(" i = " + i + " j = " + j);
- tmppoints[i][j] = new Point4(
- (t1*tmppoints[i-1][j].x + t2*tmppoints[i-1][j-1].x) * tmul,
- (t1*tmppoints[i-1][j].y + t2*tmppoints[i-1][j-1].y) * tmul,
- (t1*tmppoints[i-1][j].z + t2*tmppoints[i-1][j-1].z) * tmul,
- (t1*tmppoints[i-1][j].w + t2*tmppoints[i-1][j-1].w) * tmul);
- }
- }
- // System.err.println("FINAL = " + tmppoints[getUOrder()-1][oldKnotIndex]);
- temp.controlNet.controlPoints[newKnotIndex][kk] =
- tmppoints[getUOrder()-1][oldKnotIndex];
- }
- }
-
-
- /**
- * Split this NURBS into two at the given location.
- * @param dottedline the knot parameter at which to cut
- * @return an array of two Nurbs objects
- */
- protected Nurbs[] split(float dottedline) {
- //
- // Not fully implemented yet
-
- //
- // Find where in the knot sequence dottedline lies
- //
- int knotIndex = Knot.interval(u.knot, dottedline);
- System.err.println("Index knotIndex = " + knotIndex +
- " out of " + getNumUKnots());
-
- //
- // First create the Knots
- //
- int order = 0;
- // need order more for the end of the sequence where the split is.
- int numKnots = getNumUKnots() + getUOrder();
- int numControlPoints = 0;
-
- //
- // Copy first portion of knot sequence
- //
- float[] tmpknot = new float[numKnots];
- for (int i=0; i<knotIndex; i++) {
- tmpknot[i] = this.u.knot[i];
- }
-
- int ir = getUOrder();
- for (int i=0; i<getUOrder(); i++) {
- if (dottedline == u.knot[knotIndex - i])
- ir--;
- }
-
- int iin = knotIndex;
- for (int i=0; i<ir; i++) {
- tmpknot[iin] = dottedline;
- iin++;
- }
-
- //
- // Copy second portion of knot sequence
- //
- for (int i=knotIndex; i<getNumUKnots(); i++) {
- tmpknot[i] = this.u.knot[i];
- }
-
- Knot kk = new Knot(order, numControlPoints);
- kk.setKnots(tmpknot);
-
- System.err.println("split: Old U Knot Vector = " +
- u.toString());
- System.err.println("split: New U Knot Vector = " +
- kk.toString());
-
- //OSLO
- //
- // Second create the new ControlNet
- //
- Nurbs tmp = new Nurbs();
- Point4[][] tmppoints = new Point4[tmp.u.numControlPoints]
- [tmp.v.numControlPoints];
- for (int i=0; i<tmp.u.numControlPoints; i++) {
- for (int j=0; j<tmp.v.numControlPoints; j++) {
- // Creates the Point4 objects and initializes
- // them to zero
- tmppoints[i][j] = new Point4();
- }
- }
- ControlNet tmpcontrol = new ControlNet(tmp.u.numControlPoints,
- tmp.v.numControlPoints,
- tmppoints);
- tmp.setControlNet(tmpcontrol);
-
- //
- // Now refine in U direction
- //
- // should this be over numKnots? no, since
- // numControlPoints = numKnots - order, and since we
- // know that the last order knots are unchanged, we don't
- // have to deal with those.
- //
- for (int i=0; i<tmp.u.numControlPoints; i++) {
- // knotIndex tells us where this particular new knot lies
- // in the old knot series
- knotIndex = Knot.interval(this.u.knot, tmp.u.knot[i]);
- this.subdivide(tmp, knotIndex, i);
- }
-
- // blah blah
-
-
- Nurbs[] temp = new Nurbs[2];
- temp[0] = new Nurbs();
- // temp[0] = new Nurbs(kk, (Knot) v.clone(), controlNet);
- temp[1] = new Nurbs();
-
- return temp;
- }
-
-
- /**
- * Perform a linear extrusion of this NURBS along the vector w
- * to produce a new NURBS. Leaves this instance unchanged.
- * If this Nurbs instance is a surface, extrude the v.knot[0]
- * curve only.
- * @oaram w a vector for the extrusion.
- * @return a Nurbs surface.
- */
- public Nurbs extrude(Point4 w) {
- //
- // Curve and Surface Construction using Rational B-splines
- // Piegl and Tiller CAD Vol 19 #9 November 1987 pp 485-498
- //
- int order = 2; // linear extrusion
- int numControlPoints = 2;
- int numKnots = 4;
-
- //
- // First create the Knots
- //
- float[] temp = new float[numKnots];
- temp[0] = 0.0f;
- temp[1] = 0.0f;
- temp[2] = 1.0f;
- temp[3] = 1.0f;
-
- Knot tmpVKnot = new Knot(order, numControlPoints);
- tmpVKnot.setKnots(temp);
-
- //
- // Second create the ControlNet
- //
- Point4[][] tmppoints = new Point4[u.numControlPoints]
- [tmpVKnot.numControlPoints];
- for (int i=0; i<u.numControlPoints; i++) {
- for (int j=0; j<tmpVKnot.numControlPoints; j++) {
- // Creates the Point4 objects and initializes
- // them to zero
- tmppoints[i][j] = new Point4();
- }
- }
-
- for (int i=0; i<u.numControlPoints; i++) {
- for (int j=0; j<tmpVKnot.numControlPoints; j++) {
-
- /* Change added 11/02/90 Steve Larkin :
- * Have multiplied the term wcoord to the extrusion vector
- * before adding to the curve coordinates. Not really sure
- * this is the correct fix, but it works !
- */
- tmppoints[i][j].x = this.controlNet.controlPoints[i][0].x +
- j*w.x;
- tmppoints[i][j].y = this.controlNet.controlPoints[i][0].y +
- j*w.y;
- tmppoints[i][j].z = this.controlNet.controlPoints[i][0].z +
- j*w.z;
- tmppoints[i][j].w = this.controlNet.controlPoints[i][0].w;
- }
- }
- ControlNet tmpcontrol = new ControlNet(u.numControlPoints,
- tmpVKnot.numControlPoints,
- tmppoints);
- return new Nurbs((Knot) u.clone(), tmpVKnot, tmpcontrol);
- }
-
-
- /**
- * Do a surface of revolution about the Y axis.
- * Assumes curve is in X-Y plane
- * The profile NURBS will first be projected onto the Z=0 plane,
- * the resulting projection will be revolved about the Y axis.
- * Currently, thetamin and thetamax are ignored and 0, 2*PI is
- * used in all cases.
- * @param thetamin the angle at which to start the revolution
- * @param thetamax the angle at which to end the revolution
- * @return a Nurbs surface.
- */
- public Nurbs revolve(float thetamin, float thetamax) {
- //
- // Curve and Surface Construction using Rational B-splines
- // Piegl and Tiller CAD Vol 19 #9 November 1987 pp 485-498
- //
- int order = 3;
- int numControlPoints = 9;
- int numKnots = 12;
-
- //
- // First create the Knots for the circular cross section
- // If we want to limit this to an arc , would be done here
- // using the createArc() code rather than the createCircle()
- // code.
- //
- float[] temp = { 0.00f, 0.00f, 0.00f,
- 0.25f, 0.25f,
- 0.50f, 0.50f,
- 0.75f, 0.75f,
- 1.00f, 1.00f, 1.00f };
-
- Knot tmpUKnot = new Knot(order, numControlPoints);
- tmpUKnot.setKnots(temp);
-
- //
- // Second create the ControlNet
- //
- Point4[][] tmppoints = new Point4[tmpUKnot.numControlPoints]
- [u.numControlPoints];
- for (int i=0; i<tmpUKnot.numControlPoints; i++) {
- for (int j=0; j<u.numControlPoints; j++) {
- tmppoints[i][j] = new Point4();
- }
- }
-
- //
- // Copy profile curve.
- //
- for (int i=0; i<u.numControlPoints; i++) {
- tmppoints[0][i].x = controlNet.controlPoints[i][0].x;
- tmppoints[0][i].y = controlNet.controlPoints[i][0].y;
- tmppoints[0][i].z = 0.0f; // Project onto Z=0 plane
- tmppoints[0][i].w = controlNet.controlPoints[i][0].w;
- }
-
- float ww = (float) Math.sqrt(2.0)/2.0f;
-
- //
- // Sweep about Y axis. The V direction will be
- // a circle around the Y axis.
- //
- for (int i=0; i<u.numControlPoints; i++) {
- for (int j=1; j<tmpUKnot.numControlPoints; j++) {
- tmppoints[j][i].y = tmppoints[0][i].y;
- }
-
- float radius = tmppoints[0][i].x;
- float weight = tmppoints[0][i].w;
-
- int jj = 1;
- for (int j=0; j<4; j++) {
- tmppoints[jj ][i].y *= ww;
- tmppoints[jj ][i].w = ww * weight;
- tmppoints[jj+1][i].w = weight;
- jj += 2;
- }
-
- tmppoints[1][i].x = radius * ww;
- tmppoints[1][i].z = radius * ww;
- tmppoints[7][i].x = radius * ww;
- tmppoints[7][i].z = -radius * ww;
- tmppoints[2][i].x = 0.0f;
- tmppoints[2][i].z = radius;
- tmppoints[6][i].x = 0.0f;
- tmppoints[6][i].z = -radius;
- tmppoints[3][i].x = -radius * ww;
- tmppoints[3][i].z = radius * ww;
- tmppoints[5][i].x = -radius * ww;
- tmppoints[5][i].z = -radius * ww;
- tmppoints[4][i].x = -radius;
- tmppoints[4][i].z = 0.0f;
- tmppoints[8][i].x = radius;
- tmppoints[8][i].z = 0.0f;
- }
- ControlNet tmpcontrol = new ControlNet(tmpUKnot.numControlPoints,
- u.numControlPoints,
- tmppoints);
- return new Nurbs(tmpUKnot, (Knot) u.clone(), tmpcontrol);
- }
-
-
- /**
- * Increase the order of this NURBS
- * @return the elevated Nurbs
- */
- public Nurbs elevate() {
- //
- // Not implemented yet
- //
- return new Nurbs();
- }
-
-
- /**
- * Creates a String representation for the NURBS
- * in the form of a VRML Coordinate node.
- * @return a string representation of the Nurbs
- */
- public String toVRMLString() {
- return controlNet.toVRMLString();
- }
-
-
- /**
- * Creates a String representation for the NURBS
- * in the form of a VRML Coordinate node.
- * @return a string representation of the Nurbs
- */
- public String toVRMLCoordinateNode() {
- return controlNet.toVRMLCoordinateNode();
- }
-
-
- /**
- * Creates a String representation for the NURBS
- * in the form of a VRML coordIndex field.
- * @return a string representation of the Nurbs
- */
- public int[] toVRMLCoordIndex() {
- return controlNet.toVRMLCoordIndex();
- }
-
-
- /**
- * Creates a String representation for the NURBS
- * @return a string representation of the Nurbs
- */
- public String toString() {
- // Reserve enough space initially for a "typical" curve
- // in order to minimize buffer resizing.
- StringBuffer buffer = new StringBuffer(1024);
- buffer.append(getClass().getName() + ": ");
- buffer.append("uOrder=" + getUOrder() + ", " +
- "vOrder=" + getVOrder() + "\n");
- buffer.append("U Knot Sequence: " + u.toString() + "\n");
- buffer.append("V Knot Sequence: " + v.toString() + "\n");
- buffer.append("Control Points: " + controlNet.toString());
- return buffer.toString();
- }
- }
-