// @ts-check /** * The shape of a stroke, shared by everything that draws one: the screen * (renderer.js) and the page file (page_file.js). * * A stroke is a chain of *parts*, one per point. The part for a point runs * from the midpoint before it to the midpoint after it, curving through the * point, with a width taken from the pen pressure at that point. At the * ends of the stroke the part runs to the end point itself. */ import { POINT_SIZE, pointCount } from './page_model.js'; /** @typedef {import('./page_model.js').Stroke} Stroke */ /** * @typedef {object} Part * @property {number} fromX * @property {number} fromY * @property {number} x The point itself, the control point of the curve. * @property {number} y * @property {number} toX * @property {number} toY * @property {number} width * @property {boolean} dot The stroke is a single point: a filled circle * of diameter `width` at (x, y). */ /** * The drawn width of a stroke at one of its points. The stroke's nominal * width is drawn at pressure 0.4, about the pressure of ordinary writing * (Andy's averaged 0.34 to 0.45 on a Cintiq 16 and rarely passed 0.8). A * firm press, 0.75, is about 2.6 times as wide. * @param {Stroke} stroke * @param {number} index Point index. */ export function widthAt(stroke, index) { const pressure = stroke.points[index * POINT_SIZE + 2]; return stroke.width * (0.36 + 4 * pressure * pressure); } /** * The part of a stroke that belongs to point `k`. * @param {Stroke} stroke * @param {number} k * @returns {Part} */ export function partAt(stroke, k) { const p = stroke.points; const last = pointCount(stroke) - 1; const x = p[k * POINT_SIZE]; const y = p[k * POINT_SIZE + 1]; return { fromX: k === 0 ? x : (p[(k - 1) * POINT_SIZE] + x) / 2, fromY: k === 0 ? y : (p[(k - 1) * POINT_SIZE + 1] + y) / 2, x, y, toX: k === last ? x : (x + p[(k + 1) * POINT_SIZE]) / 2, toY: k === last ? y : (y + p[(k + 1) * POINT_SIZE + 1]) / 2, width: widthAt(stroke, k), dot: last === 0, }; }