From 6dbf7685d884ce1be0bc7c57548cf3a2cb09f268 Mon Sep 17 00:00:00 2001 From: Preet Shihn Date: Thu, 20 Dec 2018 14:11:32 -0800 Subject: [PATCH] refactor - wip --- bin/canvas-base.d.ts | 2 - bin/canvas-base.js | 4 - bin/fillers/dot-filler.d.ts | 4 +- bin/fillers/dot-filler.js | 14 +- bin/fillers/filler-interface.d.ts | 7 +- bin/fillers/filler-utils.d.ts | 3 +- bin/fillers/filler-utils.js | 7 +- bin/fillers/filler.d.ts | 4 +- bin/fillers/filler.js | 10 +- bin/fillers/hachure-filler.d.ts | 4 +- bin/fillers/hachure-filler.js | 10 +- bin/generator-base.d.ts | 3 - bin/generator-base.js | 5 - bin/generator.d.ts | 2 +- bin/generator.js | 37 +- bin/renderer-factory-old.d.ts | 0 bin/renderer-factory-old.js | 16 + bin/renderer-new.d.ts | 14 + bin/renderer-new.js | 598 +++++++ bin/renderer-old.d.ts | 25 + bin/renderer-old.js | 597 +++++++ bin/renderer.d.ts | 40 +- bin/renderer.js | 1116 ++++++------- bin/rough.d.ts | 11 +- bin/rough.js | 15 - bin/svg-base.d.ts | 2 - bin/svg-base.js | 6 +- package-lock.json | 1404 +---------------- package.json | 15 +- src/{canvas-async.ts => canvas-async.ts.old} | 0 src/canvas-base.ts | 7 +- src/fillers/dot-filler.ts | 19 +- src/fillers/filler-interface.ts | 8 +- src/fillers/filler-utils.ts | 8 +- src/fillers/filler.ts | 12 +- src/fillers/hachure-filler.ts | 15 +- ...erator-async.ts => generator-async.ts.old} | 0 src/generator-base.ts | 9 +- src/generator.ts | 37 +- src/renderer-factory.ts | 17 - src/renderer-factory.ts.old | 17 + src/renderer.ts | 1155 +++++++------- src/renderer.ts.old | 619 ++++++++ src/rough.ts | 23 +- src/{svg-async.ts => svg-async.ts.old} | 0 src/svg-base.ts | 7 +- src/svg.ts | 18 +- tsconfig.json | 4 +- 48 files changed, 3245 insertions(+), 2705 deletions(-) create mode 100644 bin/renderer-factory-old.d.ts create mode 100644 bin/renderer-factory-old.js create mode 100644 bin/renderer-new.d.ts create mode 100644 bin/renderer-new.js create mode 100644 bin/renderer-old.d.ts create mode 100644 bin/renderer-old.js rename src/{canvas-async.ts => canvas-async.ts.old} (100%) rename src/{generator-async.ts => generator-async.ts.old} (100%) delete mode 100644 src/renderer-factory.ts create mode 100644 src/renderer-factory.ts.old create mode 100644 src/renderer.ts.old rename src/{svg-async.ts => svg-async.ts.old} (100%) diff --git a/bin/canvas-base.d.ts b/bin/canvas-base.d.ts index 2fa0ebf..c44b774 100644 --- a/bin/canvas-base.d.ts +++ b/bin/canvas-base.d.ts @@ -1,10 +1,8 @@ import { ResolvedOptions, Drawable } from './core'; -import { RoughRenderer } from './renderer'; export declare abstract class RoughCanvasBase { protected canvas: HTMLCanvasElement; protected ctx: CanvasRenderingContext2D; constructor(canvas: HTMLCanvasElement); - static createRenderer(): RoughRenderer; abstract getDefaultOptions(): ResolvedOptions; draw(drawable: Drawable): void; private computeBBox; diff --git a/bin/canvas-base.js b/bin/canvas-base.js index 017bdb7..a877d43 100644 --- a/bin/canvas-base.js +++ b/bin/canvas-base.js @@ -1,13 +1,9 @@ -import { RoughRenderer } from './renderer'; const hasDocument = typeof document !== 'undefined'; export class RoughCanvasBase { constructor(canvas) { this.canvas = canvas; this.ctx = this.canvas.getContext('2d'); } - static createRenderer() { - return new RoughRenderer(); - } draw(drawable) { const sets = drawable.sets || []; const o = drawable.options || this.getDefaultOptions(); diff --git a/bin/fillers/dot-filler.d.ts b/bin/fillers/dot-filler.d.ts index ab02cfe..b41f236 100644 --- a/bin/fillers/dot-filler.d.ts +++ b/bin/fillers/dot-filler.d.ts @@ -1,9 +1,7 @@ -import { PatternFiller, RenderHelper } from './filler-interface'; +import { PatternFiller } from './filler-interface'; import { ResolvedOptions, OpSet } from '../core'; import { Point } from '../geometry'; export declare class DotFiller implements PatternFiller { - renderer: RenderHelper; - constructor(renderer: RenderHelper); fillPolygon(points: Point[], o: ResolvedOptions): OpSet; fillEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions): OpSet; private dotsOnLines; diff --git a/bin/fillers/dot-filler.js b/bin/fillers/dot-filler.js index 1cfb21e..ed43afa 100644 --- a/bin/fillers/dot-filler.js +++ b/bin/fillers/dot-filler.js @@ -1,8 +1,6 @@ import { hachureLinesForPolygon, hachureLinesForEllipse, lineLength } from './filler-utils'; +import { randOffsetWithRange, ellipse } from '../renderer'; export class DotFiller { - constructor(renderer) { - this.renderer = renderer; - } fillPolygon(points, o) { o = Object.assign({}, o, { curveStepCount: 4, hachureAngle: 0 }); const lines = hachureLinesForPolygon(points, o); @@ -10,7 +8,7 @@ export class DotFiller { } fillEllipse(cx, cy, width, height, o) { o = Object.assign({}, o, { curveStepCount: 4, hachureAngle: 0 }); - const lines = hachureLinesForEllipse(cx, cy, width, height, o, this.renderer); + const lines = hachureLinesForEllipse(cx, cy, width, height, o); return this.dotsOnLines(lines, o); } dotsOnLines(lines, o) { @@ -34,10 +32,10 @@ export class DotFiller { const dy = l * Math.sin(alpha); const dx = l * Math.cos(alpha); const c = [line[0][0] - dx, line[0][1] + dy]; - const cx = this.renderer.getOffset(c[0] - gap / 4, c[0] + gap / 4, o); - const cy = this.renderer.getOffset(c[1] - gap / 4, c[1] + gap / 4, o); - const ellipse = this.renderer.ellipse(cx, cy, fweight, fweight, o); - ops = ops.concat(ellipse.ops); + const cx = randOffsetWithRange(c[0] - gap / 4, c[0] + gap / 4, o); + const cy = randOffsetWithRange(c[1] - gap / 4, c[1] + gap / 4, o); + const el = ellipse(cx, cy, fweight, fweight, o); + ops = ops.concat(el.ops); } } return { type: 'fillSketch', ops }; diff --git a/bin/fillers/filler-interface.d.ts b/bin/fillers/filler-interface.d.ts index bd2be7d..b605dbe 100644 --- a/bin/fillers/filler-interface.d.ts +++ b/bin/fillers/filler-interface.d.ts @@ -1,11 +1,6 @@ -import { ResolvedOptions, OpSet, Op } from '../core'; +import { ResolvedOptions, OpSet } from '../core'; import { Point } from '../geometry'; export interface PatternFiller { fillPolygon(points: Point[], o: ResolvedOptions): OpSet; fillEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions): OpSet; } -export interface RenderHelper { - doubleLine(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): Op[]; - getOffset(min: number, max: number, ops: ResolvedOptions): number; - ellipse(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet; -} diff --git a/bin/fillers/filler-utils.d.ts b/bin/fillers/filler-utils.d.ts index 7386f43..9a25e09 100644 --- a/bin/fillers/filler-utils.d.ts +++ b/bin/fillers/filler-utils.d.ts @@ -1,8 +1,7 @@ import { Point, Line } from '../geometry'; import { ResolvedOptions } from '../core'; -import { RenderHelper } from './filler-interface'; export declare function lineLength(line: Line): number; export declare function getIntersectingLines(line: number[], points: Point[]): Point[]; export declare function affine(x: number, y: number, cx: number, cy: number, sinAnglePrime: number, cosAnglePrime: number, R: number): Point; export declare function hachureLinesForPolygon(points: Point[], o: ResolvedOptions): Line[]; -export declare function hachureLinesForEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions, renderer: RenderHelper): Line[]; +export declare function hachureLinesForEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions): Line[]; diff --git a/bin/fillers/filler-utils.js b/bin/fillers/filler-utils.js index 316141a..9694988 100644 --- a/bin/fillers/filler-utils.js +++ b/bin/fillers/filler-utils.js @@ -1,5 +1,6 @@ import { Segment } from '../geometry'; import { HachureIterator } from '../utils/hachure'; +import { randOffset } from '../renderer'; export function lineLength(line) { const p1 = line[0]; const p2 = line[1]; @@ -67,12 +68,12 @@ export function hachureLinesForPolygon(points, o) { } return ret; } -export function hachureLinesForEllipse(cx, cy, width, height, o, renderer) { +export function hachureLinesForEllipse(cx, cy, width, height, o) { const ret = []; let rx = Math.abs(width / 2); let ry = Math.abs(height / 2); - rx += renderer.getOffset(-rx * 0.05, rx * 0.05, o); - ry += renderer.getOffset(-ry * 0.05, ry * 0.05, o); + rx += randOffset(rx * 0.05, o); + ry += randOffset(ry * 0.05, o); const angle = o.hachureAngle; let gap = o.hachureGap; if (gap <= 0) { diff --git a/bin/fillers/filler.d.ts b/bin/fillers/filler.d.ts index 26d1d4d..1330d93 100644 --- a/bin/fillers/filler.d.ts +++ b/bin/fillers/filler.d.ts @@ -1,3 +1,3 @@ import { ResolvedOptions } from '../core'; -import { PatternFiller, RenderHelper } from './filler-interface'; -export declare function getFiller(renderer: RenderHelper, o: ResolvedOptions): PatternFiller; +import { PatternFiller } from './filler-interface'; +export declare function getFiller(o: ResolvedOptions): PatternFiller; diff --git a/bin/fillers/filler.js b/bin/fillers/filler.js index 3f51a2e..b2a4ab4 100644 --- a/bin/fillers/filler.js +++ b/bin/fillers/filler.js @@ -3,30 +3,30 @@ import { ZigZagFiller } from './zigzag-filler'; import { HatchFiller } from './hatch-filler'; import { DotFiller } from './dot-filler'; const fillers = {}; -export function getFiller(renderer, o) { +export function getFiller(o) { let fillerName = o.fillStyle || 'hachure'; if (!fillers[fillerName]) { switch (fillerName) { case 'zigzag': if (!fillers[fillerName]) { - fillers[fillerName] = new ZigZagFiller(renderer); + fillers[fillerName] = new ZigZagFiller(); } break; case 'cross-hatch': if (!fillers[fillerName]) { - fillers[fillerName] = new HatchFiller(renderer); + fillers[fillerName] = new HatchFiller(); } break; case 'dots': if (!fillers[fillerName]) { - fillers[fillerName] = new DotFiller(renderer); + fillers[fillerName] = new DotFiller(); } break; case 'hachure': default: fillerName = 'hachure'; if (!fillers[fillerName]) { - fillers[fillerName] = new HachureFiller(renderer); + fillers[fillerName] = new HachureFiller(); } break; } diff --git a/bin/fillers/hachure-filler.d.ts b/bin/fillers/hachure-filler.d.ts index 387d7bc..4b98869 100644 --- a/bin/fillers/hachure-filler.d.ts +++ b/bin/fillers/hachure-filler.d.ts @@ -1,9 +1,7 @@ -import { PatternFiller, RenderHelper } from './filler-interface'; +import { PatternFiller } from './filler-interface'; import { ResolvedOptions, OpSet } from '../core'; import { Point } from '../geometry'; export declare class HachureFiller implements PatternFiller { - renderer: RenderHelper; - constructor(renderer: RenderHelper); fillPolygon(points: Point[], o: ResolvedOptions): OpSet; fillEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions): OpSet; protected _fillPolygon(points: Point[], o: ResolvedOptions, connectEnds?: boolean): OpSet; diff --git a/bin/fillers/hachure-filler.js b/bin/fillers/hachure-filler.js index c193186..f9c3a71 100644 --- a/bin/fillers/hachure-filler.js +++ b/bin/fillers/hachure-filler.js @@ -1,8 +1,6 @@ import { hachureLinesForPolygon, hachureLinesForEllipse } from './filler-utils'; +import { doubleLineOps } from '../renderer'; export class HachureFiller { - constructor(renderer) { - this.renderer = renderer; - } fillPolygon(points, o) { return this._fillPolygon(points, o); } @@ -15,7 +13,7 @@ export class HachureFiller { return { type: 'fillSketch', ops }; } _fillEllipse(cx, cy, width, height, o, connectEnds = false) { - const lines = hachureLinesForEllipse(cx, cy, width, height, o, this.renderer); + const lines = hachureLinesForEllipse(cx, cy, width, height, o); const ops = this.renderLines(lines, o, connectEnds); return { type: 'fillSketch', ops }; } @@ -23,9 +21,9 @@ export class HachureFiller { let ops = []; let prevPoint = null; for (const line of lines) { - ops = ops.concat(this.renderer.doubleLine(line[0][0], line[0][1], line[1][0], line[1][1], o)); + ops = ops.concat(doubleLineOps(line[0][0], line[0][1], line[1][0], line[1][1], o)); if (connectEnds && prevPoint) { - ops = ops.concat(this.renderer.doubleLine(prevPoint[0], prevPoint[1], line[0][0], line[0][1], o)); + ops = ops.concat(doubleLineOps(prevPoint[0], prevPoint[1], line[0][0], line[0][1], o)); } prevPoint = line[1]; } diff --git a/bin/generator-base.d.ts b/bin/generator-base.d.ts index 24fbd61..bb2870a 100644 --- a/bin/generator-base.d.ts +++ b/bin/generator-base.d.ts @@ -1,15 +1,12 @@ -import { RoughRenderer } from './renderer.js'; import { Config, DrawingSurface, Options, ResolvedOptions, Drawable, OpSet, PathInfo } from './core'; import { Point } from './geometry.js'; export declare abstract class RoughGeneratorBase { protected config: Config; protected surface: DrawingSurface; - protected renderer: RoughRenderer; defaultOptions: ResolvedOptions; constructor(config: Config | null, surface: DrawingSurface); protected _options(options?: Options): ResolvedOptions; protected _drawable(shape: string, sets: OpSet[], options: ResolvedOptions): Drawable; - protected readonly lib: RoughRenderer; private getCanvasSize; protected computePolygonSize(points: Point[]): Point; protected polygonPath(points: Point[]): string; diff --git a/bin/generator-base.js b/bin/generator-base.js index 2fb76ef..cab4d13 100644 --- a/bin/generator-base.js +++ b/bin/generator-base.js @@ -1,4 +1,3 @@ -import { createRenderer } from './renderer-factory.js'; const hasSelf = typeof self !== 'undefined'; export class RoughGeneratorBase { constructor(config, surface) { @@ -17,7 +16,6 @@ export class RoughGeneratorBase { }; this.config = config || {}; this.surface = surface; - this.renderer = createRenderer(this.config); if (this.config.options) { this.defaultOptions = this._options(this.config.options); } @@ -28,9 +26,6 @@ export class RoughGeneratorBase { _drawable(shape, sets, options) { return { shape, sets: sets || [], options: options || this.defaultOptions }; } - get lib() { - return this.renderer; - } getCanvasSize() { const val = (w) => { if (w && typeof w === 'object') { diff --git a/bin/generator.d.ts b/bin/generator.d.ts index 4b4e4af..22bfe5b 100644 --- a/bin/generator.d.ts +++ b/bin/generator.d.ts @@ -1,6 +1,6 @@ import { Config, DrawingSurface, Options, Drawable } from './core'; import { Point } from './geometry.js'; -import { RoughGeneratorBase } from './generator-base'; +import { RoughGeneratorBase } from './generator-base.js'; export declare class RoughGenerator extends RoughGeneratorBase { constructor(config: Config | null, surface: DrawingSurface); line(x1: number, y1: number, x2: number, y2: number, options?: Options): Drawable; diff --git a/bin/generator.js b/bin/generator.js index 3b4661c..da54717 100644 --- a/bin/generator.js +++ b/bin/generator.js @@ -1,11 +1,12 @@ -import { RoughGeneratorBase } from './generator-base'; +import { RoughGeneratorBase } from './generator-base.js'; +import { line, solidFillPolygon, patternFillPolygon, rectangle, ellipse, patternFillEllipse, linearPath, arc, patternFillArc, curve, svgPath } from './renderer.js'; export class RoughGenerator extends RoughGeneratorBase { constructor(config, surface) { super(config, surface); } line(x1, y1, x2, y2, options) { const o = this._options(options); - return this._drawable('line', [this.lib.line(x1, y1, x2, y2, o)], o); + return this._drawable('line', [line(x1, y1, x2, y2, o)], o); } rectangle(x, y, width, height, options) { const o = this._options(options); @@ -13,13 +14,13 @@ export class RoughGenerator extends RoughGeneratorBase { if (o.fill) { const points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; if (o.fillStyle === 'solid') { - paths.push(this.lib.solidFillPolygon(points, o)); + paths.push(solidFillPolygon(points, o)); } else { - paths.push(this.lib.patternFillPolygon(points, o)); + paths.push(patternFillPolygon(points, o)); } } - paths.push(this.lib.rectangle(x, y, width, height, o)); + paths.push(rectangle(x, y, width, height, o)); return this._drawable('rectangle', paths, o); } ellipse(x, y, width, height, options) { @@ -27,15 +28,15 @@ export class RoughGenerator extends RoughGeneratorBase { const paths = []; if (o.fill) { if (o.fillStyle === 'solid') { - const shape = this.lib.ellipse(x, y, width, height, o); + const shape = ellipse(x, y, width, height, o); shape.type = 'fillPath'; paths.push(shape); } else { - paths.push(this.lib.patternFillEllipse(x, y, width, height, o)); + paths.push(patternFillEllipse(x, y, width, height, o)); } } - paths.push(this.lib.ellipse(x, y, width, height, o)); + paths.push(ellipse(x, y, width, height, o)); return this._drawable('ellipse', paths, o); } circle(x, y, diameter, options) { @@ -45,34 +46,34 @@ export class RoughGenerator extends RoughGeneratorBase { } linearPath(points, options) { const o = this._options(options); - return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); + return this._drawable('linearPath', [linearPath(points, false, o)], o); } arc(x, y, width, height, start, stop, closed = false, options) { const o = this._options(options); const paths = []; if (closed && o.fill) { if (o.fillStyle === 'solid') { - const shape = this.lib.arc(x, y, width, height, start, stop, true, false, o); + const shape = arc(x, y, width, height, start, stop, true, false, o); shape.type = 'fillPath'; paths.push(shape); } else { - paths.push(this.lib.patternFillArc(x, y, width, height, start, stop, o)); + paths.push(patternFillArc(x, y, width, height, start, stop, o)); } } - paths.push(this.lib.arc(x, y, width, height, start, stop, closed, true, o)); + paths.push(arc(x, y, width, height, start, stop, closed, true, o)); return this._drawable('arc', paths, o); } curve(points, options) { const o = this._options(options); - return this._drawable('curve', [this.lib.curve(points, o)], o); + return this._drawable('curve', [curve(points, o)], o); } polygon(points, options) { const o = this._options(options); const paths = []; if (o.fill) { if (o.fillStyle === 'solid') { - paths.push(this.lib.solidFillPolygon(points, o)); + paths.push(solidFillPolygon(points, o)); } else { const size = this.computePolygonSize(points); @@ -82,14 +83,14 @@ export class RoughGenerator extends RoughGeneratorBase { [size[0], size[1]], [0, size[1]] ]; - const shape = this.lib.patternFillPolygon(fillPoints, o); + const shape = patternFillPolygon(fillPoints, o); shape.type = 'path2Dpattern'; shape.size = size; shape.path = this.polygonPath(points); paths.push(shape); } } - paths.push(this.lib.linearPath(points, true, o)); + paths.push(linearPath(points, true, o)); return this._drawable('polygon', paths, o); } path(d, options) { @@ -111,14 +112,14 @@ export class RoughGenerator extends RoughGeneratorBase { [size[0], size[1]], [0, size[1]] ]; - const shape = this.lib.patternFillPolygon(points, o); + const shape = patternFillPolygon(points, o); shape.type = 'path2Dpattern'; shape.size = size; shape.path = d; paths.push(shape); } } - paths.push(this.lib.svgPath(d, o)); + paths.push(svgPath(d, o)); return this._drawable('path', paths, o); } } diff --git a/bin/renderer-factory-old.d.ts b/bin/renderer-factory-old.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/bin/renderer-factory-old.js b/bin/renderer-factory-old.js new file mode 100644 index 0000000..1a837bf --- /dev/null +++ b/bin/renderer-factory-old.js @@ -0,0 +1,16 @@ +"use strict"; +// import { Config } from './core'; +// import { RoughRenderer } from './renderer'; +// const hasSelf = typeof self !== 'undefined'; +// const roughScript = hasSelf && self && self.document && self.document.currentScript && (self.document.currentScript as HTMLScriptElement).src; +// export function createRenderer(config: Config): RoughRenderer { +// if (hasSelf && roughScript && self && (self as any).workly && config.async && (!config.noWorker)) { +// const worklySource = config.worklyURL || 'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js'; +// if (worklySource) { +// const code = `importScripts('${worklySource}', '${roughScript}');\nworkly.expose(self.rough.createRenderer());`; +// const ourl = URL.createObjectURL(new Blob([code])); +// return (self as any).workly.proxy(ourl); +// } +// } +// return new RoughRenderer(); +// } diff --git a/bin/renderer-new.d.ts b/bin/renderer-new.d.ts new file mode 100644 index 0000000..e36a997 --- /dev/null +++ b/bin/renderer-new.d.ts @@ -0,0 +1,14 @@ +import { ResolvedOptions, OpSet } from './core'; +import { Point } from './geometry'; +export declare function line(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): OpSet; +export declare function linearPath(points: Point[], close: boolean, o: ResolvedOptions): OpSet; +export declare function polygon(points: Point[], o: ResolvedOptions): OpSet; +export declare function rectangle(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet; +export declare function curve(points: Point[], o: ResolvedOptions): OpSet; +export declare function ellipse(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet; +export declare function arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean, roughClosure: boolean, o: ResolvedOptions): OpSet; +export declare function svgPath(path: string, o: ResolvedOptions): OpSet; +export declare function solidFillPolygon(points: Point[], o: ResolvedOptions): OpSet; +export declare function patternFillPolygon(points: Point[], o: ResolvedOptions): OpSet; +export declare function patternFillEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions): OpSet; +export declare function patternFillArc(x: number, y: number, width: number, height: number, start: number, stop: number, o: ResolvedOptions): OpSet; diff --git a/bin/renderer-new.js b/bin/renderer-new.js new file mode 100644 index 0000000..c8b9503 --- /dev/null +++ b/bin/renderer-new.js @@ -0,0 +1,598 @@ +import { RoughPath, PathFitter, RoughArcConverter } from './path.js'; +import { getFiller } from './fillers/filler'; +export function line(x1, y1, x2, y2, o) { + return { type: 'path', ops: _doubleLine(x1, y1, x2, y2, o) }; +} +export function linearPath(points, close, o) { + const len = (points || []).length; + if (len > 2) { + let ops = []; + for (let i = 0; i < (len - 1); i++) { + ops = ops.concat(_doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); + } + if (close) { + ops = ops.concat(_doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o)); + } + return { type: 'path', ops }; + } + else if (len === 2) { + return line(points[0][0], points[0][1], points[1][0], points[1][1], o); + } + return { type: 'path', ops: [] }; +} +export function polygon(points, o) { + return linearPath(points, true, o); +} +export function rectangle(x, y, width, height, o) { + const points = [ + [x, y], [x + width, y], [x + width, y + height], [x, y + height] + ]; + return polygon(points, o); +} +export function curve(points, o) { + const o1 = _curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); + const o2 = _curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); + return { type: 'path', ops: o1.concat(o2) }; +} +export function ellipse(x, y, width, height, o) { + const increment = (Math.PI * 2) / o.curveStepCount; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += _offsetOpt(rx * 0.05, o); + ry += _offsetOpt(ry * 0.05, o); + const o1 = _ellipse(increment, x, y, rx, ry, 1, increment * _offset(0.1, _offset(0.4, 1, o), o), o); + const o2 = _ellipse(increment, x, y, rx, ry, 1.5, 0, o); + return { type: 'path', ops: o1.concat(o2) }; +} +export function arc(x, y, width, height, start, stop, closed, roughClosure, o) { + const cx = x; + const cy = y; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += _offsetOpt(rx * 0.01, o); + ry += _offsetOpt(ry * 0.01, o); + let strt = start; + let stp = stop; + while (strt < 0) { + strt += Math.PI * 2; + stp += Math.PI * 2; + } + if ((stp - strt) > (Math.PI * 2)) { + strt = 0; + stp = Math.PI * 2; + } + const ellipseInc = (Math.PI * 2) / o.curveStepCount; + const arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2); + const o1 = _arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); + const o2 = _arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); + let ops = o1.concat(o2); + if (closed) { + if (roughClosure) { + ops = ops.concat(_doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); + ops = ops.concat(_doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); + } + else { + ops.push({ op: 'lineTo', data: [cx, cy] }); + ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); + } + } + return { type: 'path', ops }; +} +export function svgPath(path, o) { + path = (path || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' '); + let p = new RoughPath(path); + if (o.simplification) { + const fitter = new PathFitter(p.linearPoints, p.closed); + const d = fitter.fit(o.simplification); + p = new RoughPath(d); + } + let ops = []; + const segments = p.segments || []; + for (let i = 0; i < segments.length; i++) { + const s = segments[i]; + const prev = i > 0 ? segments[i - 1] : null; + const opList = _processSegment(p, s, prev, o); + if (opList && opList.length) { + ops = ops.concat(opList); + } + } + return { type: 'path', ops }; +} +// Fills +export function solidFillPolygon(points, o) { + const ops = []; + if (points.length) { + const offset = o.maxRandomnessOffset || 0; + const len = points.length; + if (len > 2) { + ops.push({ op: 'move', data: [points[0][0] + _offsetOpt(offset, o), points[0][1] + _offsetOpt(offset, o)] }); + for (let i = 1; i < len; i++) { + ops.push({ op: 'lineTo', data: [points[i][0] + _offsetOpt(offset, o), points[i][1] + _offsetOpt(offset, o)] }); + } + } + } + return { type: 'fillPath', ops }; +} +export function patternFillPolygon(points, o) { + return getFiller(o).fillPolygon(points, o); +} +export function patternFillEllipse(cx, cy, width, height, o) { + return getFiller(o).fillEllipse(cx, cy, width, height, o); +} +export function patternFillArc(x, y, width, height, start, stop, o) { + const cx = x; + const cy = y; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += _offsetOpt(rx * 0.01, o); + ry += _offsetOpt(ry * 0.01, o); + let strt = start; + let stp = stop; + while (strt < 0) { + strt += Math.PI * 2; + stp += Math.PI * 2; + } + if ((stp - strt) > (Math.PI * 2)) { + strt = 0; + stp = Math.PI * 2; + } + const increment = (stp - strt) / o.curveStepCount; + const points = []; + for (let angle = strt; angle <= stp; angle = angle + increment) { + points.push([cx + rx * Math.cos(angle), cy + ry * Math.sin(angle)]); + } + points.push([cx + rx * Math.cos(stp), cy + ry * Math.sin(stp)]); + points.push([cx, cy]); + return patternFillPolygon(points, o); +} +// Private helpers +function _offset(min, max, ops) { + return ops.roughness * ((Math.random() * (max - min)) + min); +} +function _offsetOpt(x, ops) { + return _offset(-x, x, ops); +} +function _doubleLine(x1, y1, x2, y2, o) { + const o1 = _line(x1, y1, x2, y2, o, true, false); + const o2 = _line(x1, y1, x2, y2, o, true, true); + return o1.concat(o2); +} +function _line(x1, y1, x2, y2, o, move, overlay) { + const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); + let offset = o.maxRandomnessOffset || 0; + if ((offset * offset * 100) > lengthSq) { + offset = Math.sqrt(lengthSq) / 10; + } + const halfOffset = offset / 2; + const divergePoint = 0.2 + Math.random() * 0.2; + let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; + let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; + midDispX = _offsetOpt(midDispX, o); + midDispY = _offsetOpt(midDispY, o); + const ops = []; + const randomHalf = () => _offsetOpt(halfOffset, o); + const randomFull = () => _offsetOpt(offset, o); + if (move) { + if (overlay) { + ops.push({ + op: 'move', data: [ + x1 + randomHalf(), + y1 + randomHalf() + ] + }); + } + else { + ops.push({ + op: 'move', data: [ + x1 + _offsetOpt(offset, o), + y1 + _offsetOpt(offset, o) + ] + }); + } + } + if (overlay) { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + randomHalf(), + midDispY + y1 + (y2 - y1) * divergePoint + randomHalf(), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + randomHalf(), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + randomHalf(), + x2 + randomHalf(), + y2 + randomHalf() + ] + }); + } + else { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + randomFull(), + midDispY + y1 + (y2 - y1) * divergePoint + randomFull(), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + randomFull(), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + randomFull(), + x2 + randomFull(), + y2 + randomFull() + ] + }); + } + return ops; +} +function _curveWithOffset(points, offset, o) { + const ps = []; + ps.push([ + points[0][0] + _offsetOpt(offset, o), + points[0][1] + _offsetOpt(offset, o), + ]); + ps.push([ + points[0][0] + _offsetOpt(offset, o), + points[0][1] + _offsetOpt(offset, o), + ]); + for (let i = 1; i < points.length; i++) { + ps.push([ + points[i][0] + _offsetOpt(offset, o), + points[i][1] + _offsetOpt(offset, o), + ]); + if (i === (points.length - 1)) { + ps.push([ + points[i][0] + _offsetOpt(offset, o), + points[i][1] + _offsetOpt(offset, o), + ]); + } + } + return _curve(ps, null, o); +} +function _curve(points, closePoint, o) { + const len = points.length; + let ops = []; + if (len > 3) { + const b = []; + const s = 1 - o.curveTightness; + ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); + for (let i = 1; (i + 2) < len; i++) { + const cachedVertArray = points[i]; + b[0] = [cachedVertArray[0], cachedVertArray[1]]; + b[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6]; + b[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6]; + b[3] = [points[i + 1][0], points[i + 1][1]]; + ops.push({ op: 'bcurveTo', data: [b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]] }); + } + if (closePoint && closePoint.length === 2) { + const ro = o.maxRandomnessOffset; + ops.push({ op: 'lineTo', data: [closePoint[0] + _offsetOpt(ro, o), closePoint[1] + _offsetOpt(ro, o)] }); + } + } + else if (len === 3) { + ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); + ops.push({ + op: 'bcurveTo', data: [ + points[1][0], points[1][1], + points[2][0], points[2][1], + points[2][0], points[2][1] + ] + }); + } + else if (len === 2) { + ops = ops.concat(_doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); + } + return ops; +} +function _ellipse(increment, cx, cy, rx, ry, offset, overlap, o) { + const radOffset = _offsetOpt(0.5, o) - (Math.PI / 2); + const points = []; + points.push([ + _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { + points.push([ + _offsetOpt(offset, o) + cx + rx * Math.cos(angle), + _offsetOpt(offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + _offsetOpt(offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), + _offsetOpt(offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) + ]); + points.push([ + _offsetOpt(offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), + _offsetOpt(offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) + ]); + points.push([ + _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), + _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) + ]); + return _curve(points, null, o); +} +function _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) { + const radOffset = strt + _offsetOpt(0.1, o); + const points = []; + points.push([ + _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle <= stp; angle = angle + increment) { + points.push([ + _offsetOpt(offset, o) + cx + rx * Math.cos(angle), + _offsetOpt(offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + cx + rx * Math.cos(stp), + cy + ry * Math.sin(stp) + ]); + points.push([ + cx + rx * Math.cos(stp), + cy + ry * Math.sin(stp) + ]); + return _curve(points, null, o); +} +function _bezierTo(x1, y1, x2, y2, x, y, path, o) { + const ops = []; + const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; + let f = [0, 0]; + for (let i = 0; i < 2; i++) { + if (i === 0) { + ops.push({ op: 'move', data: [path.x, path.y] }); + } + else { + ops.push({ op: 'move', data: [path.x + _offsetOpt(ros[0], o), path.y + _offsetOpt(ros[0], o)] }); + } + f = [x + _offsetOpt(ros[i], o), y + _offsetOpt(ros[i], o)]; + ops.push({ + op: 'bcurveTo', data: [ + x1 + _offsetOpt(ros[i], o), y1 + _offsetOpt(ros[i], o), + x2 + _offsetOpt(ros[i], o), y2 + _offsetOpt(ros[i], o), + f[0], f[1] + ] + }); + } + path.setPosition(f[0], f[1]); + return ops; +} +function _processSegment(path, seg, prevSeg, o) { + let ops = []; + switch (seg.key) { + case 'M': + case 'm': { + const delta = seg.key === 'm'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + const ro = 1 * (o.maxRandomnessOffset || 0); + x = x + _offsetOpt(ro, o); + y = y + _offsetOpt(ro, o); + path.setPosition(x, y); + ops.push({ op: 'move', data: [x, y] }); + } + break; + } + case 'L': + case 'l': { + const delta = seg.key === 'l'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + ops = ops.concat(_doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } + break; + } + case 'H': + case 'h': { + const delta = seg.key === 'h'; + if (seg.data.length) { + let x = +seg.data[0]; + if (delta) { + x += path.x; + } + ops = ops.concat(_doubleLine(path.x, path.y, x, path.y, o)); + path.setPosition(x, path.y); + } + break; + } + case 'V': + case 'v': { + const delta = seg.key === 'v'; + if (seg.data.length) { + let y = +seg.data[0]; + if (delta) { + y += path.y; + } + ops = ops.concat(_doubleLine(path.x, path.y, path.x, y, o)); + path.setPosition(path.x, y); + } + break; + } + case 'Z': + case 'z': { + if (path.first) { + ops = ops.concat(_doubleLine(path.x, path.y, path.first[0], path.first[1], o)); + path.setPosition(path.first[0], path.first[1]); + path.first = null; + } + break; + } + case 'C': + case 'c': { + const delta = seg.key === 'c'; + if (seg.data.length >= 6) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x2 = +seg.data[2]; + let y2 = +seg.data[3]; + let x = +seg.data[4]; + let y = +seg.data[5]; + if (delta) { + x1 += path.x; + x2 += path.x; + x += path.x; + y1 += path.y; + y2 += path.y; + y += path.y; + } + const ob = _bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'S': + case 's': { + const delta = seg.key === 's'; + if (seg.data.length >= 4) { + let x2 = +seg.data[0]; + let y2 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x2 += path.x; + x += path.x; + y2 += path.y; + y += path.y; + } + let x1 = x2; + let y1 = y2; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref = null; + if (prevKey === 'c' || prevKey === 'C' || prevKey === 's' || prevKey === 'S') { + ref = path.bezierReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const ob = _bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'Q': + case 'q': { + const delta = seg.key === 'q'; + if (seg.data.length >= 4) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x1 += path.x; + x += path.x; + y1 += path.y; + y += path.y; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + _offsetOpt(offset1, o), path.y + _offsetOpt(offset1, o)] }); + let f = [x + _offsetOpt(offset1, o), y + _offsetOpt(offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + _offsetOpt(offset1, o), y1 + _offsetOpt(offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + _offsetOpt(offset2, o), path.y + _offsetOpt(offset2, o)] }); + f = [x + _offsetOpt(offset2, o), y + _offsetOpt(offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + _offsetOpt(offset2, o), y1 + _offsetOpt(offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'T': + case 't': { + const delta = seg.key === 't'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + let x1 = x; + let y1 = y; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref = null; + if (prevKey === 'q' || prevKey === 'Q' || prevKey === 't' || prevKey === 'T') { + ref = path.quadReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + _offsetOpt(offset1, o), path.y + _offsetOpt(offset1, o)] }); + let f = [x + _offsetOpt(offset1, o), y + _offsetOpt(offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + _offsetOpt(offset1, o), y1 + _offsetOpt(offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + _offsetOpt(offset2, o), path.y + _offsetOpt(offset2, o)] }); + f = [x + _offsetOpt(offset2, o), y + _offsetOpt(offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + _offsetOpt(offset2, o), y1 + _offsetOpt(offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'A': + case 'a': { + const delta = seg.key === 'a'; + if (seg.data.length >= 7) { + const rx = +seg.data[0]; + const ry = +seg.data[1]; + const angle = +seg.data[2]; + const largeArcFlag = +seg.data[3]; + const sweepFlag = +seg.data[4]; + let x = +seg.data[5]; + let y = +seg.data[6]; + if (delta) { + x += path.x; + y += path.y; + } + if (x === path.x && y === path.y) { + break; + } + if (rx === 0 || ry === 0) { + ops = ops.concat(_doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } + else { + for (let i = 0; i < 1; i++) { + const arcConverter = new RoughArcConverter([path.x, path.y], [x, y], [rx, ry], angle, largeArcFlag ? true : false, sweepFlag ? true : false); + let segment = arcConverter.getNextSegment(); + while (segment) { + const ob = _bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); + ops = ops.concat(ob); + segment = arcConverter.getNextSegment(); + } + } + } + } + break; + } + default: + break; + } + return ops; +} diff --git a/bin/renderer-old.d.ts b/bin/renderer-old.d.ts new file mode 100644 index 0000000..a866dc8 --- /dev/null +++ b/bin/renderer-old.d.ts @@ -0,0 +1,25 @@ +import { ResolvedOptions, OpSet, Op } from './core'; +import { Point } from './geometry'; +export declare class RoughRenderer { + line(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): OpSet; + linearPath(points: Point[], close: boolean, o: ResolvedOptions): OpSet; + polygon(points: Point[], o: ResolvedOptions): OpSet; + rectangle(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet; + curve(points: Point[], o: ResolvedOptions): OpSet; + ellipse(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet; + arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean, roughClosure: boolean, o: ResolvedOptions): OpSet; + svgPath(path: string, o: ResolvedOptions): OpSet; + solidFillPolygon(points: Point[], o: ResolvedOptions): OpSet; + patternFillPolygon(points: Point[], o: ResolvedOptions): OpSet; + patternFillEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions): OpSet; + patternFillArc(x: number, y: number, width: number, height: number, start: number, stop: number, o: ResolvedOptions): OpSet; + getOffset(min: number, max: number, ops: ResolvedOptions): number; + doubleLine(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): Op[]; + private _line; + private _curve; + private _ellipse; + private _curveWithOffset; + private _arc; + private _bezierTo; + private _processSegment; +} diff --git a/bin/renderer-old.js b/bin/renderer-old.js new file mode 100644 index 0000000..00bf745 --- /dev/null +++ b/bin/renderer-old.js @@ -0,0 +1,597 @@ +import { RoughPath, RoughArcConverter, PathFitter } from './path.js'; +import { getFiller } from './fillers/filler'; +export class RoughRenderer { + line(x1, y1, x2, y2, o) { + const ops = this.doubleLine(x1, y1, x2, y2, o); + return { type: 'path', ops }; + } + linearPath(points, close, o) { + const len = (points || []).length; + if (len > 2) { + let ops = []; + for (let i = 0; i < (len - 1); i++) { + ops = ops.concat(this.doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); + } + if (close) { + ops = ops.concat(this.doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o)); + } + return { type: 'path', ops }; + } + else if (len === 2) { + return this.line(points[0][0], points[0][1], points[1][0], points[1][1], o); + } + return { type: 'path', ops: [] }; + } + polygon(points, o) { + return this.linearPath(points, true, o); + } + rectangle(x, y, width, height, o) { + const points = [ + [x, y], [x + width, y], [x + width, y + height], [x, y + height] + ]; + return this.polygon(points, o); + } + curve(points, o) { + const o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); + const o2 = this._curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); + return { type: 'path', ops: o1.concat(o2) }; + } + ellipse(x, y, width, height, o) { + const increment = (Math.PI * 2) / o.curveStepCount; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.05, rx * 0.05, o); + ry += this.getOffset(-ry * 0.05, ry * 0.05, o); + const o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this.getOffset(0.1, this.getOffset(0.4, 1, o), o), o); + const o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o); + return { type: 'path', ops: o1.concat(o2) }; + } + arc(x, y, width, height, start, stop, closed, roughClosure, o) { + const cx = x; + const cy = y; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, ry * 0.01, o); + let strt = start; + let stp = stop; + while (strt < 0) { + strt += Math.PI * 2; + stp += Math.PI * 2; + } + if ((stp - strt) > (Math.PI * 2)) { + strt = 0; + stp = Math.PI * 2; + } + const ellipseInc = (Math.PI * 2) / o.curveStepCount; + const arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2); + const o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); + const o2 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); + let ops = o1.concat(o2); + if (closed) { + if (roughClosure) { + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); + } + else { + ops.push({ op: 'lineTo', data: [cx, cy] }); + ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); + } + } + return { type: 'path', ops }; + } + svgPath(path, o) { + path = (path || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' '); + let p = new RoughPath(path); + if (o.simplification) { + const fitter = new PathFitter(p.linearPoints, p.closed); + const d = fitter.fit(o.simplification); + p = new RoughPath(d); + } + let ops = []; + const segments = p.segments || []; + for (let i = 0; i < segments.length; i++) { + const s = segments[i]; + const prev = i > 0 ? segments[i - 1] : null; + const opList = this._processSegment(p, s, prev, o); + if (opList && opList.length) { + ops = ops.concat(opList); + } + } + return { type: 'path', ops }; + } + solidFillPolygon(points, o) { + const ops = []; + if (points.length) { + const offset = o.maxRandomnessOffset || 0; + const len = points.length; + if (len > 2) { + ops.push({ op: 'move', data: [points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)] }); + for (let i = 1; i < len; i++) { + ops.push({ op: 'lineTo', data: [points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)] }); + } + } + } + return { type: 'fillPath', ops }; + } + patternFillPolygon(points, o) { + const filler = getFiller(this, o); + return filler.fillPolygon(points, o); + } + patternFillEllipse(cx, cy, width, height, o) { + const filler = getFiller(this, o); + return filler.fillEllipse(cx, cy, width, height, o); + } + patternFillArc(x, y, width, height, start, stop, o) { + const cx = x; + const cy = y; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, ry * 0.01, o); + let strt = start; + let stp = stop; + while (strt < 0) { + strt += Math.PI * 2; + stp += Math.PI * 2; + } + if ((stp - strt) > (Math.PI * 2)) { + strt = 0; + stp = Math.PI * 2; + } + const increment = (stp - strt) / o.curveStepCount; + const points = []; + for (let angle = strt; angle <= stp; angle = angle + increment) { + points.push([cx + rx * Math.cos(angle), cy + ry * Math.sin(angle)]); + } + points.push([cx + rx * Math.cos(stp), cy + ry * Math.sin(stp)]); + points.push([cx, cy]); + return this.patternFillPolygon(points, o); + } + /// + getOffset(min, max, ops) { + return ops.roughness * ((Math.random() * (max - min)) + min); + } + doubleLine(x1, y1, x2, y2, o) { + const o1 = this._line(x1, y1, x2, y2, o, true, false); + const o2 = this._line(x1, y1, x2, y2, o, true, true); + return o1.concat(o2); + } + _line(x1, y1, x2, y2, o, move, overlay) { + const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); + let offset = o.maxRandomnessOffset || 0; + if ((offset * offset * 100) > lengthSq) { + offset = Math.sqrt(lengthSq) / 10; + } + const halfOffset = offset / 2; + const divergePoint = 0.2 + Math.random() * 0.2; + let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; + let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; + midDispX = this.getOffset(-midDispX, midDispX, o); + midDispY = this.getOffset(-midDispY, midDispY, o); + const ops = []; + if (move) { + if (overlay) { + ops.push({ + op: 'move', data: [ + x1 + this.getOffset(-halfOffset, halfOffset, o), + y1 + this.getOffset(-halfOffset, halfOffset, o) + ] + }); + } + else { + ops.push({ + op: 'move', data: [ + x1 + this.getOffset(-offset, offset, o), + y1 + this.getOffset(-offset, offset, o) + ] + }); + } + } + if (overlay) { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + x2 + this.getOffset(-halfOffset, halfOffset, o), + y2 + this.getOffset(-halfOffset, halfOffset, o) + ] + }); + } + else { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), + midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), + x2 + this.getOffset(-offset, offset, o), + y2 + this.getOffset(-offset, offset, o) + ] + }); + } + return ops; + } + _curve(points, closePoint, o) { + const len = points.length; + let ops = []; + if (len > 3) { + const b = []; + const s = 1 - o.curveTightness; + ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); + for (let i = 1; (i + 2) < len; i++) { + const cachedVertArray = points[i]; + b[0] = [cachedVertArray[0], cachedVertArray[1]]; + b[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6]; + b[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6]; + b[3] = [points[i + 1][0], points[i + 1][1]]; + ops.push({ op: 'bcurveTo', data: [b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]] }); + } + if (closePoint && closePoint.length === 2) { + const ro = o.maxRandomnessOffset; + ops.push({ op: 'lineTo', data: [closePoint[0] + this.getOffset(-ro, ro, o), closePoint[1] + +this.getOffset(-ro, ro, o)] }); + } + } + else if (len === 3) { + ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); + ops.push({ + op: 'bcurveTo', data: [ + points[1][0], points[1][1], + points[2][0], points[2][1], + points[2][0], points[2][1] + ] + }); + } + else if (len === 2) { + ops = ops.concat(this.doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); + } + return ops; + } + _ellipse(increment, cx, cy, rx, ry, offset, overlap, o) { + const radOffset = this.getOffset(-0.5, 0.5, o) - (Math.PI / 2); + const points = []; + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) + ]); + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), + this.getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) + ]); + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) + ]); + return this._curve(points, null, o); + } + _curveWithOffset(points, offset, o) { + const ps = []; + ps.push([ + points[0][0] + this.getOffset(-offset, offset, o), + points[0][1] + this.getOffset(-offset, offset, o), + ]); + ps.push([ + points[0][0] + this.getOffset(-offset, offset, o), + points[0][1] + this.getOffset(-offset, offset, o), + ]); + for (let i = 1; i < points.length; i++) { + ps.push([ + points[i][0] + this.getOffset(-offset, offset, o), + points[i][1] + this.getOffset(-offset, offset, o), + ]); + if (i === (points.length - 1)) { + ps.push([ + points[i][0] + this.getOffset(-offset, offset, o), + points[i][1] + this.getOffset(-offset, offset, o), + ]); + } + } + return this._curve(ps, null, o); + } + _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) { + const radOffset = strt + this.getOffset(-0.1, 0.1, o); + const points = []; + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle <= stp; angle = angle + increment) { + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + cx + rx * Math.cos(stp), + cy + ry * Math.sin(stp) + ]); + points.push([ + cx + rx * Math.cos(stp), + cy + ry * Math.sin(stp) + ]); + return this._curve(points, null, o); + } + _bezierTo(x1, y1, x2, y2, x, y, path, o) { + const ops = []; + const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; + let f = [0, 0]; + for (let i = 0; i < 2; i++) { + if (i === 0) { + ops.push({ op: 'move', data: [path.x, path.y] }); + } + else { + ops.push({ op: 'move', data: [path.x + this.getOffset(-ros[0], ros[0], o), path.y + this.getOffset(-ros[0], ros[0], o)] }); + } + f = [x + this.getOffset(-ros[i], ros[i], o), y + this.getOffset(-ros[i], ros[i], o)]; + ops.push({ + op: 'bcurveTo', data: [ + x1 + this.getOffset(-ros[i], ros[i], o), y1 + this.getOffset(-ros[i], ros[i], o), + x2 + this.getOffset(-ros[i], ros[i], o), y2 + this.getOffset(-ros[i], ros[i], o), + f[0], f[1] + ] + }); + } + path.setPosition(f[0], f[1]); + return ops; + } + _processSegment(path, seg, prevSeg, o) { + let ops = []; + switch (seg.key) { + case 'M': + case 'm': { + const delta = seg.key === 'm'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + const ro = 1 * (o.maxRandomnessOffset || 0); + x = x + this.getOffset(-ro, ro, o); + y = y + this.getOffset(-ro, ro, o); + path.setPosition(x, y); + ops.push({ op: 'move', data: [x, y] }); + } + break; + } + case 'L': + case 'l': { + const delta = seg.key === 'l'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } + break; + } + case 'H': + case 'h': { + const delta = seg.key === 'h'; + if (seg.data.length) { + let x = +seg.data[0]; + if (delta) { + x += path.x; + } + ops = ops.concat(this.doubleLine(path.x, path.y, x, path.y, o)); + path.setPosition(x, path.y); + } + break; + } + case 'V': + case 'v': { + const delta = seg.key === 'v'; + if (seg.data.length) { + let y = +seg.data[0]; + if (delta) { + y += path.y; + } + ops = ops.concat(this.doubleLine(path.x, path.y, path.x, y, o)); + path.setPosition(path.x, y); + } + break; + } + case 'Z': + case 'z': { + if (path.first) { + ops = ops.concat(this.doubleLine(path.x, path.y, path.first[0], path.first[1], o)); + path.setPosition(path.first[0], path.first[1]); + path.first = null; + } + break; + } + case 'C': + case 'c': { + const delta = seg.key === 'c'; + if (seg.data.length >= 6) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x2 = +seg.data[2]; + let y2 = +seg.data[3]; + let x = +seg.data[4]; + let y = +seg.data[5]; + if (delta) { + x1 += path.x; + x2 += path.x; + x += path.x; + y1 += path.y; + y2 += path.y; + y += path.y; + } + const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'S': + case 's': { + const delta = seg.key === 's'; + if (seg.data.length >= 4) { + let x2 = +seg.data[0]; + let y2 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x2 += path.x; + x += path.x; + y2 += path.y; + y += path.y; + } + let x1 = x2; + let y1 = y2; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref = null; + if (prevKey === 'c' || prevKey === 'C' || prevKey === 's' || prevKey === 'S') { + ref = path.bezierReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'Q': + case 'q': { + const delta = seg.key === 'q'; + if (seg.data.length >= 4) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x1 += path.x; + x += path.x; + y1 += path.y; + y += path.y; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); + let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); + f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'T': + case 't': { + const delta = seg.key === 't'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + let x1 = x; + let y1 = y; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref = null; + if (prevKey === 'q' || prevKey === 'Q' || prevKey === 't' || prevKey === 'T') { + ref = path.quadReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); + let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); + f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'A': + case 'a': { + const delta = seg.key === 'a'; + if (seg.data.length >= 7) { + const rx = +seg.data[0]; + const ry = +seg.data[1]; + const angle = +seg.data[2]; + const largeArcFlag = +seg.data[3]; + const sweepFlag = +seg.data[4]; + let x = +seg.data[5]; + let y = +seg.data[6]; + if (delta) { + x += path.x; + y += path.y; + } + if (x === path.x && y === path.y) { + break; + } + if (rx === 0 || ry === 0) { + ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } + else { + for (let i = 0; i < 1; i++) { + const arcConverter = new RoughArcConverter([path.x, path.y], [x, y], [rx, ry], angle, largeArcFlag ? true : false, sweepFlag ? true : false); + let segment = arcConverter.getNextSegment(); + while (segment) { + const ob = this._bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); + ops = ops.concat(ob); + segment = arcConverter.getNextSegment(); + } + } + } + } + break; + } + default: + break; + } + return ops; + } +} diff --git a/bin/renderer.d.ts b/bin/renderer.d.ts index a866dc8..e5d1c1c 100644 --- a/bin/renderer.d.ts +++ b/bin/renderer.d.ts @@ -1,25 +1,17 @@ -import { ResolvedOptions, OpSet, Op } from './core'; +import { ResolvedOptions, Op, OpSet } from './core'; import { Point } from './geometry'; -export declare class RoughRenderer { - line(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): OpSet; - linearPath(points: Point[], close: boolean, o: ResolvedOptions): OpSet; - polygon(points: Point[], o: ResolvedOptions): OpSet; - rectangle(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet; - curve(points: Point[], o: ResolvedOptions): OpSet; - ellipse(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet; - arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean, roughClosure: boolean, o: ResolvedOptions): OpSet; - svgPath(path: string, o: ResolvedOptions): OpSet; - solidFillPolygon(points: Point[], o: ResolvedOptions): OpSet; - patternFillPolygon(points: Point[], o: ResolvedOptions): OpSet; - patternFillEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions): OpSet; - patternFillArc(x: number, y: number, width: number, height: number, start: number, stop: number, o: ResolvedOptions): OpSet; - getOffset(min: number, max: number, ops: ResolvedOptions): number; - doubleLine(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): Op[]; - private _line; - private _curve; - private _ellipse; - private _curveWithOffset; - private _arc; - private _bezierTo; - private _processSegment; -} +export declare function line(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): OpSet; +export declare function linearPath(points: Point[], close: boolean, o: ResolvedOptions): OpSet; +export declare function polygon(points: Point[], o: ResolvedOptions): OpSet; +export declare function rectangle(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet; +export declare function curve(points: Point[], o: ResolvedOptions): OpSet; +export declare function ellipse(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet; +export declare function arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean, roughClosure: boolean, o: ResolvedOptions): OpSet; +export declare function svgPath(path: string, o: ResolvedOptions): OpSet; +export declare function solidFillPolygon(points: Point[], o: ResolvedOptions): OpSet; +export declare function patternFillPolygon(points: Point[], o: ResolvedOptions): OpSet; +export declare function patternFillEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions): OpSet; +export declare function patternFillArc(x: number, y: number, width: number, height: number, start: number, stop: number, o: ResolvedOptions): OpSet; +export declare function randOffset(x: number, o: ResolvedOptions): number; +export declare function randOffsetWithRange(min: number, max: number, o: ResolvedOptions): number; +export declare function doubleLineOps(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): Op[]; diff --git a/bin/renderer.js b/bin/renderer.js index 00bf745..d1a9049 100644 --- a/bin/renderer.js +++ b/bin/renderer.js @@ -1,597 +1,607 @@ -import { RoughPath, RoughArcConverter, PathFitter } from './path.js'; +import { RoughPath, PathFitter, RoughArcConverter } from './path.js'; import { getFiller } from './fillers/filler'; -export class RoughRenderer { - line(x1, y1, x2, y2, o) { - const ops = this.doubleLine(x1, y1, x2, y2, o); - return { type: 'path', ops }; - } - linearPath(points, close, o) { - const len = (points || []).length; - if (len > 2) { - let ops = []; - for (let i = 0; i < (len - 1); i++) { - ops = ops.concat(this.doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); - } - if (close) { - ops = ops.concat(this.doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o)); - } - return { type: 'path', ops }; - } - else if (len === 2) { - return this.line(points[0][0], points[0][1], points[1][0], points[1][1], o); - } - return { type: 'path', ops: [] }; - } - polygon(points, o) { - return this.linearPath(points, true, o); - } - rectangle(x, y, width, height, o) { - const points = [ - [x, y], [x + width, y], [x + width, y + height], [x, y + height] - ]; - return this.polygon(points, o); - } - curve(points, o) { - const o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); - const o2 = this._curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); - return { type: 'path', ops: o1.concat(o2) }; - } - ellipse(x, y, width, height, o) { - const increment = (Math.PI * 2) / o.curveStepCount; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this.getOffset(-rx * 0.05, rx * 0.05, o); - ry += this.getOffset(-ry * 0.05, ry * 0.05, o); - const o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this.getOffset(0.1, this.getOffset(0.4, 1, o), o), o); - const o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o); - return { type: 'path', ops: o1.concat(o2) }; - } - arc(x, y, width, height, start, stop, closed, roughClosure, o) { - const cx = x; - const cy = y; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this.getOffset(-rx * 0.01, rx * 0.01, o); - ry += this.getOffset(-ry * 0.01, ry * 0.01, o); - let strt = start; - let stp = stop; - while (strt < 0) { - strt += Math.PI * 2; - stp += Math.PI * 2; - } - if ((stp - strt) > (Math.PI * 2)) { - strt = 0; - stp = Math.PI * 2; - } - const ellipseInc = (Math.PI * 2) / o.curveStepCount; - const arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2); - const o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); - const o2 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); - let ops = o1.concat(o2); - if (closed) { - if (roughClosure) { - ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); - ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); - } - else { - ops.push({ op: 'lineTo', data: [cx, cy] }); - ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); - } - } - return { type: 'path', ops }; - } - svgPath(path, o) { - path = (path || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' '); - let p = new RoughPath(path); - if (o.simplification) { - const fitter = new PathFitter(p.linearPoints, p.closed); - const d = fitter.fit(o.simplification); - p = new RoughPath(d); - } +export function line(x1, y1, x2, y2, o) { + return { type: 'path', ops: _doubleLine(x1, y1, x2, y2, o) }; +} +export function linearPath(points, close, o) { + const len = (points || []).length; + if (len > 2) { let ops = []; - const segments = p.segments || []; - for (let i = 0; i < segments.length; i++) { - const s = segments[i]; - const prev = i > 0 ? segments[i - 1] : null; - const opList = this._processSegment(p, s, prev, o); - if (opList && opList.length) { - ops = ops.concat(opList); - } + for (let i = 0; i < (len - 1); i++) { + ops = ops.concat(_doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); + } + if (close) { + ops = ops.concat(_doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o)); } return { type: 'path', ops }; } - solidFillPolygon(points, o) { - const ops = []; - if (points.length) { - const offset = o.maxRandomnessOffset || 0; - const len = points.length; - if (len > 2) { - ops.push({ op: 'move', data: [points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)] }); - for (let i = 1; i < len; i++) { - ops.push({ op: 'lineTo', data: [points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)] }); - } + else if (len === 2) { + return line(points[0][0], points[0][1], points[1][0], points[1][1], o); + } + return { type: 'path', ops: [] }; +} +export function polygon(points, o) { + return linearPath(points, true, o); +} +export function rectangle(x, y, width, height, o) { + const points = [ + [x, y], [x + width, y], [x + width, y + height], [x, y + height] + ]; + return polygon(points, o); +} +export function curve(points, o) { + const o1 = _curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); + const o2 = _curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); + return { type: 'path', ops: o1.concat(o2) }; +} +export function ellipse(x, y, width, height, o) { + const increment = (Math.PI * 2) / o.curveStepCount; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += _offsetOpt(rx * 0.05, o); + ry += _offsetOpt(ry * 0.05, o); + const o1 = _ellipse(increment, x, y, rx, ry, 1, increment * _offset(0.1, _offset(0.4, 1, o), o), o); + const o2 = _ellipse(increment, x, y, rx, ry, 1.5, 0, o); + return { type: 'path', ops: o1.concat(o2) }; +} +export function arc(x, y, width, height, start, stop, closed, roughClosure, o) { + const cx = x; + const cy = y; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += _offsetOpt(rx * 0.01, o); + ry += _offsetOpt(ry * 0.01, o); + let strt = start; + let stp = stop; + while (strt < 0) { + strt += Math.PI * 2; + stp += Math.PI * 2; + } + if ((stp - strt) > (Math.PI * 2)) { + strt = 0; + stp = Math.PI * 2; + } + const ellipseInc = (Math.PI * 2) / o.curveStepCount; + const arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2); + const o1 = _arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); + const o2 = _arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); + let ops = o1.concat(o2); + if (closed) { + if (roughClosure) { + ops = ops.concat(_doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); + ops = ops.concat(_doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); + } + else { + ops.push({ op: 'lineTo', data: [cx, cy] }); + ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); + } + } + return { type: 'path', ops }; +} +export function svgPath(path, o) { + path = (path || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' '); + let p = new RoughPath(path); + if (o.simplification) { + const fitter = new PathFitter(p.linearPoints, p.closed); + const d = fitter.fit(o.simplification); + p = new RoughPath(d); + } + let ops = []; + const segments = p.segments || []; + for (let i = 0; i < segments.length; i++) { + const s = segments[i]; + const prev = i > 0 ? segments[i - 1] : null; + const opList = _processSegment(p, s, prev, o); + if (opList && opList.length) { + ops = ops.concat(opList); + } + } + return { type: 'path', ops }; +} +// Fills +export function solidFillPolygon(points, o) { + const ops = []; + if (points.length) { + const offset = o.maxRandomnessOffset || 0; + const len = points.length; + if (len > 2) { + ops.push({ op: 'move', data: [points[0][0] + _offsetOpt(offset, o), points[0][1] + _offsetOpt(offset, o)] }); + for (let i = 1; i < len; i++) { + ops.push({ op: 'lineTo', data: [points[i][0] + _offsetOpt(offset, o), points[i][1] + _offsetOpt(offset, o)] }); } } - return { type: 'fillPath', ops }; } - patternFillPolygon(points, o) { - const filler = getFiller(this, o); - return filler.fillPolygon(points, o); + return { type: 'fillPath', ops }; +} +export function patternFillPolygon(points, o) { + return getFiller(o).fillPolygon(points, o); +} +export function patternFillEllipse(cx, cy, width, height, o) { + return getFiller(o).fillEllipse(cx, cy, width, height, o); +} +export function patternFillArc(x, y, width, height, start, stop, o) { + const cx = x; + const cy = y; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += _offsetOpt(rx * 0.01, o); + ry += _offsetOpt(ry * 0.01, o); + let strt = start; + let stp = stop; + while (strt < 0) { + strt += Math.PI * 2; + stp += Math.PI * 2; } - patternFillEllipse(cx, cy, width, height, o) { - const filler = getFiller(this, o); - return filler.fillEllipse(cx, cy, width, height, o); + if ((stp - strt) > (Math.PI * 2)) { + strt = 0; + stp = Math.PI * 2; } - patternFillArc(x, y, width, height, start, stop, o) { - const cx = x; - const cy = y; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this.getOffset(-rx * 0.01, rx * 0.01, o); - ry += this.getOffset(-ry * 0.01, ry * 0.01, o); - let strt = start; - let stp = stop; - while (strt < 0) { - strt += Math.PI * 2; - stp += Math.PI * 2; - } - if ((stp - strt) > (Math.PI * 2)) { - strt = 0; - stp = Math.PI * 2; - } - const increment = (stp - strt) / o.curveStepCount; - const points = []; - for (let angle = strt; angle <= stp; angle = angle + increment) { - points.push([cx + rx * Math.cos(angle), cy + ry * Math.sin(angle)]); - } - points.push([cx + rx * Math.cos(stp), cy + ry * Math.sin(stp)]); - points.push([cx, cy]); - return this.patternFillPolygon(points, o); + const increment = (stp - strt) / o.curveStepCount; + const points = []; + for (let angle = strt; angle <= stp; angle = angle + increment) { + points.push([cx + rx * Math.cos(angle), cy + ry * Math.sin(angle)]); } - /// - getOffset(min, max, ops) { - return ops.roughness * ((Math.random() * (max - min)) + min); + points.push([cx + rx * Math.cos(stp), cy + ry * Math.sin(stp)]); + points.push([cx, cy]); + return patternFillPolygon(points, o); +} +export function randOffset(x, o) { + return _offsetOpt(x, o); +} +export function randOffsetWithRange(min, max, o) { + return _offset(min, max, o); +} +export function doubleLineOps(x1, y1, x2, y2, o) { + return _doubleLine(x1, y1, x2, y2, o); +} +// Private helpers +function _offset(min, max, ops) { + return ops.roughness * ((Math.random() * (max - min)) + min); +} +function _offsetOpt(x, ops) { + return _offset(-x, x, ops); +} +function _doubleLine(x1, y1, x2, y2, o) { + const o1 = _line(x1, y1, x2, y2, o, true, false); + const o2 = _line(x1, y1, x2, y2, o, true, true); + return o1.concat(o2); +} +function _line(x1, y1, x2, y2, o, move, overlay) { + const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); + let offset = o.maxRandomnessOffset || 0; + if ((offset * offset * 100) > lengthSq) { + offset = Math.sqrt(lengthSq) / 10; } - doubleLine(x1, y1, x2, y2, o) { - const o1 = this._line(x1, y1, x2, y2, o, true, false); - const o2 = this._line(x1, y1, x2, y2, o, true, true); - return o1.concat(o2); - } - _line(x1, y1, x2, y2, o, move, overlay) { - const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); - let offset = o.maxRandomnessOffset || 0; - if ((offset * offset * 100) > lengthSq) { - offset = Math.sqrt(lengthSq) / 10; - } - const halfOffset = offset / 2; - const divergePoint = 0.2 + Math.random() * 0.2; - let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; - let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; - midDispX = this.getOffset(-midDispX, midDispX, o); - midDispY = this.getOffset(-midDispY, midDispY, o); - const ops = []; - if (move) { - if (overlay) { - ops.push({ - op: 'move', data: [ - x1 + this.getOffset(-halfOffset, halfOffset, o), - y1 + this.getOffset(-halfOffset, halfOffset, o) - ] - }); - } - else { - ops.push({ - op: 'move', data: [ - x1 + this.getOffset(-offset, offset, o), - y1 + this.getOffset(-offset, offset, o) - ] - }); - } - } + const halfOffset = offset / 2; + const divergePoint = 0.2 + Math.random() * 0.2; + let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; + let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; + midDispX = _offsetOpt(midDispX, o); + midDispY = _offsetOpt(midDispY, o); + const ops = []; + const randomHalf = () => _offsetOpt(halfOffset, o); + const randomFull = () => _offsetOpt(offset, o); + if (move) { if (overlay) { ops.push({ - op: 'bcurveTo', data: [ - midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), - midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), - midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), - midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), - x2 + this.getOffset(-halfOffset, halfOffset, o), - y2 + this.getOffset(-halfOffset, halfOffset, o) + op: 'move', data: [ + x1 + randomHalf(), + y1 + randomHalf() ] }); } else { ops.push({ - op: 'bcurveTo', data: [ - midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), - midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), - midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), - midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), - x2 + this.getOffset(-offset, offset, o), - y2 + this.getOffset(-offset, offset, o) + op: 'move', data: [ + x1 + _offsetOpt(offset, o), + y1 + _offsetOpt(offset, o) ] }); } - return ops; } - _curve(points, closePoint, o) { - const len = points.length; - let ops = []; - if (len > 3) { - const b = []; - const s = 1 - o.curveTightness; - ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); - for (let i = 1; (i + 2) < len; i++) { - const cachedVertArray = points[i]; - b[0] = [cachedVertArray[0], cachedVertArray[1]]; - b[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6]; - b[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6]; - b[3] = [points[i + 1][0], points[i + 1][1]]; - ops.push({ op: 'bcurveTo', data: [b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]] }); - } - if (closePoint && closePoint.length === 2) { - const ro = o.maxRandomnessOffset; - ops.push({ op: 'lineTo', data: [closePoint[0] + this.getOffset(-ro, ro, o), closePoint[1] + +this.getOffset(-ro, ro, o)] }); - } - } - else if (len === 3) { - ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); - ops.push({ - op: 'bcurveTo', data: [ - points[1][0], points[1][1], - points[2][0], points[2][1], - points[2][0], points[2][1] - ] - }); - } - else if (len === 2) { - ops = ops.concat(this.doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); - } - return ops; + if (overlay) { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + randomHalf(), + midDispY + y1 + (y2 - y1) * divergePoint + randomHalf(), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + randomHalf(), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + randomHalf(), + x2 + randomHalf(), + y2 + randomHalf() + ] + }); } - _ellipse(increment, cx, cy, rx, ry, offset, overlap, o) { - const radOffset = this.getOffset(-0.5, 0.5, o) - (Math.PI / 2); - const points = []; - points.push([ - this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), - this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) - ]); - for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { - points.push([ - this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), - this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) - ]); - } - points.push([ - this.getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), - this.getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) - ]); - points.push([ - this.getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), - this.getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) - ]); - points.push([ - this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), - this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) - ]); - return this._curve(points, null, o); + else { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + randomFull(), + midDispY + y1 + (y2 - y1) * divergePoint + randomFull(), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + randomFull(), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + randomFull(), + x2 + randomFull(), + y2 + randomFull() + ] + }); } - _curveWithOffset(points, offset, o) { - const ps = []; + return ops; +} +function _curveWithOffset(points, offset, o) { + const ps = []; + ps.push([ + points[0][0] + _offsetOpt(offset, o), + points[0][1] + _offsetOpt(offset, o), + ]); + ps.push([ + points[0][0] + _offsetOpt(offset, o), + points[0][1] + _offsetOpt(offset, o), + ]); + for (let i = 1; i < points.length; i++) { ps.push([ - points[0][0] + this.getOffset(-offset, offset, o), - points[0][1] + this.getOffset(-offset, offset, o), + points[i][0] + _offsetOpt(offset, o), + points[i][1] + _offsetOpt(offset, o), ]); - ps.push([ - points[0][0] + this.getOffset(-offset, offset, o), - points[0][1] + this.getOffset(-offset, offset, o), - ]); - for (let i = 1; i < points.length; i++) { + if (i === (points.length - 1)) { ps.push([ - points[i][0] + this.getOffset(-offset, offset, o), - points[i][1] + this.getOffset(-offset, offset, o), - ]); - if (i === (points.length - 1)) { - ps.push([ - points[i][0] + this.getOffset(-offset, offset, o), - points[i][1] + this.getOffset(-offset, offset, o), - ]); - } - } - return this._curve(ps, null, o); - } - _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) { - const radOffset = strt + this.getOffset(-0.1, 0.1, o); - const points = []; - points.push([ - this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), - this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) - ]); - for (let angle = radOffset; angle <= stp; angle = angle + increment) { - points.push([ - this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), - this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) + points[i][0] + _offsetOpt(offset, o), + points[i][1] + _offsetOpt(offset, o), ]); } - points.push([ - cx + rx * Math.cos(stp), - cy + ry * Math.sin(stp) - ]); - points.push([ - cx + rx * Math.cos(stp), - cy + ry * Math.sin(stp) - ]); - return this._curve(points, null, o); } - _bezierTo(x1, y1, x2, y2, x, y, path, o) { - const ops = []; - const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; - let f = [0, 0]; - for (let i = 0; i < 2; i++) { - if (i === 0) { - ops.push({ op: 'move', data: [path.x, path.y] }); - } - else { - ops.push({ op: 'move', data: [path.x + this.getOffset(-ros[0], ros[0], o), path.y + this.getOffset(-ros[0], ros[0], o)] }); - } - f = [x + this.getOffset(-ros[i], ros[i], o), y + this.getOffset(-ros[i], ros[i], o)]; - ops.push({ - op: 'bcurveTo', data: [ - x1 + this.getOffset(-ros[i], ros[i], o), y1 + this.getOffset(-ros[i], ros[i], o), - x2 + this.getOffset(-ros[i], ros[i], o), y2 + this.getOffset(-ros[i], ros[i], o), - f[0], f[1] - ] - }); + return _curve(ps, null, o); +} +function _curve(points, closePoint, o) { + const len = points.length; + let ops = []; + if (len > 3) { + const b = []; + const s = 1 - o.curveTightness; + ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); + for (let i = 1; (i + 2) < len; i++) { + const cachedVertArray = points[i]; + b[0] = [cachedVertArray[0], cachedVertArray[1]]; + b[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6]; + b[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6]; + b[3] = [points[i + 1][0], points[i + 1][1]]; + ops.push({ op: 'bcurveTo', data: [b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]] }); + } + if (closePoint && closePoint.length === 2) { + const ro = o.maxRandomnessOffset; + ops.push({ op: 'lineTo', data: [closePoint[0] + _offsetOpt(ro, o), closePoint[1] + _offsetOpt(ro, o)] }); } - path.setPosition(f[0], f[1]); - return ops; } - _processSegment(path, seg, prevSeg, o) { - let ops = []; - switch (seg.key) { - case 'M': - case 'm': { - const delta = seg.key === 'm'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - const ro = 1 * (o.maxRandomnessOffset || 0); - x = x + this.getOffset(-ro, ro, o); - y = y + this.getOffset(-ro, ro, o); - path.setPosition(x, y); - ops.push({ op: 'move', data: [x, y] }); + else if (len === 3) { + ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); + ops.push({ + op: 'bcurveTo', data: [ + points[1][0], points[1][1], + points[2][0], points[2][1], + points[2][0], points[2][1] + ] + }); + } + else if (len === 2) { + ops = ops.concat(_doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); + } + return ops; +} +function _ellipse(increment, cx, cy, rx, ry, offset, overlap, o) { + const radOffset = _offsetOpt(0.5, o) - (Math.PI / 2); + const points = []; + points.push([ + _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { + points.push([ + _offsetOpt(offset, o) + cx + rx * Math.cos(angle), + _offsetOpt(offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + _offsetOpt(offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), + _offsetOpt(offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) + ]); + points.push([ + _offsetOpt(offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), + _offsetOpt(offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) + ]); + points.push([ + _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), + _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) + ]); + return _curve(points, null, o); +} +function _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) { + const radOffset = strt + _offsetOpt(0.1, o); + const points = []; + points.push([ + _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle <= stp; angle = angle + increment) { + points.push([ + _offsetOpt(offset, o) + cx + rx * Math.cos(angle), + _offsetOpt(offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + cx + rx * Math.cos(stp), + cy + ry * Math.sin(stp) + ]); + points.push([ + cx + rx * Math.cos(stp), + cy + ry * Math.sin(stp) + ]); + return _curve(points, null, o); +} +function _bezierTo(x1, y1, x2, y2, x, y, path, o) { + const ops = []; + const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; + let f = [0, 0]; + for (let i = 0; i < 2; i++) { + if (i === 0) { + ops.push({ op: 'move', data: [path.x, path.y] }); + } + else { + ops.push({ op: 'move', data: [path.x + _offsetOpt(ros[0], o), path.y + _offsetOpt(ros[0], o)] }); + } + f = [x + _offsetOpt(ros[i], o), y + _offsetOpt(ros[i], o)]; + ops.push({ + op: 'bcurveTo', data: [ + x1 + _offsetOpt(ros[i], o), y1 + _offsetOpt(ros[i], o), + x2 + _offsetOpt(ros[i], o), y2 + _offsetOpt(ros[i], o), + f[0], f[1] + ] + }); + } + path.setPosition(f[0], f[1]); + return ops; +} +function _processSegment(path, seg, prevSeg, o) { + let ops = []; + switch (seg.key) { + case 'M': + case 'm': { + const delta = seg.key === 'm'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; } - break; + const ro = 1 * (o.maxRandomnessOffset || 0); + x = x + _offsetOpt(ro, o); + y = y + _offsetOpt(ro, o); + path.setPosition(x, y); + ops.push({ op: 'move', data: [x, y] }); } - case 'L': - case 'l': { - const delta = seg.key === 'l'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); + break; + } + case 'L': + case 'l': { + const delta = seg.key === 'l'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + ops = ops.concat(_doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } + break; + } + case 'H': + case 'h': { + const delta = seg.key === 'h'; + if (seg.data.length) { + let x = +seg.data[0]; + if (delta) { + x += path.x; + } + ops = ops.concat(_doubleLine(path.x, path.y, x, path.y, o)); + path.setPosition(x, path.y); + } + break; + } + case 'V': + case 'v': { + const delta = seg.key === 'v'; + if (seg.data.length) { + let y = +seg.data[0]; + if (delta) { + y += path.y; + } + ops = ops.concat(_doubleLine(path.x, path.y, path.x, y, o)); + path.setPosition(path.x, y); + } + break; + } + case 'Z': + case 'z': { + if (path.first) { + ops = ops.concat(_doubleLine(path.x, path.y, path.first[0], path.first[1], o)); + path.setPosition(path.first[0], path.first[1]); + path.first = null; + } + break; + } + case 'C': + case 'c': { + const delta = seg.key === 'c'; + if (seg.data.length >= 6) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x2 = +seg.data[2]; + let y2 = +seg.data[3]; + let x = +seg.data[4]; + let y = +seg.data[5]; + if (delta) { + x1 += path.x; + x2 += path.x; + x += path.x; + y1 += path.y; + y2 += path.y; + y += path.y; + } + const ob = _bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'S': + case 's': { + const delta = seg.key === 's'; + if (seg.data.length >= 4) { + let x2 = +seg.data[0]; + let y2 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x2 += path.x; + x += path.x; + y2 += path.y; + y += path.y; + } + let x1 = x2; + let y1 = y2; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref = null; + if (prevKey === 'c' || prevKey === 'C' || prevKey === 's' || prevKey === 'S') { + ref = path.bezierReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const ob = _bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'Q': + case 'q': { + const delta = seg.key === 'q'; + if (seg.data.length >= 4) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x1 += path.x; + x += path.x; + y1 += path.y; + y += path.y; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + _offsetOpt(offset1, o), path.y + _offsetOpt(offset1, o)] }); + let f = [x + _offsetOpt(offset1, o), y + _offsetOpt(offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + _offsetOpt(offset1, o), y1 + _offsetOpt(offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + _offsetOpt(offset2, o), path.y + _offsetOpt(offset2, o)] }); + f = [x + _offsetOpt(offset2, o), y + _offsetOpt(offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + _offsetOpt(offset2, o), y1 + _offsetOpt(offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'T': + case 't': { + const delta = seg.key === 't'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + let x1 = x; + let y1 = y; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref = null; + if (prevKey === 'q' || prevKey === 'Q' || prevKey === 't' || prevKey === 'T') { + ref = path.quadReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + _offsetOpt(offset1, o), path.y + _offsetOpt(offset1, o)] }); + let f = [x + _offsetOpt(offset1, o), y + _offsetOpt(offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + _offsetOpt(offset1, o), y1 + _offsetOpt(offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + _offsetOpt(offset2, o), path.y + _offsetOpt(offset2, o)] }); + f = [x + _offsetOpt(offset2, o), y + _offsetOpt(offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + _offsetOpt(offset2, o), y1 + _offsetOpt(offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'A': + case 'a': { + const delta = seg.key === 'a'; + if (seg.data.length >= 7) { + const rx = +seg.data[0]; + const ry = +seg.data[1]; + const angle = +seg.data[2]; + const largeArcFlag = +seg.data[3]; + const sweepFlag = +seg.data[4]; + let x = +seg.data[5]; + let y = +seg.data[6]; + if (delta) { + x += path.x; + y += path.y; + } + if (x === path.x && y === path.y) { + break; + } + if (rx === 0 || ry === 0) { + ops = ops.concat(_doubleLine(path.x, path.y, x, y, o)); path.setPosition(x, y); } - break; - } - case 'H': - case 'h': { - const delta = seg.key === 'h'; - if (seg.data.length) { - let x = +seg.data[0]; - if (delta) { - x += path.x; - } - ops = ops.concat(this.doubleLine(path.x, path.y, x, path.y, o)); - path.setPosition(x, path.y); - } - break; - } - case 'V': - case 'v': { - const delta = seg.key === 'v'; - if (seg.data.length) { - let y = +seg.data[0]; - if (delta) { - y += path.y; - } - ops = ops.concat(this.doubleLine(path.x, path.y, path.x, y, o)); - path.setPosition(path.x, y); - } - break; - } - case 'Z': - case 'z': { - if (path.first) { - ops = ops.concat(this.doubleLine(path.x, path.y, path.first[0], path.first[1], o)); - path.setPosition(path.first[0], path.first[1]); - path.first = null; - } - break; - } - case 'C': - case 'c': { - const delta = seg.key === 'c'; - if (seg.data.length >= 6) { - let x1 = +seg.data[0]; - let y1 = +seg.data[1]; - let x2 = +seg.data[2]; - let y2 = +seg.data[3]; - let x = +seg.data[4]; - let y = +seg.data[5]; - if (delta) { - x1 += path.x; - x2 += path.x; - x += path.x; - y1 += path.y; - y2 += path.y; - y += path.y; - } - const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); - ops = ops.concat(ob); - path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; - } - break; - } - case 'S': - case 's': { - const delta = seg.key === 's'; - if (seg.data.length >= 4) { - let x2 = +seg.data[0]; - let y2 = +seg.data[1]; - let x = +seg.data[2]; - let y = +seg.data[3]; - if (delta) { - x2 += path.x; - x += path.x; - y2 += path.y; - y += path.y; - } - let x1 = x2; - let y1 = y2; - const prevKey = prevSeg ? prevSeg.key : ''; - let ref = null; - if (prevKey === 'c' || prevKey === 'C' || prevKey === 's' || prevKey === 'S') { - ref = path.bezierReflectionPoint; - } - if (ref) { - x1 = ref[0]; - y1 = ref[1]; - } - const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); - ops = ops.concat(ob); - path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; - } - break; - } - case 'Q': - case 'q': { - const delta = seg.key === 'q'; - if (seg.data.length >= 4) { - let x1 = +seg.data[0]; - let y1 = +seg.data[1]; - let x = +seg.data[2]; - let y = +seg.data[3]; - if (delta) { - x1 += path.x; - x += path.x; - y1 += path.y; - y += path.y; - } - const offset1 = 1 * (1 + o.roughness * 0.2); - const offset2 = 1.5 * (1 + o.roughness * 0.22); - ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); - let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), - f[0], f[1] - ] - }); - ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); - f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), - f[0], f[1] - ] - }); - path.setPosition(f[0], f[1]); - path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; - } - break; - } - case 'T': - case 't': { - const delta = seg.key === 't'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - let x1 = x; - let y1 = y; - const prevKey = prevSeg ? prevSeg.key : ''; - let ref = null; - if (prevKey === 'q' || prevKey === 'Q' || prevKey === 't' || prevKey === 'T') { - ref = path.quadReflectionPoint; - } - if (ref) { - x1 = ref[0]; - y1 = ref[1]; - } - const offset1 = 1 * (1 + o.roughness * 0.2); - const offset2 = 1.5 * (1 + o.roughness * 0.22); - ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); - let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), - f[0], f[1] - ] - }); - ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); - f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), - f[0], f[1] - ] - }); - path.setPosition(f[0], f[1]); - path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; - } - break; - } - case 'A': - case 'a': { - const delta = seg.key === 'a'; - if (seg.data.length >= 7) { - const rx = +seg.data[0]; - const ry = +seg.data[1]; - const angle = +seg.data[2]; - const largeArcFlag = +seg.data[3]; - const sweepFlag = +seg.data[4]; - let x = +seg.data[5]; - let y = +seg.data[6]; - if (delta) { - x += path.x; - y += path.y; - } - if (x === path.x && y === path.y) { - break; - } - if (rx === 0 || ry === 0) { - ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); - path.setPosition(x, y); - } - else { - for (let i = 0; i < 1; i++) { - const arcConverter = new RoughArcConverter([path.x, path.y], [x, y], [rx, ry], angle, largeArcFlag ? true : false, sweepFlag ? true : false); - let segment = arcConverter.getNextSegment(); - while (segment) { - const ob = this._bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); - ops = ops.concat(ob); - segment = arcConverter.getNextSegment(); - } + else { + for (let i = 0; i < 1; i++) { + const arcConverter = new RoughArcConverter([path.x, path.y], [x, y], [rx, ry], angle, largeArcFlag ? true : false, sweepFlag ? true : false); + let segment = arcConverter.getNextSegment(); + while (segment) { + const ob = _bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); + ops = ops.concat(ob); + segment = arcConverter.getNextSegment(); } } } - break; } - default: - break; + break; } - return ops; + default: + break; } + return ops; } diff --git a/bin/rough.d.ts b/bin/rough.d.ts index afb6b25..093fcfe 100644 --- a/bin/rough.d.ts +++ b/bin/rough.d.ts @@ -1,15 +1,10 @@ import { Config, DrawingSurface } from './core'; import { RoughCanvas } from './canvas'; -import { RoughRenderer } from './renderer'; import { RoughGenerator } from './generator'; -import { RoughGeneratorAsync } from './generator-async'; -import { RoughCanvasAsync } from './canvas-async'; import { RoughSVG } from './svg'; -import { RoughSVGAsync } from './svg-async'; declare const _default: { - canvas(canvas: HTMLCanvasElement, config?: Config | undefined): RoughCanvasAsync | RoughCanvas; - svg(svg: SVGSVGElement, config?: Config | undefined): RoughSVG | RoughSVGAsync; - createRenderer(): RoughRenderer; - generator(config: Config | null, surface: DrawingSurface): RoughGeneratorAsync | RoughGenerator; + canvas(canvas: HTMLCanvasElement, config?: Config | undefined): RoughCanvas; + svg(svg: SVGSVGElement, config?: Config | undefined): RoughSVG; + generator(config: Config | null, surface: DrawingSurface): RoughGenerator; }; export default _default; diff --git a/bin/rough.js b/bin/rough.js index 47d7f37..f7749eb 100644 --- a/bin/rough.js +++ b/bin/rough.js @@ -1,29 +1,14 @@ import { RoughCanvas } from './canvas'; import { RoughGenerator } from './generator'; -import { RoughGeneratorAsync } from './generator-async'; -import { RoughCanvasAsync } from './canvas-async'; import { RoughSVG } from './svg'; -import { RoughSVGAsync } from './svg-async'; export default { canvas(canvas, config) { - if (config && config.async) { - return new RoughCanvasAsync(canvas, config); - } return new RoughCanvas(canvas, config); }, svg(svg, config) { - if (config && config.async) { - return new RoughSVGAsync(svg, config); - } return new RoughSVG(svg, config); }, - createRenderer() { - return RoughCanvas.createRenderer(); - }, generator(config, surface) { - if (config && config.async) { - return new RoughGeneratorAsync(config, surface); - } return new RoughGenerator(config, surface); } }; diff --git a/bin/svg-base.d.ts b/bin/svg-base.d.ts index 28e5bef..ac09f68 100644 --- a/bin/svg-base.d.ts +++ b/bin/svg-base.d.ts @@ -1,12 +1,10 @@ import { Drawable, OpSet, ResolvedOptions } from './core'; -import { RoughRenderer } from './renderer'; export declare abstract class RoughSVGBase { protected svg: SVGSVGElement; protected _defs?: SVGDefsElement; constructor(svg: SVGSVGElement); abstract getDefaultOptions(): ResolvedOptions; abstract opsToPath(drawing: OpSet): string; - static createRenderer(): RoughRenderer; readonly defs: SVGDefsElement | null; draw(drawable: Drawable): SVGGElement; private fillSketch; diff --git a/bin/svg-base.js b/bin/svg-base.js index 741cb96..acdebfc 100644 --- a/bin/svg-base.js +++ b/bin/svg-base.js @@ -1,12 +1,8 @@ -import { RoughRenderer } from './renderer'; const hasDocument = typeof document !== 'undefined'; export class RoughSVGBase { constructor(svg) { this.svg = svg; } - static createRenderer() { - return new RoughRenderer(); - } get defs() { const doc = this.svg.ownerDocument || (hasDocument && document); if (doc) { @@ -26,7 +22,7 @@ export class RoughSVGBase { draw(drawable) { const sets = drawable.sets || []; const o = drawable.options || this.getDefaultOptions(); - const doc = this.svg.ownerDocument || (hasDocument && document); + const doc = this.svg.ownerDocument || window.document; const g = doc.createElementNS('http://www.w3.org/2000/svg', 'g'); for (const drawing of sets) { let path = null; diff --git a/package-lock.json b/package-lock.json index 9098ba3..1d119fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,9 @@ { "name": "roughjs", - "version": "2.2.0", + "version": "2.2.5", "lockfileVersion": 1, "requires": true, "dependencies": { - "@comandeer/babel-plugin-banner": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@comandeer/babel-plugin-banner/-/babel-plugin-banner-2.0.2.tgz", - "integrity": "sha512-5/GMOcgqBy/+cMYfOTFhqrioolOP+g7ADjl4jo1nYONwVGvMpzKPdweuVaJsC/Ol2PH+GmwlF2WrNP3xjWoSiw==", - "dev": true - }, "@types/estree": { "version": "0.0.39", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", @@ -17,9 +11,9 @@ "dev": true }, "@types/node": { - "version": "10.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.3.tgz", - "integrity": "sha512-/gwCgiI2e9RzzZTKbl+am3vgNqOt7a9fJ/uxv4SqYKxenoEDNVU3KZEadlpusWhQI0A0dOrZ0T68JYKVjzmgdQ==", + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==", "dev": true }, "ansi-regex": { @@ -52,919 +46,23 @@ "chalk": "^1.1.3", "esutils": "^2.0.2", "js-tokens": "^3.0.2" - } - }, - "babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - } - }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - } - }, - "babel-helper-builder-binary-assignment-operator-visitor": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", - "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", - "dev": true, - "requires": { - "babel-helper-explode-assignable-expression": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-call-delegate": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", - "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-define-map": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", - "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-helper-evaluate-path": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.4.3.tgz", - "integrity": "sha1-ComvcCwGshcCf6NxkI3UmJ0+Yz8=", - "dev": true - }, - "babel-helper-explode-assignable-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", - "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-flip-expressions": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.4.3.tgz", - "integrity": "sha1-NpZzahKKwYvCUlS19AoizrPB0/0=", - "dev": true - }, - "babel-helper-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", - "dev": true, - "requires": { - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-hoist-variables": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", - "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-is-nodes-equiv": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/babel-helper-is-nodes-equiv/-/babel-helper-is-nodes-equiv-0.0.1.tgz", - "integrity": "sha1-NOmzALFHnd2Y7HfqC76TQt/jloQ=", - "dev": true - }, - "babel-helper-is-void-0": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-helper-is-void-0/-/babel-helper-is-void-0-0.4.3.tgz", - "integrity": "sha1-fZwBtFYee5Xb2g9u7kj1tg5nMT4=", - "dev": true - }, - "babel-helper-mark-eval-scopes": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.4.3.tgz", - "integrity": "sha1-0kSjvvmESHJgP/tG4izorN9VFWI=", - "dev": true - }, - "babel-helper-optimise-call-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", - "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-regex": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", - "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-helper-remap-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", - "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-remove-or-void": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.4.3.tgz", - "integrity": "sha1-pPA7QAd6D/6I5F0HAQ3uJB/1rmA=", - "dev": true - }, - "babel-helper-replace-supers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", - "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", - "dev": true, - "requires": { - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-to-multiple-sequence-expressions": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.4.3.tgz", - "integrity": "sha1-W1GLESf0ezA4dzOGoVYaK0jmMrY=", - "dev": true - }, - "babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-check-es2015-constants": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", - "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-external-helpers": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-external-helpers/-/babel-plugin-external-helpers-6.22.0.tgz", - "integrity": "sha1-IoX0iwK9Xe3oUXXK+MYuhq3M76E=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-minify-builtins": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-builtins/-/babel-plugin-minify-builtins-0.4.3.tgz", - "integrity": "sha1-nqPVn0rEp7uVjXEtKVVqH4b3+B4=", - "dev": true, - "requires": { - "babel-helper-evaluate-path": "^0.4.3" - } - }, - "babel-plugin-minify-constant-folding": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-constant-folding/-/babel-plugin-minify-constant-folding-0.4.3.tgz", - "integrity": "sha1-MA+d6N2ghEoXaxk2U5YOJK0z4ZE=", - "dev": true, - "requires": { - "babel-helper-evaluate-path": "^0.4.3" - } - }, - "babel-plugin-minify-dead-code-elimination": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.4.3.tgz", - "integrity": "sha1-c2KCZYZPkAjQAnUG9Yq+s8HQLZg=", - "dev": true, - "requires": { - "babel-helper-evaluate-path": "^0.4.3", - "babel-helper-mark-eval-scopes": "^0.4.3", - "babel-helper-remove-or-void": "^0.4.3", - "lodash.some": "^4.6.0" - } - }, - "babel-plugin-minify-flip-comparisons": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-flip-comparisons/-/babel-plugin-minify-flip-comparisons-0.4.3.tgz", - "integrity": "sha1-AMqHDLjxO0XAOLPB68DyJyk8llo=", - "dev": true, - "requires": { - "babel-helper-is-void-0": "^0.4.3" - } - }, - "babel-plugin-minify-guarded-expressions": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-guarded-expressions/-/babel-plugin-minify-guarded-expressions-0.4.3.tgz", - "integrity": "sha1-zHCbRFP9IbHzAod0RMifiEJ845c=", - "dev": true, - "requires": { - "babel-helper-flip-expressions": "^0.4.3" - } - }, - "babel-plugin-minify-infinity": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-infinity/-/babel-plugin-minify-infinity-0.4.3.tgz", - "integrity": "sha1-37h2obCKBldjhO8/kuZTumB7Oco=", - "dev": true - }, - "babel-plugin-minify-mangle-names": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-mangle-names/-/babel-plugin-minify-mangle-names-0.4.3.tgz", - "integrity": "sha1-FvG/90t6fJPfwkHngx3V+0sCPvc=", - "dev": true, - "requires": { - "babel-helper-mark-eval-scopes": "^0.4.3" - } - }, - "babel-plugin-minify-numeric-literals": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-numeric-literals/-/babel-plugin-minify-numeric-literals-0.4.3.tgz", - "integrity": "sha1-jk/VYcefeAEob/YOjF/Z3u6TwLw=", - "dev": true - }, - "babel-plugin-minify-replace": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-replace/-/babel-plugin-minify-replace-0.4.3.tgz", - "integrity": "sha1-nSifS6FdTmAR6HmfpfG6d+yBIZ0=", - "dev": true - }, - "babel-plugin-minify-simplify": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-simplify/-/babel-plugin-minify-simplify-0.4.3.tgz", - "integrity": "sha1-N3VthcYURktLCSfytOQXGR1Vc4o=", - "dev": true, - "requires": { - "babel-helper-flip-expressions": "^0.4.3", - "babel-helper-is-nodes-equiv": "^0.0.1", - "babel-helper-to-multiple-sequence-expressions": "^0.4.3" - } - }, - "babel-plugin-minify-type-constructors": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-minify-type-constructors/-/babel-plugin-minify-type-constructors-0.4.3.tgz", - "integrity": "sha1-G8bxW4f3qxCF1CszC3F2V6IVZQA=", - "dev": true, - "requires": { - "babel-helper-is-void-0": "^0.4.3" - } - }, - "babel-plugin-syntax-async-functions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", - "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", - "dev": true - }, - "babel-plugin-syntax-exponentiation-operator": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", - "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", - "dev": true - }, - "babel-plugin-syntax-trailing-function-commas": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", - "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", - "dev": true - }, - "babel-plugin-transform-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", - "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", - "dev": true, - "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-functions": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-arrow-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", - "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-block-scoped-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", - "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-block-scoping": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", - "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-plugin-transform-es2015-classes": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", - "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", - "dev": true, - "requires": { - "babel-helper-define-map": "^6.24.1", - "babel-helper-function-name": "^6.24.1", - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-helper-replace-supers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-computed-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", - "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-destructuring": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", - "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-duplicate-keys": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", - "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-for-of": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", - "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", - "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", - "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-modules-amd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", - "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", - "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", - "dev": true, - "requires": { - "babel-plugin-transform-strict-mode": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-types": "^6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-systemjs": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", - "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-modules-umd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", - "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-object-super": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", - "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", - "dev": true, - "requires": { - "babel-helper-replace-supers": "^6.24.1", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-parameters": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", - "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", - "dev": true, - "requires": { - "babel-helper-call-delegate": "^6.24.1", - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-shorthand-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", - "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-spread": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", - "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-sticky-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", - "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", - "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-template-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", - "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-typeof-symbol": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", - "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-unicode-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", - "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", - "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "regexpu-core": "^2.0.0" - } - }, - "babel-plugin-transform-exponentiation-operator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", - "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", - "dev": true, - "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", - "babel-plugin-syntax-exponentiation-operator": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-inline-consecutive-adds": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz", - "integrity": "sha1-Mj1Ho+pjqDp6w8gRro5pQfrysNE=", - "dev": true - }, - "babel-plugin-transform-member-expression-literals": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-member-expression-literals/-/babel-plugin-transform-member-expression-literals-6.9.4.tgz", - "integrity": "sha1-NwOcmgwzE6OUlfqsL/OmtbnQOL8=", - "dev": true - }, - "babel-plugin-transform-merge-sibling-variables": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-merge-sibling-variables/-/babel-plugin-transform-merge-sibling-variables-6.9.4.tgz", - "integrity": "sha1-hbQi/DN3tEnJ0c3kQIcgNTJAHa4=", - "dev": true - }, - "babel-plugin-transform-minify-booleans": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.9.4.tgz", - "integrity": "sha1-rLs+VqNVXdI5KOS1gtKFFi3SsZg=", - "dev": true - }, - "babel-plugin-transform-property-literals": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.9.4.tgz", - "integrity": "sha1-mMHSHiVXNlc/k+zlRFn2ziSYXTk=", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "babel-plugin-transform-regenerator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", - "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", - "dev": true, - "requires": { - "regenerator-transform": "^0.10.0" - } - }, - "babel-plugin-transform-regexp-constructors": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regexp-constructors/-/babel-plugin-transform-regexp-constructors-0.4.3.tgz", - "integrity": "sha1-WLd3W2OvzzMyj66aX4j71PsLSWU=", - "dev": true - }, - "babel-plugin-transform-remove-console": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz", - "integrity": "sha1-uYA2DAZzhOJLNXpYjYB9PINSd4A=", - "dev": true - }, - "babel-plugin-transform-remove-debugger": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-debugger/-/babel-plugin-transform-remove-debugger-6.9.4.tgz", - "integrity": "sha1-QrcnYxyXl44estGZp67IShgznvI=", - "dev": true - }, - "babel-plugin-transform-remove-undefined": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-undefined/-/babel-plugin-transform-remove-undefined-0.4.3.tgz", - "integrity": "sha1-1AsNp/kcCMBsxyt2dHTAHEiU3gI=", - "dev": true, - "requires": { - "babel-helper-evaluate-path": "^0.4.3" - } - }, - "babel-plugin-transform-simplify-comparison-operators": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.9.4.tgz", - "integrity": "sha1-9ir+CWyrDh9ootdT/fKDiIRxzrk=", - "dev": true - }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-undefined-to-void": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.9.4.tgz", - "integrity": "sha1-viQcqBQEAwZ4t0hxcyK4nQyP4oA=", - "dev": true - }, - "babel-polyfill": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", - "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "regenerator-runtime": "^0.10.5" }, "dependencies": { - "regenerator-runtime": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", - "dev": true + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } } } }, - "babel-preset-env": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", - "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-to-generator": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.23.0", - "babel-plugin-transform-es2015-classes": "^6.23.0", - "babel-plugin-transform-es2015-computed-properties": "^6.22.0", - "babel-plugin-transform-es2015-destructuring": "^6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", - "babel-plugin-transform-es2015-for-of": "^6.23.0", - "babel-plugin-transform-es2015-function-name": "^6.22.0", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.22.0", - "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-umd": "^6.23.0", - "babel-plugin-transform-es2015-object-super": "^6.22.0", - "babel-plugin-transform-es2015-parameters": "^6.23.0", - "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", - "babel-plugin-transform-exponentiation-operator": "^6.22.0", - "babel-plugin-transform-regenerator": "^6.22.0", - "browserslist": "^3.2.6", - "invariant": "^2.2.2", - "semver": "^5.3.0" - } - }, - "babel-preset-es2015": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", - "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.24.1", - "babel-plugin-transform-es2015-classes": "^6.24.1", - "babel-plugin-transform-es2015-computed-properties": "^6.24.1", - "babel-plugin-transform-es2015-destructuring": "^6.22.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", - "babel-plugin-transform-es2015-for-of": "^6.22.0", - "babel-plugin-transform-es2015-function-name": "^6.24.1", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", - "babel-plugin-transform-es2015-modules-umd": "^6.24.1", - "babel-plugin-transform-es2015-object-super": "^6.24.1", - "babel-plugin-transform-es2015-parameters": "^6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", - "babel-plugin-transform-regenerator": "^6.24.1" - } - }, - "babel-preset-minify": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-preset-minify/-/babel-preset-minify-0.4.3.tgz", - "integrity": "sha1-spw91pGJBThFmPCSuVUVLiah/g8=", - "dev": true, - "requires": { - "babel-plugin-minify-builtins": "^0.4.3", - "babel-plugin-minify-constant-folding": "^0.4.3", - "babel-plugin-minify-dead-code-elimination": "^0.4.3", - "babel-plugin-minify-flip-comparisons": "^0.4.3", - "babel-plugin-minify-guarded-expressions": "^0.4.3", - "babel-plugin-minify-infinity": "^0.4.3", - "babel-plugin-minify-mangle-names": "^0.4.3", - "babel-plugin-minify-numeric-literals": "^0.4.3", - "babel-plugin-minify-replace": "^0.4.3", - "babel-plugin-minify-simplify": "^0.4.3", - "babel-plugin-minify-type-constructors": "^0.4.3", - "babel-plugin-transform-inline-consecutive-adds": "^0.4.3", - "babel-plugin-transform-member-expression-literals": "^6.9.4", - "babel-plugin-transform-merge-sibling-variables": "^6.9.4", - "babel-plugin-transform-minify-booleans": "^6.9.4", - "babel-plugin-transform-property-literals": "^6.9.4", - "babel-plugin-transform-regexp-constructors": "^0.4.3", - "babel-plugin-transform-remove-console": "^6.9.4", - "babel-plugin-transform-remove-debugger": "^6.9.4", - "babel-plugin-transform-remove-undefined": "^0.4.3", - "babel-plugin-transform-simplify-comparison-operators": "^6.9.4", - "babel-plugin-transform-undefined-to-void": "^6.9.4", - "lodash.isplainobject": "^4.0.6" - } - }, - "babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", - "dev": true, - "requires": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, - "babelrc-rollup": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/babelrc-rollup/-/babelrc-rollup-3.0.0.tgz", - "integrity": "sha1-/Ozb4+tkAM9OdpIzwXhqL6/otWw=", - "dev": true, - "requires": { - "resolve": "^1.1.7" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -981,48 +79,50 @@ "concat-map": "0.0.1" } }, - "browserslist": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", - "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000844", - "electron-to-chromium": "^1.3.47" - } - }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, - "caniuse-lite": { - "version": "1.0.30000856", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000856.tgz", - "integrity": "sha512-x3mYcApHMQemyaHuH/RyqtKCGIYTgEA63fdi+VBvDz8xUSmRiVWTLeyKcoGQCGG6UPR9/+4qG4OKrTa6aSQRKg==", - "dev": true - }, "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { - "color-name": "^1.1.1" + "color-name": "1.1.3" } }, "color-name": { @@ -1032,9 +132,9 @@ "dev": true }, "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", "dev": true }, "concat-map": { @@ -1043,48 +143,12 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", - "dev": true - }, - "core-js": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, - "electron-to-chromium": { - "version": "1.3.48", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.48.tgz", - "integrity": "sha1-07DYWTgUBE4JLs4hCPw6ya6kuQA=", - "dev": true - }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -1092,15 +156,9 @@ "dev": true }, "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", - "dev": true - }, - "estree-walker": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.2.1.tgz", - "integrity": "sha1-va/oCVOD2EFNXcLs9MkXO225QS4=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "esutils": { @@ -1116,9 +174,9 @@ "dev": true }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -1129,12 +187,6 @@ "path-is-absolute": "^1.0.0" } }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -1150,16 +202,6 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" - } - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1176,24 +218,6 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -1210,54 +234,6 @@ "esprima": "^4.0.0" } }, - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", - "dev": true - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, - "lodash.some": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=", - "dev": true - }, - "loose-envify": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "dev": true, - "requires": { - "js-tokens": "^3.0.0" - } - }, - "magic-string": { - "version": "0.24.1", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.24.1.tgz", - "integrity": "sha512-YBfNxbJiixMzxW40XqJEIldzHyh5f7CZKalo1uZffevyrPEX8Qgo9s0dmcORLHdV47UyvJg8/zD+6hQG3qvJrA==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.1" - } - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1267,33 +243,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1303,194 +252,52 @@ "wrappy": "1" } }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, - "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - }, - "regenerator-transform": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", - "dev": true, - "requires": { - "babel-runtime": "^6.18.0", - "babel-types": "^6.19.0", - "private": "^0.1.6" - } - }, - "regexpu-core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", - "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", - "dev": true, - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, "resolve": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", - "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz", + "integrity": "sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ==", "dev": true, "requires": { - "path-parse": "^1.0.5" + "path-parse": "^1.0.6" } }, "rollup": { - "version": "0.60.7", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.60.7.tgz", - "integrity": "sha512-Uj5I1A2PnDgA79P+v1dsNs1IHVydNgeJdKWRfoEJJdNMmyx07TRYqUtPUINaZ/gDusncFy1SZsT3lJnBBI8CGw==", + "version": "0.68.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.68.1.tgz", + "integrity": "sha512-8DNKos2p/B7gDoxI42kyIHHX8d+Zt+bwhjUgXnTqnSP+CSPkRNNIQyHIcTqeGdYWR70qG6c1DaRcrjWAiG6Akg==", "dev": true, "requires": { "@types/estree": "0.0.39", "@types/node": "*" } }, - "rollup-plugin-babel": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/rollup-plugin-babel/-/rollup-plugin-babel-3.0.4.tgz", - "integrity": "sha512-TGhQbliTZnRoUhd2214K3r4KJUBu9J1DPzcrAnkluVXOVrveU9OvAaYQ16KyOmujAoq+LMC1+x6YF2xBrU7t+g==", - "dev": true, - "requires": { - "rollup-pluginutils": "^1.5.0" - } - }, - "rollup-plugin-babel-minify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-babel-minify/-/rollup-plugin-babel-minify-5.0.0.tgz", - "integrity": "sha512-/XMozvFf1wm0O04FEybfEGl05jq9RQ408Xx/GjCfCau9ETzKYlEkfdi19pj2EvmpbSO/r0yspMKmwnUHNycdsw==", - "dev": true, - "requires": { - "@comandeer/babel-plugin-banner": "^2.0.2", - "babel-core": "^6.26.0", - "babel-preset-minify": "^0.4.0", - "magic-string": "^0.24.0" - } - }, - "rollup-pluginutils": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz", - "integrity": "sha1-HhVud4+UtyVb+hs9AXi+j1xVJAg=", - "dev": true, - "requires": { - "estree-walker": "^0.2.1", - "minimatch": "^3.0.2" - } - }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "^0.5.6" - } - }, - "sourcemap-codec": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz", - "integrity": "sha512-hX1eNBNuilj8yfFnECh0DzLgwKpBLMIvmhgEhixXNui8lMLBInTI8Kyxt++RwJnMNu7cAUo635L2+N1TxMJCzA==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", "dev": true }, "sprintf-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -1503,28 +310,16 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, "tslib": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz", - "integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", "dev": true }, "tslint": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.10.0.tgz", - "integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.12.0.tgz", + "integrity": "sha512-CKEcH1MHUBhoV43SA/Jmy1l24HJJgI0eyLbBNSRyFlsQvb9v6Zdq+Nz2vEOH00nC5SUx4SneJ59PZUS/ARcokQ==", "dev": true, "requires": { "babel-code-frame": "^6.22.0", @@ -1538,53 +333,22 @@ "resolve": "^1.3.2", "semver": "^5.3.0", "tslib": "^1.8.0", - "tsutils": "^2.12.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "tsutils": "^2.27.2" } }, "tsutils": { - "version": "2.27.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.1.tgz", - "integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==", + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", "dev": true, "requires": { "tslib": "^1.8.1" } }, "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz", + "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==", "dev": true }, "wrappy": { diff --git a/package.json b/package.json index 8d086ad..9e6ca21 100644 --- a/package.json +++ b/package.json @@ -29,15 +29,8 @@ }, "homepage": "https://roughjs.com", "devDependencies": { - "babel-plugin-external-helpers": "^6.22.0", - "babel-polyfill": "^6.26.0", - "babel-preset-env": "^1.7.0", - "babel-preset-es2015": "^6.24.1", - "babelrc-rollup": "^3.0.0", - "rollup": "^0.60.7", - "rollup-plugin-babel": "^3.0.4", - "rollup-plugin-babel-minify": "^5.0.0", - "tslint": "^5.10.0", - "typescript": "^2.9.2" + "rollup": "^0.68.1", + "tslint": "^5.12.0", + "typescript": "^3.2.2" } -} \ No newline at end of file +} diff --git a/src/canvas-async.ts b/src/canvas-async.ts.old similarity index 100% rename from src/canvas-async.ts rename to src/canvas-async.ts.old diff --git a/src/canvas-base.ts b/src/canvas-base.ts index 3496379..4b990af 100644 --- a/src/canvas-base.ts +++ b/src/canvas-base.ts @@ -1,5 +1,4 @@ import { ResolvedOptions, Drawable, OpSet } from './core'; -import { RoughRenderer } from './renderer'; const hasDocument = typeof document !== 'undefined'; @@ -12,10 +11,6 @@ export abstract class RoughCanvasBase { this.ctx = this.canvas.getContext('2d')!; } - static createRenderer(): RoughRenderer { - return new RoughRenderer(); - } - abstract getDefaultOptions(): ResolvedOptions; draw(drawable: Drawable) { @@ -65,7 +60,7 @@ export abstract class RoughCanvasBase { } this.fillSketch(hcontext, drawing, o); this.ctx.save(); - this.ctx.fillStyle = this.ctx.createPattern(hcanvas, 'repeat'); + this.ctx.fillStyle = this.ctx.createPattern(hcanvas, 'repeat')!; const p2d = new Path2D(drawing.path); this.ctx.fill(p2d); this.ctx.restore(); diff --git a/src/fillers/dot-filler.ts b/src/fillers/dot-filler.ts index 86f3f9b..439f0af 100644 --- a/src/fillers/dot-filler.ts +++ b/src/fillers/dot-filler.ts @@ -1,15 +1,10 @@ -import { PatternFiller, RenderHelper } from './filler-interface'; +import { PatternFiller } from './filler-interface'; import { ResolvedOptions, OpSet, Op } from '../core'; import { Point, Line } from '../geometry'; import { hachureLinesForPolygon, hachureLinesForEllipse, lineLength } from './filler-utils'; +import { randOffsetWithRange, ellipse } from '../renderer'; export class DotFiller implements PatternFiller { - renderer: RenderHelper; - - constructor(renderer: RenderHelper) { - this.renderer = renderer; - } - fillPolygon(points: Point[], o: ResolvedOptions): OpSet { o = Object.assign({}, o, { curveStepCount: 4, hachureAngle: 0 }); const lines = hachureLinesForPolygon(points, o); @@ -18,7 +13,7 @@ export class DotFiller implements PatternFiller { fillEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions): OpSet { o = Object.assign({}, o, { curveStepCount: 4, hachureAngle: 0 }); - const lines = hachureLinesForEllipse(cx, cy, width, height, o, this.renderer); + const lines = hachureLinesForEllipse(cx, cy, width, height, o); return this.dotsOnLines(lines, o); } @@ -43,10 +38,10 @@ export class DotFiller implements PatternFiller { const dy = l * Math.sin(alpha); const dx = l * Math.cos(alpha); const c: Point = [line[0][0] - dx, line[0][1] + dy]; - const cx = this.renderer.getOffset(c[0] - gap / 4, c[0] + gap / 4, o); - const cy = this.renderer.getOffset(c[1] - gap / 4, c[1] + gap / 4, o); - const ellipse = this.renderer.ellipse(cx, cy, fweight, fweight, o); - ops = ops.concat(ellipse.ops); + const cx = randOffsetWithRange(c[0] - gap / 4, c[0] + gap / 4, o); + const cy = randOffsetWithRange(c[1] - gap / 4, c[1] + gap / 4, o); + const el = ellipse(cx, cy, fweight, fweight, o); + ops = ops.concat(el.ops); } } return { type: 'fillSketch', ops }; diff --git a/src/fillers/filler-interface.ts b/src/fillers/filler-interface.ts index 403c7a7..cfc668e 100644 --- a/src/fillers/filler-interface.ts +++ b/src/fillers/filler-interface.ts @@ -1,13 +1,7 @@ -import { ResolvedOptions, OpSet, Op } from '../core'; +import { ResolvedOptions, OpSet } from '../core'; import { Point } from '../geometry'; export interface PatternFiller { fillPolygon(points: Point[], o: ResolvedOptions): OpSet; fillEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions): OpSet; -} - -export interface RenderHelper { - doubleLine(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): Op[]; - getOffset(min: number, max: number, ops: ResolvedOptions): number; - ellipse(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet; } \ No newline at end of file diff --git a/src/fillers/filler-utils.ts b/src/fillers/filler-utils.ts index a90f785..c49e16a 100644 --- a/src/fillers/filler-utils.ts +++ b/src/fillers/filler-utils.ts @@ -1,7 +1,7 @@ import { Point, Segment, Line } from '../geometry'; import { ResolvedOptions } from '../core'; import { HachureIterator } from '../utils/hachure'; -import { RenderHelper } from './filler-interface'; +import { randOffset } from '../renderer'; export function lineLength(line: Line): number { const p1 = line[0]; @@ -74,12 +74,12 @@ export function hachureLinesForPolygon(points: Point[], o: ResolvedOptions): Lin return ret; } -export function hachureLinesForEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions, renderer: RenderHelper): Line[] { +export function hachureLinesForEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions): Line[] { const ret: Line[] = []; let rx = Math.abs(width / 2); let ry = Math.abs(height / 2); - rx += renderer.getOffset(-rx * 0.05, rx * 0.05, o); - ry += renderer.getOffset(-ry * 0.05, ry * 0.05, o); + rx += randOffset(rx * 0.05, o); + ry += randOffset(ry * 0.05, o); const angle = o.hachureAngle; let gap = o.hachureGap; if (gap <= 0) { diff --git a/src/fillers/filler.ts b/src/fillers/filler.ts index dc825b5..17954b4 100644 --- a/src/fillers/filler.ts +++ b/src/fillers/filler.ts @@ -1,5 +1,5 @@ import { ResolvedOptions } from '../core'; -import { PatternFiller, RenderHelper } from './filler-interface'; +import { PatternFiller } from './filler-interface'; import { HachureFiller } from './hachure-filler'; import { ZigZagFiller } from './zigzag-filler'; import { HatchFiller } from './hatch-filler'; @@ -7,30 +7,30 @@ import { DotFiller } from './dot-filler'; const fillers: { [name: string]: PatternFiller } = {}; -export function getFiller(renderer: RenderHelper, o: ResolvedOptions): PatternFiller { +export function getFiller(o: ResolvedOptions): PatternFiller { let fillerName = o.fillStyle || 'hachure'; if (!fillers[fillerName]) { switch (fillerName) { case 'zigzag': if (!fillers[fillerName]) { - fillers[fillerName] = new ZigZagFiller(renderer); + fillers[fillerName] = new ZigZagFiller(); } break; case 'cross-hatch': if (!fillers[fillerName]) { - fillers[fillerName] = new HatchFiller(renderer); + fillers[fillerName] = new HatchFiller(); } break; case 'dots': if (!fillers[fillerName]) { - fillers[fillerName] = new DotFiller(renderer); + fillers[fillerName] = new DotFiller(); } break; case 'hachure': default: fillerName = 'hachure'; if (!fillers[fillerName]) { - fillers[fillerName] = new HachureFiller(renderer); + fillers[fillerName] = new HachureFiller(); } break; } diff --git a/src/fillers/hachure-filler.ts b/src/fillers/hachure-filler.ts index 3e642ef..9356bb5 100644 --- a/src/fillers/hachure-filler.ts +++ b/src/fillers/hachure-filler.ts @@ -1,15 +1,10 @@ -import { PatternFiller, RenderHelper } from './filler-interface'; +import { PatternFiller } from './filler-interface'; import { ResolvedOptions, OpSet, Op } from '../core'; import { Point, Line } from '../geometry'; import { hachureLinesForPolygon, hachureLinesForEllipse } from './filler-utils'; +import { doubleLineOps } from '../renderer'; export class HachureFiller implements PatternFiller { - renderer: RenderHelper; - - constructor(renderer: RenderHelper) { - this.renderer = renderer; - } - fillPolygon(points: Point[], o: ResolvedOptions): OpSet { return this._fillPolygon(points, o); } @@ -25,7 +20,7 @@ export class HachureFiller implements PatternFiller { } protected _fillEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions, connectEnds: boolean = false): OpSet { - const lines = hachureLinesForEllipse(cx, cy, width, height, o, this.renderer); + const lines = hachureLinesForEllipse(cx, cy, width, height, o); const ops = this.renderLines(lines, o, connectEnds); return { type: 'fillSketch', ops }; } @@ -34,9 +29,9 @@ export class HachureFiller implements PatternFiller { let ops: Op[] = []; let prevPoint: Point | null = null; for (const line of lines) { - ops = ops.concat(this.renderer.doubleLine(line[0][0], line[0][1], line[1][0], line[1][1], o)); + ops = ops.concat(doubleLineOps(line[0][0], line[0][1], line[1][0], line[1][1], o)); if (connectEnds && prevPoint) { - ops = ops.concat(this.renderer.doubleLine(prevPoint[0], prevPoint[1], line[0][0], line[0][1], o)); + ops = ops.concat(doubleLineOps(prevPoint[0], prevPoint[1], line[0][0], line[0][1], o)); } prevPoint = line[1]; } diff --git a/src/generator-async.ts b/src/generator-async.ts.old similarity index 100% rename from src/generator-async.ts rename to src/generator-async.ts.old diff --git a/src/generator-base.ts b/src/generator-base.ts index f89fb41..74c4a93 100644 --- a/src/generator-base.ts +++ b/src/generator-base.ts @@ -1,14 +1,12 @@ -import { RoughRenderer } from './renderer.js'; import { Config, DrawingSurface, Options, ResolvedOptions, Drawable, OpSet, PathInfo, PatternInfo } from './core'; import { Point } from './geometry.js'; -import { createRenderer } from './renderer-factory.js'; const hasSelf = typeof self !== 'undefined'; export abstract class RoughGeneratorBase { protected config: Config; protected surface: DrawingSurface; - protected renderer: RoughRenderer; + defaultOptions: ResolvedOptions = { maxRandomnessOffset: 2, roughness: 1, @@ -26,7 +24,6 @@ export abstract class RoughGeneratorBase { constructor(config: Config | null, surface: DrawingSurface) { this.config = config || {}; this.surface = surface; - this.renderer = createRenderer(this.config); if (this.config.options) { this.defaultOptions = this._options(this.config.options); } @@ -40,10 +37,6 @@ export abstract class RoughGeneratorBase { return { shape, sets: sets || [], options: options || this.defaultOptions }; } - protected get lib(): RoughRenderer { - return this.renderer; - } - private getCanvasSize(): Point { const val = (w: any): number => { if (w && typeof w === 'object') { diff --git a/src/generator.ts b/src/generator.ts index 96b4cc3..b44f81e 100644 --- a/src/generator.ts +++ b/src/generator.ts @@ -1,6 +1,7 @@ import { Config, DrawingSurface, Options, Drawable, OpSet } from './core'; import { Point } from './geometry.js'; -import { RoughGeneratorBase } from './generator-base'; +import { RoughGeneratorBase } from './generator-base.js'; +import { line, solidFillPolygon, patternFillPolygon, rectangle, ellipse, patternFillEllipse, linearPath, arc, patternFillArc, curve, svgPath } from './renderer.js'; export class RoughGenerator extends RoughGeneratorBase { constructor(config: Config | null, surface: DrawingSurface) { @@ -9,7 +10,7 @@ export class RoughGenerator extends RoughGeneratorBase { line(x1: number, y1: number, x2: number, y2: number, options?: Options): Drawable { const o = this._options(options); - return this._drawable('line', [this.lib.line(x1, y1, x2, y2, o)], o); + return this._drawable('line', [line(x1, y1, x2, y2, o)], o); } rectangle(x: number, y: number, width: number, height: number, options?: Options): Drawable { @@ -18,12 +19,12 @@ export class RoughGenerator extends RoughGeneratorBase { if (o.fill) { const points: Point[] = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; if (o.fillStyle === 'solid') { - paths.push(this.lib.solidFillPolygon(points, o)); + paths.push(solidFillPolygon(points, o)); } else { - paths.push(this.lib.patternFillPolygon(points, o)); + paths.push(patternFillPolygon(points, o)); } } - paths.push(this.lib.rectangle(x, y, width, height, o)); + paths.push(rectangle(x, y, width, height, o)); return this._drawable('rectangle', paths, o); } @@ -32,14 +33,14 @@ export class RoughGenerator extends RoughGeneratorBase { const paths = []; if (o.fill) { if (o.fillStyle === 'solid') { - const shape = this.lib.ellipse(x, y, width, height, o); + const shape = ellipse(x, y, width, height, o); shape.type = 'fillPath'; paths.push(shape); } else { - paths.push(this.lib.patternFillEllipse(x, y, width, height, o)); + paths.push(patternFillEllipse(x, y, width, height, o)); } } - paths.push(this.lib.ellipse(x, y, width, height, o)); + paths.push(ellipse(x, y, width, height, o)); return this._drawable('ellipse', paths, o); } @@ -51,7 +52,7 @@ export class RoughGenerator extends RoughGeneratorBase { linearPath(points: Point[], options?: Options): Drawable { const o = this._options(options); - return this._drawable('linearPath', [this.lib.linearPath(points, false, o)], o); + return this._drawable('linearPath', [linearPath(points, false, o)], o); } arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean = false, options?: Options): Drawable { @@ -59,20 +60,20 @@ export class RoughGenerator extends RoughGeneratorBase { const paths = []; if (closed && o.fill) { if (o.fillStyle === 'solid') { - const shape = this.lib.arc(x, y, width, height, start, stop, true, false, o); + const shape = arc(x, y, width, height, start, stop, true, false, o); shape.type = 'fillPath'; paths.push(shape); } else { - paths.push(this.lib.patternFillArc(x, y, width, height, start, stop, o)); + paths.push(patternFillArc(x, y, width, height, start, stop, o)); } } - paths.push(this.lib.arc(x, y, width, height, start, stop, closed, true, o)); + paths.push(arc(x, y, width, height, start, stop, closed, true, o)); return this._drawable('arc', paths, o); } curve(points: Point[], options?: Options): Drawable { const o = this._options(options); - return this._drawable('curve', [this.lib.curve(points, o)], o); + return this._drawable('curve', [curve(points, o)], o); } polygon(points: Point[], options?: Options): Drawable { @@ -80,7 +81,7 @@ export class RoughGenerator extends RoughGeneratorBase { const paths = []; if (o.fill) { if (o.fillStyle === 'solid') { - paths.push(this.lib.solidFillPolygon(points, o)); + paths.push(solidFillPolygon(points, o)); } else { const size = this.computePolygonSize(points); const fillPoints: Point[] = [ @@ -89,14 +90,14 @@ export class RoughGenerator extends RoughGeneratorBase { [size[0], size[1]], [0, size[1]] ]; - const shape = this.lib.patternFillPolygon(fillPoints, o); + const shape = patternFillPolygon(fillPoints, o); shape.type = 'path2Dpattern'; shape.size = size; shape.path = this.polygonPath(points); paths.push(shape); } } - paths.push(this.lib.linearPath(points, true, o)); + paths.push(linearPath(points, true, o)); return this._drawable('polygon', paths, o); } @@ -118,14 +119,14 @@ export class RoughGenerator extends RoughGeneratorBase { [size[0], size[1]], [0, size[1]] ]; - const shape = this.lib.patternFillPolygon(points, o); + const shape = patternFillPolygon(points, o); shape.type = 'path2Dpattern'; shape.size = size; shape.path = d; paths.push(shape); } } - paths.push(this.lib.svgPath(d, o)); + paths.push(svgPath(d, o)); return this._drawable('path', paths, o); } } \ No newline at end of file diff --git a/src/renderer-factory.ts b/src/renderer-factory.ts deleted file mode 100644 index 8261095..0000000 --- a/src/renderer-factory.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Config } from './core'; -import { RoughRenderer } from './renderer'; - -const hasSelf = typeof self !== 'undefined'; -const roughScript = hasSelf && self && self.document && self.document.currentScript && (self.document.currentScript as HTMLScriptElement).src; - -export function createRenderer(config: Config): RoughRenderer { - if (hasSelf && roughScript && self && (self as any).workly && config.async && (!config.noWorker)) { - const worklySource = config.worklyURL || 'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js'; - if (worklySource) { - const code = `importScripts('${worklySource}', '${roughScript}');\nworkly.expose(self.rough.createRenderer());`; - const ourl = URL.createObjectURL(new Blob([code])); - return (self as any).workly.proxy(ourl); - } - } - return new RoughRenderer(); -} \ No newline at end of file diff --git a/src/renderer-factory.ts.old b/src/renderer-factory.ts.old new file mode 100644 index 0000000..1e432e5 --- /dev/null +++ b/src/renderer-factory.ts.old @@ -0,0 +1,17 @@ +// import { Config } from './core'; +// import { RoughRenderer } from './renderer'; + +// const hasSelf = typeof self !== 'undefined'; +// const roughScript = hasSelf && self && self.document && self.document.currentScript && (self.document.currentScript as HTMLScriptElement).src; + +// export function createRenderer(config: Config): RoughRenderer { +// if (hasSelf && roughScript && self && (self as any).workly && config.async && (!config.noWorker)) { +// const worklySource = config.worklyURL || 'https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.min.js'; +// if (worklySource) { +// const code = `importScripts('${worklySource}', '${roughScript}');\nworkly.expose(self.rough.createRenderer());`; +// const ourl = URL.createObjectURL(new Blob([code])); +// return (self as any).workly.proxy(ourl); +// } +// } +// return new RoughRenderer(); +// } \ No newline at end of file diff --git a/src/renderer.ts b/src/renderer.ts index f0af9dd..4ab5f13 100644 --- a/src/renderer.ts +++ b/src/renderer.ts @@ -1,619 +1,634 @@ -import { ResolvedOptions, OpSet, Op } from './core'; -import { RoughPath, RoughArcConverter, PathFitter, Segment } from './path.js'; +import { ResolvedOptions, Op, OpSet } from './core'; import { Point } from './geometry'; +import { RoughPath, PathFitter, Segment, RoughArcConverter } from './path.js'; import { getFiller } from './fillers/filler'; -export class RoughRenderer { - line(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): OpSet { - const ops = this.doubleLine(x1, y1, x2, y2, o); - return { type: 'path', ops }; - } +export function line(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): OpSet { + return { type: 'path', ops: _doubleLine(x1, y1, x2, y2, o) }; +} - linearPath(points: Point[], close: boolean, o: ResolvedOptions): OpSet { - const len = (points || []).length; - if (len > 2) { - let ops: Op[] = []; - for (let i = 0; i < (len - 1); i++) { - ops = ops.concat(this.doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); - } - if (close) { - ops = ops.concat(this.doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o)); - } - return { type: 'path', ops }; - } else if (len === 2) { - return this.line(points[0][0], points[0][1], points[1][0], points[1][1], o); - } - return { type: 'path', ops: [] }; - } - - polygon(points: Point[], o: ResolvedOptions): OpSet { - return this.linearPath(points, true, o); - } - - rectangle(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet { - const points: Point[] = [ - [x, y], [x + width, y], [x + width, y + height], [x, y + height] - ]; - return this.polygon(points, o); - } - - curve(points: Point[], o: ResolvedOptions): OpSet { - const o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); - const o2 = this._curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); - return { type: 'path', ops: o1.concat(o2) }; - } - - ellipse(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet { - const increment = (Math.PI * 2) / o.curveStepCount; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this.getOffset(-rx * 0.05, rx * 0.05, o); - ry += this.getOffset(-ry * 0.05, ry * 0.05, o); - const o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this.getOffset(0.1, this.getOffset(0.4, 1, o), o), o); - const o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o); - return { type: 'path', ops: o1.concat(o2) }; - } - - arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean, roughClosure: boolean, o: ResolvedOptions): OpSet { - const cx = x; - const cy = y; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this.getOffset(-rx * 0.01, rx * 0.01, o); - ry += this.getOffset(-ry * 0.01, ry * 0.01, o); - let strt = start; - let stp = stop; - while (strt < 0) { - strt += Math.PI * 2; - stp += Math.PI * 2; - } - if ((stp - strt) > (Math.PI * 2)) { - strt = 0; - stp = Math.PI * 2; - } - const ellipseInc = (Math.PI * 2) / o.curveStepCount; - const arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2); - const o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); - const o2 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); - let ops = o1.concat(o2); - if (closed) { - if (roughClosure) { - ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); - ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); - } else { - ops.push({ op: 'lineTo', data: [cx, cy] }); - ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); - } - } - return { type: 'path', ops }; - } - - svgPath(path: string, o: ResolvedOptions): OpSet { - path = (path || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' '); - let p = new RoughPath(path); - if (o.simplification) { - const fitter = new PathFitter(p.linearPoints, p.closed); - const d = fitter.fit(o.simplification); - p = new RoughPath(d); - } +export function linearPath(points: Point[], close: boolean, o: ResolvedOptions): OpSet { + const len = (points || []).length; + if (len > 2) { let ops: Op[] = []; - const segments = p.segments || []; - for (let i = 0; i < segments.length; i++) { - const s = segments[i]; - const prev = i > 0 ? segments[i - 1] : null; - const opList = this._processSegment(p, s, prev, o); - if (opList && opList.length) { - ops = ops.concat(opList); - } + for (let i = 0; i < (len - 1); i++) { + ops = ops.concat(_doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); + } + if (close) { + ops = ops.concat(_doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o)); } return { type: 'path', ops }; + } else if (len === 2) { + return line(points[0][0], points[0][1], points[1][0], points[1][1], o); } + return { type: 'path', ops: [] }; +} - solidFillPolygon(points: Point[], o: ResolvedOptions): OpSet { - const ops: Op[] = []; - if (points.length) { - const offset = o.maxRandomnessOffset || 0; - const len = points.length; - if (len > 2) { - ops.push({ op: 'move', data: [points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)] }); - for (let i = 1; i < len; i++) { - ops.push({ op: 'lineTo', data: [points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)] }); - } +export function polygon(points: Point[], o: ResolvedOptions): OpSet { + return linearPath(points, true, o); +} + +export function rectangle(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet { + const points: Point[] = [ + [x, y], [x + width, y], [x + width, y + height], [x, y + height] + ]; + return polygon(points, o); +} + +export function curve(points: Point[], o: ResolvedOptions): OpSet { + const o1 = _curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); + const o2 = _curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); + return { type: 'path', ops: o1.concat(o2) }; +} + +export function ellipse(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet { + const increment = (Math.PI * 2) / o.curveStepCount; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += _offsetOpt(rx * 0.05, o); + ry += _offsetOpt(ry * 0.05, o); + const o1 = _ellipse(increment, x, y, rx, ry, 1, increment * _offset(0.1, _offset(0.4, 1, o), o), o); + const o2 = _ellipse(increment, x, y, rx, ry, 1.5, 0, o); + return { type: 'path', ops: o1.concat(o2) }; +} + +export function arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean, roughClosure: boolean, o: ResolvedOptions): OpSet { + const cx = x; + const cy = y; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += _offsetOpt(rx * 0.01, o); + ry += _offsetOpt(ry * 0.01, o); + let strt = start; + let stp = stop; + while (strt < 0) { + strt += Math.PI * 2; + stp += Math.PI * 2; + } + if ((stp - strt) > (Math.PI * 2)) { + strt = 0; + stp = Math.PI * 2; + } + const ellipseInc = (Math.PI * 2) / o.curveStepCount; + const arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2); + const o1 = _arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); + const o2 = _arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); + let ops = o1.concat(o2); + if (closed) { + if (roughClosure) { + ops = ops.concat(_doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); + ops = ops.concat(_doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); + } else { + ops.push({ op: 'lineTo', data: [cx, cy] }); + ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); + } + } + return { type: 'path', ops }; +} + +export function svgPath(path: string, o: ResolvedOptions): OpSet { + path = (path || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' '); + let p = new RoughPath(path); + if (o.simplification) { + const fitter = new PathFitter(p.linearPoints, p.closed); + const d = fitter.fit(o.simplification); + p = new RoughPath(d); + } + let ops: Op[] = []; + const segments = p.segments || []; + for (let i = 0; i < segments.length; i++) { + const s = segments[i]; + const prev = i > 0 ? segments[i - 1] : null; + const opList = _processSegment(p, s, prev, o); + if (opList && opList.length) { + ops = ops.concat(opList); + } + } + return { type: 'path', ops }; +} + +// Fills + +export function solidFillPolygon(points: Point[], o: ResolvedOptions): OpSet { + const ops: Op[] = []; + if (points.length) { + const offset = o.maxRandomnessOffset || 0; + const len = points.length; + if (len > 2) { + ops.push({ op: 'move', data: [points[0][0] + _offsetOpt(offset, o), points[0][1] + _offsetOpt(offset, o)] }); + for (let i = 1; i < len; i++) { + ops.push({ op: 'lineTo', data: [points[i][0] + _offsetOpt(offset, o), points[i][1] + _offsetOpt(offset, o)] }); } } - return { type: 'fillPath', ops }; } + return { type: 'fillPath', ops }; +} - patternFillPolygon(points: Point[], o: ResolvedOptions): OpSet { - const filler = getFiller(this, o); - return filler.fillPolygon(points, o); +export function patternFillPolygon(points: Point[], o: ResolvedOptions): OpSet { + return getFiller(o).fillPolygon(points, o); +} + +export function patternFillEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions): OpSet { + return getFiller(o).fillEllipse(cx, cy, width, height, o); +} + +export function patternFillArc(x: number, y: number, width: number, height: number, start: number, stop: number, o: ResolvedOptions): OpSet { + const cx = x; + const cy = y; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += _offsetOpt(rx * 0.01, o); + ry += _offsetOpt(ry * 0.01, o); + let strt = start; + let stp = stop; + while (strt < 0) { + strt += Math.PI * 2; + stp += Math.PI * 2; } - - patternFillEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions): OpSet { - const filler = getFiller(this, o); - return filler.fillEllipse(cx, cy, width, height, o); + if ((stp - strt) > (Math.PI * 2)) { + strt = 0; + stp = Math.PI * 2; } - - patternFillArc(x: number, y: number, width: number, height: number, start: number, stop: number, o: ResolvedOptions): OpSet { - const cx = x; - const cy = y; - let rx = Math.abs(width / 2); - let ry = Math.abs(height / 2); - rx += this.getOffset(-rx * 0.01, rx * 0.01, o); - ry += this.getOffset(-ry * 0.01, ry * 0.01, o); - let strt = start; - let stp = stop; - while (strt < 0) { - strt += Math.PI * 2; - stp += Math.PI * 2; - } - if ((stp - strt) > (Math.PI * 2)) { - strt = 0; - stp = Math.PI * 2; - } - const increment = (stp - strt) / o.curveStepCount; - const points: Point[] = []; - for (let angle = strt; angle <= stp; angle = angle + increment) { - points.push([cx + rx * Math.cos(angle), cy + ry * Math.sin(angle)]); - } - points.push([cx + rx * Math.cos(stp), cy + ry * Math.sin(stp)]); - points.push([cx, cy]); - return this.patternFillPolygon(points, o); + const increment = (stp - strt) / o.curveStepCount; + const points: Point[] = []; + for (let angle = strt; angle <= stp; angle = angle + increment) { + points.push([cx + rx * Math.cos(angle), cy + ry * Math.sin(angle)]); } + points.push([cx + rx * Math.cos(stp), cy + ry * Math.sin(stp)]); + points.push([cx, cy]); + return patternFillPolygon(points, o); +} - /// +export function randOffset(x: number, o: ResolvedOptions): number { + return _offsetOpt(x, o); +} - getOffset(min: number, max: number, ops: ResolvedOptions): number { - return ops.roughness * ((Math.random() * (max - min)) + min); +export function randOffsetWithRange(min: number, max: number, o: ResolvedOptions): number { + return _offset(min, max, o); +} + +export function doubleLineOps(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): Op[] { + return _doubleLine(x1, y1, x2, y2, o); +} + +// Private helpers + +function _offset(min: number, max: number, ops: ResolvedOptions): number { + return ops.roughness * ((Math.random() * (max - min)) + min); +} + +function _offsetOpt(x: number, ops: ResolvedOptions): number { + return _offset(-x, x, ops); +} + +function _doubleLine(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): Op[] { + const o1 = _line(x1, y1, x2, y2, o, true, false); + const o2 = _line(x1, y1, x2, y2, o, true, true); + return o1.concat(o2); +} + +function _line(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions, move: boolean, overlay: boolean): Op[] { + const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); + let offset = o.maxRandomnessOffset || 0; + if ((offset * offset * 100) > lengthSq) { + offset = Math.sqrt(lengthSq) / 10; } - - doubleLine(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): Op[] { - const o1 = this._line(x1, y1, x2, y2, o, true, false); - const o2 = this._line(x1, y1, x2, y2, o, true, true); - return o1.concat(o2); - } - - private _line(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions, move: boolean, overlay: boolean): Op[] { - const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); - let offset = o.maxRandomnessOffset || 0; - if ((offset * offset * 100) > lengthSq) { - offset = Math.sqrt(lengthSq) / 10; - } - const halfOffset = offset / 2; - const divergePoint = 0.2 + Math.random() * 0.2; - let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; - let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; - midDispX = this.getOffset(-midDispX, midDispX, o); - midDispY = this.getOffset(-midDispY, midDispY, o); - const ops: Op[] = []; - if (move) { - if (overlay) { - ops.push({ - op: 'move', data: [ - x1 + this.getOffset(-halfOffset, halfOffset, o), - y1 + this.getOffset(-halfOffset, halfOffset, o) - ] - }); - } else { - ops.push({ - op: 'move', data: [ - x1 + this.getOffset(-offset, offset, o), - y1 + this.getOffset(-offset, offset, o) - ] - }); - } - } + const halfOffset = offset / 2; + const divergePoint = 0.2 + Math.random() * 0.2; + let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; + let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; + midDispX = _offsetOpt(midDispX, o); + midDispY = _offsetOpt(midDispY, o); + const ops: Op[] = []; + const randomHalf = () => _offsetOpt(halfOffset, o); + const randomFull = () => _offsetOpt(offset, o); + if (move) { if (overlay) { ops.push({ - op: 'bcurveTo', data: [ - midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), - midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), - midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), - midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), - x2 + this.getOffset(-halfOffset, halfOffset, o), - y2 + this.getOffset(-halfOffset, halfOffset, o) + op: 'move', data: [ + x1 + randomHalf(), + y1 + randomHalf() ] }); } else { ops.push({ - op: 'bcurveTo', data: [ - midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), - midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), - midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), - midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), - x2 + this.getOffset(-offset, offset, o), - y2 + this.getOffset(-offset, offset, o) + op: 'move', data: [ + x1 + _offsetOpt(offset, o), + y1 + _offsetOpt(offset, o) ] }); } - return ops; } - - private _curve(points: Point[], closePoint: Point | null, o: ResolvedOptions): Op[] { - const len = points.length; - let ops: Op[] = []; - if (len > 3) { - const b = []; - const s = 1 - o.curveTightness; - ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); - for (let i = 1; (i + 2) < len; i++) { - const cachedVertArray = points[i]; - b[0] = [cachedVertArray[0], cachedVertArray[1]]; - b[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6]; - b[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6]; - b[3] = [points[i + 1][0], points[i + 1][1]]; - ops.push({ op: 'bcurveTo', data: [b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]] }); - } - if (closePoint && closePoint.length === 2) { - const ro = o.maxRandomnessOffset; - ops.push({ op: 'lineTo', data: [closePoint[0] + this.getOffset(-ro, ro, o), closePoint[1] + + this.getOffset(-ro, ro, o)] }); - } - } else if (len === 3) { - ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); - ops.push({ - op: 'bcurveTo', data: [ - points[1][0], points[1][1], - points[2][0], points[2][1], - points[2][0], points[2][1]] - }); - } else if (len === 2) { - ops = ops.concat(this.doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); - } - return ops; + if (overlay) { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + randomHalf(), + midDispY + y1 + (y2 - y1) * divergePoint + randomHalf(), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + randomHalf(), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + randomHalf(), + x2 + randomHalf(), + y2 + randomHalf() + ] + }); + } else { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + randomFull(), + midDispY + y1 + (y2 - y1) * divergePoint + randomFull(), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + randomFull(), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + randomFull(), + x2 + randomFull(), + y2 + randomFull() + ] + }); } + return ops; +} - private _ellipse(increment: number, cx: number, cy: number, rx: number, ry: number, offset: number, overlap: number, o: ResolvedOptions): Op[] { - const radOffset = this.getOffset(-0.5, 0.5, o) - (Math.PI / 2); - const points: Point[] = []; - points.push([ - this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), - this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) - ]); - for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { - points.push([ - this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), - this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) - ]); - } - points.push([ - this.getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), - this.getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) - ]); - points.push([ - this.getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), - this.getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) - ]); - points.push([ - this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), - this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) - ]); - return this._curve(points, null, o); - } - - private _curveWithOffset(points: Point[], offset: number, o: ResolvedOptions): Op[] { - const ps: Point[] = []; +function _curveWithOffset(points: Point[], offset: number, o: ResolvedOptions): Op[] { + const ps: Point[] = []; + ps.push([ + points[0][0] + _offsetOpt(offset, o), + points[0][1] + _offsetOpt(offset, o), + ]); + ps.push([ + points[0][0] + _offsetOpt(offset, o), + points[0][1] + _offsetOpt(offset, o), + ]); + for (let i = 1; i < points.length; i++) { ps.push([ - points[0][0] + this.getOffset(-offset, offset, o), - points[0][1] + this.getOffset(-offset, offset, o), + points[i][0] + _offsetOpt(offset, o), + points[i][1] + _offsetOpt(offset, o), ]); - ps.push([ - points[0][0] + this.getOffset(-offset, offset, o), - points[0][1] + this.getOffset(-offset, offset, o), - ]); - for (let i = 1; i < points.length; i++) { + if (i === (points.length - 1)) { ps.push([ - points[i][0] + this.getOffset(-offset, offset, o), - points[i][1] + this.getOffset(-offset, offset, o), - ]); - if (i === (points.length - 1)) { - ps.push([ - points[i][0] + this.getOffset(-offset, offset, o), - points[i][1] + this.getOffset(-offset, offset, o), - ]); - } - } - return this._curve(ps, null, o); - } - - private _arc(increment: number, cx: number, cy: number, rx: number, ry: number, strt: number, stp: number, offset: number, o: ResolvedOptions) { - const radOffset = strt + this.getOffset(-0.1, 0.1, o); - const points: Point[] = []; - points.push([ - this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), - this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) - ]); - for (let angle = radOffset; angle <= stp; angle = angle + increment) { - points.push([ - this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), - this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) + points[i][0] + _offsetOpt(offset, o), + points[i][1] + _offsetOpt(offset, o), ]); } - points.push([ - cx + rx * Math.cos(stp), - cy + ry * Math.sin(stp) - ]); - points.push([ - cx + rx * Math.cos(stp), - cy + ry * Math.sin(stp) - ]); - return this._curve(points, null, o); } + return _curve(ps, null, o); +} - private _bezierTo(x1: number, y1: number, x2: number, y2: number, x: number, y: number, path: RoughPath, o: ResolvedOptions): Op[] { - const ops: Op[] = []; - const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; - let f: Point = [0, 0]; - for (let i = 0; i < 2; i++) { - if (i === 0) { - ops.push({ op: 'move', data: [path.x, path.y] }); - } else { - ops.push({ op: 'move', data: [path.x + this.getOffset(-ros[0], ros[0], o), path.y + this.getOffset(-ros[0], ros[0], o)] }); - } - f = [x + this.getOffset(-ros[i], ros[i], o), y + this.getOffset(-ros[i], ros[i], o)]; - ops.push({ - op: 'bcurveTo', data: [ - x1 + this.getOffset(-ros[i], ros[i], o), y1 + this.getOffset(-ros[i], ros[i], o), - x2 + this.getOffset(-ros[i], ros[i], o), y2 + this.getOffset(-ros[i], ros[i], o), - f[0], f[1] - ] - }); +function _curve(points: Point[], closePoint: Point | null, o: ResolvedOptions): Op[] { + const len = points.length; + let ops: Op[] = []; + if (len > 3) { + const b = []; + const s = 1 - o.curveTightness; + ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); + for (let i = 1; (i + 2) < len; i++) { + const cachedVertArray = points[i]; + b[0] = [cachedVertArray[0], cachedVertArray[1]]; + b[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6]; + b[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6]; + b[3] = [points[i + 1][0], points[i + 1][1]]; + ops.push({ op: 'bcurveTo', data: [b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]] }); } - path.setPosition(f[0], f[1]); - return ops; + if (closePoint && closePoint.length === 2) { + const ro = o.maxRandomnessOffset; + ops.push({ op: 'lineTo', data: [closePoint[0] + _offsetOpt(ro, o), closePoint[1] + _offsetOpt(ro, o)] }); + } + } else if (len === 3) { + ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); + ops.push({ + op: 'bcurveTo', data: [ + points[1][0], points[1][1], + points[2][0], points[2][1], + points[2][0], points[2][1]] + }); + } else if (len === 2) { + ops = ops.concat(_doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); } + return ops; +} - private _processSegment(path: RoughPath, seg: Segment, prevSeg: Segment | null, o: ResolvedOptions): Op[] { - let ops: Op[] = []; - switch (seg.key) { - case 'M': - case 'm': { - const delta = seg.key === 'm'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - const ro = 1 * (o.maxRandomnessOffset || 0); - x = x + this.getOffset(-ro, ro, o); - y = y + this.getOffset(-ro, ro, o); +function _ellipse(increment: number, cx: number, cy: number, rx: number, ry: number, offset: number, overlap: number, o: ResolvedOptions): Op[] { + const radOffset = _offsetOpt(0.5, o) - (Math.PI / 2); + const points: Point[] = []; + points.push([ + _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { + points.push([ + _offsetOpt(offset, o) + cx + rx * Math.cos(angle), + _offsetOpt(offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + _offsetOpt(offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), + _offsetOpt(offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) + ]); + points.push([ + _offsetOpt(offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), + _offsetOpt(offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) + ]); + points.push([ + _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), + _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) + ]); + return _curve(points, null, o); +} + +function _arc(increment: number, cx: number, cy: number, rx: number, ry: number, strt: number, stp: number, offset: number, o: ResolvedOptions) { + const radOffset = strt + _offsetOpt(0.1, o); + const points: Point[] = []; + points.push([ + _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle <= stp; angle = angle + increment) { + points.push([ + _offsetOpt(offset, o) + cx + rx * Math.cos(angle), + _offsetOpt(offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + cx + rx * Math.cos(stp), + cy + ry * Math.sin(stp) + ]); + points.push([ + cx + rx * Math.cos(stp), + cy + ry * Math.sin(stp) + ]); + return _curve(points, null, o); +} + +function _bezierTo(x1: number, y1: number, x2: number, y2: number, x: number, y: number, path: RoughPath, o: ResolvedOptions): Op[] { + const ops: Op[] = []; + const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; + let f: Point = [0, 0]; + for (let i = 0; i < 2; i++) { + if (i === 0) { + ops.push({ op: 'move', data: [path.x, path.y] }); + } else { + ops.push({ op: 'move', data: [path.x + _offsetOpt(ros[0], o), path.y + _offsetOpt(ros[0], o)] }); + } + f = [x + _offsetOpt(ros[i], o), y + _offsetOpt(ros[i], o)]; + ops.push({ + op: 'bcurveTo', data: [ + x1 + _offsetOpt(ros[i], o), y1 + _offsetOpt(ros[i], o), + x2 + _offsetOpt(ros[i], o), y2 + _offsetOpt(ros[i], o), + f[0], f[1] + ] + }); + } + path.setPosition(f[0], f[1]); + return ops; +} + +function _processSegment(path: RoughPath, seg: Segment, prevSeg: Segment | null, o: ResolvedOptions): Op[] { + let ops: Op[] = []; + switch (seg.key) { + case 'M': + case 'm': { + const delta = seg.key === 'm'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + const ro = 1 * (o.maxRandomnessOffset || 0); + x = x + _offsetOpt(ro, o); + y = y + _offsetOpt(ro, o); + path.setPosition(x, y); + ops.push({ op: 'move', data: [x, y] }); + } + break; + } + case 'L': + case 'l': { + const delta = seg.key === 'l'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + ops = ops.concat(_doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } + break; + } + case 'H': + case 'h': { + const delta = seg.key === 'h'; + if (seg.data.length) { + let x = +seg.data[0]; + if (delta) { + x += path.x; + } + ops = ops.concat(_doubleLine(path.x, path.y, x, path.y, o)); + path.setPosition(x, path.y); + } + break; + } + case 'V': + case 'v': { + const delta = seg.key === 'v'; + if (seg.data.length) { + let y = +seg.data[0]; + if (delta) { + y += path.y; + } + ops = ops.concat(_doubleLine(path.x, path.y, path.x, y, o)); + path.setPosition(path.x, y); + } + break; + } + case 'Z': + case 'z': { + if (path.first) { + ops = ops.concat(_doubleLine(path.x, path.y, path.first[0], path.first[1], o)); + path.setPosition(path.first[0], path.first[1]); + path.first = null; + } + break; + } + case 'C': + case 'c': { + const delta = seg.key === 'c'; + if (seg.data.length >= 6) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x2 = +seg.data[2]; + let y2 = +seg.data[3]; + let x = +seg.data[4]; + let y = +seg.data[5]; + if (delta) { + x1 += path.x; + x2 += path.x; + x += path.x; + y1 += path.y; + y2 += path.y; + y += path.y; + } + const ob = _bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'S': + case 's': { + const delta = seg.key === 's'; + if (seg.data.length >= 4) { + let x2 = +seg.data[0]; + let y2 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x2 += path.x; + x += path.x; + y2 += path.y; + y += path.y; + } + let x1 = x2; + let y1 = y2; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref: Point | null = null; + if (prevKey === 'c' || prevKey === 'C' || prevKey === 's' || prevKey === 'S') { + ref = path.bezierReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const ob = _bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'Q': + case 'q': { + const delta = seg.key === 'q'; + if (seg.data.length >= 4) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x1 += path.x; + x += path.x; + y1 += path.y; + y += path.y; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + _offsetOpt(offset1, o), path.y + _offsetOpt(offset1, o)] }); + let f = [x + _offsetOpt(offset1, o), y + _offsetOpt(offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + _offsetOpt(offset1, o), y1 + _offsetOpt(offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + _offsetOpt(offset2, o), path.y + _offsetOpt(offset2, o)] }); + f = [x + _offsetOpt(offset2, o), y + _offsetOpt(offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + _offsetOpt(offset2, o), y1 + _offsetOpt(offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'T': + case 't': { + const delta = seg.key === 't'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + let x1 = x; + let y1 = y; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref: Point | null = null; + if (prevKey === 'q' || prevKey === 'Q' || prevKey === 't' || prevKey === 'T') { + ref = path.quadReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + _offsetOpt(offset1, o), path.y + _offsetOpt(offset1, o)] }); + let f = [x + _offsetOpt(offset1, o), y + _offsetOpt(offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + _offsetOpt(offset1, o), y1 + _offsetOpt(offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + _offsetOpt(offset2, o), path.y + _offsetOpt(offset2, o)] }); + f = [x + _offsetOpt(offset2, o), y + _offsetOpt(offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + _offsetOpt(offset2, o), y1 + _offsetOpt(offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'A': + case 'a': { + const delta = seg.key === 'a'; + if (seg.data.length >= 7) { + const rx = +seg.data[0]; + const ry = +seg.data[1]; + const angle = +seg.data[2]; + const largeArcFlag = +seg.data[3]; + const sweepFlag = +seg.data[4]; + let x = +seg.data[5]; + let y = +seg.data[6]; + if (delta) { + x += path.x; + y += path.y; + } + if (x === path.x && y === path.y) { + break; + } + if (rx === 0 || ry === 0) { + ops = ops.concat(_doubleLine(path.x, path.y, x, y, o)); path.setPosition(x, y); - ops.push({ op: 'move', data: [x, y] }); - } - break; - } - case 'L': - case 'l': { - const delta = seg.key === 'l'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); - path.setPosition(x, y); - } - break; - } - case 'H': - case 'h': { - const delta = seg.key === 'h'; - if (seg.data.length) { - let x = +seg.data[0]; - if (delta) { - x += path.x; - } - ops = ops.concat(this.doubleLine(path.x, path.y, x, path.y, o)); - path.setPosition(x, path.y); - } - break; - } - case 'V': - case 'v': { - const delta = seg.key === 'v'; - if (seg.data.length) { - let y = +seg.data[0]; - if (delta) { - y += path.y; - } - ops = ops.concat(this.doubleLine(path.x, path.y, path.x, y, o)); - path.setPosition(path.x, y); - } - break; - } - case 'Z': - case 'z': { - if (path.first) { - ops = ops.concat(this.doubleLine(path.x, path.y, path.first[0], path.first[1], o)); - path.setPosition(path.first[0], path.first[1]); - path.first = null; - } - break; - } - case 'C': - case 'c': { - const delta = seg.key === 'c'; - if (seg.data.length >= 6) { - let x1 = +seg.data[0]; - let y1 = +seg.data[1]; - let x2 = +seg.data[2]; - let y2 = +seg.data[3]; - let x = +seg.data[4]; - let y = +seg.data[5]; - if (delta) { - x1 += path.x; - x2 += path.x; - x += path.x; - y1 += path.y; - y2 += path.y; - y += path.y; - } - const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); - ops = ops.concat(ob); - path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; - } - break; - } - case 'S': - case 's': { - const delta = seg.key === 's'; - if (seg.data.length >= 4) { - let x2 = +seg.data[0]; - let y2 = +seg.data[1]; - let x = +seg.data[2]; - let y = +seg.data[3]; - if (delta) { - x2 += path.x; - x += path.x; - y2 += path.y; - y += path.y; - } - let x1 = x2; - let y1 = y2; - const prevKey = prevSeg ? prevSeg.key : ''; - let ref: Point | null = null; - if (prevKey === 'c' || prevKey === 'C' || prevKey === 's' || prevKey === 'S') { - ref = path.bezierReflectionPoint; - } - if (ref) { - x1 = ref[0]; - y1 = ref[1]; - } - const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); - ops = ops.concat(ob); - path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; - } - break; - } - case 'Q': - case 'q': { - const delta = seg.key === 'q'; - if (seg.data.length >= 4) { - let x1 = +seg.data[0]; - let y1 = +seg.data[1]; - let x = +seg.data[2]; - let y = +seg.data[3]; - if (delta) { - x1 += path.x; - x += path.x; - y1 += path.y; - y += path.y; - } - const offset1 = 1 * (1 + o.roughness * 0.2); - const offset2 = 1.5 * (1 + o.roughness * 0.22); - ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); - let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), - f[0], f[1] - ] - }); - ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); - f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), - f[0], f[1] - ] - }); - path.setPosition(f[0], f[1]); - path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; - } - break; - } - case 'T': - case 't': { - const delta = seg.key === 't'; - if (seg.data.length >= 2) { - let x = +seg.data[0]; - let y = +seg.data[1]; - if (delta) { - x += path.x; - y += path.y; - } - let x1 = x; - let y1 = y; - const prevKey = prevSeg ? prevSeg.key : ''; - let ref: Point | null = null; - if (prevKey === 'q' || prevKey === 'Q' || prevKey === 't' || prevKey === 'T') { - ref = path.quadReflectionPoint; - } - if (ref) { - x1 = ref[0]; - y1 = ref[1]; - } - const offset1 = 1 * (1 + o.roughness * 0.2); - const offset2 = 1.5 * (1 + o.roughness * 0.22); - ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); - let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), - f[0], f[1] - ] - }); - ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); - f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; - ops.push({ - op: 'qcurveTo', data: [ - x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), - f[0], f[1] - ] - }); - path.setPosition(f[0], f[1]); - path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; - } - break; - } - case 'A': - case 'a': { - const delta = seg.key === 'a'; - if (seg.data.length >= 7) { - const rx = +seg.data[0]; - const ry = +seg.data[1]; - const angle = +seg.data[2]; - const largeArcFlag = +seg.data[3]; - const sweepFlag = +seg.data[4]; - let x = +seg.data[5]; - let y = +seg.data[6]; - if (delta) { - x += path.x; - y += path.y; - } - if (x === path.x && y === path.y) { - break; - } - if (rx === 0 || ry === 0) { - ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); - path.setPosition(x, y); - } else { - for (let i = 0; i < 1; i++) { - const arcConverter = new RoughArcConverter( - [path.x, path.y], - [x, y], - [rx, ry], - angle, - largeArcFlag ? true : false, - sweepFlag ? true : false - ); - let segment = arcConverter.getNextSegment(); - while (segment) { - const ob = this._bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); - ops = ops.concat(ob); - segment = arcConverter.getNextSegment(); - } + } else { + for (let i = 0; i < 1; i++) { + const arcConverter = new RoughArcConverter( + [path.x, path.y], + [x, y], + [rx, ry], + angle, + largeArcFlag ? true : false, + sweepFlag ? true : false + ); + let segment = arcConverter.getNextSegment(); + while (segment) { + const ob = _bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); + ops = ops.concat(ob); + segment = arcConverter.getNextSegment(); } } } - break; } - default: - break; + break; } - return ops; + default: + break; } + return ops; } \ No newline at end of file diff --git a/src/renderer.ts.old b/src/renderer.ts.old new file mode 100644 index 0000000..f0af9dd --- /dev/null +++ b/src/renderer.ts.old @@ -0,0 +1,619 @@ +import { ResolvedOptions, OpSet, Op } from './core'; +import { RoughPath, RoughArcConverter, PathFitter, Segment } from './path.js'; +import { Point } from './geometry'; +import { getFiller } from './fillers/filler'; + +export class RoughRenderer { + line(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): OpSet { + const ops = this.doubleLine(x1, y1, x2, y2, o); + return { type: 'path', ops }; + } + + linearPath(points: Point[], close: boolean, o: ResolvedOptions): OpSet { + const len = (points || []).length; + if (len > 2) { + let ops: Op[] = []; + for (let i = 0; i < (len - 1); i++) { + ops = ops.concat(this.doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o)); + } + if (close) { + ops = ops.concat(this.doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o)); + } + return { type: 'path', ops }; + } else if (len === 2) { + return this.line(points[0][0], points[0][1], points[1][0], points[1][1], o); + } + return { type: 'path', ops: [] }; + } + + polygon(points: Point[], o: ResolvedOptions): OpSet { + return this.linearPath(points, true, o); + } + + rectangle(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet { + const points: Point[] = [ + [x, y], [x + width, y], [x + width, y + height], [x, y + height] + ]; + return this.polygon(points, o); + } + + curve(points: Point[], o: ResolvedOptions): OpSet { + const o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o); + const o2 = this._curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o); + return { type: 'path', ops: o1.concat(o2) }; + } + + ellipse(x: number, y: number, width: number, height: number, o: ResolvedOptions): OpSet { + const increment = (Math.PI * 2) / o.curveStepCount; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.05, rx * 0.05, o); + ry += this.getOffset(-ry * 0.05, ry * 0.05, o); + const o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this.getOffset(0.1, this.getOffset(0.4, 1, o), o), o); + const o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o); + return { type: 'path', ops: o1.concat(o2) }; + } + + arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean, roughClosure: boolean, o: ResolvedOptions): OpSet { + const cx = x; + const cy = y; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, ry * 0.01, o); + let strt = start; + let stp = stop; + while (strt < 0) { + strt += Math.PI * 2; + stp += Math.PI * 2; + } + if ((stp - strt) > (Math.PI * 2)) { + strt = 0; + stp = Math.PI * 2; + } + const ellipseInc = (Math.PI * 2) / o.curveStepCount; + const arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2); + const o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o); + const o2 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o); + let ops = o1.concat(o2); + if (closed) { + if (roughClosure) { + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o)); + ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o)); + } else { + ops.push({ op: 'lineTo', data: [cx, cy] }); + ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] }); + } + } + return { type: 'path', ops }; + } + + svgPath(path: string, o: ResolvedOptions): OpSet { + path = (path || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' '); + let p = new RoughPath(path); + if (o.simplification) { + const fitter = new PathFitter(p.linearPoints, p.closed); + const d = fitter.fit(o.simplification); + p = new RoughPath(d); + } + let ops: Op[] = []; + const segments = p.segments || []; + for (let i = 0; i < segments.length; i++) { + const s = segments[i]; + const prev = i > 0 ? segments[i - 1] : null; + const opList = this._processSegment(p, s, prev, o); + if (opList && opList.length) { + ops = ops.concat(opList); + } + } + return { type: 'path', ops }; + } + + solidFillPolygon(points: Point[], o: ResolvedOptions): OpSet { + const ops: Op[] = []; + if (points.length) { + const offset = o.maxRandomnessOffset || 0; + const len = points.length; + if (len > 2) { + ops.push({ op: 'move', data: [points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)] }); + for (let i = 1; i < len; i++) { + ops.push({ op: 'lineTo', data: [points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)] }); + } + } + } + return { type: 'fillPath', ops }; + } + + patternFillPolygon(points: Point[], o: ResolvedOptions): OpSet { + const filler = getFiller(this, o); + return filler.fillPolygon(points, o); + } + + patternFillEllipse(cx: number, cy: number, width: number, height: number, o: ResolvedOptions): OpSet { + const filler = getFiller(this, o); + return filler.fillEllipse(cx, cy, width, height, o); + } + + patternFillArc(x: number, y: number, width: number, height: number, start: number, stop: number, o: ResolvedOptions): OpSet { + const cx = x; + const cy = y; + let rx = Math.abs(width / 2); + let ry = Math.abs(height / 2); + rx += this.getOffset(-rx * 0.01, rx * 0.01, o); + ry += this.getOffset(-ry * 0.01, ry * 0.01, o); + let strt = start; + let stp = stop; + while (strt < 0) { + strt += Math.PI * 2; + stp += Math.PI * 2; + } + if ((stp - strt) > (Math.PI * 2)) { + strt = 0; + stp = Math.PI * 2; + } + const increment = (stp - strt) / o.curveStepCount; + const points: Point[] = []; + for (let angle = strt; angle <= stp; angle = angle + increment) { + points.push([cx + rx * Math.cos(angle), cy + ry * Math.sin(angle)]); + } + points.push([cx + rx * Math.cos(stp), cy + ry * Math.sin(stp)]); + points.push([cx, cy]); + return this.patternFillPolygon(points, o); + } + + /// + + getOffset(min: number, max: number, ops: ResolvedOptions): number { + return ops.roughness * ((Math.random() * (max - min)) + min); + } + + doubleLine(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions): Op[] { + const o1 = this._line(x1, y1, x2, y2, o, true, false); + const o2 = this._line(x1, y1, x2, y2, o, true, true); + return o1.concat(o2); + } + + private _line(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions, move: boolean, overlay: boolean): Op[] { + const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2); + let offset = o.maxRandomnessOffset || 0; + if ((offset * offset * 100) > lengthSq) { + offset = Math.sqrt(lengthSq) / 10; + } + const halfOffset = offset / 2; + const divergePoint = 0.2 + Math.random() * 0.2; + let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200; + let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200; + midDispX = this.getOffset(-midDispX, midDispX, o); + midDispY = this.getOffset(-midDispY, midDispY, o); + const ops: Op[] = []; + if (move) { + if (overlay) { + ops.push({ + op: 'move', data: [ + x1 + this.getOffset(-halfOffset, halfOffset, o), + y1 + this.getOffset(-halfOffset, halfOffset, o) + ] + }); + } else { + ops.push({ + op: 'move', data: [ + x1 + this.getOffset(-offset, offset, o), + y1 + this.getOffset(-offset, offset, o) + ] + }); + } + } + if (overlay) { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o), + x2 + this.getOffset(-halfOffset, halfOffset, o), + y2 + this.getOffset(-halfOffset, halfOffset, o) + ] + }); + } else { + ops.push({ + op: 'bcurveTo', data: [ + midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), + midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), + midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o), + midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o), + x2 + this.getOffset(-offset, offset, o), + y2 + this.getOffset(-offset, offset, o) + ] + }); + } + return ops; + } + + private _curve(points: Point[], closePoint: Point | null, o: ResolvedOptions): Op[] { + const len = points.length; + let ops: Op[] = []; + if (len > 3) { + const b = []; + const s = 1 - o.curveTightness; + ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); + for (let i = 1; (i + 2) < len; i++) { + const cachedVertArray = points[i]; + b[0] = [cachedVertArray[0], cachedVertArray[1]]; + b[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6]; + b[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6]; + b[3] = [points[i + 1][0], points[i + 1][1]]; + ops.push({ op: 'bcurveTo', data: [b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]] }); + } + if (closePoint && closePoint.length === 2) { + const ro = o.maxRandomnessOffset; + ops.push({ op: 'lineTo', data: [closePoint[0] + this.getOffset(-ro, ro, o), closePoint[1] + + this.getOffset(-ro, ro, o)] }); + } + } else if (len === 3) { + ops.push({ op: 'move', data: [points[1][0], points[1][1]] }); + ops.push({ + op: 'bcurveTo', data: [ + points[1][0], points[1][1], + points[2][0], points[2][1], + points[2][0], points[2][1]] + }); + } else if (len === 2) { + ops = ops.concat(this.doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o)); + } + return ops; + } + + private _ellipse(increment: number, cx: number, cy: number, rx: number, ry: number, offset: number, overlap: number, o: ResolvedOptions): Op[] { + const radOffset = this.getOffset(-0.5, 0.5, o) - (Math.PI / 2); + const points: Point[] = []; + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) { + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5) + ]); + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap), + this.getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap) + ]); + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5) + ]); + return this._curve(points, null, o); + } + + private _curveWithOffset(points: Point[], offset: number, o: ResolvedOptions): Op[] { + const ps: Point[] = []; + ps.push([ + points[0][0] + this.getOffset(-offset, offset, o), + points[0][1] + this.getOffset(-offset, offset, o), + ]); + ps.push([ + points[0][0] + this.getOffset(-offset, offset, o), + points[0][1] + this.getOffset(-offset, offset, o), + ]); + for (let i = 1; i < points.length; i++) { + ps.push([ + points[i][0] + this.getOffset(-offset, offset, o), + points[i][1] + this.getOffset(-offset, offset, o), + ]); + if (i === (points.length - 1)) { + ps.push([ + points[i][0] + this.getOffset(-offset, offset, o), + points[i][1] + this.getOffset(-offset, offset, o), + ]); + } + } + return this._curve(ps, null, o); + } + + private _arc(increment: number, cx: number, cy: number, rx: number, ry: number, strt: number, stp: number, offset: number, o: ResolvedOptions) { + const radOffset = strt + this.getOffset(-0.1, 0.1, o); + const points: Point[] = []; + points.push([ + this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment), + this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment) + ]); + for (let angle = radOffset; angle <= stp; angle = angle + increment) { + points.push([ + this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle), + this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle) + ]); + } + points.push([ + cx + rx * Math.cos(stp), + cy + ry * Math.sin(stp) + ]); + points.push([ + cx + rx * Math.cos(stp), + cy + ry * Math.sin(stp) + ]); + return this._curve(points, null, o); + } + + private _bezierTo(x1: number, y1: number, x2: number, y2: number, x: number, y: number, path: RoughPath, o: ResolvedOptions): Op[] { + const ops: Op[] = []; + const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]; + let f: Point = [0, 0]; + for (let i = 0; i < 2; i++) { + if (i === 0) { + ops.push({ op: 'move', data: [path.x, path.y] }); + } else { + ops.push({ op: 'move', data: [path.x + this.getOffset(-ros[0], ros[0], o), path.y + this.getOffset(-ros[0], ros[0], o)] }); + } + f = [x + this.getOffset(-ros[i], ros[i], o), y + this.getOffset(-ros[i], ros[i], o)]; + ops.push({ + op: 'bcurveTo', data: [ + x1 + this.getOffset(-ros[i], ros[i], o), y1 + this.getOffset(-ros[i], ros[i], o), + x2 + this.getOffset(-ros[i], ros[i], o), y2 + this.getOffset(-ros[i], ros[i], o), + f[0], f[1] + ] + }); + } + path.setPosition(f[0], f[1]); + return ops; + } + + private _processSegment(path: RoughPath, seg: Segment, prevSeg: Segment | null, o: ResolvedOptions): Op[] { + let ops: Op[] = []; + switch (seg.key) { + case 'M': + case 'm': { + const delta = seg.key === 'm'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + const ro = 1 * (o.maxRandomnessOffset || 0); + x = x + this.getOffset(-ro, ro, o); + y = y + this.getOffset(-ro, ro, o); + path.setPosition(x, y); + ops.push({ op: 'move', data: [x, y] }); + } + break; + } + case 'L': + case 'l': { + const delta = seg.key === 'l'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } + break; + } + case 'H': + case 'h': { + const delta = seg.key === 'h'; + if (seg.data.length) { + let x = +seg.data[0]; + if (delta) { + x += path.x; + } + ops = ops.concat(this.doubleLine(path.x, path.y, x, path.y, o)); + path.setPosition(x, path.y); + } + break; + } + case 'V': + case 'v': { + const delta = seg.key === 'v'; + if (seg.data.length) { + let y = +seg.data[0]; + if (delta) { + y += path.y; + } + ops = ops.concat(this.doubleLine(path.x, path.y, path.x, y, o)); + path.setPosition(path.x, y); + } + break; + } + case 'Z': + case 'z': { + if (path.first) { + ops = ops.concat(this.doubleLine(path.x, path.y, path.first[0], path.first[1], o)); + path.setPosition(path.first[0], path.first[1]); + path.first = null; + } + break; + } + case 'C': + case 'c': { + const delta = seg.key === 'c'; + if (seg.data.length >= 6) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x2 = +seg.data[2]; + let y2 = +seg.data[3]; + let x = +seg.data[4]; + let y = +seg.data[5]; + if (delta) { + x1 += path.x; + x2 += path.x; + x += path.x; + y1 += path.y; + y2 += path.y; + y += path.y; + } + const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'S': + case 's': { + const delta = seg.key === 's'; + if (seg.data.length >= 4) { + let x2 = +seg.data[0]; + let y2 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x2 += path.x; + x += path.x; + y2 += path.y; + y += path.y; + } + let x1 = x2; + let y1 = y2; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref: Point | null = null; + if (prevKey === 'c' || prevKey === 'C' || prevKey === 's' || prevKey === 'S') { + ref = path.bezierReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o); + ops = ops.concat(ob); + path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)]; + } + break; + } + case 'Q': + case 'q': { + const delta = seg.key === 'q'; + if (seg.data.length >= 4) { + let x1 = +seg.data[0]; + let y1 = +seg.data[1]; + let x = +seg.data[2]; + let y = +seg.data[3]; + if (delta) { + x1 += path.x; + x += path.x; + y1 += path.y; + y += path.y; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); + let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); + f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'T': + case 't': { + const delta = seg.key === 't'; + if (seg.data.length >= 2) { + let x = +seg.data[0]; + let y = +seg.data[1]; + if (delta) { + x += path.x; + y += path.y; + } + let x1 = x; + let y1 = y; + const prevKey = prevSeg ? prevSeg.key : ''; + let ref: Point | null = null; + if (prevKey === 'q' || prevKey === 'Q' || prevKey === 't' || prevKey === 'T') { + ref = path.quadReflectionPoint; + } + if (ref) { + x1 = ref[0]; + y1 = ref[1]; + } + const offset1 = 1 * (1 + o.roughness * 0.2); + const offset2 = 1.5 * (1 + o.roughness * 0.22); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] }); + let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o), + f[0], f[1] + ] + }); + ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] }); + f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)]; + ops.push({ + op: 'qcurveTo', data: [ + x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o), + f[0], f[1] + ] + }); + path.setPosition(f[0], f[1]); + path.quadReflectionPoint = [x + (x - x1), y + (y - y1)]; + } + break; + } + case 'A': + case 'a': { + const delta = seg.key === 'a'; + if (seg.data.length >= 7) { + const rx = +seg.data[0]; + const ry = +seg.data[1]; + const angle = +seg.data[2]; + const largeArcFlag = +seg.data[3]; + const sweepFlag = +seg.data[4]; + let x = +seg.data[5]; + let y = +seg.data[6]; + if (delta) { + x += path.x; + y += path.y; + } + if (x === path.x && y === path.y) { + break; + } + if (rx === 0 || ry === 0) { + ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o)); + path.setPosition(x, y); + } else { + for (let i = 0; i < 1; i++) { + const arcConverter = new RoughArcConverter( + [path.x, path.y], + [x, y], + [rx, ry], + angle, + largeArcFlag ? true : false, + sweepFlag ? true : false + ); + let segment = arcConverter.getNextSegment(); + while (segment) { + const ob = this._bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o); + ops = ops.concat(ob); + segment = arcConverter.getNextSegment(); + } + } + } + } + break; + } + default: + break; + } + return ops; + } +} \ No newline at end of file diff --git a/src/rough.ts b/src/rough.ts index 51029d2..7eb8717 100644 --- a/src/rough.ts +++ b/src/rough.ts @@ -1,35 +1,18 @@ import { Config, DrawingSurface } from './core'; import { RoughCanvas } from './canvas'; -import { RoughRenderer } from './renderer'; import { RoughGenerator } from './generator'; -import { RoughGeneratorAsync } from './generator-async'; -import { RoughCanvasAsync } from './canvas-async'; import { RoughSVG } from './svg'; -import { RoughSVGAsync } from './svg-async'; export default { - canvas(canvas: HTMLCanvasElement, config?: Config): RoughCanvas | RoughCanvasAsync { - if (config && config.async) { - return new RoughCanvasAsync(canvas, config); - } + canvas(canvas: HTMLCanvasElement, config?: Config): RoughCanvas { return new RoughCanvas(canvas, config); }, - svg(svg: SVGSVGElement, config?: Config): RoughSVG | RoughSVGAsync { - if (config && config.async) { - return new RoughSVGAsync(svg, config); - } + svg(svg: SVGSVGElement, config?: Config): RoughSVG { return new RoughSVG(svg, config); }, - createRenderer(): RoughRenderer { - return RoughCanvas.createRenderer(); - }, - - generator(config: Config | null, surface: DrawingSurface): RoughGenerator | RoughGeneratorAsync { - if (config && config.async) { - return new RoughGeneratorAsync(config, surface); - } + generator(config: Config | null, surface: DrawingSurface): RoughGenerator { return new RoughGenerator(config, surface); } }; \ No newline at end of file diff --git a/src/svg-async.ts b/src/svg-async.ts.old similarity index 100% rename from src/svg-async.ts rename to src/svg-async.ts.old diff --git a/src/svg-base.ts b/src/svg-base.ts index 23dfae4..31368d2 100644 --- a/src/svg-base.ts +++ b/src/svg-base.ts @@ -1,5 +1,4 @@ import { Drawable, OpSet, ResolvedOptions } from './core'; -import { RoughRenderer } from './renderer'; const hasDocument = typeof document !== 'undefined'; @@ -15,10 +14,6 @@ export abstract class RoughSVGBase { abstract opsToPath(drawing: OpSet): string; - static createRenderer(): RoughRenderer { - return new RoughRenderer(); - } - get defs(): SVGDefsElement | null { const doc = this.svg.ownerDocument || (hasDocument && document); if (doc) { @@ -38,7 +33,7 @@ export abstract class RoughSVGBase { draw(drawable: Drawable): SVGGElement { const sets = drawable.sets || []; const o = drawable.options || this.getDefaultOptions(); - const doc = this.svg.ownerDocument || (hasDocument && document); + const doc = this.svg.ownerDocument || window.document; const g = doc.createElementNS('http://www.w3.org/2000/svg', 'g'); for (const drawing of sets) { let path = null; diff --git a/src/svg.ts b/src/svg.ts index 57b18d6..bafc385 100644 --- a/src/svg.ts +++ b/src/svg.ts @@ -23,47 +23,47 @@ export class RoughSVG extends RoughSVGBase { return this.gen.opsToPath(drawing); } - line(x1: number, y1: number, x2: number, y2: number, options?: Options) { + line(x1: number, y1: number, x2: number, y2: number, options?: Options): SVGGElement { const d = this.gen.line(x1, y1, x2, y2, options); return this.draw(d); } - rectangle(x: number, y: number, width: number, height: number, options?: Options) { + rectangle(x: number, y: number, width: number, height: number, options?: Options): SVGGElement { const d = this.gen.rectangle(x, y, width, height, options); return this.draw(d); } - ellipse(x: number, y: number, width: number, height: number, options?: Options) { + ellipse(x: number, y: number, width: number, height: number, options?: Options): SVGGElement { const d = this.gen.ellipse(x, y, width, height, options); return this.draw(d); } - circle(x: number, y: number, diameter: number, options?: Options) { + circle(x: number, y: number, diameter: number, options?: Options): SVGGElement { const d = this.gen.circle(x, y, diameter, options); return this.draw(d); } - linearPath(points: Point[], options?: Options) { + linearPath(points: Point[], options?: Options): SVGGElement { const d = this.gen.linearPath(points, options); return this.draw(d); } - polygon(points: Point[], options?: Options) { + polygon(points: Point[], options?: Options): SVGGElement { const d = this.gen.polygon(points, options); return this.draw(d); } - arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean = false, options?: Options) { + arc(x: number, y: number, width: number, height: number, start: number, stop: number, closed: boolean = false, options?: Options): SVGGElement { const d = this.gen.arc(x, y, width, height, start, stop, closed, options); return this.draw(d); } - curve(points: Point[], options?: Options) { + curve(points: Point[], options?: Options): SVGGElement { const d = this.gen.curve(points, options); return this.draw(d); } - path(d: string, options?: Options) { + path(d: string, options?: Options): SVGGElement { const drawing = this.gen.path(d, options); return this.draw(drawing); } diff --git a/tsconfig.json b/tsconfig.json index d8b4e1f..747687a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,9 +16,7 @@ "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "experimentalDecorators": true, - "emitDecoratorMetadata": true + "noFallthroughCasesInSwitch": true }, "include": [ "src/**/*.ts"