All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.graphbuilder.curve.CubicBSpline Maven / Gradle / Ivy

Go to download

The Apache Commons Codec package contains simple encoder and decoders for various formats such as Base64 and Hexadecimal. In addition to these widely used encoders and decoders, the codec package also maintains a collection of phonetic encoding utilities.

There is a newer version: 62
Show newest version
/*
* Copyright (c) 2005, Graph Builder
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Graph Builder nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package com.graphbuilder.curve;


/**

The cubic B-spline is defined by third order polynomial basis functions. Each point on the curve is locally controlled by 4 control-points. In general, the curve does not pass through the control points, only near. The exceptions to this are the first and last control-points and if there are duplicate sequential control-points.

The CubicBSpline is the same as the BSpline with degree 3. However, the CubicBSpline is much faster to compute than the BSpline. The following table represents the approximate speed difference in computation between the CubicBSpline and BSpline of degree 3.

Number of PointsTimes Faster
104.6
206.9
309.5
4011
Table 1: Efficiency of CubicBSpline

As the number of points increases, the BSpline gets slower and slower. The reason is the CubicBSpline is built in segments, using 4 points at a time. However, the BSpline is a single segment, and requires iteration through all the points to compute a single point on the curve. Unlike the BSpline, the CubicBSpline does not have a knot vector, a definable sample limit, range or degree, which means the memory required for the CubicBSpline is less than the BSpline.

Relative to other curves, the cubic B-spline is computationally inexpensive, and easy to work with. To create a closed cubic B-spline, use "0:n-1,0:2" as the control-string and set interpolateEndpoints to false. Figures 1, 2 & 3 show some examples of cubic B-splines. See the appendTo method for more information.

@see BSpline */ public class CubicBSpline extends ParametricCurve { private static final ThreadLocal SHARED_DATA = new ThreadLocal(){ protected SharedData initialValue() { return new SharedData(); } }; private final SharedData sharedData = SHARED_DATA.get(); private static class SharedData { private int section = 0; private int numPoints = 0; private double[][] pt = new double[4][]; private double[] b = new double[4]; } private boolean interpolateEndpoints = false; public CubicBSpline(ControlPath cp, GroupIterator gi) { super(cp, gi); } protected void eval(double[] p) { double t = p[p.length - 1]; double t2 = t * t; double t3 = t2 * t; double u = 1 - t; double u2 = u * u; double u3 = u2 * u; if (sharedData.numPoints == 4) { sharedData.b[0] = u2 * u; sharedData.b[1] = 3 * u2 * t; sharedData.b[2] = 3 * u * t2; sharedData.b[3] = t3; } else if (sharedData.numPoints == 5) { if (sharedData.section == 0) { sharedData.b[0] = u3; sharedData.b[1] = 7 * t3 / 4 - 9 * t2 / 2 + 3 * t; sharedData.b[2] = -t3 + 3 * t2 / 2; sharedData.b[3] = t3 / 4; } else { sharedData.b[0] = u3 / 4; sharedData.b[1] = -u3 + 3 * u2 / 2; sharedData.b[2] = 7 * u3 / 4 - 9 * u2 / 2 + 3 * u; sharedData.b[3] = t3; } } else if (sharedData.numPoints == 6) { if (sharedData.section == 0) { sharedData.b[0] = u3; sharedData.b[1] = 7 * t3 / 4 - 9 * t2 / 2 + 3 * t; sharedData.b[2] = -11 * t3 / 12 + 3 * t2 / 2; sharedData.b[3] = t3 / 6; } else if (sharedData.section == 1) { sharedData.b[0] = u3 / 4; sharedData.b[1] = 7 * t3 / 12 - 5 * t2 / 4 + t / 4 + 7.0 / 12; sharedData.b[2] = -7 * t3 / 12 + t2 / 2 + t / 2 + 1.0 / 6; sharedData.b[3] = t3 / 4; } else { sharedData.b[0] = u3 / 6; sharedData.b[1] = -11 * u3 / 12 + 3 * u2 / 2; sharedData.b[2] = 7 * u3 / 4 - 9 * u2 / 2 + 3 * u; sharedData.b[3] = t3; } } else { // 7 and >= 8 have the same basis functions if (sharedData.section == 0) { sharedData.b[0] = u3; sharedData.b[1] = 7 * t3 / 4 - 9 * t2 / 2 + 3 * t; sharedData.b[2] = -11 * t3 / 12 + 3 * t2 / 2; sharedData.b[3] = t3 / 6; } else if (sharedData.section == 1) { sharedData.b[0] = u3 / 4; sharedData.b[1] = 7 * t3 / 12 - 5 * t2 / 4 + t / 4 + 7.0 / 12; sharedData.b[2] = -t3 / 2 + t2 / 2 + t / 2 + 1.0 / 6; sharedData.b[3] = t3 / 6; } else if (sharedData.section == 2) { // if numPoints == 7 then section 2 gets skipped sharedData.b[0] = u3 / 6; sharedData.b[1] = t3 / 2 - t2 + 2.0 / 3; sharedData.b[2] = (-t3 + t2 + t) / 2 + 1.0 / 6; sharedData.b[3] = t3 / 6; } else if (sharedData.section == 3) { sharedData.b[0] = u3 / 6; sharedData.b[1] = -u3 / 2 + u2 / 2 + u / 2 + 1.0 / 6; sharedData.b[2] = 7 * u3 / 12 - 5 * u2 / 4 + u / 4 + 7.0 / 12; sharedData.b[3] = t3 / 4; } else { sharedData.b[0] = u3 / 6; sharedData.b[1] = -11 * u3 / 12 + 3 * u2 / 2; sharedData.b[2] = 7 * u3 / 4 - 9 * u2 / 2 + 3 * u; sharedData.b[3] = t3; } } for (int i = 0; i < 4; i++) { for (int j = 0; j < p.length - 1; j++) p[j] = p[j] + sharedData.pt[i][j] * sharedData.b[i]; } } /** Returns a value of 1. */ public int getSampleLimit() { return 1; } /** Sets the curve to start at the first control-point and end at the last control-point specified by the group-iterator. @see #getInterpolateEndpoints() */ public void setInterpolateEndpoints(boolean b) { interpolateEndpoints = b; } /** Returns the interpolateEndpoints value. The default value is false. @see #setInterpolateEndpoints(boolean) */ public boolean getInterpolateEndpoints() { return interpolateEndpoints; } /** The requirements for this curve are the group-iterator must be in-range and have a group size of at least 4. If these requirements are not met then this method throws IllegalArgumentException */ public void appendTo(MultiPath mp) { if (!gi.isInRange(0, cp.numPoints())) throw new IllegalArgumentException("Group iterator not in range"); int n = gi.getGroupSize(); if (n < 4) throw new IllegalArgumentException("Group iterator size < 4"); if (interpolateEndpoints) { sharedData.numPoints = n; sharedData.section = 0; } else { sharedData.numPoints = -1; // defaults to numPoints >= 7 in the eval method sharedData.section = 2; // section doesn't change when interpolateEndpoints == false } gi.set(0, 0); int index_i = 0; int count_j = 0; for (int i = 0; i < 4; i++) sharedData.pt[i] = cp.getPoint(gi.next()).getLocation(); double[] d = new double[mp.getDimension() + 1]; eval(d); if (connect) mp.lineTo(d); else mp.moveTo(d); int j = 3; while (true) { BinaryCurveApproximationAlgorithm.genPts(this, 0.0, 1.0, mp); j++; if (j == n) break; gi.set(index_i, count_j); gi.next(); index_i = gi.index_i(); count_j = gi.count_j(); for (int i = 0; i < 4; i++) sharedData.pt[i] = cp.getPoint(gi.next()).getLocation(); if (interpolateEndpoints) { if (n < 7) { sharedData.section++; } else { if (sharedData.section != 2) sharedData.section++; if (sharedData.section == 2 && j == n - 2) sharedData.section++; } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy