API Docs for:
Show:

File: /home/padewitte/projets/webcomponents/myscript-js/src/rendering/shapeRenderer.js

'use strict';

(function (scope) {
    /**
     * Represent the Shape Renderer. It's used to calculate the shape ink rendering in HTML5 canvas
     *
     * @class ShapeRenderer
     * @extends AbstractRenderer
     * @param {Object} context
     * @constructor
     */
    function ShapeRenderer(context) {
        scope.AbstractRenderer.call(this, context);
    }

    /**
     * Inheritance property
     */
    ShapeRenderer.prototype = new scope.AbstractRenderer();

    /**
     * Constructor property
     */
    ShapeRenderer.prototype.constructor = ShapeRenderer;

    /**
     * Draw shape recognition result on HTML5 canvas
     *
     * @method drawRecognitionResult
     * @param {AbstractComponent[]} components
     * @param {ShapeDocument} document
     */
    ShapeRenderer.prototype.drawRecognitionResult = function (components, document) {
        this.clear();
        if (document && (document instanceof scope.ShapeDocument)) {
            this.drawShapes(components, document.getSegments());
            var lastComponents = [];
            var processedComponents = _extractComponents(components, document.getInkRanges());

            for (var i in components) {
                var component = components[i];
                if (processedComponents.indexOf(component) !== -1) {
                    lastComponents.push(component);
                }
            }
            this.drawComponents(lastComponents);
        } else {
            this.drawComponents(components);
        }
        return {components : components, document : document}
    };

    /**
     * Draw components
     *
     * @method drawComponents
     * @param {AbstractComponent[]} components
     */
    ShapeRenderer.prototype.drawComponents = function (components) {
        for (var i in components) {
            var component = components[i];
            if (component instanceof scope.AbstractShapePrimitive) {
                _drawShapePrimitive(component, this.getContext(), this.getParameters());
            } else if (component instanceof scope.AbstractComponent) {
                scope.AbstractRenderer.prototype.drawComponent.call(this, component); // super
            } else {
                throw new Error('not implemented');
            }
        }
    };

    /**
     * Draw the shapes
     *
     * @method drawShapes
     * @param {AbstractComponent[]} components
     * @param {ShapeSegment[]} shapes
     */
    ShapeRenderer.prototype.drawShapes = function (components, shapes) {
        for (var i in shapes) {
            this.drawShapeSegment(components, shapes[i]);
        }
    };

    /**
     * Draw shape segment
     *
     * @method drawShapeSegment
     * @param {AbstractComponent[]} components
     * @param {ShapeSegment} segment
     */
    ShapeRenderer.prototype.drawShapeSegment = function (components, segment) {
        var candidate = segment.getSelectedCandidate();
        if (candidate instanceof scope.ShapeRecognized) {
            _drawShapeRecognized(candidate, this.getContext(), this.getParameters());
        } else if (candidate instanceof scope.ShapeNotRecognized) {
            this.drawComponents(_extractComponents(components, segment.getInkRanges()));
        } else {
            throw new Error('not implemented');
        }
    };

    /**
     * This method allow you to draw not recognized shape
     *
     * @method drawShapeNotRecognized
     * @param {AbstractComponent[]} components
     * @param {ShapeInkRange[]} inkRanges
     */
    ShapeRenderer.prototype.drawShapeNotRecognized = function (components, inkRanges) {
        this.drawComponents(_extractComponents(components, inkRanges));
    };

    /**
     * Draw shape primitive
     *
     * @method drawShapePrimitive
     * @param {AbstractShapePrimitive} primitive
     */
    ShapeRenderer.prototype.drawShapePrimitive = function (primitive) {
        _drawShapePrimitive(primitive, this.getContext(), this.getParameters());
    };

    /**
     * This method allow you to draw recognized shape
     *
     * @private
     * @method _drawShapeRecognized
     * @param {ShapeRecognized} shapeRecognized
     * @param {Object} context
     * @param {PenParameters} parameters
     */
    var _drawShapeRecognized = function (shapeRecognized, context, parameters) {
        for (var i in shapeRecognized.getPrimitives()) {
            _drawShapePrimitive(shapeRecognized.getPrimitives()[i], context, parameters);
        }
    };

    /**
     * Draw shape primitive
     *
     * @private
     * @method _drawShapePrimitive
     * @param {AbstractShapePrimitive} primitive
     * @param {Object} context
     * @param {PenParameters} parameters
     */
    var _drawShapePrimitive = function (primitive, context, parameters) {
        if (primitive instanceof scope.ShapeEllipse) {
            _drawShapeEllipse(primitive, context, parameters);
        } else if (primitive instanceof scope.ShapeLine) {
            _drawShapeLine(primitive, context, parameters);
        } else {
            throw new Error('Primitive not implemented: ' + primitive.getType());
        }
    };

    /**
     * Draw shape line
     *
     * @private
     * @method _drawShapeLine
     * @param {ShapeLine} shapeLine
     * @param {Object} context
     * @param {PenParameters} parameters
     */
    var _drawShapeLine = function (shapeLine, context, parameters) {
        _drawLine(shapeLine.getFirstPoint(), shapeLine.getLastPoint(), context, parameters);
        if (shapeLine.hasBeginDecoration() && shapeLine.getBeginDecoration() === 'ARROW_HEAD') {
            _drawArrowHead(shapeLine.getFirstPoint(), shapeLine.getBeginTangentAngle(), 12.0, context, parameters);
        }
        if (shapeLine.hasEndDecoration() && shapeLine.getEndDecoration() === 'ARROW_HEAD') {
            _drawArrowHead(shapeLine.getLastPoint(), shapeLine.getEndTangentAngle(), 12.0, context, parameters);
        }
    };

    /**
     * Draw shape ellipse
     *
     * @private
     * @method _drawShapeEllipse
     * @param {ShapeEllipse} shapeEllipse
     * @param {Object} context
     * @param {PenParameters} parameters
     */
    var _drawShapeEllipse = function (shapeEllipse, context, parameters) {
        var points = _drawEllipseArc(
            shapeEllipse.getCenter(),
            shapeEllipse.getMaxRadius(),
            shapeEllipse.getMinRadius(),
            shapeEllipse.getOrientation(),
            shapeEllipse.getStartAngle(),
            shapeEllipse.getSweepAngle(),
            context, parameters);

        if (shapeEllipse.hasBeginDecoration() && shapeEllipse.getBeginDecoration() === 'ARROW_HEAD') {
            _drawArrowHead(points[0], shapeEllipse.getBeginTangentAngle(), 12.0, context, parameters);
        }
        if (shapeEllipse.hasEndDecoration() && shapeEllipse.getEndDecoration() === 'ARROW_HEAD') {
            _drawArrowHead(points[1], shapeEllipse.getEndTangentAngle(), 12.0, context, parameters);
        }
    };

    /**
     * Draw an ellipse arc on context
     *
     * @private
     * @method _drawEllipseArc
     * @param {Point} centerPoint
     * @param {Number} maxRadius
     * @param {Number} minRadius
     * @param {String} orientation
     * @param {Number} startAngle
     * @param {Number} sweepAngle
     * @param {Object} context The canvas 2d context
     * @param {PenParameters} parameters
     * @returns {Point[]}
     */
    var _drawEllipseArc = function (centerPoint, maxRadius, minRadius, orientation, startAngle, sweepAngle, context, parameters) {

        var angleStep = 0.02; // angle delta between interpolated

        var z1 = Math.cos(orientation);
        var z3 = Math.sin(orientation);
        var z2 = z1;
        var z4 = z3;
        z1 *= maxRadius;
        z2 *= minRadius;
        z3 *= maxRadius;
        z4 *= minRadius;

        var n = Math.floor(Math.abs(sweepAngle) / angleStep);

        var boundariesPoints = [];

        context.save();
        try {
            context.fillStyle = parameters.getColor();
            context.strokeStyle = parameters.getColor();
            context.lineWidth = 0.5 * parameters.getWidth();

            context.beginPath();

            for (var i = 0; i <= n; i++) {

                var angle = startAngle + (i / n) * sweepAngle; // points on the arc, in radian
                var alpha = Math.atan2(Math.sin(angle) / minRadius, Math.cos(angle) / maxRadius);

                var cosAlpha = Math.cos(alpha);
                var sinAlpha = Math.sin(alpha);

                // current point
                var x = centerPoint.x + z1 * cosAlpha - z4 * sinAlpha;
                var y = centerPoint.y + z2 * sinAlpha + z3 * cosAlpha;
                if (i === 0) {
                    context.moveTo(x, y);
                } else {
                    context.lineTo(x, y);
                }

                if (i === 0 || i === n) {
                    boundariesPoints.push(new scope.Point({x: x, y: y}));
                }
            }

            context.stroke();

        } finally {
            context.restore();
        }

        return boundariesPoints;
    };

    /**
     * Draw a line on context
     *
     * @private
     * @method _drawLine
     * @param {Point} p1
     * @param {Point} p2
     * @param {Object} context The canvas 2d context
     * @param {PenParameters} parameters
     */
    var _drawLine = function (p1, p2, context, parameters) {
        context.save();
        try {
            context.fillStyle = parameters.getColor();
            context.strokeStyle = parameters.getColor();
            context.lineWidth = 0.5 * parameters.getWidth();

            context.beginPath();
            context.moveTo(p1.getX(), p1.getY());
            context.lineTo(p2.getX(), p2.getY());
            context.stroke();
        } finally {
            context.restore();
        }
    };

    /**
     * Clamp an angle into the range [-PI, +PI]
     *
     * @private
     * @method _phi
     * @param {Number} angle
     * @returns {Number}
     */
    var _phi = function (angle) {
        angle = ((angle + Math.PI) % (Math.PI * 2)) - Math.PI;
        if (angle < -Math.PI) {
            angle += Math.PI * 2;
        }
        return angle;
    };

    /**
     * Draw an arrow head on context
     *
     * @private
     * @method _drawArrowHead
     * @param {Point} headPoint
     * @param {Number} angle
     * @param {Number} length
     * @param {Object} context The canvas 2d context
     * @param {PenParameters} parameters
     */
    var _drawArrowHead = function (headPoint, angle, length, context, parameters) {
        var alpha = _phi(angle + Math.PI - (Math.PI / 8)),
            beta = _phi(angle - Math.PI + (Math.PI / 8));

        context.save();
        try {
            context.fillStyle = parameters.getColor();
            context.strokeStyle = parameters.getColor();
            context.lineWidth = 0.5 * parameters.getWidth();

            context.moveTo(headPoint.getX(), headPoint.getY());
            context.beginPath();
            context.lineTo(headPoint.getX() + (length * Math.cos(alpha)), headPoint.getY() + (length * Math.sin(alpha)));
            context.lineTo(headPoint.getX() + (length * Math.cos(beta)), headPoint.getY() + (length * Math.sin(beta)));
            context.lineTo(headPoint.getX(), headPoint.getY());
            context.fill();

        } finally {
            context.restore();
        }

    };

    /**
     * Return components from ink ranges
     *
     * @private
     * @param components
     * @param inkRanges
     * @returns {AbstractComponent[]}
     */
    var _extractComponents = function (components, inkRanges) {
        var result = [];

        for (var i in inkRanges) {
            var inkRange = inkRanges[i];

            var firstPointIndex = Math.floor(inkRange.getFirstPoint());
            var lastPointIndex = Math.ceil(inkRange.getLastPoint());

            for (var strokeIndex = inkRange.getFirstStroke(); strokeIndex <= inkRange.getLastStroke(); strokeIndex++) {
                var currentStroke = components[strokeIndex];
                var currentStrokePointCount = currentStroke.getX().length;

                var newStroke = new scope.StrokeComponent();
                newStroke.setColor(currentStroke.getColor());
                newStroke.setWidth(currentStroke.getWidth());

                for (var pointIndex = firstPointIndex; (strokeIndex === inkRange.getLastStroke() && pointIndex <= lastPointIndex && pointIndex < currentStrokePointCount) || (strokeIndex !== inkRange.getLastStroke() && pointIndex < currentStrokePointCount); pointIndex++) {
                    newStroke.addPoint(currentStroke.getX()[pointIndex], currentStroke.getY()[pointIndex], currentStroke.getT()[pointIndex]);
                }
                result.push(newStroke);
            }
        }
        return result;

    };

    // Export
    scope.ShapeRenderer = ShapeRenderer;
})(MyScript);