mirror of
https://github.com/rough-stuff/rough.git
synced 2026-04-22 03:00:28 -04:00
support filling of curves
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { Config, DrawingSurface, Options, Drawable, OpSet, ResolvedOptions, PathInfo, PatternInfo, SVGNS } from './core';
|
||||
import { Point } from './geometry.js';
|
||||
import { line, solidFillPolygon, patternFillPolygon, rectangle, ellipseWithParams, generateEllipseParams, linearPath, arc, patternFillArc, curve, svgPath } from './renderer.js';
|
||||
import { Point, getPointsOnBezierCurves } from './geometry.js';
|
||||
import { line, solidFillPolygon, patternFillPolygon, rectangle, ellipseWithParams, generateEllipseParams, linearPath, arc, patternFillArc, curve, svgPath, curveAsBezierPoints } from './renderer.js';
|
||||
import { randomSeed } from './math';
|
||||
|
||||
const hasSelf = typeof self !== 'undefined';
|
||||
@@ -125,12 +125,28 @@ export class RoughGenerator {
|
||||
|
||||
curve(points: Point[], options?: Options): Drawable {
|
||||
const o = this._options(options);
|
||||
return this._drawable('curve', [curve(points, o)], o);
|
||||
const paths: OpSet[] = [];
|
||||
const outline = curve(points, o);
|
||||
if (o.fill && o.fill !== NOS) {
|
||||
const bezPoints = curveAsBezierPoints(points, o);
|
||||
if (bezPoints.length >= 4) {
|
||||
const polyPoints = getPointsOnBezierCurves(bezPoints, Math.min(50, 50 * o.roughness));
|
||||
if (o.fillStyle === 'solid') {
|
||||
paths.push(solidFillPolygon(polyPoints, o));
|
||||
} else {
|
||||
paths.push(patternFillPolygon(polyPoints, o));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (o.stroke !== NOS) {
|
||||
paths.push(outline);
|
||||
}
|
||||
return this._drawable('curve', paths, o);
|
||||
}
|
||||
|
||||
polygon(points: Point[], options?: Options): Drawable {
|
||||
const o = this._options(options);
|
||||
const paths = [];
|
||||
const paths: OpSet[] = [];
|
||||
const outline = linearPath(points, true, o);
|
||||
if (o.fill) {
|
||||
if (o.fillStyle === 'solid') {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export declare type Point = [number, number];
|
||||
|
||||
export declare type Line = [Point, Point];
|
||||
export type Point = [number, number];
|
||||
export type Line = [Point, Point];
|
||||
|
||||
export interface Rectangle {
|
||||
x: number;
|
||||
@@ -33,4 +32,79 @@ export function lineLength(line: Line): number {
|
||||
const p1 = line[0];
|
||||
const p2 = line[1];
|
||||
return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2));
|
||||
}
|
||||
|
||||
export function getPointsOnBezierCurves(points: Point[], tolerance: number): Point[] {
|
||||
const newPoints: Point[] = [];
|
||||
const numSegments = (points.length - 1) / 3;
|
||||
for (let i = 0; i < numSegments; i++) {
|
||||
const offset = i * 3;
|
||||
getPointsOnBezierCurveWithSplitting(points, offset, tolerance, newPoints);
|
||||
}
|
||||
return newPoints;
|
||||
}
|
||||
|
||||
function lerp(a: Point, b: Point, t: number): Point {
|
||||
return [
|
||||
a[0] + (b[0] - a[0]) * t,
|
||||
a[1] + (b[1] - a[1]) * t,
|
||||
];
|
||||
}
|
||||
|
||||
function flatness(points: Point[], offset: number): number {
|
||||
const p1 = points[offset + 0];
|
||||
const p2 = points[offset + 1];
|
||||
const p3 = points[offset + 2];
|
||||
const p4 = points[offset + 3];
|
||||
|
||||
let ux = 3 * p2[0] - 2 * p1[0] - p4[0]; ux *= ux;
|
||||
let uy = 3 * p2[1] - 2 * p1[1] - p4[1]; uy *= uy;
|
||||
let vx = 3 * p3[0] - 2 * p4[0] - p1[0]; vx *= vx;
|
||||
let vy = 3 * p3[1] - 2 * p4[1] - p1[1]; vy *= vy;
|
||||
|
||||
if (ux < vx) {
|
||||
ux = vx;
|
||||
}
|
||||
|
||||
if (uy < vy) {
|
||||
uy = vy;
|
||||
}
|
||||
|
||||
return ux + uy;
|
||||
}
|
||||
|
||||
function getPointsOnBezierCurveWithSplitting(points: Point[], offset: number, tolerance: number, newPoints: Point[]) {
|
||||
const outPoints = newPoints || [];
|
||||
if (flatness(points, offset) < tolerance) {
|
||||
const p0 = points[offset + 0];
|
||||
if (outPoints.length) {
|
||||
const d = lineLength([outPoints[outPoints.length - 1], p0]);
|
||||
if (d > 1) {
|
||||
outPoints.push(p0);
|
||||
}
|
||||
} else {
|
||||
outPoints.push(p0);
|
||||
}
|
||||
outPoints.push(points[offset + 3]);
|
||||
} else {
|
||||
// subdivide
|
||||
const t = .5;
|
||||
const p1 = points[offset + 0];
|
||||
const p2 = points[offset + 1];
|
||||
const p3 = points[offset + 2];
|
||||
const p4 = points[offset + 3];
|
||||
|
||||
const q1 = lerp(p1, p2, t);
|
||||
const q2 = lerp(p2, p3, t);
|
||||
const q3 = lerp(p3, p4, t);
|
||||
|
||||
const r1 = lerp(q1, q2, t);
|
||||
const r2 = lerp(q2, q3, t);
|
||||
|
||||
const red = lerp(r1, r2, t);
|
||||
|
||||
getPointsOnBezierCurveWithSplitting([p1, q1, r1, red], 0, tolerance, outPoints);
|
||||
getPointsOnBezierCurveWithSplitting([red, r2, q3, p4], 0, tolerance, outPoints);
|
||||
}
|
||||
return outPoints;
|
||||
}
|
||||
@@ -56,6 +56,24 @@ export function curve(points: Point[], o: ResolvedOptions): OpSet {
|
||||
return { type: 'path', ops: o1.concat(o2) };
|
||||
}
|
||||
|
||||
export function curveAsBezierPoints(points: Point[], o: ResolvedOptions): Point[] {
|
||||
const ops = _curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o);
|
||||
const bez: Point[] = [];
|
||||
ops.forEach((op) => {
|
||||
switch (op.op) {
|
||||
case 'move':
|
||||
bez.push([op.data[0], op.data[1]]);
|
||||
break;
|
||||
case 'bcurveTo':
|
||||
bez.push([op.data[0], op.data[1]]);
|
||||
bez.push([op.data[2], op.data[3]]);
|
||||
bez.push([op.data[4], op.data[5]]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
return bez;
|
||||
}
|
||||
|
||||
export interface EllipseResult {
|
||||
opset: OpSet;
|
||||
estimatedPoints: Point[];
|
||||
|
||||
Reference in New Issue
Block a user