home *** CD-ROM | disk | FTP | other *** search
/ Late Night VRML 2.0 with Java CD-ROM / code.zip / Ch15 / NURBS / Knot.java < prev    next >
Encoding:
Java Source  |  1997-01-29  |  8.1 KB  |  272 lines

  1. /*
  2.  * Copyright (c) 1997 ORC Incorporated.  All Rights Reserved.
  3.  *
  4.  * Permission to use, copy, modify, and distribute this software
  5.  * and its documentation for NON-COMMERCIAL purposes and without
  6.  * fee is hereby granted provided that this copyright notice
  7.  * appears in all copies. Please contact <info@ocnus.com> for
  8.  * further information regarding copyright and licensing.
  9.  * This code is provided without warranty, either expressed or implied.
  10.  */
  11. /* 
  12.  * Portions of this code appear in the book
  13.  * "Late Night VRML 2.0 with Java", Ziff-Davis Press, 1997
  14.  *
  15.  * Knot.java
  16.  *
  17.  * author   Timothy F. Rohaly
  18.  * version  1.0, 01/15/97
  19.  */
  20.  
  21. package nurbs;
  22.  
  23.  
  24. /**
  25.  * Sequence of basis functions for use by Nurbs
  26.  * @author   Timothy F.  Rohaly
  27.  * @version  1.0, 1/15/97
  28.  */
  29. public class Knot implements Cloneable {
  30.  
  31.     protected int     order;
  32.     protected int     numControlPoints;
  33.     protected int     numKnots;
  34.     protected float[] knot;
  35.  
  36.     public static final float TOLERANCE = 1.0E-7f;
  37.  
  38.  
  39.     /**
  40.      * Constructor.
  41.      * @param order the order of the Knot
  42.      * @param numControlPoints the number of control points
  43.      */
  44.     public Knot(int order, int numControlPoints) {
  45.         this.order            = order;
  46.         this.numControlPoints = numControlPoints;
  47.         this.numKnots         = order + numControlPoints;
  48.     }
  49.  
  50.  
  51.     /**
  52.      * Constructor.
  53.      * @param order the order of the Knot
  54.      * @param numControlPoints the number of control points
  55.      * @param knot the array of knot values
  56.      * @exception java.lang.IllegalArgumentException when the
  57.      * number of knots does not equal the order plus the number
  58.      * of control points
  59.      */
  60.     public Knot(int order, int numControlPoints, float[] knot) {
  61.         String errorString = getClass().getName() + ":  " +
  62.                              "Number of knots in array must equal " +
  63.                              "order + number of control points";
  64.         if (knot.length != order + numControlPoints)
  65.             throw new IllegalArgumentException(errorString);
  66.  
  67.         this.order            = order;
  68.         this.numControlPoints = numControlPoints;
  69.         this.numKnots         = order + numControlPoints;
  70.         this.knot             = knot;
  71.     }
  72.  
  73.  
  74.     /**
  75.      * Return number of control points.
  76.      * @return the number of control points
  77.      */
  78.     public int getNumControlPoints() {
  79.         return numControlPoints;
  80.     }
  81.  
  82.  
  83.     /**
  84.      * Accessor method for the knot[] array
  85.      * @param knot the array of knots
  86.      */
  87.     public void setKnots(float[] knot) {
  88.         String errorString = getClass().getName() + ":  " +
  89.                              "Number of knots in array must equal " +
  90.                              "order + number of control points";
  91.         if (knot.length != order + numControlPoints)
  92.             throw new IllegalArgumentException(errorString);
  93.  
  94.         this.knot     = knot;
  95.         this.numKnots = knot.length;
  96.     }
  97.  
  98.  
  99.     /**
  100.      * Accessor method for the knot[] array
  101.      * @return an array of knots
  102.      */
  103.     public float[] getKnots() {
  104.         return knot;
  105.     }
  106.  
  107.  
  108.     /**
  109.      * Determine where in the knot sequence t lies.
  110.      * The knot parameter t must lie within the half-open
  111.      * interval <tt>knot[0] <= t < knot[knot.length-1]</tt>.
  112.      * @param knot the array of knot values
  113.      * @param t the value to compare
  114.      * @return the array index of the highest-index knot
  115.      * which is less than or equal to t
  116.      * @exception java.lang.IllegalArgumentException when the
  117.      * knot parameter t is not within the half-open interval
  118.      */
  119.     protected static int interval(float[] knot, float t) {
  120.         //
  121.         // knot is an ordered (non-decreasing) array, so
  122.         // we could make this search a lot smarter, but
  123.         // doesn't pay unless knot sequence is large.
  124.         //
  125.         if (knot[0]<=t) {
  126.             for (int i=0; i<knot.length-1; i++) {
  127.                 if (t<knot[i+1])
  128.                     return i;
  129.             }
  130.         }
  131.         throw new IllegalArgumentException();
  132.     }
  133.  
  134.  
  135.     /**
  136.      * Produce numKnots knots between tmin and tmax, with
  137.      * tmin, tmax repeated order times and the remaining knots
  138.      * distributed uniformly between.  Existing knot sequence
  139.      * will be overwritten.
  140.      * @param tmin the minimum knot value
  141.      * @param tmax the maximum knot value
  142.      */
  143.     protected void makeKnots(float tmin, float tmax) {
  144.         //
  145.         // Replicate start and end knots order times
  146.         //
  147.         knot = new float[numKnots];
  148.         for (int i=0; i<order; i++) {
  149.             knot[i]                    = tmin;
  150.             knot[i + numControlPoints] = tmax;
  151.         }
  152.  
  153.         //
  154.         // Evenly distribute remainder
  155.         //
  156.         float interval = (tmax - tmin) / (numKnots - order - order + 1);
  157.         for (int i=order; i<numControlPoints; i++)
  158.             knot[i] = tmin + (i - order + 1) * interval;
  159.     }
  160.  
  161.  
  162.     /**
  163.      * This function returns true if one and two
  164.      * are equal, or their difference is less than
  165.      * the constant tolerance.
  166.      * @param one the first float
  167.      * @param two the second float
  168.      * @return true if approximately equal
  169.      */
  170.     private boolean equal(float one, float two) {
  171.         return ((float) Math.abs(one - two) <= TOLERANCE);
  172.     }
  173.  
  174.  
  175.     /**
  176.      * Merge the knot sequence knot2 into the current
  177.      * knot.  Do not add knot parameters from knot2
  178.      * which duplicate parameters already in this knot.
  179.      * @param knot2 the Knot to merge into this
  180.      * @return a new Knot
  181.      */
  182.     protected Knot unionKnots(Knot knot2) {
  183.         boolean done = false;
  184.         int numNewKnots = 0;
  185.         int i1  = 0;
  186.         int i2  = 0;
  187.         float t;
  188.  
  189.         //
  190.         // If no knot duplication, we will have at most
  191.         // this.numKnots + knot2.numKnots, so allocate
  192.         // enough room.
  193.         //
  194.         float[] newknot = new float[this.numKnots + knot2.numKnots];
  195.  
  196.         while (!done) {
  197.             if (equal(this.knot[i1], knot2.knot[i2])) {
  198.                 t = this.knot[i1];
  199.                 i1++;
  200.                 i2++;
  201.             }
  202.             else {
  203.                 if (this.knot[i1] < knot2.knot[i2]) {
  204.                     t = this.knot[i1];
  205.                     i1++;
  206.                 }
  207.                 else {
  208.                     t = knot2.knot[i2];
  209.                     i2++;
  210.                 }
  211.             }
  212.             newknot[numNewKnots] = t;   // store knot parameter
  213.             numNewKnots++;
  214.             done = (i1 >= this.numKnots || i2 >= knot2.numKnots);
  215.         }
  216.  
  217.         //
  218.         // If both are equal copy one and increment both,
  219.         // otherwise take the smaller of the two knots
  220.         // 
  221.         if (i1 < this.numKnots) {
  222.             for (int i=i1; i<this.numKnots; i++) {
  223.                 newknot[numNewKnots] = this.knot[i];
  224.                 numNewKnots++;
  225.             }
  226.         }
  227.         else {
  228.             for (int i=i2; i<knot2.numKnots; i++) {
  229.                 newknot[numNewKnots] = knot2.knot[i];
  230.                 numNewKnots++;
  231.             }
  232.         }
  233.         float[] temp = new float[numNewKnots];
  234.         System.arraycopy(newknot, 0, temp, 0, numNewKnots);
  235.         return new Knot(order, numNewKnots - order, temp);
  236.     }
  237.  
  238.  
  239.     /**
  240.      * Performs a deep copy of this Knot.
  241.      * @return a new instance of Knot
  242.      */
  243.     public Object clone() {
  244.         try {
  245.             Knot klone = (Knot) super.clone();
  246.             System.arraycopy(knot, 0,   klone.knot, 0, numKnots);
  247.             return klone;
  248.         }
  249.         catch (CloneNotSupportedException e) {
  250.             String errorString = getClass().getName() + ":  " +
  251.                                  "CloneNotSupportedException " +
  252.                                  "for a Cloneable class";
  253.             throw new InternalError(errorString);  // this should never happen
  254.         }
  255.     }
  256.  
  257.  
  258.     /**
  259.      * Creates a String representation for the Knot
  260.      * @return a string representation of the Knot
  261.      */
  262.     public String toString() {
  263.         StringBuffer buffer = new StringBuffer(64);
  264.         buffer.append(getClass().getName() + ": numKnots=" + numKnots);
  265.         for (int i=0;  i<numKnots; i++) {
  266.             if ((i % 4) == 0) buffer.append("\n");
  267.             buffer.append(knot[i] + " ");
  268.         }
  269.         return buffer.toString();
  270.     }
  271. }
  272.