mirror of
https://github.com/rough-stuff/rough.git
synced 2026-04-22 03:00:28 -04:00
fitter
This commit is contained in:
110
dist/rough.js
vendored
110
dist/rough.js
vendored
@@ -248,6 +248,11 @@ class ParsedPath {
|
||||
this.processPoints();
|
||||
}
|
||||
|
||||
loadFromSegments(segments) {
|
||||
this.segments = segments;
|
||||
this.processPoints();
|
||||
}
|
||||
|
||||
processPoints() {
|
||||
let first = null, currentPoint = [0, 0];
|
||||
for (let i = 0; i < this.segments.length; i++) {
|
||||
@@ -428,17 +433,32 @@ class RoughPath {
|
||||
return this.parsed.closed;
|
||||
}
|
||||
|
||||
get points() {
|
||||
if (!this._points) {
|
||||
const points = [];
|
||||
get linearPoints() {
|
||||
if (!this._linearPoints) {
|
||||
const lp = [];
|
||||
let points = [];
|
||||
for (let s of this.parsed.segments) {
|
||||
let key = s.key.toLowerCase();
|
||||
if (key === 'm' || key === 'z') {
|
||||
if (points.length) {
|
||||
lp.push(points);
|
||||
points = [];
|
||||
}
|
||||
if (key === 'z') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (s.point) {
|
||||
points.push(s.point);
|
||||
}
|
||||
}
|
||||
this._points = points;
|
||||
if (points.length) {
|
||||
lp.push(points);
|
||||
points = [];
|
||||
}
|
||||
this._linearPoints = lp;
|
||||
}
|
||||
return this._points;
|
||||
return this._linearPoints;
|
||||
}
|
||||
|
||||
get first() {
|
||||
@@ -559,6 +579,76 @@ class RoughArcConverter {
|
||||
}
|
||||
}
|
||||
|
||||
class PathFitter {
|
||||
constructor(sets, closed) {
|
||||
this.sets = sets;
|
||||
this.closed = closed;
|
||||
}
|
||||
|
||||
fit(simplification) {
|
||||
let outSets = [];
|
||||
for (const set of this.sets) {
|
||||
let length = set.length;
|
||||
let estLength = Math.floor(simplification * length);
|
||||
if (estLength < 5) {
|
||||
if (length <= 5) {
|
||||
continue;
|
||||
}
|
||||
estLength = 5;
|
||||
}
|
||||
outSets.push(this.reduce(set, estLength));
|
||||
}
|
||||
|
||||
let d = '';
|
||||
for (const set of outSets) {
|
||||
for (let i = 0; i < set.length; i++) {
|
||||
let point = set[i];
|
||||
if (i === 0) {
|
||||
d += 'M' + point[0] + "," + point[1];
|
||||
} else {
|
||||
d += 'L' + point[0] + "," + point[1];
|
||||
}
|
||||
}
|
||||
if (this.closed) {
|
||||
d += 'z ';
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
distance(p1, p2) {
|
||||
return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2));
|
||||
}
|
||||
|
||||
reduce(set, count) {
|
||||
if (set.length <= count) {
|
||||
return set;
|
||||
}
|
||||
let points = set.slice(0);
|
||||
while (points.length > count) {
|
||||
let minArea = -1;
|
||||
let minIndex = -1;
|
||||
for (let i = 1; i < (points.length - 1); i++) {
|
||||
let a = this.distance(points[i - 1], points[i]);
|
||||
let b = this.distance(points[i], points[i + 1]);
|
||||
let c = this.distance(points[i - 1], points[i + 1]);
|
||||
let s = (a + b + c) / 2.0;
|
||||
let area = Math.sqrt(s * (s - a) * (s - b) * (s - c));
|
||||
if ((minArea < 0) || (area < minArea)) {
|
||||
minArea = area;
|
||||
minIndex = i;
|
||||
}
|
||||
}
|
||||
if (minIndex > 0) {
|
||||
points.splice(minIndex, 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return points;
|
||||
}
|
||||
}
|
||||
|
||||
class RoughRenderer {
|
||||
line(x1, y1, x2, y2, o) {
|
||||
let o1 = this._line(x1, y1, x2, y2, o, true, false);
|
||||
@@ -780,8 +870,13 @@ class RoughRenderer {
|
||||
svgPath(path, o) {
|
||||
path = (path || '').replace(/\n/g, " ").replace(/(-)/g, " -").replace(/(-\s)/g, "-").replace("/(\s\s)/g", " ");
|
||||
let p = new RoughPath(path);
|
||||
let segments = p.segments || [];
|
||||
if (o.simplification) {
|
||||
let fitter = new PathFitter(p.linearPoints, p.closed);
|
||||
let d = fitter.fit(o.simplification);
|
||||
p = new RoughPath(d);
|
||||
}
|
||||
let ops = [];
|
||||
let segments = p.segments || [];
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
let s = segments[i];
|
||||
let prev = i > 0 ? segments[i - 1] : null;
|
||||
@@ -1431,6 +1526,9 @@ class RoughCanvas {
|
||||
}
|
||||
|
||||
async path(d, options) {
|
||||
if (!d) {
|
||||
return;
|
||||
}
|
||||
let o = this._options(options);
|
||||
let lib = await this.lib();
|
||||
if (o.fill) {
|
||||
|
||||
2
dist/rough.min.js
vendored
2
dist/rough.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -2,8 +2,8 @@
|
||||
|
||||
<head>
|
||||
<title>RoughJS sample</title>
|
||||
<!-- <script src="https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.js"></script> -->
|
||||
<script src="../../dist/rough.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/gh/pshihn/workly/dist/workly.js"></script>
|
||||
<script src="../../dist/rough.min.js"></script>
|
||||
<script src="//d3js.org/d3.v3.min.js"></script>
|
||||
<script src="//d3js.org/topojson.v1.min.js"></script>
|
||||
<style>
|
||||
@@ -23,7 +23,13 @@
|
||||
|
||||
<script>
|
||||
(async () => {
|
||||
const rough = new RoughCanvas(document.getElementById('canvas'));
|
||||
const rough = new RoughCanvas(document.getElementById('canvas'),
|
||||
{
|
||||
options: {
|
||||
simplification: 0.2, roughness: 0.5,
|
||||
fill: 'rgba(0,0,255,0.8)', fsillStyle: 'solid'
|
||||
}
|
||||
});
|
||||
|
||||
const width = 960, height = 500;
|
||||
|
||||
@@ -37,13 +43,9 @@
|
||||
d3.json("./us.json", async (error, us) => {
|
||||
if (error) throw error;
|
||||
let topo = topojson.feature(us, us.objects.states).features;
|
||||
await rough.path(path(topo[2]), { simplify: true });
|
||||
// await rough.path(path(topo[9]), { simplify: true });
|
||||
// await rough.path(path(topo[12]));
|
||||
// await rough.path(path(topo[13]));
|
||||
// for (let feature of topo) {
|
||||
// await rough.path(path(feature));
|
||||
// }
|
||||
for (let feature of topo) {
|
||||
await rough.path(path(feature));
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
@@ -150,6 +150,9 @@ export default class RoughCanvas {
|
||||
}
|
||||
|
||||
async path(d, options) {
|
||||
if (!d) {
|
||||
return;
|
||||
}
|
||||
let o = this._options(options);
|
||||
let lib = await this.lib();
|
||||
if (o.fill) {
|
||||
|
||||
102
src/path.js
102
src/path.js
@@ -41,6 +41,11 @@ class ParsedPath {
|
||||
this.processPoints();
|
||||
}
|
||||
|
||||
loadFromSegments(segments) {
|
||||
this.segments = segments;
|
||||
this.processPoints();
|
||||
}
|
||||
|
||||
processPoints() {
|
||||
let first = null, prev = null, currentPoint = [0, 0];
|
||||
for (let i = 0; i < this.segments.length; i++) {
|
||||
@@ -222,17 +227,32 @@ export class RoughPath {
|
||||
return this.parsed.closed;
|
||||
}
|
||||
|
||||
get points() {
|
||||
if (!this._points) {
|
||||
const points = [];
|
||||
get linearPoints() {
|
||||
if (!this._linearPoints) {
|
||||
const lp = [];
|
||||
let points = [];
|
||||
for (let s of this.parsed.segments) {
|
||||
let key = s.key.toLowerCase();
|
||||
if (key === 'm' || key === 'z') {
|
||||
if (points.length) {
|
||||
lp.push(points);
|
||||
points = [];
|
||||
}
|
||||
if (key === 'z') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (s.point) {
|
||||
points.push(s.point);
|
||||
}
|
||||
}
|
||||
this._points = points;
|
||||
if (points.length) {
|
||||
lp.push(points);
|
||||
points = [];
|
||||
}
|
||||
this._linearPoints = lp;
|
||||
}
|
||||
return this._points;
|
||||
return this._linearPoints;
|
||||
}
|
||||
|
||||
get first() {
|
||||
@@ -351,4 +371,76 @@ export class RoughArcConverter {
|
||||
return tb - ta;
|
||||
return 2 * Math.PI - (ta - tb);
|
||||
}
|
||||
}
|
||||
|
||||
export class PathFitter {
|
||||
constructor(sets, closed) {
|
||||
this.sets = sets;
|
||||
this.closed = closed;
|
||||
}
|
||||
|
||||
fit(simplification) {
|
||||
let outSets = [];
|
||||
for (const set of this.sets) {
|
||||
let length = set.length;
|
||||
let estLength = Math.floor(simplification * length);
|
||||
if (estLength < 5) {
|
||||
if (length <= 5) {
|
||||
continue;
|
||||
}
|
||||
estLength = 5;
|
||||
}
|
||||
outSets.push(this.reduce(set, estLength));
|
||||
}
|
||||
|
||||
let d = '';
|
||||
for (const set of outSets) {
|
||||
for (let i = 0; i < set.length; i++) {
|
||||
let point = set[i];
|
||||
if (i === 0) {
|
||||
d += 'M' + point[0] + "," + point[1];
|
||||
} else {
|
||||
d += 'L' + point[0] + "," + point[1];
|
||||
}
|
||||
}
|
||||
if (this.closed) {
|
||||
d += 'z ';
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
distance(p1, p2) {
|
||||
return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2));
|
||||
}
|
||||
|
||||
reduce(set, count) {
|
||||
if (set.length <= count) {
|
||||
return set;
|
||||
}
|
||||
let points = set.slice(0);
|
||||
while (points.length > count) {
|
||||
let areas = [];
|
||||
let minArea = -1;
|
||||
let minIndex = -1;
|
||||
for (let i = 1; i < (points.length - 1); i++) {
|
||||
let a = this.distance(points[i - 1], points[i]);
|
||||
let b = this.distance(points[i], points[i + 1]);
|
||||
let c = this.distance(points[i - 1], points[i + 1]);
|
||||
let s = (a + b + c) / 2.0;
|
||||
let area = Math.sqrt(s * (s - a) * (s - b) * (s - c));
|
||||
areas.push(area);
|
||||
if ((minArea < 0) || (area < minArea)) {
|
||||
minArea = area;
|
||||
minIndex = i;
|
||||
}
|
||||
}
|
||||
if (minIndex > 0) {
|
||||
points.splice(minIndex, 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return points;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { RoughHachureIterator } from './hachure.js';
|
||||
import { RoughSegmentRelation, RoughSegment } from './segment.js';
|
||||
import { RoughPath, RoughArcConverter } from './path.js';
|
||||
import { RoughPath, RoughArcConverter, PathFitter } from './path.js';
|
||||
|
||||
export class RoughRenderer {
|
||||
line(x1, y1, x2, y2, o) {
|
||||
@@ -224,8 +224,13 @@ export class RoughRenderer {
|
||||
svgPath(path, o) {
|
||||
path = (path || '').replace(/\n/g, " ").replace(/(-)/g, " -").replace(/(-\s)/g, "-").replace("/(\s\s)/g", " ");
|
||||
let p = new RoughPath(path);
|
||||
let segments = p.segments || [];
|
||||
if (o.simplification) {
|
||||
let fitter = new PathFitter(p.linearPoints, p.closed);
|
||||
let d = fitter.fit(o.simplification);
|
||||
p = new RoughPath(d);
|
||||
}
|
||||
let ops = [];
|
||||
let segments = p.segments || [];
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
let s = segments[i];
|
||||
let prev = i > 0 ? segments[i - 1] : null;
|
||||
|
||||
Reference in New Issue
Block a user