fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
1 lines • 18 kB
Source Map (JSON)
{"version":3,"file":"Polyline.min.mjs","names":[],"sources":["../../../src/shapes/Polyline.ts"],"sourcesContent":["import { config } from '../config';\nimport { SHARED_ATTRIBUTES } from '../parser/attributes';\nimport { parseAttributes } from '../parser/parseAttributes';\nimport { parsePointsAttribute } from '../parser/parsePointsAttribute';\nimport type { XY } from '../Point';\nimport { Point } from '../Point';\nimport type { Abortable, TClassProperties, TOptions } from '../typedefs';\nimport { classRegistry } from '../ClassRegistry';\nimport { makeBoundingBoxFromPoints } from '../util/misc/boundingBoxFromPoints';\nimport { calcDimensionsMatrix, transformPoint } from '../util/misc/matrix';\nimport { projectStrokeOnPoints } from '../util/misc/projectStroke';\nimport type { TProjectStrokeOnPointsOptions } from '../util/misc/projectStroke/types';\nimport { degreesToRadians } from '../util/misc/radiansDegreesConversion';\nimport { toFixed } from '../util/misc/toFixed';\nimport { FabricObject, cacheProperties } from './Object/FabricObject';\nimport type { FabricObjectProps, SerializedObjectProps } from './Object/types';\nimport type { ObjectEvents } from '../EventTypeDefs';\nimport {\n CENTER,\n LEFT,\n SCALE_X,\n SCALE_Y,\n SKEW_X,\n SKEW_Y,\n TOP,\n} from '../constants';\nimport type { CSSRules } from '../parser/typedefs';\nimport { escapeXml } from '../util/lang_string';\n\nexport const polylineDefaultValues: Partial<TClassProperties<Polyline>> = {\n /**\n * @deprecated transient option soon to be removed in favor of a different design\n */\n exactBoundingBox: false,\n};\n\nexport interface SerializedPolylineProps extends SerializedObjectProps {\n points: XY[];\n}\n\nexport class Polyline<\n Props extends TOptions<FabricObjectProps> = Partial<FabricObjectProps>,\n SProps extends SerializedPolylineProps = SerializedPolylineProps,\n EventSpec extends ObjectEvents = ObjectEvents,\n> extends FabricObject<Props, SProps, EventSpec> {\n /**\n * Points array\n * @type Array\n */\n declare points: XY[];\n\n /**\n * WARNING: Feature in progress\n * Calculate the exact bounding box taking in account strokeWidth on acute angles\n * this will be turned to true by default on fabric 6.0\n * maybe will be left in as an optimization since calculations may be slow\n * @deprecated transient option soon to be removed in favor of a different design\n * @type Boolean\n * @default false\n */\n declare exactBoundingBox: boolean;\n\n declare private initialized: true | undefined;\n\n static ownDefaults = polylineDefaultValues;\n\n static type = 'Polyline';\n\n static getDefaults(): Record<string, any> {\n return {\n ...super.getDefaults(),\n ...Polyline.ownDefaults,\n };\n }\n\n /**\n * A list of properties that if changed trigger a recalculation of dimensions\n * @todo check if you really need to recalculate for all cases\n */\n static layoutProperties: (keyof Polyline)[] = [\n SKEW_X,\n SKEW_Y,\n 'strokeLineCap',\n 'strokeLineJoin',\n 'strokeMiterLimit',\n 'strokeWidth',\n 'strokeUniform',\n 'points',\n ];\n\n declare pathOffset: Point;\n\n declare strokeOffset: Point;\n\n static cacheProperties = [...cacheProperties, 'points'];\n\n strokeDiff: Point;\n\n /**\n * Constructor\n * @param {Array} points Array of points (where each point is an object with x and y)\n * @param {Object} [options] Options object\n * @return {Polyline} thisArg\n * @example\n * var poly = new Polyline([\n * { x: 10, y: 10 },\n * { x: 50, y: 30 },\n * { x: 40, y: 70 },\n * { x: 60, y: 50 },\n * { x: 100, y: 150 },\n * { x: 40, y: 100 }\n * ], {\n * stroke: 'red',\n * left: 100,\n * top: 100\n * });\n */\n constructor(points: XY[] = [], options: Props = {} as Props) {\n super();\n Object.assign(this, Polyline.ownDefaults);\n this.setOptions(options);\n this.points = points;\n const { left, top } = options;\n this.initialized = true;\n this.setBoundingBox(true);\n typeof left === 'number' && this.set(LEFT, left);\n typeof top === 'number' && this.set(TOP, top);\n }\n\n protected isOpen() {\n return true;\n }\n\n private _projectStrokeOnPoints(options: TProjectStrokeOnPointsOptions) {\n return projectStrokeOnPoints(this.points, options, this.isOpen());\n }\n\n /**\n * Calculate the polygon bounding box\n * @private\n */\n _calcDimensions(options?: Partial<TProjectStrokeOnPointsOptions>) {\n options = {\n scaleX: this.scaleX,\n scaleY: this.scaleY,\n skewX: this.skewX,\n skewY: this.skewY,\n strokeLineCap: this.strokeLineCap,\n strokeLineJoin: this.strokeLineJoin,\n strokeMiterLimit: this.strokeMiterLimit,\n strokeUniform: this.strokeUniform,\n strokeWidth: this.strokeWidth,\n ...(options || {}),\n };\n const points = this.exactBoundingBox\n ? this._projectStrokeOnPoints(\n options as TProjectStrokeOnPointsOptions,\n ).map((projection) => projection.projectedPoint)\n : this.points;\n if (points.length === 0) {\n return {\n left: 0,\n top: 0,\n width: 0,\n height: 0,\n pathOffset: new Point(),\n strokeOffset: new Point(),\n strokeDiff: new Point(),\n };\n }\n const bbox = makeBoundingBoxFromPoints(points),\n // Remove scale effect, since it's applied after\n matrix = calcDimensionsMatrix({ ...options, scaleX: 1, scaleY: 1 }),\n bboxNoStroke = makeBoundingBoxFromPoints(\n this.points.map((p) => transformPoint(p, matrix, true)),\n ),\n scale = new Point(this.scaleX, this.scaleY);\n let offsetX = bbox.left + bbox.width / 2,\n offsetY = bbox.top + bbox.height / 2;\n if (this.exactBoundingBox) {\n offsetX = offsetX - offsetY * Math.tan(degreesToRadians(this.skewX));\n // Order of those assignments is important.\n // offsetY relies on offsetX being already changed by the line above\n offsetY = offsetY - offsetX * Math.tan(degreesToRadians(this.skewY));\n }\n\n return {\n ...bbox,\n pathOffset: new Point(offsetX, offsetY),\n strokeOffset: new Point(bboxNoStroke.left, bboxNoStroke.top)\n .subtract(new Point(bbox.left, bbox.top))\n .multiply(scale),\n strokeDiff: new Point(bbox.width, bbox.height)\n .subtract(new Point(bboxNoStroke.width, bboxNoStroke.height))\n .multiply(scale),\n };\n }\n\n /**\n * This function is an helper for svg import. it returns the center of the object in the svg\n * untransformed coordinates, by look at the polyline/polygon points.\n * @private\n * @return {Point} center point from element coordinates\n */\n _findCenterFromElement(): Point {\n const bbox = makeBoundingBoxFromPoints(this.points);\n return new Point(bbox.left + bbox.width / 2, bbox.top + bbox.height / 2);\n }\n\n setDimensions() {\n this.setBoundingBox();\n }\n\n setBoundingBox(adjustPosition?: boolean) {\n const { left, top, width, height, pathOffset, strokeOffset, strokeDiff } =\n this._calcDimensions();\n this.set({ width, height, pathOffset, strokeOffset, strokeDiff });\n adjustPosition &&\n this.setPositionByOrigin(\n new Point(left + width / 2, top + height / 2),\n CENTER,\n CENTER,\n );\n }\n\n /**\n * @deprecated intermidiate method to be removed, do not use\n */\n protected isStrokeAccountedForInDimensions() {\n return this.exactBoundingBox;\n }\n\n /**\n * @override stroke is taken in account in size\n */\n _getNonTransformedDimensions() {\n return this.exactBoundingBox\n ? // TODO: fix this\n new Point(this.width, this.height)\n : super._getNonTransformedDimensions();\n }\n\n /**\n * @override stroke and skewing are taken into account when projecting stroke on points,\n * therefore we don't want the default calculation to account for skewing as well.\n * Though it is possible to pass `width` and `height` in `options`, doing so is very strange, use with discretion.\n *\n * @private\n */\n _getTransformedDimensions(options: any = {}) {\n if (this.exactBoundingBox) {\n let size: Point;\n /* When `strokeUniform = true`, any changes to the properties require recalculating the `width` and `height` because\n the stroke projections are affected.\n When `strokeUniform = false`, we don't need to recalculate for scale transformations, as the effect of scale on\n projections follows a linear function (e.g. scaleX of 2 just multiply width by 2)*/\n if (\n Object.keys(options).some(\n (key) =>\n this.strokeUniform ||\n (this.constructor as typeof Polyline).layoutProperties.includes(\n key as keyof TProjectStrokeOnPointsOptions,\n ),\n )\n ) {\n const { width, height } = this._calcDimensions(options);\n size = new Point(options.width ?? width, options.height ?? height);\n } else {\n size = new Point(\n options.width ?? this.width,\n options.height ?? this.height,\n );\n }\n return size.multiply(\n new Point(options.scaleX || this.scaleX, options.scaleY || this.scaleY),\n );\n } else {\n return super._getTransformedDimensions(options);\n }\n }\n\n /**\n * Recalculates dimensions when changing skew and scale\n * @private\n */\n _set(key: string, value: any) {\n const changed = this.initialized && this[key as keyof this] !== value;\n const output = super._set(key, value);\n if (\n this.exactBoundingBox &&\n changed &&\n (((key === SCALE_X || key === SCALE_Y) &&\n this.strokeUniform &&\n (this.constructor as typeof Polyline).layoutProperties.includes(\n 'strokeUniform',\n )) ||\n (this.constructor as typeof Polyline).layoutProperties.includes(\n key as keyof Polyline,\n ))\n ) {\n this.setDimensions();\n }\n return output;\n }\n\n /**\n * Returns object representation of an instance\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} Object representation of an instance\n */\n toObject<\n T extends Omit<Props & TClassProperties<this>, keyof SProps>,\n K extends keyof T = never,\n >(propertiesToInclude: K[] = []): Pick<T, K> & SProps {\n return {\n ...super.toObject(propertiesToInclude),\n points: this.points.map(({ x, y }) => ({ x, y })),\n };\n }\n\n /**\n * Returns svg representation of an instance\n * @return {Array} an array of strings with the specific svg representation\n * of the instance\n */\n _toSVG() {\n const diffX = this.pathOffset.x,\n diffY = this.pathOffset.y,\n NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS;\n\n const points = this.points\n .map(\n ({ x, y }) =>\n `${toFixed(x - diffX, NUM_FRACTION_DIGITS)},${toFixed(y - diffY, NUM_FRACTION_DIGITS)}`,\n )\n .join(' ');\n\n return [\n `<${\n escapeXml((this.constructor as typeof Polyline).type).toLowerCase() as\n | 'polyline'\n | 'polygon'\n } `,\n 'COMMON_PARTS',\n `points=\"${points}\" />\\n`,\n ];\n }\n\n /**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */\n _render(ctx: CanvasRenderingContext2D) {\n const len = this.points.length,\n x = this.pathOffset.x,\n y = this.pathOffset.y;\n\n if (!len || isNaN(this.points[len - 1].y)) {\n // do not draw if no points or odd points\n // NaN comes from parseFloat of a empty string in parser\n return;\n }\n ctx.beginPath();\n ctx.moveTo(this.points[0].x - x, this.points[0].y - y);\n for (let i = 0; i < len; i++) {\n const point = this.points[i];\n ctx.lineTo(point.x - x, point.y - y);\n }\n !this.isOpen() && ctx.closePath();\n this._renderPaintInOrder(ctx);\n }\n\n /**\n * Returns complexity of an instance\n * @return {Number} complexity of this instance\n */\n complexity(): number {\n return this.points.length;\n }\n\n /* _FROM_SVG_START_ */\n\n /**\n * List of attribute names to account for when parsing SVG element (used by {@link Polyline.fromElement})\n * @see: http://www.w3.org/TR/SVG/shapes.html#PolylineElement\n */\n static ATTRIBUTE_NAMES = [...SHARED_ATTRIBUTES];\n\n /**\n * Returns Polyline instance from an SVG element\n * @param {HTMLElement} element Element to parser\n * @param {Object} [options] Options object\n */\n static async fromElement(\n element: HTMLElement | SVGElement,\n options?: Abortable,\n cssRules?: CSSRules,\n ) {\n const points = parsePointsAttribute(element.getAttribute('points')),\n // we omit left and top to instruct the constructor to position the object using the bbox\n\n { left, top, ...parsedAttributes } = parseAttributes(\n element,\n this.ATTRIBUTE_NAMES,\n cssRules,\n );\n return new this(points, {\n ...parsedAttributes,\n ...options,\n });\n }\n\n /* _FROM_SVG_END_ */\n\n /**\n * Returns Polyline instance from an object representation\n * @param {Object} object Object to create an instance from\n * @returns {Promise<Polyline>}\n */\n static fromObject<T extends TOptions<SerializedPolylineProps>>(object: T) {\n return this._fromObject<Polyline>(object, {\n extraParam: 'points',\n });\n }\n}\n\nclassRegistry.setClass(Polyline);\nclassRegistry.setSVGClass(Polyline);\n"],"mappings":"qkCA6BA,MAAa,EAA6D,CAIxE,iBAAA,CAAkB,EAAA,CAOpB,IAAa,EAAb,MAAa,UAIH,CAAA,CAwBR,OAAA,aAAO,CACL,MAAO,CAAA,GACF,MAAM,aAAA,CAAA,GACN,EAAS,YAAA,CA8ChB,YAAY,EAAe,EAAA,CAAI,EAAiB,EAAA,CAAA,CAC9C,OAAA,CAAA,EAAA,KAtBF,aAAA,IAAA,GAAA,CAuBE,OAAO,OAAO,KAAM,EAAS,YAAA,CAC7B,KAAK,WAAW,EAAA,CAChB,KAAK,OAAS,EACd,GAAA,CAAM,KAAE,EAAA,IAAM,GAAQ,EACtB,KAAK,YAAA,CAAc,EACnB,KAAK,eAAA,CAAe,EAAA,CACJ,OAAT,GAAS,UAAY,KAAK,IAAA,OAAU,EAAA,CAC5B,OAAR,GAAQ,UAAY,KAAK,IAAA,MAAS,EAAA,CAG3C,QAAA,CACE,MAAA,CAAO,EAGT,uBAA+B,EAAA,CAC7B,OAAO,EAAsB,KAAK,OAAQ,EAAS,KAAK,QAAA,CAAA,CAO1D,gBAAgB,EAAA,CACd,EAAU,CACR,OAAQ,KAAK,OACb,OAAQ,KAAK,OACb,MAAO,KAAK,MACZ,MAAO,KAAK,MACZ,cAAe,KAAK,cACpB,eAAgB,KAAK,eACrB,iBAAkB,KAAK,iBACvB,cAAe,KAAK,cACpB,YAAa,KAAK,YAAA,GACd,GAAW,EAAA,CAAA,CAEjB,IAAM,EAAS,KAAK,iBAChB,KAAK,uBACH,EAAA,CACA,IAAK,GAAe,EAAW,eAAA,CACjC,KAAK,OACT,GAAI,EAAO,SAAW,EACpB,MAAO,CACL,KAAM,EACN,IAAK,EACL,MAAO,EACP,OAAQ,EACR,WAAY,IAAI,EAChB,aAAc,IAAI,EAClB,WAAY,IAAI,EAAA,CAGpB,IAAM,EAAO,EAA0B,EAAA,CAErC,EAAS,EAAqB,CAAA,GAAK,EAAS,OAAQ,EAAG,OAAQ,EAAA,CAAA,CAC/D,EAAe,EACb,KAAK,OAAO,IAAK,GAAM,EAAe,EAAG,EAAA,CAAQ,EAAA,CAAA,CAAA,CAEnD,EAAQ,IAAI,EAAM,KAAK,OAAQ,KAAK,OAAA,CAClC,EAAU,EAAK,KAAO,EAAK,MAAQ,EACrC,EAAU,EAAK,IAAM,EAAK,OAAS,EAQrC,OAPI,KAAK,mBACP,GAAoB,EAAU,KAAK,IAAI,EAAiB,KAAK,MAAA,CAAA,CAG7D,GAAoB,EAAU,KAAK,IAAI,EAAiB,KAAK,MAAA,CAAA,EAGxD,CAAA,GACF,EACH,WAAY,IAAI,EAAM,EAAS,EAAA,CAC/B,aAAc,IAAI,EAAM,EAAa,KAAM,EAAa,IAAA,CACrD,SAAS,IAAI,EAAM,EAAK,KAAM,EAAK,IAAA,CAAA,CACnC,SAAS,EAAA,CACZ,WAAY,IAAI,EAAM,EAAK,MAAO,EAAK,OAAA,CACpC,SAAS,IAAI,EAAM,EAAa,MAAO,EAAa,OAAA,CAAA,CACpD,SAAS,EAAA,CAAA,CAUhB,wBAAA,CACE,IAAM,EAAO,EAA0B,KAAK,OAAA,CAC5C,OAAO,IAAI,EAAM,EAAK,KAAO,EAAK,MAAQ,EAAG,EAAK,IAAM,EAAK,OAAS,EAAA,CAGxE,eAAA,CACE,KAAK,gBAAA,CAGP,eAAe,EAAA,CACb,GAAA,CAAM,KAAE,EAAA,IAAM,EAAA,MAAK,EAAA,OAAO,EAAA,WAAQ,EAAA,aAAY,EAAA,WAAc,GAC1D,KAAK,iBAAA,CACP,KAAK,IAAI,CAAE,MAAA,EAAO,OAAA,EAAQ,WAAA,EAAY,aAAA,EAAc,WAAA,EAAA,CAAA,CACpD,GACE,KAAK,oBACH,IAAI,EAAM,EAAO,EAAQ,EAAG,EAAM,EAAS,EAAA,CAAE,SAAA,SAAA,CASnD,kCAAA,CACE,OAAO,KAAK,iBAMd,8BAAA,CACE,OAAO,KAAK,iBAER,IAAI,EAAM,KAAK,MAAO,KAAK,OAAA,CAC3B,MAAM,8BAAA,CAUZ,0BAA0B,EAAe,EAAA,CAAA,CACvC,GAAI,KAAK,iBAAkB,CACzB,IAAI,EAKJ,GACE,OAAO,KAAK,EAAA,CAAS,KAClB,GACC,KAAK,eACJ,KAAK,YAAgC,iBAAiB,SACrD,EAAA,CAAA,CAGN,CAAA,IAAA,EAAA,EACA,GAAA,CAAM,MAAE,EAAA,OAAO,GAAW,KAAK,gBAAgB,EAAA,CAC/C,EAAO,IAAI,GAAA,EAAM,EAAQ,QAAA,KAAS,EAAT,GAAS,EAAO,EAAQ,SAAA,KAAU,EAAV,EAAU,KACtD,CAAA,IAAA,EAAA,EACL,EAAO,IAAI,GAAA,EACT,EAAQ,QAAA,KAAS,KAAK,MAAd,GAAc,EACtB,EAAQ,SAAA,KAAU,KAAK,OAAf,EAAe,CAG3B,OAAO,EAAK,SACV,IAAI,EAAM,EAAQ,QAAU,KAAK,OAAQ,EAAQ,QAAU,KAAK,OAAA,CAAA,CAGlE,OAAO,MAAM,0BAA0B,EAAA,CAQ3C,KAAK,EAAa,EAAA,CAChB,IAAM,EAAU,KAAK,aAAe,KAAK,KAAuB,EAC1D,EAAS,MAAM,KAAK,EAAK,EAAA,CAe/B,OAbE,KAAK,kBACL,KACG,IAAA,UAAmB,IAAA,WACpB,KAAK,eACJ,KAAK,YAAgC,iBAAiB,SACrD,gBAAA,EAED,KAAK,YAAgC,iBAAiB,SACrD,EAAA,GAGJ,KAAK,eAAA,CAEA,EAQT,SAGE,EAA2B,EAAA,CAAA,CAC3B,MAAO,CAAA,GACF,MAAM,SAAS,EAAA,CAClB,OAAQ,KAAK,OAAO,KAAA,CAAO,EAAA,EAAG,EAAA,MAAA,CAAW,EAAA,EAAG,EAAA,EAAA,EAAA,CAAA,CAShD,QAAA,CACE,IAAM,EAAQ,KAAK,WAAW,EAC5B,EAAQ,KAAK,WAAW,EACxB,EAAsB,EAAO,oBAEzB,EAAS,KAAK,OACjB,KAAA,CACI,EAAA,EAAG,EAAA,KACJ,GAAG,EAAQ,EAAI,EAAO,EAAA,CAAA,GAAwB,EAAQ,EAAI,EAAO,EAAA,GAAA,CAEpE,KAAK,IAAA,CAER,MAAO,CACL,IACE,EAAW,KAAK,YAAgC,KAAA,CAAM,aAAA,CAAA,GAIxD,eACA,WAAW,EAAA,QAAA,CAQf,QAAQ,EAAA,CACN,IAAM,EAAM,KAAK,OAAO,OACtB,EAAI,KAAK,WAAW,EACpB,EAAI,KAAK,WAAW,EAEtB,GAAK,GAAA,CAAO,MAAM,KAAK,OAAO,EAAM,GAAG,EAAA,CAAvC,CAKA,EAAI,WAAA,CACJ,EAAI,OAAO,KAAK,OAAO,GAAG,EAAI,EAAG,KAAK,OAAO,GAAG,EAAI,EAAA,CACpD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,IAAK,CAC5B,IAAM,EAAQ,KAAK,OAAO,GAC1B,EAAI,OAAO,EAAM,EAAI,EAAG,EAAM,EAAI,EAAA,CAAA,CAEnC,KAAK,QAAA,EAAY,EAAI,WAAA,CACtB,KAAK,oBAAoB,EATvB,EAgBJ,YAAA,CACE,OAAO,KAAK,OAAO,OAgBrB,aAAA,YACE,EACA,EACA,EAAA,CAEA,IAAM,EAAS,EAAqB,EAAQ,aAAa,SAAA,CAAA,CAAA,CAGvD,KAAE,EAAA,IAAM,EAAA,GAAQ,GAAqB,EACnC,EACA,KAAK,gBACL,EAAA,CAEJ,OAAO,IAAI,KAAK,EAAQ,CAAA,GACnB,EAAA,GACA,EAAA,CAAA,CAWP,OAAA,WAA+D,EAAA,CAC7D,OAAO,KAAK,YAAsB,EAAQ,CACxC,WAAY,SAAA,CAAA,GAAA,EAAA,EArWT,cAAc,EAAA,CAAA,EAAA,EAEd,OAAO,WAAA,CAAA,EAAA,EAaP,mBAAuC,CAC5C,EACA,EACA,gBACA,iBACA,mBACA,cACA,gBACA,SAAA,CAAA,CAAA,EAAA,EAOK,kBAAkB,CAAA,GAAI,EAAiB,SAAA,CAAA,CAAA,EAAA,EAoSvC,kBAAkB,CAAA,GAAI,EAAA,CAAA,CAwC/B,EAAc,SAAS,EAAA,CACvB,EAAc,YAAY,EAAA,CAAA,OAAA,KAAA,SAAA,KAAA"}