From 21941ce2cd517fc0f86520ae38d29f2d08ee6eda Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Tue, 11 Feb 2020 07:55:14 +1300 Subject: [PATCH 01/16] Refactored pathGraphics example to easily support rendering path bounding boxes --- examples/Graphics/pathGraphics.ts | 70 ++++++++++++++++--------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/examples/Graphics/pathGraphics.ts b/examples/Graphics/pathGraphics.ts index ae9da0b..98eb1d9 100755 --- a/examples/Graphics/pathGraphics.ts +++ b/examples/Graphics/pathGraphics.ts @@ -1,54 +1,56 @@ import createHiDPICanvas from "../../lib/hidpi-canvas"; + +function createPathShape(path, strokeColor, fillColor = null) { + const shape = new createjs.Shape(); + + if (fillColor) { + shape.graphics.beginFill(fillColor); + } + shape.graphics + .setStrokeStyle(4) + .beginStroke(strokeColor) + .decodeSVGPath(path); + + return shape; +} + export default function init() { const canvas = createHiDPICanvas(1080, 420, 1); document.body.appendChild(canvas); const stage = new createjs.Stage(canvas); - const a = new createjs.Shape(); - a.graphics.setStrokeStyle(4); - a.graphics.beginStroke("#00F"); - a.graphics.beginFill("#F00"); - a.graphics.decodeSVGPath("M 300 200 h -150 a 150 150 0 1 0 150 -150 z"); + const a = createPathShape( + "M 300 200 h -150 a 150 150 0 1 0 150 -150 z", + "#00F", + "#F00" + ); stage.addChild(a); - const b = new createjs.Shape(); - b.graphics.setStrokeStyle(4); - b.graphics.beginStroke("#000"); - b.graphics.beginFill("#FF0"); - b.graphics.decodeSVGPath("M 275 175 v -150 a 150 150 0 0 0 -150 150 z"); + const b = createPathShape( + "M 275 175 v -150 a 150 150 0 0 0 -150 150 z", + "#000", + "#FF0" + ); + stage.addChild(b); - const c = new createjs.Shape(); - c.graphics.setStrokeStyle(4); - c.graphics.beginStroke("#F00"); - c.graphics.decodeSVGPath( - "M 600 400 l 50 -25 a25 25 -30 0 1 50 -25 l 50 -25 a25 50 -30 0 1 50 -25 l 50 -25 a25 75 -30 0 1 50 -25 l 50 -25 a 25 100 -30 0 1 50 -25 l50 -25" + const c = createPathShape( + "M 600 400 l 50 -25 a25 25 -30 0 1 50 -25 l 50 -25 a25 50 -30 0 1 50 -25 l 50 -25 a25 75 -30 0 1 50 -25 l 50 -25 a 25 100 -30 0 1 50 -25 l50 -25", + "#F00" ); stage.addChild(c); - let d = new createjs.Shape(); - d.graphics.setStrokeStyle(4); - d.graphics.beginStroke("#F00"); - d.graphics.decodeSVGPath("M 600,75 a100,50 0 0,0 100,50"); + const d = createPathShape("M 600,75 a100,50 0 0,0 100,50", "#F00"); stage.addChild(d); - d = new createjs.Shape(); - d.graphics.setStrokeStyle(4); - d.graphics.beginStroke("#0F0"); - d.graphics.decodeSVGPath("M 600,75 a100,50 0 0,1 100,50"); - stage.addChild(d); + const e = createPathShape("M 600,75 a100,50 0 0,1 100,50", "#0F0"); + stage.addChild(e); - d = new createjs.Shape(); - d.graphics.setStrokeStyle(4); - d.graphics.beginStroke("#00F"); - d.graphics.decodeSVGPath("M 600,75 a100,50 0 1,0 100,50"); - stage.addChild(d); + const f = createPathShape("M 600,75 a100,50 0 1,0 100,50", "#00F"); + stage.addChild(f); - d = new createjs.Shape(); - d.graphics.setStrokeStyle(4); - d.graphics.beginStroke("#F0F"); - d.graphics.decodeSVGPath("M 600,75 a100,50 0 1,1 100,50"); - stage.addChild(d); + const g = createPathShape("M 600,75 a100,50 0 1,1 100,50", "#F0F"); + stage.addChild(g); stage.update(); return stage; From 5daae68f5ee708c135dc610580cdc56b5bc3b9a3 Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Tue, 11 Feb 2020 16:51:31 +1300 Subject: [PATCH 02/16] Inital work on gettin bounds for SVG Paths --- examples/Graphics/bounding_box.ts | 29 +++ examples/Graphics/pathGraphics.ts | 10 + examples/graphics.ts | 3 +- index.html | 3 + src/Character.ts | 2 + src/Glyph.ts | 35 ++- src/Graphics.ts | 2 +- src/PathBounds.ts | 342 ++++++++++++++++++++++++++++++ src/SVGPath.ts | 22 +- src/index.ts | 4 +- tests/get-bounds.js | 47 ++++ 11 files changed, 493 insertions(+), 6 deletions(-) create mode 100644 examples/Graphics/bounding_box.ts create mode 100644 src/PathBounds.ts create mode 100644 tests/get-bounds.js diff --git a/examples/Graphics/bounding_box.ts b/examples/Graphics/bounding_box.ts new file mode 100644 index 0000000..4041039 --- /dev/null +++ b/examples/Graphics/bounding_box.ts @@ -0,0 +1,29 @@ +import createHiDPICanvas from "../../lib/hidpi-canvas"; +import svgPath from "../fixtures/svg-glyph"; +export default function init() { + let canvas = createHiDPICanvas(1000, 1000, 2); + document.body.appendChild(canvas); + let stage = new createjs.Stage(canvas); + + var shape = new createjs.Shape(); + + shape.graphics.beginFill("#000"); + shape.graphics.decodeSVGPath(svgPath); + shape.graphics.endFill(); + shape.y = 30; + stage.addChild(shape); + + var boundingBox = new createjs.Rectangle(); + boundingBox = txt.svgPathBoundingBox(svgPath); + var boundaryLine = new createjs.Shape(); + boundaryLine.y = 30; + boundaryLine.graphics + .beginStroke("#dd0000") + .setStrokeStyle(2) + .setStrokeDash([15, 5]) + .rect(boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height); + stage.addChild(boundaryLine); + + stage.update(); + return stage; +} diff --git a/examples/Graphics/pathGraphics.ts b/examples/Graphics/pathGraphics.ts index 98eb1d9..97b8af3 100755 --- a/examples/Graphics/pathGraphics.ts +++ b/examples/Graphics/pathGraphics.ts @@ -11,6 +11,16 @@ function createPathShape(path, strokeColor, fillColor = null) { .beginStroke(strokeColor) .decodeSVGPath(path); + let bounds = txt.svgPathBoundingBox(path); + shape.setBounds(bounds.x, bounds.y, bounds.width, bounds.height); + + shape.graphics + .endFill() + .setStrokeStyle(1) + .setStrokeDash([20, 10], 0) + .beginStroke("#93F") + .drawRect(bounds.x, bounds.y, bounds.width, bounds.height); + return shape; } diff --git a/examples/graphics.ts b/examples/graphics.ts index 261da33..2ae08e5 100644 --- a/examples/graphics.ts +++ b/examples/graphics.ts @@ -1,5 +1,6 @@ import pathGraphics from "./Graphics/pathGraphics"; import pathGraphics2 from "./Graphics/pathGraphics2"; import pathGraphics3 from "./Graphics/pathGraphics3"; +import bounding_box from "./Graphics/bounding_box"; -export default { pathGraphics, pathGraphics2, pathGraphics3 }; +export default { pathGraphics, pathGraphics2, pathGraphics3, bounding_box }; diff --git a/index.html b/index.html index 6accee6..5dcc743 100644 --- a/index.html +++ b/index.html @@ -312,6 +312,9 @@

Graphics

  • Glyph Rendering (inverted)
  • +
  • + SVG Path Bounds +
  • diff --git a/src/Character.ts b/src/Character.ts index ba10ef5..6e5be03 100644 --- a/src/Character.ts +++ b/src/Character.ts @@ -119,6 +119,8 @@ export default class Character extends createjs.Shape { this._font.ascent - this._font.descent ); this.hitArea = ha; + + this._glyph.boundingLine(); } setGlyph(glyph: Glyph) { diff --git a/src/Glyph.ts b/src/Glyph.ts index 16c4a2e..ed39681 100755 --- a/src/Glyph.ts +++ b/src/Glyph.ts @@ -1,17 +1,23 @@ +import { svgPathBoundingBox } from "./SVGPath"; + /** * Represents a single Glyph within a Font. */ - export default class Glyph { /** SVG path data */ path = ""; + private _bounds: createjs.Rectangle = null; offset: number; kerning: any = {}; + private _graphic: createjs.Graphics = null; + private _boundaryLine: createjs.Graphics = null; _fill: createjs.Graphics.Fill; _stroke: createjs.Graphics.Stroke; _strokeStyle: createjs.Graphics.StrokeStyle; + static debug: false; + graphic() { if (this._graphic == null) { this._graphic = new createjs.Graphics(); @@ -39,8 +45,35 @@ export default class Glyph { return this._graphic; } + getBounds() { + if (!this._bounds) { + this._bounds = svgPathBoundingBox(this.path); + } + return this._bounds; + } + + boundingLine() { + if (this._boundaryLine == null) { + this._boundaryLine = new createjs.Graphics(); + let bounds = this.getBounds(); + this._boundaryLine.append( + new createjs.Graphics.Rect( + bounds.x, + bounds.y, + bounds.width, + bounds.height + ) + ); + this._boundaryLine.append(new createjs.Graphics.StrokeDash([10, 4])); + this._boundaryLine.append(new createjs.Graphics.Stroke("#FF00FF", true)); + } + } + draw(ctx: CanvasRenderingContext2D): boolean { this._graphic.draw(ctx); + if (Glyph.debug) { + this._boundaryLine.draw(ctx); + } return true; } diff --git a/src/Graphics.ts b/src/Graphics.ts index 75c3cc9..8a61a93 100644 --- a/src/Graphics.ts +++ b/src/Graphics.ts @@ -1,5 +1,5 @@ import SVGArc from "./SVGArc"; -import { parsePathData } from "./SVGPath"; +import { parsePathData, svgPathBoundingBox } from "./SVGPath"; export default class Graphics { /** diff --git a/src/PathBounds.ts b/src/PathBounds.ts new file mode 100644 index 0000000..fe9f68d --- /dev/null +++ b/src/PathBounds.ts @@ -0,0 +1,342 @@ +function getBoundsOfCurve(x, y, x1, y1, x2, y2, tempX, tempY) { + // TODO: implement getBoundsOfCurve + return []; +} + +function getBoundsOfArc(fx, fy, rx, ry, rot, large, sweep, tx, ty) { + // TODO: implement getBoundsOfArc + return []; +} + +export default function pathBounds(path) { + let aX = [], + aY = [], + current, // current instruction + previous = null, + subpathStartX = 0, + subpathStartY = 0, + x = 0, // current x + y = 0, // current y + controlX = 0, // current control point x + controlY = 0, // current control point y + tempX, + tempY, + bounds; + + for (var i = 0, len = path.length; i < len; ++i) { + current = path[i].points.flat(); + + current.unshift(path[i].command); + + switch ( + path[i].command // first letter + ) { + case "l": // lineto, relative + x += current[1]; + y += current[2]; + bounds = []; + break; + + case "L": // lineto, absolute + x = current[1]; + y = current[2]; + bounds = []; + break; + + case "h": // horizontal lineto, relative + x += current[1]; + bounds = []; + break; + + case "H": // horizontal lineto, absolute + x = current[1]; + bounds = []; + break; + + case "v": // vertical lineto, relative + y += current[1]; + bounds = []; + break; + + case "V": // verical lineto, absolute + y = current[1]; + bounds = []; + break; + + case "m": // moveTo, relative + x += current[1]; + y += current[2]; + subpathStartX = x; + subpathStartY = y; + bounds = []; + break; + + case "M": // moveTo, absolute + x = current[1]; + y = current[2]; + subpathStartX = x; + subpathStartY = y; + bounds = []; + break; + + case "c": // bezierCurveTo, relative + tempX = x + current[5]; + tempY = y + current[6]; + controlX = x + current[3]; + controlY = y + current[4]; + bounds = getBoundsOfCurve( + x, + y, + x + current[1], // x1 + y + current[2], // y1 + controlX, // x2 + controlY, // y2 + tempX, + tempY + ); + x = tempX; + y = tempY; + break; + + case "C": // bezierCurveTo, absolute + controlX = current[3]; + controlY = current[4]; + bounds = getBoundsOfCurve( + x, + y, + current[1], + current[2], + controlX, + controlY, + current[5], + current[6] + ); + x = current[5]; + y = current[6]; + break; + + case "s": // shorthand cubic bezierCurveTo, relative + // transform to absolute x,y + tempX = x + current[3]; + tempY = y + current[4]; + + if (previous[0].match(/[CcSs]/) === null) { + // If there is no previous command or if the previous command was not a C, c, S, or s, + // the control point is coincident with the current point + controlX = x; + controlY = y; + } else { + // calculate reflection of previous control points + controlX = 2 * x - controlX; + controlY = 2 * y - controlY; + } + + bounds = getBoundsOfCurve( + x, + y, + controlX, + controlY, + x + current[1], + y + current[2], + tempX, + tempY + ); + // set control point to 2nd one of this command + // "... the first control point is assumed to be + // the reflection of the second control point on + // the previous command relative to the current point." + controlX = x + current[1]; + controlY = y + current[2]; + x = tempX; + y = tempY; + break; + + case "S": // shorthand cubic bezierCurveTo, absolute + tempX = current[3]; + tempY = current[4]; + if (previous[0].match(/[CcSs]/) === null) { + // If there is no previous command or if the previous command was not a C, c, S, or s, + // the control point is coincident with the current point + controlX = x; + controlY = y; + } else { + // calculate reflection of previous control points + controlX = 2 * x - controlX; + controlY = 2 * y - controlY; + } + bounds = getBoundsOfCurve( + x, + y, + controlX, + controlY, + current[1], + current[2], + tempX, + tempY + ); + x = tempX; + y = tempY; + // set control point to 2nd one of this command + // "... the first control point is assumed to be + // the reflection of the second control point on + // the previous command relative to the current point." + controlX = current[1]; + controlY = current[2]; + break; + + case "q": // quadraticCurveTo, relative + // transform to absolute x,y + tempX = x + current[3]; + tempY = y + current[4]; + controlX = x + current[1]; + controlY = y + current[2]; + bounds = getBoundsOfCurve( + x, + y, + controlX, + controlY, + controlX, + controlY, + tempX, + tempY + ); + x = tempX; + y = tempY; + break; + + case "Q": // quadraticCurveTo, absolute + controlX = current[1]; + controlY = current[2]; + bounds = getBoundsOfCurve( + x, + y, + controlX, + controlY, + controlX, + controlY, + current[3], + current[4] + ); + x = current[3]; + y = current[4]; + break; + + case "t": // shorthand quadraticCurveTo, relative + // transform to absolute x,y + tempX = x + current[1]; + tempY = y + current[2]; + if (previous[0].match(/[QqTt]/) === null) { + // If there is no previous command or if the previous command was not a Q, q, T or t, + // assume the control point is coincident with the current point + controlX = x; + controlY = y; + } else { + // calculate reflection of previous control point + controlX = 2 * x - controlX; + controlY = 2 * y - controlY; + } + + bounds = getBoundsOfCurve( + x, + y, + controlX, + controlY, + controlX, + controlY, + tempX, + tempY + ); + x = tempX; + y = tempY; + + break; + + case "T": + tempX = current[1]; + tempY = current[2]; + + if (previous[0].match(/[QqTt]/) === null) { + // If there is no previous command or if the previous command was not a Q, q, T or t, + // assume the control point is coincident with the current point + controlX = x; + controlY = y; + } else { + // calculate reflection of previous control point + controlX = 2 * x - controlX; + controlY = 2 * y - controlY; + } + bounds = getBoundsOfCurve( + x, + y, + controlX, + controlY, + controlX, + controlY, + tempX, + tempY + ); + x = tempX; + y = tempY; + break; + + case "na": // TODO: implement getBoundsOfArc + bounds = getBoundsOfArc( + x, + y, + current[1], + current[2], + current[3], + current[4], + current[5], + current[6] + x, + current[7] + y + ); + x += current[6]; + y += current[7]; + break; + + case "nA": // TODO: implement getBoundsOfArc absolute + bounds = getBoundsOfArc( + x, + y, + current[1], + current[2], + current[3], + current[4], + current[5], + current[6], + current[7] + ); + x = current[6]; + y = current[7]; + break; + + case "z": + case "Z": + x = subpathStartX; + y = subpathStartY; + break; + } + previous = current; + bounds.forEach(function(point) { + aX.push(point.x); + aY.push(point.y); + }); + aX.push(x); + aY.push(y); + } + + var minX = Math.min(...aX) || 0, + minY = Math.min(...aY) || 0, + maxX = Math.max(...aX) || 0, + maxY = Math.max(...aY) || 0, + deltaX = maxX - minX, + deltaY = maxY - minY; + + return { + left: minX, + top: minY, + width: deltaX, + height: deltaY + }; +} diff --git a/src/SVGPath.ts b/src/SVGPath.ts index a8061e3..b87a6a5 100644 --- a/src/SVGPath.ts +++ b/src/SVGPath.ts @@ -1,4 +1,24 @@ -export function parsePathData(data) { +import pathBounds from "./PathBounds"; + +/** + * Useful SVG path tutorial on MDN + * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths + * @param data + */ + +export function svgPathBoundingBox(svgpath: string) { + // TODO: access a cached array? + var ca = parsePathData(svgpath); + let bounds = pathBounds(ca); + return new createjs.Rectangle( + bounds.left, + bounds.top, + bounds.width, + bounds.height + ); +} + +export function parsePathData(data: string) { if (!data) { return []; } diff --git a/src/index.ts b/src/index.ts index 8d42aa2..39ba6e8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ import "./GraphicsMixin"; +import copyEventListeners from "./utils/apply-shape-event-listeners"; export { default as Accessibility } from "./Accessibility"; export { default as Align } from "./Align"; @@ -16,8 +17,7 @@ export { default as Path, PathAlign, PathFit } from "./Path"; export { default as PathText } from "./PathText"; export { default as VerticalAlign } from "./VerticalAlign"; export { default as Word } from "./Word"; - -import copyEventListeners from "./utils/apply-shape-event-listeners"; +export { svgPathBoundingBox } from "./SVGPath"; export const Util = { copyEventListeners diff --git a/tests/get-bounds.js b/tests/get-bounds.js new file mode 100644 index 0000000..153d8dc --- /dev/null +++ b/tests/get-bounds.js @@ -0,0 +1,47 @@ +describe("Text", function() { + var canvas; + var stage; + beforeEach(function() { + canvas = txtExamples.createHiDPICanvas(300, 300, 2); + document.body.appendChild(canvas); + + stage = new createjs.Stage(canvas); + }); + + afterEach(function() { + txtExamples.clearExample(); + }); + + it("getBounds of Glyph", function() { + var glyph = new txt.Glyph(); + glyph.offset = 1063 / 2048; + glyph.path = + "M492 -246v226q-72 1 -136 28.5t-111 75t-74.5 111.5t-27.5 137v57l129 21v-78q0 -47 17 -88t47 -72t70 -49.5t86 -20.5v582q-66 24 -128.5 52.5t-111.5 72.5t-79 108t-30 158v27q0 72 27.5 135.5t74.5 111.5t111 76t136 29v184h102v-184q71 -1 134 -29.5t110 -76 t74.5 -111t27.5 -135.5v-37l-129 -21v58q0 46 -17 87t-46 71.5t-69 49.5t-85 21v-555q65 -25 129 -55t114.5 -75t81.5 -110.5t31 -160.5v-43q0 -73 -27.5 -137t-75.5 -112t-112 -75.5t-137 -27.5h-4v-226h-102zM821 375q0 57 -18 99.5t-48.5 74t-72 54.5t-88.5 42v-543 q47 0 88.5 18t72 49.5t48.5 73t18 89.5v43v0zM272 1075q0 -53 17 -92.5t47 -69.5t70 -53t86 -43v514q-46 -2 -86 -21t-70 -49.5t-47 -71.5t-17 -87v-27v0z"; + var bounds = glyph.getBounds(); + expect(bounds.width).toBeGreaterThan(0); + }); + + it("getBounds of Text", function() { + var text = new txt.Text({ + text: "First poiretone", + font: "poiretone", + width: 400, + height: 400, + size: 100, + x: 0, + y: 0, + accessibilityPriority: 0 + }); + + stage.addChild(text); + + var bounds = text.getBounds(); + + expect(bounds).not.toBeNull(); + + expect(bounds.x).toBe(0); + expect(bounds.y).toBe(0); + expect(bounds.width).toBe(400); + expect(bounds.height).toBe(400); + }); +}); From 5a27ea8f8b68e776292049744358d801f4d3742a Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Wed, 12 Feb 2020 07:40:25 +1300 Subject: [PATCH 03/16] Added SVGPath comments from KineticJS --- examples/Graphics/bounding_box.ts | 10 ++-- examples/Graphics/pathGraphics.ts | 2 +- src/Glyph.ts | 2 +- src/Graphics.ts | 3 ++ src/PathBounds.ts | 4 +- src/SVGPath.ts | 84 +++++++++++++++++++------------ 6 files changed, 65 insertions(+), 40 deletions(-) diff --git a/examples/Graphics/bounding_box.ts b/examples/Graphics/bounding_box.ts index 4041039..7d23829 100644 --- a/examples/Graphics/bounding_box.ts +++ b/examples/Graphics/bounding_box.ts @@ -1,11 +1,11 @@ import createHiDPICanvas from "../../lib/hidpi-canvas"; import svgPath from "../fixtures/svg-glyph"; export default function init() { - let canvas = createHiDPICanvas(1000, 1000, 2); + const canvas = createHiDPICanvas(1000, 1000, 2); document.body.appendChild(canvas); - let stage = new createjs.Stage(canvas); + const stage = new createjs.Stage(canvas); - var shape = new createjs.Shape(); + const shape = new createjs.Shape(); shape.graphics.beginFill("#000"); shape.graphics.decodeSVGPath(svgPath); @@ -13,9 +13,9 @@ export default function init() { shape.y = 30; stage.addChild(shape); - var boundingBox = new createjs.Rectangle(); + let boundingBox = new createjs.Rectangle(); boundingBox = txt.svgPathBoundingBox(svgPath); - var boundaryLine = new createjs.Shape(); + const boundaryLine = new createjs.Shape(); boundaryLine.y = 30; boundaryLine.graphics .beginStroke("#dd0000") diff --git a/examples/Graphics/pathGraphics.ts b/examples/Graphics/pathGraphics.ts index 97b8af3..ef88ac4 100755 --- a/examples/Graphics/pathGraphics.ts +++ b/examples/Graphics/pathGraphics.ts @@ -11,7 +11,7 @@ function createPathShape(path, strokeColor, fillColor = null) { .beginStroke(strokeColor) .decodeSVGPath(path); - let bounds = txt.svgPathBoundingBox(path); + const bounds = txt.svgPathBoundingBox(path); shape.setBounds(bounds.x, bounds.y, bounds.width, bounds.height); shape.graphics diff --git a/src/Glyph.ts b/src/Glyph.ts index ed39681..7b32e6f 100755 --- a/src/Glyph.ts +++ b/src/Glyph.ts @@ -55,7 +55,7 @@ export default class Glyph { boundingLine() { if (this._boundaryLine == null) { this._boundaryLine = new createjs.Graphics(); - let bounds = this.getBounds(); + const bounds = this.getBounds(); this._boundaryLine.append( new createjs.Graphics.Rect( bounds.x, diff --git a/src/Graphics.ts b/src/Graphics.ts index 8a61a93..885ac66 100644 --- a/src/Graphics.ts +++ b/src/Graphics.ts @@ -4,6 +4,9 @@ import { parsePathData, svgPathBoundingBox } from "./SVGPath"; export default class Graphics { /** * Build up createjs Graphics commands based on path data. + * + * Adapted from KineticJS + * @see https://github.com/ericdrowell/KineticJS/blob/master/src/plugins/Path.js#L41 */ static init(target, svgpath: string) { const ca = parsePathData(svgpath); diff --git a/src/PathBounds.ts b/src/PathBounds.ts index fe9f68d..22ac8a2 100644 --- a/src/PathBounds.ts +++ b/src/PathBounds.ts @@ -23,7 +23,7 @@ export default function pathBounds(path) { tempY, bounds; - for (var i = 0, len = path.length; i < len; ++i) { + for (let i = 0, len = path.length; i < len; ++i) { current = path[i].points.flat(); current.unshift(path[i].command); @@ -326,7 +326,7 @@ export default function pathBounds(path) { aY.push(y); } - var minX = Math.min(...aX) || 0, + const minX = Math.min(...aX) || 0, minY = Math.min(...aY) || 0, maxX = Math.max(...aX) || 0, maxY = Math.max(...aY) || 0, diff --git a/src/SVGPath.ts b/src/SVGPath.ts index b87a6a5..cbb9697 100644 --- a/src/SVGPath.ts +++ b/src/SVGPath.ts @@ -8,8 +8,8 @@ import pathBounds from "./PathBounds"; export function svgPathBoundingBox(svgpath: string) { // TODO: access a cached array? - var ca = parsePathData(svgpath); - let bounds = pathBounds(ca); + const ca = parsePathData(svgpath); + const bounds = pathBounds(ca); return new createjs.Rectangle( bounds.left, bounds.top, @@ -18,39 +18,51 @@ export function svgPathBoundingBox(svgpath: string) { ); } +/** + * Adapted from KineticJS + * @see https://github.com/ericdrowell/KineticJS/blob/master/src/plugins/Path.js#L210 + */ export function parsePathData(data: string) { if (!data) { return []; } + // command string let cs = data; + + // command chars const cc = [ - "m", - "M", - "l", - "L", - "v", - "V", - "h", - "H", - "z", - "Z", - "c", - "C", - "q", - "Q", - "t", - "T", - "s", - "S", - "a", - "A" + // Path Data Segment must begin with a moveTo + "m", //m (x y)+ Relative moveTo (subsequent points are treated as lineTo) + "M", //M (x y)+ Absolute moveTo (subsequent points are treated as lineTo) + "l", //l (x y)+ Relative lineTo + "L", //L (x y)+ Absolute LineTo + "v", //v (y)+ Relative vertical lineTo + "V", //V (y)+ Absolute vertical lineTo + "h", //h (x)+ Relative horizontal lineTo + "H", //H (x)+ Absolute horizontal lineTo + "z", //z (closepath) + "Z", //Z (closepath) + "c", //c (x1 y1 x2 y2 x y)+ Relative Bezier curve + "C", //C (x1 y1 x2 y2 x y)+ Absolute Bezier curve + "q", //q (x1 y1 x y)+ Relative Quadratic Bezier + "Q", //Q (x1 y1 x y)+ Absolute Quadratic Bezier + "t", //t (x y)+ Shorthand/Smooth Relative Quadratic Bezier + "T", //T (x y)+ Shorthand/Smooth Absolute Quadratic Bezier + "s", //s (x2 y2 x y)+ Shorthand/Smooth Relative Bezier curve + "S", //S (x2 y2 x y)+ Shorthand/Smooth Absolute Bezier curve + "a", //a (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Relative Elliptical Arc + "A" //A (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Absolute Elliptical Arc ]; + // convert white spaces to commas cs = cs.replace(new RegExp(" ", "g"), ","); + // create pipes so that we can split the data for (let n = 0; n < cc.length; n++) { cs = cs.replace(new RegExp(cc[n], "g"), "|" + cc[n]); } + // create array const arr = cs.split("|"); const ca = []; + // init context point let cpx = 0; let cpy = 0; const arrLength = arr.length; @@ -59,16 +71,20 @@ export function parsePathData(data: string) { let str = arr[n]; let c = str.charAt(0); str = str.slice(1); + // remove ,- for consistency str = str.replace(new RegExp(",-", "g"), "-"); + // add commas so that it's easy to split str = str.replace(new RegExp("-", "g"), ",-"); str = str.replace(new RegExp("e,-", "g"), "e-"); - let p = str.split(","); - if (p.length > 0 && p[0] === "") { - p.shift(); + const segments = str.split(","); + if (segments.length > 0 && segments[0] === "") { + segments.shift(); } - const pLength = p.length; - for (let i = 0; i < pLength; i++) { - p[i] = parseFloat(p[i]); + let p = []; + + // convert strings to floats + for (let i = 0; i < segments.length; i++) { + p[i] = parseFloat(segments[i]); } if (c === "z" || c === "Z") { p = [true]; @@ -76,17 +92,21 @@ export function parsePathData(data: string) { while (p.length > 0) { if (isNaN(p[0])) { + // case for a trailing comma before next command break; } let cmd = null; let points = []; const startX = cpx, startY = cpy; - let prevCmd, ctlPtx, ctlPty; - let rx, ry, psi, fa, fs, x1, y1; + // Move var from within the switch to up here (jshint) + let prevCmd, ctlPtx, ctlPty; // Ss, Tt + let rx, ry, psi, fa, fs, x1, y1; // Aa let dx, dy; + // convert l, H, h, V, and v to L switch (c) { + // Note: Keep the lineTo's above the moveTo's in this switch case "l": cpx += p.shift(); cpy += p.shift(); @@ -99,7 +119,7 @@ export function parsePathData(data: string) { cpy = p.shift(); points.push(cpx, cpy); break; - + // Note: lineTo handlers need to be above this point case "m": dx = p.shift(); dy = p.shift(); @@ -111,6 +131,7 @@ export function parsePathData(data: string) { cmd = "M"; points.push(cpx, cpy); c = "l"; + // subsequent points are treated as relative lineTo break; case "M": @@ -121,6 +142,7 @@ export function parsePathData(data: string) { startPoint = [cpx, cpy]; } points.push(cpx, cpy); + // subsequent points are treated as absolute lineTo c = "L"; break; From 1c1aee7d41a2c1a74ba130dbce2884d64894a460 Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Wed, 12 Feb 2020 19:41:53 +1300 Subject: [PATCH 04/16] Refactored boundary debug graphics building into it's own fuction --- src/Glyph.ts | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/Glyph.ts b/src/Glyph.ts index 7b32e6f..b02c1a9 100755 --- a/src/Glyph.ts +++ b/src/Glyph.ts @@ -54,21 +54,26 @@ export default class Glyph { boundingLine() { if (this._boundaryLine == null) { - this._boundaryLine = new createjs.Graphics(); const bounds = this.getBounds(); - this._boundaryLine.append( - new createjs.Graphics.Rect( - bounds.x, - bounds.y, - bounds.width, - bounds.height - ) - ); - this._boundaryLine.append(new createjs.Graphics.StrokeDash([10, 4])); - this._boundaryLine.append(new createjs.Graphics.Stroke("#FF00FF", true)); + this._boundaryLine = Glyph.buildBoundaryGraphics(bounds); } } + static buildBoundaryGraphics(bounds: createjs.Rectangle): createjs.Graphics { + const boundary = new createjs.Graphics(); + boundary.append( + new createjs.Graphics.Rect( + bounds.x, + bounds.y, + bounds.width, + bounds.height + ) + ); + boundary.append(new createjs.Graphics.StrokeDash([10, 4])); + boundary.append(new createjs.Graphics.Stroke("#FF00FF", true)); + return boundary; + } + draw(ctx: CanvasRenderingContext2D): boolean { this._graphic.draw(ctx); if (Glyph.debug) { From 7edcd70ad698f15929bf36409806cb552167a125 Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Wed, 26 Feb 2020 06:58:43 +1300 Subject: [PATCH 05/16] Fix Glyph debug static --- src/Glyph.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Glyph.ts b/src/Glyph.ts index b02c1a9..ebf80d3 100755 --- a/src/Glyph.ts +++ b/src/Glyph.ts @@ -16,7 +16,7 @@ export default class Glyph { _stroke: createjs.Graphics.Stroke; _strokeStyle: createjs.Graphics.StrokeStyle; - static debug: false; + static debug: boolean = false; graphic() { if (this._graphic == null) { From c78acaa419f7e3f79f896d3d935aebc350ab2a9b Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Wed, 26 Feb 2020 06:59:01 +1300 Subject: [PATCH 06/16] Added bounding_box example --- examples/CharacterText/bounding_box.ts | 28 ++++++++++++++++++++++++++ examples/character-text.ts | 2 ++ 2 files changed, 30 insertions(+) create mode 100644 examples/CharacterText/bounding_box.ts diff --git a/examples/CharacterText/bounding_box.ts b/examples/CharacterText/bounding_box.ts new file mode 100644 index 0000000..e5057fa --- /dev/null +++ b/examples/CharacterText/bounding_box.ts @@ -0,0 +1,28 @@ +import createHiDPICanvas from "../../lib/hidpi-canvas"; +export default function init() { + let canvas = createHiDPICanvas(500, 500, 2); + document.body.appendChild(canvas); + let stage = new createjs.Stage(canvas); + + let charText = new txt.CharacterText({ + text: "The fox\n jumped over...", + font: "raleway", + tracking: 20, + minSize: 70, + width: 500, + height: 500, + size: 120, + x: 100, + y: 100, + debug: true + }); + stage.addChild(charText); + + charText.layout(); + + console.log(charText.getBounds()); + + stage.update(); + + return stage; +} diff --git a/examples/character-text.ts b/examples/character-text.ts index 72e0888..6250b82 100644 --- a/examples/character-text.ts +++ b/examples/character-text.ts @@ -4,6 +4,7 @@ import autosize_expand from "./CharacterText/autosize_expand"; import autosize_reduce from "./CharacterText/autosize_reduce"; import autosize_reduce_expand from "./CharacterText/autosize_reduce_expand"; import autosize_reduce_layout from "./CharacterText/autosize_reduce_layout"; +import bounding_box from "./CharacterText/bounding_box"; import cache from "./CharacterText/cache"; import character_case from "./CharacterText/case"; import child_events from "./CharacterText/child_events"; @@ -39,6 +40,7 @@ export const visual = { autosize_reduce, autosize_reduce_expand, autosize_reduce_layout, + bounding_box, case: character_case, column, ligatures, From 97753b3fcd4b68ada73462f228bc91093695c227 Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Wed, 26 Feb 2020 16:27:25 +1300 Subject: [PATCH 07/16] Split graphics examples into visual / non-visual --- examples/character-text.ts | 2 +- examples/graphics.ts | 12 +++++++++++- examples/index.ts | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/examples/character-text.ts b/examples/character-text.ts index 6250b82..371089a 100644 --- a/examples/character-text.ts +++ b/examples/character-text.ts @@ -40,7 +40,6 @@ export const visual = { autosize_reduce, autosize_reduce_expand, autosize_reduce_layout, - bounding_box, case: character_case, column, ligatures, @@ -66,6 +65,7 @@ export const visual = { export const nonVisual = { accessibility, + bounding_box, cache, complete, child_events, diff --git a/examples/graphics.ts b/examples/graphics.ts index 2ae08e5..f74ef24 100644 --- a/examples/graphics.ts +++ b/examples/graphics.ts @@ -3,4 +3,14 @@ import pathGraphics2 from "./Graphics/pathGraphics2"; import pathGraphics3 from "./Graphics/pathGraphics3"; import bounding_box from "./Graphics/bounding_box"; -export default { pathGraphics, pathGraphics2, pathGraphics3, bounding_box }; +export const visual = { + pathGraphics, + pathGraphics2, + pathGraphics3 +}; + +export const nonVisual = { + bounding_box +}; + +export default { ...visual, ...nonVisual }; diff --git a/examples/index.ts b/examples/index.ts index 061b8db..30cd281 100644 --- a/examples/index.ts +++ b/examples/index.ts @@ -5,7 +5,7 @@ txt.FontLoader.path = "../font/"; import { visual as CharacterTextVisual } from "./character-text"; import { visual as TextVisual } from "./text"; import { visual as PathTextVisual } from "./path-text"; -import Graphics from "./graphics"; +import { visual as GraphicsVisual } from "./graphics"; import { nonVisual as CharacterTextNonVisual } from "./character-text"; import { nonVisual as TextNonVisual } from "./text"; @@ -16,7 +16,7 @@ export const visualExamples = { CharacterText: CharacterTextVisual, Text: TextVisual, PathText: PathTextVisual, - Graphics + Graphics: GraphicsVisual }; export const nonVisualExamples = { From db0e439be0c59fd22d15beba2b92145139616172 Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Wed, 26 Feb 2020 16:28:12 +1300 Subject: [PATCH 08/16] Remove bounds rendering from pathGraphics --- examples/Graphics/pathGraphics.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/examples/Graphics/pathGraphics.ts b/examples/Graphics/pathGraphics.ts index ef88ac4..98eb1d9 100755 --- a/examples/Graphics/pathGraphics.ts +++ b/examples/Graphics/pathGraphics.ts @@ -11,16 +11,6 @@ function createPathShape(path, strokeColor, fillColor = null) { .beginStroke(strokeColor) .decodeSVGPath(path); - const bounds = txt.svgPathBoundingBox(path); - shape.setBounds(bounds.x, bounds.y, bounds.width, bounds.height); - - shape.graphics - .endFill() - .setStrokeStyle(1) - .setStrokeDash([20, 10], 0) - .beginStroke("#93F") - .drawRect(bounds.x, bounds.y, bounds.width, bounds.height); - return shape; } From 22fb0170467861290ef7c22591490e95b6d3e2b5 Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Wed, 4 Mar 2020 16:48:17 +1300 Subject: [PATCH 09/16] Eslint fixes --- examples/CharacterText/bounding_box.ts | 6 +++--- src/Glyph.ts | 2 +- src/Graphics.ts | 2 +- src/PathBounds.ts | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/CharacterText/bounding_box.ts b/examples/CharacterText/bounding_box.ts index e5057fa..6e95ac1 100644 --- a/examples/CharacterText/bounding_box.ts +++ b/examples/CharacterText/bounding_box.ts @@ -1,10 +1,10 @@ import createHiDPICanvas from "../../lib/hidpi-canvas"; export default function init() { - let canvas = createHiDPICanvas(500, 500, 2); + const canvas = createHiDPICanvas(500, 500, 2); document.body.appendChild(canvas); - let stage = new createjs.Stage(canvas); + const stage = new createjs.Stage(canvas); - let charText = new txt.CharacterText({ + const charText = new txt.CharacterText({ text: "The fox\n jumped over...", font: "raleway", tracking: 20, diff --git a/src/Glyph.ts b/src/Glyph.ts index ebf80d3..df1d77d 100755 --- a/src/Glyph.ts +++ b/src/Glyph.ts @@ -16,7 +16,7 @@ export default class Glyph { _stroke: createjs.Graphics.Stroke; _strokeStyle: createjs.Graphics.StrokeStyle; - static debug: boolean = false; + static debug = false; graphic() { if (this._graphic == null) { diff --git a/src/Graphics.ts b/src/Graphics.ts index 885ac66..4f14e52 100644 --- a/src/Graphics.ts +++ b/src/Graphics.ts @@ -1,5 +1,5 @@ import SVGArc from "./SVGArc"; -import { parsePathData, svgPathBoundingBox } from "./SVGPath"; +import { parsePathData } from "./SVGPath"; export default class Graphics { /** diff --git a/src/PathBounds.ts b/src/PathBounds.ts index 22ac8a2..9c54acf 100644 --- a/src/PathBounds.ts +++ b/src/PathBounds.ts @@ -9,9 +9,9 @@ function getBoundsOfArc(fx, fy, rx, ry, rot, large, sweep, tx, ty) { } export default function pathBounds(path) { - let aX = [], - aY = [], - current, // current instruction + const aX = [], + aY = []; + let current, // current instruction previous = null, subpathStartX = 0, subpathStartY = 0, From c00b50a2238f30110b06f878d13a1180e6cb3e48 Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Wed, 4 Mar 2020 21:17:32 +1300 Subject: [PATCH 10/16] Minor renaming for readability --- src/Character.ts | 6 +++--- src/CharacterText.ts | 9 ++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Character.ts b/src/Character.ts index 6e5be03..7e9261a 100644 --- a/src/Character.ts +++ b/src/Character.ts @@ -109,8 +109,8 @@ export default class Character extends createjs.Shape { (this._font.ascent - this._font.descent) * this.scaleX; this.measuredWidth = this.scaleX * this._glyph.offset * this._font.units; - const ha = new createjs.Shape(); - ha.graphics + const hitArea = new createjs.Shape(); + hitArea.graphics .beginFill("#000") .drawRect( 0, @@ -118,7 +118,7 @@ export default class Character extends createjs.Shape { this._glyph.offset * this._font.units, this._font.ascent - this._font.descent ); - this.hitArea = ha; + this.hitArea = hitArea; this._glyph.boundingLine(); } diff --git a/src/CharacterText.ts b/src/CharacterText.ts index cd510c6..11c8944 100644 --- a/src/CharacterText.ts +++ b/src/CharacterText.ts @@ -300,7 +300,6 @@ export default class CharacterText extends TextContainer { */ characterLayout(): boolean { //char layout - const len = this.text.length; let char: Character; const defaultStyle: Style = { size: this.size, @@ -322,9 +321,9 @@ export default class CharacterText extends TextContainer { this.lines.push(currentLine); this.block.addChild(currentLine); - // loop over characters - // place into lines - for (let i = 0; i < len; i++) { + // loop over characters, and place into lines + for (let i = 0; i < this.text.length; i++) { + // apply custom character styles if (this.style !== null && this.style[i] !== undefined) { currentStyle = this.style[i]; // make sure style contains properties needed. @@ -350,7 +349,7 @@ export default class CharacterText extends TextContainer { // new line has no character if (this.text.charAt(i) == "\n" || this.text.charAt(i) == "\r") { //only if not last char - if (i < len - 1) { + if (i < this.text.length - 1) { if (firstLine === true) { vPosition = currentStyle.size; currentLine.measuredHeight = currentStyle.size; From 9fe748dad9dd74dfb30d80865f3009b3cab9cb3d Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Thu, 9 Jul 2020 21:25:53 +1200 Subject: [PATCH 11/16] Fix type issues --- examples/CharacterText/bounding_box.ts | 3 ++- examples/Graphics/bounding_box.ts | 2 ++ src/types/createjs.d.ts | 8 ++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 src/types/createjs.d.ts diff --git a/examples/CharacterText/bounding_box.ts b/examples/CharacterText/bounding_box.ts index 6e95ac1..0804d00 100644 --- a/examples/CharacterText/bounding_box.ts +++ b/examples/CharacterText/bounding_box.ts @@ -1,3 +1,4 @@ +import * as txt from "txt"; import createHiDPICanvas from "../../lib/hidpi-canvas"; export default function init() { const canvas = createHiDPICanvas(500, 500, 2); @@ -14,7 +15,7 @@ export default function init() { size: 120, x: 100, y: 100, - debug: true + debug: true, }); stage.addChild(charText); diff --git a/examples/Graphics/bounding_box.ts b/examples/Graphics/bounding_box.ts index 7d23829..2723c8e 100644 --- a/examples/Graphics/bounding_box.ts +++ b/examples/Graphics/bounding_box.ts @@ -1,5 +1,7 @@ +import * as txt from "txt"; import createHiDPICanvas from "../../lib/hidpi-canvas"; import svgPath from "../fixtures/svg-glyph"; + export default function init() { const canvas = createHiDPICanvas(1000, 1000, 2); document.body.appendChild(canvas); diff --git a/src/types/createjs.d.ts b/src/types/createjs.d.ts new file mode 100644 index 0000000..c03b55c --- /dev/null +++ b/src/types/createjs.d.ts @@ -0,0 +1,8 @@ +// TODO: get this addded into the @types/easeljs package +declare namespace createjs { + namespace Graphics { + class StrokeDash { + constructor(segments: Array, offset?: number); + } + } +} From c609e0d307b7e4da6072407624e510c3f2d2aa70 Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Thu, 9 Jul 2020 21:26:20 +1200 Subject: [PATCH 12/16] Resize bounding box example canvas size --- examples/Graphics/bounding_box.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Graphics/bounding_box.ts b/examples/Graphics/bounding_box.ts index 2723c8e..16e35c7 100644 --- a/examples/Graphics/bounding_box.ts +++ b/examples/Graphics/bounding_box.ts @@ -3,7 +3,7 @@ import createHiDPICanvas from "../../lib/hidpi-canvas"; import svgPath from "../fixtures/svg-glyph"; export default function init() { - const canvas = createHiDPICanvas(1000, 1000, 2); + const canvas = createHiDPICanvas(500, 300, 2); document.body.appendChild(canvas); const stage = new createjs.Stage(canvas); From add94521b5aefe89870bd1a3528bd2f74b772218 Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Fri, 10 Jul 2020 15:05:13 +1200 Subject: [PATCH 13/16] Convert get-bounds test to typescript --- tests/{get-bounds.js => get-bounds.ts} | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) rename tests/{get-bounds.js => get-bounds.ts} (82%) diff --git a/tests/get-bounds.js b/tests/get-bounds.ts similarity index 82% rename from tests/get-bounds.js rename to tests/get-bounds.ts index 153d8dc..5f428c3 100644 --- a/tests/get-bounds.js +++ b/tests/get-bounds.ts @@ -1,6 +1,9 @@ +import * as txt from "txt"; +import * as txtExamples from "examples"; +import { removeCanvas } from "./helpers"; describe("Text", function() { - var canvas; - var stage; + let canvas; + let stage; beforeEach(function() { canvas = txtExamples.createHiDPICanvas(300, 300, 2); document.body.appendChild(canvas); @@ -9,20 +12,20 @@ describe("Text", function() { }); afterEach(function() { - txtExamples.clearExample(); + removeCanvas(); }); it("getBounds of Glyph", function() { - var glyph = new txt.Glyph(); + const glyph = new txt.Glyph(); glyph.offset = 1063 / 2048; glyph.path = "M492 -246v226q-72 1 -136 28.5t-111 75t-74.5 111.5t-27.5 137v57l129 21v-78q0 -47 17 -88t47 -72t70 -49.5t86 -20.5v582q-66 24 -128.5 52.5t-111.5 72.5t-79 108t-30 158v27q0 72 27.5 135.5t74.5 111.5t111 76t136 29v184h102v-184q71 -1 134 -29.5t110 -76 t74.5 -111t27.5 -135.5v-37l-129 -21v58q0 46 -17 87t-46 71.5t-69 49.5t-85 21v-555q65 -25 129 -55t114.5 -75t81.5 -110.5t31 -160.5v-43q0 -73 -27.5 -137t-75.5 -112t-112 -75.5t-137 -27.5h-4v-226h-102zM821 375q0 57 -18 99.5t-48.5 74t-72 54.5t-88.5 42v-543 q47 0 88.5 18t72 49.5t48.5 73t18 89.5v43v0zM272 1075q0 -53 17 -92.5t47 -69.5t70 -53t86 -43v514q-46 -2 -86 -21t-70 -49.5t-47 -71.5t-17 -87v-27v0z"; - var bounds = glyph.getBounds(); + const bounds = glyph.getBounds(); expect(bounds.width).toBeGreaterThan(0); }); it("getBounds of Text", function() { - var text = new txt.Text({ + const text = new txt.Text({ text: "First poiretone", font: "poiretone", width: 400, @@ -35,7 +38,7 @@ describe("Text", function() { stage.addChild(text); - var bounds = text.getBounds(); + const bounds = text.getBounds(); expect(bounds).not.toBeNull(); From 4236cee8dda6d374bf7e1dccaaf06cae4a21d462 Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Fri, 10 Jul 2020 15:11:16 +1200 Subject: [PATCH 14/16] Fix headless tests output --- testem.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/testem.js b/testem.js index 831cc3a..bddfe85 100644 --- a/testem.js +++ b/testem.js @@ -12,17 +12,17 @@ let serve_files = [ { src: coverageServer.clientFile }, { src: - "node_modules/@recreatejs/jasmine-pixelmatch/dist/jasmine-pixelmatch.js" + "node_modules/@recreatejs/jasmine-pixelmatch/dist/jasmine-pixelmatch.js", }, { src: "dist/easeljs.js" }, { src: "dist/pathseg.js" }, { src: "dist/txt.instrumented.umd.js" }, { src: "dist/examples.umd.js" }, - { src: "dist/tests.umd.js" } + { src: "dist/tests.umd.js" }, ]; -if (!process.env.HEADLESS) { - serve_files.push({ src: "!dist/esnext/tests/_headless.js" }); +if (process.env.HEADLESS) { + serve_files.push({ src: "dist/esnext/tests/_headless.js" }); } module.exports = { @@ -30,14 +30,14 @@ module.exports = { launch_in_ci: ["Chrome"], browser_args: { Chrome: chromeArgs, - Firefox: firefoxArgs + Firefox: firefoxArgs, }, test_page: "testem.mustache", src_files: ["src/**/*.ts", "examples/**/*.ts"], serve_files, css_files: [], routes: { - "/images": "images" + "/images": "images", }, proxies: coverageServer.proxies, before_tests: function(config, data, callback) { @@ -45,5 +45,5 @@ module.exports = { }, after_tests: function(config, data, callback) { coverageServer.shutdownCoverageServer(callback); - } + }, }; From 95a238eebd455b39a2fc907ec71a321b9067b09d Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Fri, 10 Jul 2020 22:41:43 +1200 Subject: [PATCH 15/16] Added bounding box example link --- index.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 5dcc743..a7ac084 100644 --- a/index.html +++ b/index.html @@ -250,7 +250,9 @@

    CharacterText

  • Fit - singleLine autoReduce + autoExpand
  • - +
  • + Bounding box +
  • From 0bb6175b3c8c84c07accd261f8f58fd6ec787d2b Mon Sep 17 00:00:00 2001 From: Jeremy Shipman Date: Fri, 10 Jul 2020 22:43:00 +1200 Subject: [PATCH 16/16] Factored out alignment helper functions --- src/Align.ts | 24 ++++++++++++++++++++++++ src/CharacterText.ts | 20 ++++---------------- src/Text.ts | 34 +++++++++++----------------------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/Align.ts b/src/Align.ts index 833aaf1..20640d2 100644 --- a/src/Align.ts +++ b/src/Align.ts @@ -20,3 +20,27 @@ enum Align { } export default Align; + +export function topAligned(alignment: number): boolean { + return ( + alignment === Align.TOP_LEFT || + alignment === Align.TOP_CENTER || + alignment === Align.TOP_RIGHT + ); +} + +export function middleAligned(alignment: number): boolean { + return ( + alignment === Align.MIDDLE_LEFT || + alignment === Align.MIDDLE_CENTER || + alignment === Align.MIDDLE_RIGHT + ); +} + +export function bottomAligned(alignment: number): boolean { + return ( + alignment === Align.BOTTOM_LEFT || + alignment === Align.BOTTOM_CENTER || + alignment === Align.BOTTOM_RIGHT + ); +} diff --git a/src/CharacterText.ts b/src/CharacterText.ts index 11c8944..bc8517b 100644 --- a/src/CharacterText.ts +++ b/src/CharacterText.ts @@ -1,5 +1,5 @@ import TextContainer from "./TextContainer"; -import Align from "./Align"; +import Align, { topAligned, middleAligned, bottomAligned } from "./Align"; import FontLoader from "./FontLoader"; import { ConstructObj, Style } from "./Interfaces"; import Font from "./Font"; @@ -577,11 +577,7 @@ export default class CharacterText extends TextContainer { } //TOP ALIGNED - if ( - this.align === a.TOP_LEFT || - this.align === a.TOP_CENTER || - this.align === a.TOP_RIGHT - ) { + if (topAligned(this.align)) { if (fnt.top == 0) { this.block.y = (this.lines[0].measuredHeight * fnt.ascent) / fnt.units; } else { @@ -591,22 +587,14 @@ export default class CharacterText extends TextContainer { } //MIDDLE ALIGNED - } else if ( - this.align === a.MIDDLE_LEFT || - this.align === a.MIDDLE_CENTER || - this.align === a.MIDDLE_RIGHT - ) { + } else if (middleAligned(this.align)) { this.block.y = this.lines[0].measuredHeight + (this.height - measuredHeight) / 2 + (this.lines[0].measuredHeight * fnt.middle) / fnt.units; //BOTTOM ALIGNED - } else if ( - this.align === a.BOTTOM_LEFT || - this.align === a.BOTTOM_CENTER || - this.align === a.BOTTOM_RIGHT - ) { + } else if (bottomAligned(this.align)) { this.block.y = this.height - this.lines[this.lines.length - 1].y + diff --git a/src/Text.ts b/src/Text.ts index 5de61e6..8344e89 100644 --- a/src/Text.ts +++ b/src/Text.ts @@ -1,5 +1,5 @@ import TextContainer from "./TextContainer"; -import Align from "./Align"; +import Align, { topAligned, middleAligned, bottomAligned } from "./Align"; import FontLoader from "./FontLoader"; import Word from "./Word"; import Line from "./Line"; @@ -451,7 +451,7 @@ export default class Text extends TextContainer { // place into text let measuredHeight = 0; let line; - const a = Align; + const fnt: Font = FontLoader.getFont(this.font); const len = this.lines.length; @@ -471,54 +471,42 @@ export default class Text extends TextContainer { } measuredHeight += line.measuredHeight; - if (this.align === a.TOP_CENTER) { + if (this.align === Align.TOP_CENTER) { //move to center line.x = (this.width - line.measuredWidth) / 2; - } else if (this.align === a.TOP_RIGHT) { + } else if (this.align === Align.TOP_RIGHT) { //move to right line.x = this.width - line.measuredWidth; - } else if (this.align === a.MIDDLE_CENTER) { + } else if (this.align === Align.MIDDLE_CENTER) { //move to center line.x = (this.width - line.measuredWidth) / 2; - } else if (this.align === a.MIDDLE_RIGHT) { + } else if (this.align === Align.MIDDLE_RIGHT) { //move to right line.x = this.width - line.measuredWidth; - } else if (this.align === a.BOTTOM_CENTER) { + } else if (this.align === Align.BOTTOM_CENTER) { //move to center line.x = (this.width - line.measuredWidth) / 2; - } else if (this.align === a.BOTTOM_RIGHT) { + } else if (this.align === Align.BOTTOM_RIGHT) { //move to right line.x = this.width - line.measuredWidth; } } //TOP ALIGNED - if ( - this.align === a.TOP_LEFT || - this.align === a.TOP_CENTER || - this.align === a.TOP_RIGHT - ) { + if (topAligned(this.align)) { this.block.y = (this.lines[0].measuredHeight * fnt.ascent) / fnt.units + (this.lines[0].measuredHeight * fnt.top) / fnt.units; //MIDDLE ALIGNED - } else if ( - this.align === a.MIDDLE_LEFT || - this.align === a.MIDDLE_CENTER || - this.align === a.MIDDLE_RIGHT - ) { + } else if (middleAligned(this.align)) { this.block.y = this.lines[0].measuredHeight + (this.height - measuredHeight) / 2 + (this.lines[0].measuredHeight * fnt.middle) / fnt.units; //BOTTOM ALIGNED - } else if ( - this.align === a.BOTTOM_LEFT || - this.align === a.BOTTOM_CENTER || - this.align === a.BOTTOM_RIGHT - ) { + } else if (bottomAligned(this.align)) { this.block.y = this.height - this.lines[this.lines.length - 1].y +