de.cau.cs.kieler.klay.layered.p5edges.splines.NubSpline.java Source code

Java tutorial

Introduction

Here is the source code for de.cau.cs.kieler.klay.layered.p5edges.splines.NubSpline.java

Source

/*
 * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient
 *
 * http://www.informatik.uni-kiel.de/rtsys/kieler/
 * 
 * Copyright 2014 by
 * + Christian-Albrechts-University of Kiel
 *   + Department of Computer Science
 *     + Real-Time and Embedded Systems Group
 * 
 * This code is provided under the terms of the Eclipse Public License (EPL).
 * See the file epl-v10.html for the license text.
 */
package de.cau.cs.kieler.klay.layered.p5edges.splines;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

import de.cau.cs.kieler.core.math.KVector;
import de.cau.cs.kieler.core.math.KVectorChain;

/**
 * Represents a Non Uniform B-Spline. This spline is not rational, thus there is no weight for the
 * control points.
 * The control points of the represented spline are stored as PolarCPs. This class also holds their
 * name in the polar form, that is described by Sederberg. 
 * For details of this notation, please refer Senderberg: "An introduction to B-Spline Curves" (2005).
 * 
 * A control point those polar coordinates are all the same is laying on the spline.
 * 
 * The knot vector does not store the irrelevant first and last vector traditionally added to it.
 * These two vectors are meaningless, thus don't need to be stored. Remember this if you want to 
 * directly work with the knot vector.
 * 
 * @author tit
 * 
 */
class NubSpline {
    /** Default dimension of the spline. */
    protected static final int DIM = 3;
    /** Doubles with a difference less than this value will be assumed to be equal. */
    private static final double EPSILON = 0.000001;

    /** The knotVector of this NubSpline. (The traditionally added 0 at the beginning and 1 at the end
     *  of the knot vector is not added, as these elements are not relevant for the calculation) */
    private List<Double> knotVector = Lists.newArrayList();
    /** The control points of this NubSpline. */
    private List<PolarCP> controlPoints = Lists.newArrayList();

    /** The dimension of this NubSpline. All contained and constructible vectors have the same dim. */
    private int dimNUBS;
    /** Specifies of this NubSpline is uniform. To be exact this only depends on the knotVecor. 
     * A NubSpline is uniform if the difference of two adjacent knots in the knot vector is either 0 or 
     * equal to a constant valid for the whole spline.*/
    private boolean isUniform;
    /** Specifies of this NubSpline is clamped. To be exact this only depends on the knotVecor. 
     * A NubSpline is clamped, if the first and last vector of it's knot vector has the multiplicity
     * equal to it's dimension. (Remember the neglected first and last vector. Otherwise this would
     * be dimension +1.) */
    private boolean isClamped;
    /** The outer margins of this spline. */
    private Rectangle outerBox;
    /** Indicates if all inner knots of the knotVector have the multiplicity of dimNUBS. */
    private boolean isBezier;
    /** The value of parameter t (time), where the resulting spline would start. */
    private double minKnot;
    /** The value of parameter t (time), where the resulting spline would end. */
    private double maxKnot;

    // ########################################################################################
    // Constructors

    /**
     * A copy constructor.
     * 
     * @param nubSpline The NubSpline to copy.
     */
    public NubSpline(final NubSpline nubSpline) {
        dimNUBS = nubSpline.dimNUBS;
        isUniform = nubSpline.isUniform;
        isClamped = nubSpline.isClamped;
        outerBox = nubSpline.outerBox;
        isBezier = nubSpline.isBezier;
        knotVector = Lists.newLinkedList(nubSpline.knotVector);
        minKnot = nubSpline.minKnot;
        maxKnot = nubSpline.maxKnot;
        controlPoints = Lists.newLinkedList(nubSpline.controlPoints);
    }

    /**
     * Creates a new uniform NubSpline with the specified control points. The dimension must be > 0.
     * 
     * @param clamped {@code true}, if the NubSpline shall be clamped.
     * @param dimension The dimension of this NubSpline.
     * @param kVectors The control points of this NubSpline.
     */
    public NubSpline(final boolean clamped, final int dimension, final List<KVector> kVectors) {
        if (dimension < 1) {
            throw new IllegalArgumentException("The dimension must be at least 1!");
        }

        // fill the list of control-points to be at least equal to dimension + 1 
        for (int i = kVectors.size() - 1; i < dimension; i++) {
            kVectors.add(0, kVectors.get(0));
        }

        if (kVectors.size() < (dimension + 1)) {
            throw new IllegalArgumentException("At (least dimension + 1) control points are necessary!");
        } else {
            dimNUBS = dimension;
            isClamped = clamped;
            isUniform = true;
            isBezier = false;

            // create the knot vector
            createUniformKnotVector(clamped, kVectors.size() + dimNUBS - 1);

            final List<Double> polarCoordinate = Lists.newArrayList();
            final Iterator<Double> knotIter = knotVector.iterator();

            // the first (dimNUBS - 1) elements of the knotVector for the "sliding window" that
            // determines the polarCoordinates of the PolarCP.
            for (int i = 0; i < (dimNUBS - 1); i++) {
                polarCoordinate.add(knotIter.next());
            }

            // Create the PolarCPs
            // A sliding window over the knot vector determines the polar coordinates of the
            // PolarCPs.
            for (final KVector kVector : kVectors) {
                polarCoordinate.add(knotIter.next());
                controlPoints.add(new PolarCP(kVector, polarCoordinate));
                polarCoordinate.remove(0);
            }
        }
    }

    /**
     * Creates a new uniform and clamped NubSpline with the specified control points. Dimension must be
     * > 0.
     * 
     * @param clamped {@code true}, if the NubSpline shall be clamped.
     * @param dimension The dimension if this NubSpline.
     * @param kVectors The control points of this NubSpline.
     */
    public NubSpline(final boolean clamped, final int dimension, final KVector... kVectors) {
        this(true, dimension, Arrays.asList(kVectors));
    }

    /**
     * Creates a new NubSpline with the given, precalculated values.
     * There is no error checking! So if the spline shall be clamped, uniform or bezier, you have to 
     * ensure the correctness of the knot vector on your own. 
     * 
     * @param clamped {@code true}, if the NubSpline is clamped.
     * @param uniform {@code true}, if the NubSpline is uniform.
     * @param bezier {@code true}, if the NubSpline is in bezier form.
     * @param dim The dimension of the NubSpline.
     * @param knotVec The knot vector if the NubSpline.
     * @param polarVectors The control points of the NubSpline already in polar form.
     */
    private NubSpline(final boolean clamped, final boolean uniform, final boolean bezier, final int dim,
            final List<Double> knotVec, final List<PolarCP> polarVectors) {
        isClamped = clamped;
        isUniform = uniform;
        isBezier = bezier;
        dimNUBS = dim;
        knotVector = knotVec;
        controlPoints = polarVectors;
        minKnot = knotVec.iterator().next();
        maxKnot = Iterables.getLast(knotVec);
    }

    // ########################################################################################
    // Generators

    /**
     * Creates a new NubSpline, that is the derivation of the given NubSpline.
     * 
     * @param nubSpline The NubSpline to create the derivation from.
     * @return The derivation.
     */
    public static NubSpline generateDerivedNUBS(final NubSpline nubSpline) {
        final boolean newClamped = nubSpline.isClamped;
        final boolean newUniform = nubSpline.isUniform;
        final boolean newBezier = nubSpline.isBezier;
        final int oldDim = nubSpline.dimNUBS;
        final int newDim = oldDim - 1;
        final List<Double> oldKnotVector = nubSpline.knotVector;
        final List<Double> newKnotVector = Lists
                .newLinkedList(nubSpline.knotVector.subList(1, nubSpline.knotVector.size() - 1));
        final List<KVector> newControlPoints = Lists.newArrayList();

        // Calculate the new control points.
        for (int i = 0; i < nubSpline.controlPoints.size() - 1; i++) {
            final KVector newCP = nubSpline.controlPoints.get(i + 1).getCp().clone()
                    .sub(nubSpline.controlPoints.get(i).getCp())
                    .scale(oldDim / (oldKnotVector.get(i + oldDim) - oldKnotVector.get(i)));
            newControlPoints.add(newCP);
        }

        // Create the PolarCPs
        final List<Double> polarCoordinate = Lists.newArrayList();
        final Iterator<Double> knotIter = newKnotVector.iterator();
        final List<PolarCP> newPolarVectors = Lists.newArrayList();

        // the first (dimNUBS - 1) elements of the knotVector for the "sliding window" that
        // determines the polarCoordinates of the PolarCP.
        for (int i = 0; i < (newDim - 1); i++) {
            polarCoordinate.add(knotIter.next());
        }

        // A sliding window over the knot vector determines the polar coordinates of the PolarCPs.
        for (final KVector kVector : newControlPoints) {
            polarCoordinate.add(knotIter.next());
            newPolarVectors.add(new PolarCP(kVector, polarCoordinate));
            polarCoordinate.remove(0);
        }

        // Create the new NubSpline.
        return new NubSpline(newClamped, newUniform, newBezier, newDim, newKnotVector, newPolarVectors);
    }

    /**
     * Creates a new NubSpline, that is a inverted copy of the given one.
     * The control-points will be inverted.
     * The distribution of the vectors on the knot-vector will be inverted, but the knot-vector will
     * be sorted ascending, again.
     *  
     * @param nubSpline The NubSpline to create a inverted copy from.
     * @return The inverted NubSpline.
     */
    public static NubSpline generateInvertedNUBS(final NubSpline nubSpline) {
        final List<Double> newKnotVector = Lists.newArrayList();
        final double maxVector = nubSpline.knotVector.get(nubSpline.knotVector.size() - 1);
        for (final Double vector : nubSpline.knotVector) {
            newKnotVector.add(0, maxVector - vector);
        }
        final List<KVector> newControlPoints = KVectorChain.reverse(nubSpline.getControlPoints());

        // Create the PolarCPs
        final List<Double> polarCoordinate = Lists.newArrayList();
        final Iterator<Double> knotIter = newKnotVector.iterator();
        final List<PolarCP> newPolarVectors = Lists.newArrayList();

        // the first (dimNUBS - 1) elements of the knotVector for the "sliding window" that
        // determines the polarCoordinates of the PolarCP.
        for (int i = 0; i < (nubSpline.dimNUBS - 1); i++) {
            polarCoordinate.add(knotIter.next());
        }

        // A sliding window over the knot vector determines the polar coordinates of the PolarCPs.
        for (final KVector kVector : newControlPoints) {
            polarCoordinate.add(knotIter.next());
            newPolarVectors.add(new PolarCP(kVector, polarCoordinate));
            polarCoordinate.remove(0);
        }

        // Create the new NubSpline.
        return new NubSpline(nubSpline.isClamped, nubSpline.isUniform, nubSpline.isBezier, nubSpline.dimNUBS,
                newKnotVector, newPolarVectors);
    }

    // ########################################################################################
    // Getter & setter

    /**
     * Returns the dimension of this NubSpline.
     * 
     * @return The dimension.
     */
    public int getDim() {
        return dimNUBS;
    }

    /**
     * Returns and, if necessary, calculates the outerBox of this spline. The outerBox lays around 
     * all parts of the spline.
     * @return A Rectangle representing a square around this NubSpline. The Rectangle is only an
     *         approximation.
     */
    public Rectangle getOuterBox() {
        if (outerBox == null) {
            calculateOuterBox();
        }
        return outerBox;
    }

    /**
     * Sets the outer box of this NubSpline. Only use if you know what you are doing!
     * 
     * @param outerRectange The outerBox of this spline.
     */
    protected void setOuterBox(final Rectangle outerRectange) {
        outerBox = outerRectange;
    }

    /**
     * Returns the control points. The control points may be modified,
     * but the list itself is only a container.
     * 
     * @return The control points.
     */
    public KVectorChain getControlPoints() {
        final KVectorChain retVal = new KVectorChain();

        for (final PolarCP polarCP : controlPoints) {
            retVal.add(polarCP.getCp());
        }
        return retVal;
    }

    /**
     * Returns the i'th control-point. May be modified.
     * 
     * @param i Position of the control-point.
     * @return The control-point.
     */
    public KVector getControlPoint(final int i) {
        return controlPoints.get(i).getCp();
    }

    /**
     * Returns the number of controlPoints in this NubSpline.
     * 
     * @return The number of control points.
     */
    public int getControlPointsSize() {
        return controlPoints.size();
    }

    /**
     * Returns a copy of the knot vector of this NubSpline.
     * 
     * @return A copy of the knotVector.
     */
    public List<Double> getKnotVector() {
        return Lists.newLinkedList(knotVector);
    }

    /**
     * Searches the given knot in the knot-vector.
     * 
     * @param knot The knot to find.
     * @return The index of the first occurrence of the knot in the knot-vector or the size of the
     *         knot-vector, if the knot is not found.
     */
    private int getIndexInKnotVector(final double knot) {
        final ListIterator<Double> iter = knotVector.listIterator();
        double currentKnot = knot - 1;

        while (iter.hasNext()) {
            currentKnot = iter.next();
            final double diff = Math.abs(currentKnot - knot);
            if (diff < EPSILON) {
                return iter.nextIndex() - 1;
            }
        }

        return knotVector.size();
    }

    /**
     * Calculates the multiplicity of a given knot.
     * 
     * @param knotToCheck The knot those multiplicity to check.
     * @return The multiplicity of the knot.
     */
    private int getMultiplicity(final double knotToCheck) {
        final Iterator<Double> iter = knotVector.listIterator();
        double currentKnot;
        int count = 0;

        while (iter.hasNext()) {
            currentKnot = iter.next();
            final double diff = currentKnot - knotToCheck;
            if (diff > EPSILON) {
                return count;
            } else if (diff > -EPSILON) {
                count++;
            }
        }

        return count;
    }

    // ########################################################################################
    // Assisting methods

    /**
     * Creates a uniform knot-vector from 0.0 to 1.0.
     * 
     * @param clamped Specifies if the knot-vector shall be clamped.
     * @param size The number of knots to generate.
     */
    private void createUniformKnotVector(final boolean clamped, final int size) {
        if (size < (2 * dimNUBS)) {
            throw new IllegalArgumentException(
                    "The knot vector must have at least two time the dimension elements.");
        }
        double mySize;

        if (clamped) {
            minKnot = 0.0;
            maxKnot = 1.0;
            for (int i = 0; i < dimNUBS; i++) {
                knotVector.add(0.0);
            }
            mySize = size + 1 - 2 * dimNUBS;
        } else {
            mySize = size + 1;
            final double ddim = (double) dimNUBS;
            minKnot = ddim / (mySize + 1);
            maxKnot = (mySize - ddim) / mySize;
        }

        final double fraction = mySize;
        for (int i = 1; i < mySize; i++) {
            knotVector.add((double) i / fraction);
        }

        if (isClamped) {
            for (int i = 0; i < dimNUBS; i++) {
                knotVector.add(1.0);
            }
        }
    }

    /**
     * Calculates the outerBox as the extreme values of all control points.
     */
    private void calculateOuterBox() {
        outerBox = new Rectangle(getControlPoints());
    }

    /**
     * Converts a polar vector to a single t.
     * 
     * @param polar The polar coordinate to convert.
     * @return A t value, representing the polar coordinate.
     */
    private static double getTFromPolar(final Collection<Double> polar) {
        double sum = 0.0;
        for (final Double val : polar) {
            sum += val;
        }

        return sum / polar.size();
    }

    /**
     * Inserts a new knots to the knot-vector. Also calculates the new list of CPs. There is a
     * limitation on the value of the inserted knot:
     * <UL>
     * <LI>minKnot < knotToInsert < maxKnot</LI>
     * <LI>multiplicity of knot to insert may not exceed the dimension of the Nubs.</LI>
     * </UL>
     * 
     * @param knotToInsert The knot to insert.
     */
    private void insertKnot(final double knotToInsert, final int insertions) {
        final ListIterator<Double> iterKnot = knotVector.listIterator();

        // Skip elements not referring to a point on the drawn spline.
        if (isClamped) {
            for (int i = 0; i < dimNUBS; i++) {
                iterKnot.next();
            }
        } else {
            for (int i = 0; i < dimNUBS - 1; i++) {
                iterKnot.next();
            }
        }

        final ListIterator<PolarCP> iterCP = controlPoints.listIterator();
        double currentKnot = iterKnot.next();

        // we always insert the new knot AFTER all identical knots.
        while (currentKnot - knotToInsert < EPSILON) {
            currentKnot = iterKnot.next();
            iterCP.next();
        }
        // One step back on the knot-vector
        iterKnot.previous();

        // Insert the new knot.
        insertKnotAtCurrentPosition(insertions, knotToInsert, iterCP, iterKnot);
    }

    /**
     * Inserts a new knot to the knot-vector and calculates the new list of CPs.
     * The correct position in knot-vector and list of controlPoints must be pre-calculated! 
     * 
     * 
     * @param insertions How many times to insert the knot?
     * @param knotToInsert The knot to insert.
     * @param iterCP A iterator over the CPs. Iterator must point to the position before the first CP,
     *            that will be used for the calculation.
     * @param iterKnot A iterator over the knot-vector. Iterator must point to position where to insert
     *            the new knot.
     */
    private void insertKnotAtCurrentPosition(final int insertions, final double knotToInsert,
            final ListIterator<PolarCP> iterCP, final ListIterator<Double> iterKnot) {
        final int multiplicity = getMultiplicity(knotToInsert);
        for (int i = 0; i < insertions; i++) {
            // Insert the new knot to the knotVector.
            iterKnot.add(knotToInsert);

            // We will first construct the new CPs and than add them.
            final List<PolarCP> newCPs = Lists.newArrayList();
            // The first CP we need for the calculation.
            PolarCP secondCP = iterCP.next();

            for (int j = multiplicity + i; j < dimNUBS; j++) {
                // The second CP we need for the calculation.
                final PolarCP firstCP = secondCP;
                secondCP = iterCP.next();

                // Calculate the new CP.
                newCPs.add(new PolarCP(firstCP, secondCP, knotToInsert));
            }

            // move to the insertion position, and on the way delete all CPs we have used for two
            // calculations as they don't belong to the new list of CPs.
            for (int j = multiplicity + i; j < dimNUBS; j++) {
                iterCP.previous();
                if (j > multiplicity + i) {
                    iterCP.remove();
                }
            }

            // now we can add the new CPs
            for (final PolarCP cp : newCPs) {
                iterCP.add(cp);
            }

            // Move back to the position in front of the first new CP, if there will be more
            // insertions
            if (i < insertions - 1) {
                for (int j = multiplicity + i; j < dimNUBS; j++) {
                    iterCP.previous();
                }
            }
        }
    }

    // ########################################################################################
    // Calculating extrema

    /**
     * Returns the t-value of the first vertical point on a NubSpline.
     * 
     * @param nubSpline The spline to check. 
     * @param accuracy The desired accuracy.
     * @param maxRecursion The maximum number of recursions.
     * @return The t-value of the maximum of the spline.
     */
    protected static KVector getFirstVerticalPoint(final NubSpline nubSpline, final double accuracy,
            final int maxRecursion) {

        final NubSpline firstDerive = generateDerivedNUBS(nubSpline);
        double currentAccuracy = Double.MAX_VALUE;
        KVector currentVector = null;
        int loopCount = 0;
        double knot = 0.0;

        while (currentAccuracy > accuracy && loopCount < maxRecursion) {
            knot = getZeroXOfControlPoligon(firstDerive);
            currentVector = firstDerive.getPointOnCurve(knot, true);
            currentAccuracy = Math.abs(currentVector.x);
            loopCount++;
        }
        return nubSpline.getPointOnCurve(knot, false);
    }

    /**
     * Returns the t-value of the last vertical point on a NubSpline. 
     * This is the value  
     * 
     * @param nubSpline The spline to check. 
     * @param accuracy The desired accuracy.
     * @param maxRecursion The maximum number of recursions.
     * @return The t-value of the maximum of the spline.
     */
    protected static KVector getLastVerticalPoint(final NubSpline nubSpline, final double accuracy,
            final int maxRecursion) {

        final NubSpline firstDerive = generateInvertedNUBS(generateDerivedNUBS(nubSpline));
        double currentAccuracy = Double.MAX_VALUE;
        KVector currentVector = null;
        int loopCount = 0;
        double knot = 0.0;

        while (currentAccuracy > accuracy && loopCount < maxRecursion) {
            knot = getZeroXOfControlPoligon(firstDerive);
            currentVector = firstDerive.getPointOnCurve(knot, true);
            currentAccuracy = Math.abs(currentVector.x);
            loopCount++;
        }
        final Double maxVal = nubSpline.getKnotVector().get(nubSpline.getKnotVector().size() - 1);
        return nubSpline.getPointOnCurve(maxVal - knot, false);
    }

    /**
     * Returns the t-value of the first horizontal point on a NubSpline.
     * 
     * @param nubSpline The spline to check. 
     * @param accuracy The desired accuracy.
     * @param maxRecursion The maximum number of recursions.
     * @return The t-value of the maximum of the spline.
     */
    protected static KVector getFirstHorizontalPoint(final NubSpline nubSpline, final double accuracy,
            final int maxRecursion) {

        final NubSpline firstDerive = generateDerivedNUBS(nubSpline);
        double currentAccuracy = Double.MAX_VALUE;
        KVector currentVector = null;
        int loopCount = 0;
        double knot = 0.0;

        while (currentAccuracy > accuracy && loopCount < maxRecursion) {
            knot = getZeroYOfControlPoligon(firstDerive);
            currentVector = firstDerive.getPointOnCurve(knot, true);
            currentAccuracy = Math.abs(currentVector.y);
            loopCount++;
        }
        return nubSpline.getPointOnCurve(knot, false);
    }

    /**
     * Returns the t-value of the last horizontal point on a NubSpline.
     * 
     * @param nubSpline The spline to check. 
     * @param accuracy The desired accuracy.
     * @param maxRecursion The maximum number of recursions.
     * @return The t-value of the maximum of the spline.
     */
    protected static KVector getLastHorizontalPoint(final NubSpline nubSpline, final double accuracy,
            final int maxRecursion) {

        final NubSpline firstDerive = generateInvertedNUBS(generateDerivedNUBS(nubSpline));
        double currentAccuracy = Double.MAX_VALUE;
        KVector currentVector = null;
        int loopCount = 0;
        double knot = 0.0;

        while (currentAccuracy > accuracy && loopCount < maxRecursion) {
            knot = getZeroYOfControlPoligon(firstDerive);
            currentVector = firstDerive.getPointOnCurve(knot, true);
            currentAccuracy = Math.abs(currentVector.y);
            loopCount++;
        }
        final Double maxVal = nubSpline.getKnotVector().get(nubSpline.getKnotVector().size() - 1);
        return nubSpline.getPointOnCurve(maxVal - knot, false);
    }

    /**
     * Calculates a point on this NubSpline at the position of value. This version does not modify this
     * Nubs. 
     * There is a limitation on the value
     * to check:
     * <UL>
     * <LI>If the NubSpline is clamped:</LI>
     * <UL>
     * <LI>knotVector[0] <= t <= knotVector[knotVector.size() - 1]</LI>
     * </UL>
     * <LI>If the NubSpline is unclamped:</LI>
     * <UL>
     * <LI>knotVector[dim - 1] <= t <= knotVector[knotVector.size() - dim]</LI>
     * 
     * @param t The position on this NubSpline to calculate. Must be in the range of the knot vector.
     * @return The position on this NubSpline.
     */
    protected KVector getPointOnCurve(final double t) {
        return getPointOnCurve(t, false);
    }

    /**
     * Calculates a point on this NubSpline at the position of value. There is a limitation on the value
     * to check:
     * <UL>
     * <LI>If the NubSpline is clamped:</LI>
     * <UL>
     * <LI>knotVector[0] <= t <= knotVector[knotVector.size() - 1]</LI>
     * </UL>
     * <LI>If the NubSpline is unclamped:</LI>
     * <UL>
     * <LI>knotVector[dim - 1] <= t <= knotVector[knotVector.size() - dim]</LI>
     * 
     * @param t The position on this NubSpline to calculate. Must be in the range of the knot vector.
     * @param modify Determines, if current Nubs is allowed to be modified.
     * @return The position on this NubSpline.
     */
    protected KVector getPointOnCurve(final double t, final boolean modify) {
        final int multiplicity = getMultiplicity(t);
        // If the multiplicity is already equal to dim, there is already a CP at the position t.
        if (multiplicity == dimNUBS) {
            return getControlPoint(getIndexInKnotVector(t));
        }

        // If the multiplicity is less than dim, we have to insert new knots. 
        if (modify) {
            insertKnot(t, dimNUBS - multiplicity);
            return getControlPoint(getIndexInKnotVector(t));
        } else {
            // To not modify this NubSpline, we will do so on a copy.
            final NubSpline copy = new NubSpline(this);
            copy.insertKnot(t, dimNUBS - multiplicity);
            return copy.getControlPoint(copy.getIndexInKnotVector(t));
        }
    }

    /**
     * Returns a progression value (t) of the first zero (Y-value) in the control polygon. 
     * If the polygon starts at zero, this zero is ignored and the next zero is returned. 
     * If there is no zero found, 0.0 is returned.
     * 
     * @param nubSpline The NubSpline those zero of control polygon to calculate.
     * @return The progression value t. min[knotVector] < t <= max[knotVector]
     */
    private static double getZeroYOfControlPoligon(final NubSpline nubSpline) {
        final Iterator<PolarCP> iter = nubSpline.controlPoints.iterator();
        // the CPs we are working with.
        PolarCP firstCP, secondCP;
        // Determines if one of the CPs is bigger than zero.
        boolean firstIsPositive, secondIsPositive;
        // Determines if one of the CPs is smaller than zero.
        boolean firstIsNegative, secondIsNegative;
        // the y values of the CPs we are working with.
        double firstY = 0.0, secondY;

        // initialize
        secondCP = iter.next();
        secondY = secondCP.getCp().y;
        secondIsPositive = secondY > EPSILON;
        secondIsNegative = secondY < -EPSILON;

        while (iter.hasNext()) {
            // Proceed to next line between CPs:
            //update first CP.
            firstCP = secondCP;
            firstY = secondY;
            firstIsPositive = secondIsPositive;
            firstIsNegative = secondIsNegative;

            // update second CP.
            secondCP = iter.next();
            secondY = secondCP.getCp().y;
            secondIsPositive = secondY > EPSILON;
            secondIsNegative = secondY < -EPSILON;

            // Check if there is a zero at the second CP.
            if (!(secondIsPositive || secondIsNegative)) {
                return getTFromPolar(secondCP.getPolarCoordinate());
            }

            // check for zero in the line between the two CPs
            if (firstIsPositive && secondIsNegative || firstIsNegative && secondIsPositive) {
                double factor;
                factor = firstY / (firstY - secondY);
                final double t1 = getTFromPolar(firstCP.getPolarCoordinate());
                final double t2 = getTFromPolar(secondCP.getPolarCoordinate());
                return factor * t1 + (1 - factor) * t2;
            }
        }
        return 0.0;
    }

    /**
     * Returns a progression value (t) of the first zero (X-value) in the control polygon. If the polygon
     * starts at zero, this zero is ignored and the next zero is returned. If there is no zero found, 
     * 0.0 is returned.
     * 
     * @param nubSpline The NubSpline those zero of control polygon to calculate.
     * @return The progression value t. min[knotVector] < t <= max[knotVector]
     */
    private static double getZeroXOfControlPoligon(final NubSpline nubSpline) {
        final Iterator<PolarCP> iter = nubSpline.controlPoints.iterator();
        // the CPs we are working with.
        PolarCP firstCP, secondCP;
        // Determines if one of the CPs is bigger than zero.
        boolean firstIsPositive, secondIsPositive;
        // Determines if one of the CPs is smaller than zero.
        boolean firstIsNegative, secondIsNegative;
        // the x values of the CPs we are working with.
        double firstX = 0.0, secondX;

        // initialize
        secondCP = iter.next();
        secondX = secondCP.getCp().x;
        secondIsPositive = secondX > EPSILON;
        secondIsNegative = secondX < -EPSILON;

        while (iter.hasNext()) {
            // Proceed to next line between CPs:
            // Update first CP.
            firstCP = secondCP;
            firstX = secondX;
            firstIsPositive = secondIsPositive;
            firstIsNegative = secondIsNegative;

            // Update second CP.
            secondCP = iter.next();
            secondX = secondCP.getCp().x;
            secondIsPositive = secondX > EPSILON;
            secondIsNegative = secondX < -EPSILON;

            // Check if there is a zero at the second CP.
            if (!(secondIsPositive || secondIsNegative)) {
                return getTFromPolar(secondCP.getPolarCoordinate());
            }

            // check for zero in the line between the two CPs
            if (firstIsPositive && secondIsNegative || firstIsNegative && secondIsPositive) {
                double factor;
                factor = firstX / (firstX - secondX);
                final double t1 = getTFromPolar(firstCP.getPolarCoordinate());
                final double t2 = getTFromPolar(secondCP.getPolarCoordinate());
                return factor * t1 + (1 - factor) * t2;
            }
        }
        return 0.0;
    }

    // ########################################################################################
    // Bezier

    /**
     * Converts this NubSpline to a bezier spline. All inner knots of the knotVector get the multiplicity
     * of dimNUBS.
     */
    public void toBezier() {
        final ListIterator<Double> iterKnot = knotVector.listIterator();
        final ListIterator<PolarCP> iterCP = controlPoints.listIterator();

        // Unclamped knotVectors have (dim-1) leading and trailing knots that are not repeated and
        // don't need to be repeated.
        // Clamped knotVectors have (dim) leading and trailing knots that are already repeated. We
        // skip them for performance.
        if (isClamped) {
            for (int i = 0; i < dimNUBS; i++) {
                iterKnot.next();
            }
        } else {
            for (int i = 0; i < dimNUBS - 1; i++) {
                iterKnot.next();
                iterKnot.remove();
            }
        }

        double currentKnot = iterKnot.next();
        // Iterate over all knots those multiplicity we possibly have to increase.
        while (maxKnot - currentKnot > EPSILON) {
            final double knotToCount = currentKnot;
            int occurrence = 0;

            // Count occurrences of knotToCount.
            while (Math.abs(currentKnot - knotToCount) < EPSILON) {
                occurrence++;
                currentKnot = iterKnot.next();
                iterCP.next();
            }

            // insert new knots, if multiplicity is not as expected (dimNUBS)
            if (occurrence < dimNUBS) {
                iterKnot.previous();
                insertKnotAtCurrentPosition(dimNUBS - occurrence, knotToCount, iterCP, iterKnot);
                iterKnot.next();
            }

            // Proceed to next elements.
            iterCP.previous();
        }

        if (!isClamped) {
            for (int i = 0; i < dimNUBS - 1; i++) {
                iterKnot.next();
                iterKnot.remove();
            }
        }
        isClamped = true;
        isBezier = true;
    }

    /**
     * Returns all bezier control points needed to create an equivalent bezier spline. If this NubSpline
     * still not holds the requirements for a bezier spline, it is converted to to so.
     * 
     * @param withSourceVector
     *            Specify if the source vector shall be included in the vector chain.
     * @param withTargetVector
     *            Specify if the target vector shall be included in the vector chain.
     * @return The bezier control-points.
     */
    public KVectorChain getBezierCP(final boolean withSourceVector, final boolean withTargetVector) {
        if (!isBezier) {
            toBezier();
        }
        final KVectorChain retVal = new KVectorChain();
        final Iterator<PolarCP> iter = controlPoints.iterator();

        if (!withSourceVector) {
            iter.next();
        }

        while (iter.hasNext()) {
            retVal.add(iter.next().getCp());
        }

        if (!withTargetVector) {
            retVal.removeLast();
        }

        return retVal;
    }

    /**
     * Returns all bezier control points needed to create an equivalent bezier spline. If this NubSpline
     * still not holds the requirements for a bezier spline, it is converted to to so. The source and
     * target vector are not included in the list of control points.
     * 
     * @return The bezier control points without source and target vector.
     */
    public KVectorChain getBezierCP() {
        return getBezierCP(false, false);
    }

    // ########################################################################################
    // Other stuff

    /**
     * Generates NubSpline CPs for a nice curve.
     * @return The nice CPs
     */
    public static NubSpline generateNiceCurve() {
        final KVectorChain niceChain = new KVectorChain();
        // CHECKSTYLEOFF Magic Numbers    
        final KVector vector1 = new KVector(5.5, 23.0);
        final KVector vector2 = new KVector(2.5, 12.0);
        final KVector vector3 = new KVector(5.0, 10.0);
        final KVector vector4 = new KVector(5.0, 9.0);
        final KVector vector5 = new KVector(3.8, 5.5);
        final KVector vector6 = new KVector(7.0, 4.0);
        final KVector vector7 = new KVector(7.0, 3.5);
        final KVector vector8 = new KVector(6.0, 2.2);
        final KVector vector9 = new KVector(8.0, 0.5);
        //CHECKSTYLE.ON: Magic Numbers     

        niceChain.add(vector1);
        niceChain.add(vector1);
        niceChain.add(vector1);
        niceChain.add(vector2);
        niceChain.add(vector3);
        niceChain.add(vector4);
        niceChain.add(vector5);
        niceChain.add(vector6);
        niceChain.add(vector7);
        niceChain.add(vector8);
        niceChain.add(vector9);
        niceChain.add(mirrorOnX(vector8, vector9.x));
        niceChain.add(mirrorOnX(vector7, vector9.x));
        niceChain.add(mirrorOnX(vector6, vector9.x));
        niceChain.add(mirrorOnX(vector5, vector9.x));
        niceChain.add(mirrorOnX(vector4, vector9.x));
        niceChain.add(mirrorOnX(vector3, vector9.x));
        niceChain.add(mirrorOnX(vector2, vector9.x));
        niceChain.add(mirrorOnX(vector1, vector9.x));
        niceChain.add(mirrorOnX(vector1, vector9.x));
        niceChain.add(mirrorOnX(vector1, vector9.x));

        niceChain.scale(5.0);
        niceChain.offset(-50.0, -80.0);

        // Create the new NubSpline.
        return new NubSpline(true, 3, niceChain);
    }

    /**
     * Creates a copy of the original KVector, mirrored on a line laying on the given x-value.
     * @param original The original KVector.
     * @param xVal The mirror, laying parallel to the y-axis, at the given x-value.
     * @return
     */
    private static KVector mirrorOnX(final KVector original, final double xVal) {
        return new KVector(xVal + xVal - original.x, original.y);
    }

    /**
     * {@inheritDoc}
     */
    public String toString() {
        return controlPoints.toString();
    }

    // ########################################################################################
    // Class for polar form of control-points.

    /**
     * Represents a control point of this NubSpline including it's polar coordinate.
     * 
     * @author tit
     * 
     */
    protected static final class PolarCP {
        /** The control point. */
        private KVector cp;
        /** The polar coordinate. (size is always = dim) */
        private List<Double> polarCoordinate;

        /**
         * Constructs a new PolarCP from the given parameters.
         * 
         * @param controlPoint The control-point of the PolarCP.
         * @param polarCoordinate The polar-coordinate of the PolarCP.
         */
        PolarCP(final KVector controlPoint, final List<Double> polarCoordinate) {
            setCp(controlPoint.clone());
            setPolarCoordinate(Lists.newLinkedList(polarCoordinate));
        }

        /**
         * Copy constructor. All fields are created newly.
         * 
         * @param polarCP The PolarCP to copy.
         */
        PolarCP(final PolarCP polarCP) {
            setCp(polarCP.getCp().clone());
            setPolarCoordinate(Lists.newLinkedList(polarCP.getPolarCoordinate()));
        }

        /**
         * This Constructor is calculating the new PolarCP from two given once. There are
         * requirements on the polarCoordinates that must be met, but are not checked (for
         * performance):
         * <ul>
         * <li>firstCP.polarCoordinates = (xW)</li>
         * <li>secondCP.polarCoordinates = (Wy)</li>
         * <li>resulting polarCoordinate = (Wz)</li>
         * </ul>
         * Also (Wz) is properly ordered.
         * 
         * @param firstCP First polarCP.
         * @param secondCP Second polarCP.
         * @param newKnot The new knotValue.
         */
        PolarCP(final PolarCP firstCP, final PolarCP secondCP, final double newKnot) {
            final double firstFactor = firstCP.polarCoordinate.iterator().next();
            final double secondFactor = Iterables.getLast(secondCP.polarCoordinate);

            // vectorC = ((b-c) * vectorA + (c-a) * vectorB) / (b-a)
            final KVector aScaled = firstCP.cp.clone().scale(secondFactor - newKnot);
            final KVector bScaled = secondCP.cp.clone().scale(newKnot - firstFactor);
            final KVector total = aScaled.add(bScaled);
            total.scale(1.0 / (secondFactor - firstFactor));

            cp = total;
            polarCoordinate = Lists.newArrayList();

            // Specifies, if the newKnot still needs to be added.
            boolean needsToBeAdded = true;

            // From the firstCP, we will take the W. (See method comment)
            final Iterator<Double> iter = firstCP.polarCoordinate.iterator();
            iter.next();
            while (iter.hasNext()) {
                final double nextKnot = iter.next();
                if (needsToBeAdded && nextKnot - newKnot > EPSILON) {
                    polarCoordinate.add(newKnot);
                    needsToBeAdded = false;
                }
                polarCoordinate.add(nextKnot);
            }
            if (needsToBeAdded) {
                polarCoordinate.add(newKnot);
            }
        }

        /**
         * Returns the current control-point. May be modified.
         * 
         * @return The control-point.
         */
        public KVector getCp() {
            return cp;
        }

        /**
         * Sets the control-point. Vector is not copied!
         * 
         * @param cp
         *            The new control-point.
         */
        public void setCp(final KVector cp) {
            this.cp = cp;
        }

        /**
         * Returns the polar-coordinate.
         * 
         * @return The polar-coordinate. May be modified.
         */
        public List<Double> getPolarCoordinate() {
            return polarCoordinate;
        }

        /**
         * Returns the polar-coordinate.
         * 
         * @return The polar-coordinate. May be modified.
         */
        public void setPolarCoordinate(final List<Double> polarCoordinate) {
            this.polarCoordinate = polarCoordinate;
        }

        // GWTExcludeStart
        /**
         * {@inheritDoc}
         */
        public String toString() {
            return polarCoordinate + " " + SplinesMath.convertKVectorToString(cp);
        }
        // GWTExcludeEnd
    }
}