src/renderer/canvas/stroker/QuadraticCanvasStroker.js
import * as StrokeComponent from '../../../model/StrokeComponent';
import { computeLinksPoints, computeMiddlePoint, computeAxeAngle } from '../../QuadraticUtils';
/**
* Stroker info
* @typedef {Object} StrokerInfo
* @property {String} type Renderer type.
* @property {String} name Stroker name.
* @property {String} apiVersion Supported api version.
*/
/**
* Define how a stroke should be drawn
* @typedef {Object} Stroker
* @property {function(): StrokerInfo} getInfo Get some information about this stroker
* @property {function(context: Object, stroke: Stroke)} drawStroke Render a stroke on the current context.
*/
/**
* Get info
* @return {StrokerInfo} Information about this stroker
*/
export function getInfo() {
return {
type: 'canvas',
name: 'quadratic',
apiVersion: 'V3'
};
}
function renderArc(context, center, radius) {
context.arc(center.x, center.y, radius, 0, Math.PI * 2, true);
}
function renderLine(context, begin, end, width) {
const linkPoints1 = computeLinksPoints(begin, computeAxeAngle(begin, end), width);
const linkPoints2 = computeLinksPoints(end, computeAxeAngle(begin, end), width);
context.moveTo(linkPoints1[0].x, linkPoints1[0].y);
context.lineTo(linkPoints2[0].x, linkPoints2[0].y);
context.lineTo(linkPoints2[1].x, linkPoints2[1].y);
context.lineTo(linkPoints1[1].x, linkPoints1[1].y);
}
function renderFinal(context, begin, end, width) {
const ARCSPLIT = 6;
const angle = computeAxeAngle(begin, end);
const linkPoints = computeLinksPoints(end, angle, width);
context.moveTo(linkPoints[0].x, linkPoints[0].y);
for (let i = 1; i <= ARCSPLIT; i++) {
const newAngle = angle - ((i * Math.PI) / ARCSPLIT);
context.lineTo(end.x - ((end.p * width) * Math.sin(newAngle)), end.y + (end.p * width * Math.cos(newAngle)));
}
}
function renderQuadratic(context, begin, end, ctrl, width) {
const linkPoints1 = computeLinksPoints(begin, computeAxeAngle(begin, ctrl), width);
const linkPoints2 = computeLinksPoints(end, computeAxeAngle(ctrl, end), width);
const linkPoints3 = computeLinksPoints(ctrl, computeAxeAngle(begin, end), width);
context.moveTo(linkPoints1[0].x, linkPoints1[0].y);
context.quadraticCurveTo(linkPoints3[0].x, linkPoints3[0].y, linkPoints2[0].x, linkPoints2[0].y);
context.lineTo(linkPoints2[1].x, linkPoints2[1].y);
context.quadraticCurveTo(linkPoints3[1].x, linkPoints3[1].y, linkPoints1[1].x, linkPoints1[1].y);
}
/**
* Draw a stroke on a canvas, using quadratics
* @param {Object} context Current rendering context
* @param {Stroke} stroke Current stroke to be drawn
*/
export function drawStroke(context, stroke) {
const contextReference = context;
const length = stroke.x.length;
const width = stroke.width > 0 ? stroke.width : contextReference.lineWidth;
const color = stroke.color ? stroke.color : contextReference.strokeStyle;
const firstPoint = StrokeComponent.getPointByIndex(stroke, 0);
const nbquadratics = length - 2;
contextReference.save();
try {
contextReference.beginPath();
if (length < 3) {
renderArc(contextReference, firstPoint, width * 0.6);
} else {
renderArc(contextReference, firstPoint, width * firstPoint.p);
renderLine(contextReference, firstPoint, computeMiddlePoint(firstPoint, StrokeComponent.getPointByIndex(stroke, 1)), width);
// Possibility to try this (the start looks better when the ink is large)
// var first = computeMiddlePoint(stroke[0], stroke[1]);
// contextReference.arc(first.x, first.y, width * first.p, 0, Math.PI * 2, true);
for (let i = 0; i < nbquadratics; i++) {
renderQuadratic(contextReference, computeMiddlePoint(StrokeComponent.getPointByIndex(stroke, i), StrokeComponent.getPointByIndex(stroke, i + 1)), computeMiddlePoint(StrokeComponent.getPointByIndex(stroke, i + 1), StrokeComponent.getPointByIndex(stroke, i + 2)), StrokeComponent.getPointByIndex(stroke, i + 1), width);
}
renderLine(contextReference, computeMiddlePoint(StrokeComponent.getPointByIndex(stroke, length - 2), StrokeComponent.getPointByIndex(stroke, length - 1)), StrokeComponent.getPointByIndex(stroke, length - 1), width);
renderFinal(contextReference, StrokeComponent.getPointByIndex(stroke, length - 2), StrokeComponent.getPointByIndex(stroke, length - 1), width);
}
contextReference.closePath();
if (color !== undefined) {
contextReference.fillStyle = color;
contextReference.fill();
}
} finally {
contextReference.restore();
}
}