# Source: core/geometry/cubic-bezier.js

    /**
     * @module montage/core/geometry/cubic-bezier
     * @requires montage/core/core
     * @requires montage/core/geometry/point
     */
    var Montage = require("../core").Montage;
    var Point = require("./point").Point;

    /**
     * @class CubicBezier
     * @extends Montage
     */
    var CubicBezier = exports.CubicBezier = Montage.specialize( /** @lends CubicBezier# */{

        /**
         * @function
         * @param {Array} controlPoints Control points.
         * @returns itself
         */
        init: {
            enumerable: false,
            value: function (controlPoints) {
                if (controlPoints !== null) {
                    if (controlPoints.length === 2) {
                        this.p1 = controlPoints[0];
                        this.p2 = controlPoints[1];
                    } else if (controlPoints.length === 4) {
                        this.p0 = controlPoints[0];
                        this.p1 = controlPoints[1];
                        this.p2 = controlPoints[2];
                        this.p3 = controlPoints[3];
                    }
                }
                return this;
            }
        },

        /**
         * @function
         * @param {number} t Control point.
         * @returns itself or `new Point().init(this.p0.x * b1 + this.p1.x * b2 +
         * this.p2.x * b3 + this.p3.x * b4, this.p0.y * b1 + this.p1.y * b2 +
         * this.p2.y * b3 + this.p3.y * b4)`
        */
        position: {
            enumerable: false,
            value: function (t) {
                if (t < 0 || t > 1) {
                    return;
                }

                t = 1 - t;

                var b1 = t * t * t,
                    b2 = 3 * t * t * (1 - t),
                    b3 = 3 * t * (1 - t) * (1 - t),
                    b4 = (1 - t) * (1 - t) * (1 - t);
                return new Point().init(this.p0.x * b1 + this.p1.x * b2 + this.p2.x * b3 + this.p3.x * b4,
                    this.p0.y * b1 + this.p1.y * b2 + this.p2.y * b3 + this.p3.y * b4);
            }
        },

        /**
         * @function
         * @param {number} t Control point.
         * @returns CubicBezier.create(CubicBezier).init([this.p0, this.p01, this.p012, this.p0123])
         */
        split: {
            enumerable: false,
            value: function (t) {
                this.makeScaffolding(t);
                return (new CubicBezier()).init([this.p0, this.p01, this.p012, this.p0123]);
            }
        },

        /**
         * @function
         * @param {number} t Control point.
         * @returns `CubicBezier.create(CubicBezier).init([new
         * Point().init(this.p01.x / xScale, this.p01.y / yScale), new
         * Point().init(this.p012.x / xScale, this.p012.y / yScale)])`
         */
        splitToTimingFunction: {
            enumerable: false,
            value: function (t) {
                this.makeScaffolding(t);
                // p0123 x and y are the scale
                var xScale = this.p0123.x,
                    yScale = this.p0123.y;
                return (new CubicBezier()).init([new Point().init(this.p01.x / xScale, this.p01.y / yScale), new Point().init(this.p012.x / xScale, this.p012.y / yScale)]);
            }
        },

        /**
         * @function
         * @param {number} t Control point.
         */
        makeScaffolding: {
            enumerable: false,
            value: function (t) {

                t = 1 - t;

                var precision = 1000000;
                Montage.defineProperty(this, 'p01', {
                    value: Point.interpolate(t, this.p0, this.p1, precision)
                });
                Montage.defineProperty(this, 'p12', {
                    value: Point.interpolate(t, this.p1, this.p2, precision)
                });
                Montage.defineProperty(this, 'p23', {
                    value: Point.interpolate(t, this.p2, this.p3, precision)
                });
                Montage.defineProperty(this, 'p012', {
                    value: Point.interpolate(t, this.p01, this.p12, precision)
                });
                Montage.defineProperty(this, 'p123', {
                    value: Point.interpolate(t, this.p12, this.p23, precision)
                });
                Montage.defineProperty(this, 'p0123', {
                    value: Point.interpolate(t, this.p012, this.p123, precision)
                });
            }
        },

        /**
         * First control point in bezier curve.
         * @type {Property}
         * @default {number} new Point().init(0, 0)
         */
        p0: {
            enumerable: true,
            value: new Point().init(0, 0)
        },

        /**
         * Second control point in bezier curve.
         * @type {Property}
         * @default {number} new Point().init(0, 0)
         */
        p1: {
            enumerable: true,
            value: new Point().init(0, 0)
        },

        /**
         * Third control point in bezier curve.
         * @type {Property}
         * @default {number} new Point().init(1, 1)
         */
        p2: {
            enumerable: true,
            value: new Point().init(1, 1)
        },

        /**
         * Fourth control point in bezier curve.
         * @type {Property}
         * @default {number}M ontage.create(Point).init(1, 1)
         */
        p3: {
            enumerable: true,
            value: new Point().init(1, 1)
        }

    });