Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions src/type/p5.Font.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,108 @@ class Font {
return renderer.textBounds(str, x, y, width, height);
}

/**
* Returns a flat array of path commands that describe the outlines of a string of text.
*
* Each command is represented as an array of the form `[type, ...coords]`, where:
* - `type` is one of `'M'`, `'L'`, `'Q'`, `'C'`, or `'Z'`,
* - `coords` are the numeric values needed for that command.
*
* `'M'` indicates a "move to" (starting a new contour),
* `'L'` a line segment,
* `'Q'` a quadratic bezier,
* `'C'` a cubic bezier, and
* `'Z'` closes the current path.
*
* The first two parameters, `x` and `y`, specify the baseline origin for the text.
* Optionally, you can provide a `width` and `height` for text wrapping; if you don't need
* wrapping, you can omit them and directly pass `options` as the fourth parameter.
*
* @param {String} str The text to convert into path commands.
* @param {Number} x x‐coordinate of the text baseline.
* @param {Number} y y‐coordinate of the text baseline.
* @param {Number} [width] Optional width for text wrapping.
* @param {Number} [height] Optional height for text wrapping.
* @param {Object} [options] Configuration object for rendering text.
* @return {Array<Array>} A flat array of path commands.
*
* @example
* <div>
* <code>
* let font;
*
* async function setup() {
* font = await loadFont('assets/inconsolata.otf');
* createCanvas(200, 200);
* background(220);
* noLoop();
* }
*
* function draw() {
* background(220);
* stroke(0);
* noFill();
* textSize(60);
*
* // Get path commands for "Hello" (drawn at baseline x=50, y=100):
* const pathCommands = font.textToPaths('Hello', 30, 110);
*
* beginShape();
* for (let i = 0; i < pathCommands.length; i++) {
* const cmd = pathCommands[i];
* const type = cmd[0];
*
* switch (type) {
* case 'M': {
* // Move to (start a new contour)
* const x = cmd[1];
* const y = cmd[2];
* endContour(); // In case we were already drawing
* beginContour();
* vertex(x, y);
* break;
* }
* case 'L': {
* // Line to
* const x = cmd[1];
* const y = cmd[2];
* vertex(x, y);
* break;
* }
* case 'Q': {
* // Quadratic curve to: ['Q', cx, cy, x, y]
* // Simplified to just draw line to endpoint
* const x = cmd[3];
* const y = cmd[4];
* vertex(x, y);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you think it's worth showing how to draw the actual curves for Q and C? i.e.:

bezierOrder(2)
bezierVertex(cx, cy)
bezierVertex(x, y)

* break;
* }
* case 'C': {
* // Cubic bezier to: ['C', x1, y1, x2, y2, x3, y3]
* // Simplified to just draw line to endpoint
* const x = cmd[5];
* const y = cmd[6];
* vertex(x, y);
* break;
* }
* case 'Z': {
* // Close path
* endContour(CLOSE);
* beginContour();
* break;
* }
* }
* }
* endContour();
* endShape();
*
* // Note: The letters will appear angular since we're only using lines
* // between points rather than the actual curves from the font outlines.
* }
* </code>
* </div>
*/

textToPaths(str, x, y, width, height, options) {

({ width, height, options } = this._parseArgs(width, height, options));
Expand Down