package medusa.georgios.color_factory;

import medusa.georgios.vecmathpackage.Point3f;
import medusa.georgios.vecmathpackage.Tuple3f;
import medusa.georgios.vecmathpackage.Vector3f;



/** 
 *    Part of SRS 3D, a system for viewing macromolecules (http://srs3d.org)
 *    
 *    Copyright (C) 2007-2008
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see http://www.gnu.org/licenses/.
 */


/**
 * Provides capabilities to compute a cubic bezier segement.
 *
 * @author Karsten Klein
 *
 * @created January 20, 2001
 */
public final class CubicBezierSegment3f {

    /** The set of control nodes used to describe the segement */
    private Point3f[] nodes;

    /**
     * The default constructor creates and initialiazes the control nodes.
     */
    public CubicBezierSegment3f() {
        nodes = new Point3f[4];
        nodes[0] = new Point3f();
        nodes[1] = new Point3f();
        nodes[2] = new Point3f();
        nodes[3] = new Point3f();
    }

    /**
     * Sets the specified note to point .
     *
     * @param point the new point.
     * @param index the index identifying which node to replace.
     */
    public final void setNode(Tuple3f point, int index) {
        nodes[index].set(point);
    }

    /**
     * This method applies a tangent to the first node and thereby modifies a inner
     * control node.
     *
     * @param tangent the tangent to apply.
     */
    public final void setInitialTangent(Tuple3f tangent) {
        Vector3f connect = new Vector3f(tangent);
        connect.scaleAdd(3, nodes[0]);
        nodes[1].set(connect);
    }

    /**
     * This method applies a tangent to the last node and thereby modifies a inner
     * control node.
     *
     * @param tangent the tangent to apply.
     */
    public final void setFinalTangent(Tuple3f tangent) {
        Vector3f connect = new Vector3f(tangent);
        connect.scaleAdd(-3, nodes[3]);
        nodes[2].set(connect);
    }

    /**
     * This method computes the internal node on the base of special criteria.
     *
     * @param prePoint the point that is assumed to be a proper coordinate for
     *        extrapolation beyond parameter 0. Can be null .
     * @param postPoint the point that is assumed to be a proper coordinate for
     *        extrapolation beyond parameter 1.Can be null .
     * @param tangentScale factor to control the implicitly computed tangent length.
     */
    public final void computeInterpolation(Tuple3f prePoint, Tuple3f postPoint,
        float tangentScale) {
        Vector3f tangent0 = null;
        Vector3f tangent1 = null;
        Vector3f dummy = new Vector3f();

        // compute tangent if prePoint is available
        if (prePoint != null) {
            tangent0 = new Vector3f(nodes[3]);
            tangent0.sub(prePoint);
            tangent0.scale(tangentScale);
        }

        // compute tangent if postPoint is available
        if (postPoint != null) {
            tangent1 = new Vector3f(postPoint);
            tangent1.sub(nodes[0]);
            tangent1.scale(tangentScale);
        }

        // compute control nodes appropriate to tangent
        if (tangent0 != null) {
            nodes[1].set(tangent0);
            nodes[1].add(nodes[0]);
        }
        if (tangent1 != null) {
            nodes[2].set(tangent1);
            nodes[2].negate();
            nodes[2].add(nodes[3]);
        }

        // step throug special cases when at least one tangent is not available
        if (tangent0 == null || tangent1 == null) {

            // both tangents are not at our disposal
            if (tangent0 == null && tangent1 == null) {
                Vector3f axis = new Vector3f(nodes[3]);
                axis.sub(nodes[0]);
                axis.normalize();

                // in this case the cubic bezier becomes a line that linearly
                // interpolates between the outer control nodes.
                nodes[1].set(axis);
                nodes[1].scale(0.25f);
                nodes[1].add(nodes[0]);
                nodes[2].set(axis);
                nodes[2].scale(0.75f);
                nodes[2].add(nodes[0]);
            } else {

                // compute default tangents for the missing one
                if (tangent0 == null) {
                    nodes[1].set(nodes[2]);
                    nodes[1].add(nodes[0]);
                    nodes[1].scale(0.5f);
                }
                if (tangent1 == null) {
                    nodes[2].set(nodes[1]);
                    nodes[2].add(nodes[3]);
                    nodes[2].scale(0.5f);
                }
            }
        }
    }

    /**
     * Computes the point coordinate to the according parameter t .
     *
     * @param t the parameter for the computation.
     *
     * @return Point3f - the computed point coordinate.
     */
    public final Point3f computePoint(float t) {
        float minusT = 1.0f - t;

        // compute blend functions
        float blend0 = minusT * minusT;
        float blend3 = t * t;
        float blend1 = 3 * t * blend0;
        float blend2 = 3 * minusT * blend3;
        blend0 *= minusT;
        blend3 *= t;
        Point3f point = new Point3f();
        point.x = nodes[0].x * blend0;
        point.x += nodes[1].x * blend1;
        point.x += nodes[2].x * blend2;
        point.x += nodes[3].x * blend3;
        point.y = nodes[0].y * blend0;
        point.y += nodes[1].y * blend1;
        point.y += nodes[2].y * blend2;
        point.y += nodes[3].y * blend3;
        point.z = nodes[0].z * blend0;
        point.z += nodes[1].z * blend1;
        point.z += nodes[2].z * blend2;
        point.z += nodes[3].z * blend3;
        return point;
    }

    /**
     * Computes the tangent to the segment at specified paramter t .
     *
     * @param t the parameter for the computation.
     *
     * @return Vector3f - the computed tangent.
     */
    public final Vector3f computeTangent(float t) {
        float oneMinusT = 1.0f - t;
        Point3f point0 = new Point3f(nodes[0]);
        Point3f point1 = new Point3f(nodes[1]);
        Vector3f point2 = new Vector3f(nodes[2]);
        point0.scale(oneMinusT);
        point1.scale(oneMinusT);
        point2.scale(oneMinusT);
        point0.x += t * nodes[1].x;
        point0.y += t * nodes[1].y;
        point0.z += t * nodes[1].z;
        point1.x += t * nodes[2].x;
        point1.y += t * nodes[2].y;
        point1.z += t * nodes[2].z;
        point2.x += t * nodes[3].x;
        point2.y += t * nodes[3].y;
        point2.z += t * nodes[3].z;
        point0.scale(oneMinusT);
        point2.scale(t);
        point0.x += t * point1.x;
        point0.y += t * point1.y;
        point0.z += t * point1.z;
        point2.x += oneMinusT * point1.x;
        point2.y += oneMinusT * point1.y;
        point2.z += oneMinusT * point1.z;
        point2.sub(point0);
        return point2;
    }

    /**
     * This method computes the point of the segment assuming parameter = 0.5f
     *
     * @return Point3f - computed point.
     */
    public final Point3f computeHalfwayPoint() {
        Point3f point = new Point3f(nodes[0]);
        point.scale(0.125f);
        Point3f immediate = new Point3f(nodes[1]);
        immediate.scale(0.375f);
        point.add(immediate);
        immediate.set(nodes[2]);
        immediate.scale(0.375f);
        point.add(immediate);
        immediate.set(nodes[3]);
        immediate.scale(0.125f);
        point.add(immediate);
        return point;
    }
}

 

Ads help maintain this website.