mirror of
https://github.com/rough-stuff/rough.git
synced 2026-01-10 23:17:57 -05:00
refactor - wip
This commit is contained in:
2
bin/canvas-base.d.ts
vendored
2
bin/canvas-base.d.ts
vendored
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
4
bin/fillers/dot-filler.d.ts
vendored
4
bin/fillers/dot-filler.d.ts
vendored
@@ -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;
|
||||
|
||||
@@ -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 };
|
||||
|
||||
7
bin/fillers/filler-interface.d.ts
vendored
7
bin/fillers/filler-interface.d.ts
vendored
@@ -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;
|
||||
}
|
||||
|
||||
3
bin/fillers/filler-utils.d.ts
vendored
3
bin/fillers/filler-utils.d.ts
vendored
@@ -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[];
|
||||
|
||||
@@ -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) {
|
||||
|
||||
4
bin/fillers/filler.d.ts
vendored
4
bin/fillers/filler.d.ts
vendored
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
4
bin/fillers/hachure-filler.d.ts
vendored
4
bin/fillers/hachure-filler.d.ts
vendored
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
3
bin/generator-base.d.ts
vendored
3
bin/generator-base.d.ts
vendored
@@ -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;
|
||||
|
||||
@@ -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') {
|
||||
|
||||
2
bin/generator.d.ts
vendored
2
bin/generator.d.ts
vendored
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
0
bin/renderer-factory-old.d.ts
vendored
Normal file
0
bin/renderer-factory-old.d.ts
vendored
Normal file
16
bin/renderer-factory-old.js
Normal file
16
bin/renderer-factory-old.js
Normal file
@@ -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();
|
||||
// }
|
||||
14
bin/renderer-new.d.ts
vendored
Normal file
14
bin/renderer-new.d.ts
vendored
Normal file
@@ -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;
|
||||
598
bin/renderer-new.js
Normal file
598
bin/renderer-new.js
Normal file
@@ -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;
|
||||
}
|
||||
25
bin/renderer-old.d.ts
vendored
Normal file
25
bin/renderer-old.d.ts
vendored
Normal file
@@ -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;
|
||||
}
|
||||
597
bin/renderer-old.js
Normal file
597
bin/renderer-old.js
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
40
bin/renderer.d.ts
vendored
40
bin/renderer.d.ts
vendored
@@ -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[];
|
||||
|
||||
1116
bin/renderer.js
1116
bin/renderer.js
File diff suppressed because it is too large
Load Diff
11
bin/rough.d.ts
vendored
11
bin/rough.d.ts
vendored
@@ -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;
|
||||
|
||||
15
bin/rough.js
15
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);
|
||||
}
|
||||
};
|
||||
|
||||
2
bin/svg-base.d.ts
vendored
2
bin/svg-base.d.ts
vendored
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
1404
package-lock.json
generated
1404
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
17
src/renderer-factory.ts.old
Normal file
17
src/renderer-factory.ts.old
Normal file
@@ -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();
|
||||
// }
|
||||
1155
src/renderer.ts
1155
src/renderer.ts
File diff suppressed because it is too large
Load Diff
619
src/renderer.ts.old
Normal file
619
src/renderer.ts.old
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
23
src/rough.ts
23
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);
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
18
src/svg.ts
18
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);
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
|
||||
Reference in New Issue
Block a user