-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b57c205
commit d5fd5d3
Showing
8 changed files
with
423 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<!doctype html> | ||
<html> | ||
<head> | ||
<title>CSG Example</title> | ||
<script src="../dist/stitch.global.js"></script> | ||
</head> | ||
<body> | ||
<script> | ||
|
||
// set up the RNG | ||
let R = new Stitch.Math.Random(); | ||
|
||
// create a new pattern | ||
let pattern = new Stitch.Core.Pattern(500, 500); | ||
|
||
// add the thread | ||
let blackThread = pattern.addThread(0, 0, 0); | ||
let redThread = pattern.addThread(255, 0, 0); | ||
|
||
// create the CSG shapes | ||
let countCircles = 20; | ||
let csgShapes = []; | ||
for (let i = 0; i < countCircles; i++) { | ||
let radius = R.random_num(0.1 * pattern.widthPx, 0.2 * pattern.widthPx); | ||
let cx = R.random_num(radius, pattern.widthPx - radius); | ||
let cy = R.random_num(radius, pattern.heightPx - radius); | ||
let polyline = new Stitch.Math.Polyline(true); | ||
for (let a = 0; a < 2 * Math.PI; a += 0.1) polyline.addVertex(radius * Math.cos(a) + cx, radius * Math.sin(a) + cy); | ||
csgShapes.push(Stitch.CSG.Shape.fromPolylines([polyline.getSimplified(1)])); | ||
} | ||
|
||
// perform the polygon clipping | ||
let blackShape = csgShapes[0].subtract(csgShapes[1]); | ||
let redShape = csgShapes[1].subtract(csgShapes[0]); | ||
for (let i = 2; i < csgShapes.length; i++) { | ||
blackShape = (i % 2 === 1) ? blackShape.subtract(csgShapes[i]) : blackShape.union(csgShapes[i]); | ||
redShape = (i % 2 === 0) ? redShape.subtract(csgShapes[i]) : redShape.union(csgShapes[i]); | ||
} | ||
|
||
let blackPolylineShapes = blackShape.toPolylines(); | ||
for (let polylineShape of blackPolylineShapes) { | ||
if (polylineShape.polyline.vertices.length > 3) { | ||
blackThread.addRun(new Stitch.Runs.Run(polylineShape.polyline, 1)); | ||
} | ||
blackThread.addRun(new Stitch.Runs.AutoFill([polylineShape.polyline, ...polylineShape.contours], R.random_num(0, Math.PI), 0.2, 3, new Stitch.Math.Vector(0, 0), new Stitch.Math.Vector(0, 0))); | ||
} | ||
|
||
let redPolylineShapes = redShape.toPolylines(); | ||
for (let polylineShape of redPolylineShapes) { | ||
if (polylineShape.polyline.vertices.length > 3) { | ||
redThread.addRun(new Stitch.Runs.Run(polylineShape.polyline, 1)); | ||
} | ||
redThread.addRun(new Stitch.Runs.AutoFill([polylineShape.polyline, ...polylineShape.contours], R.random_num(0, Math.PI), 0.2, 3, new Stitch.Math.Vector(0, 0), new Stitch.Math.Vector(0, 0))); | ||
} | ||
|
||
let svg = Stitch.Graphics.getSvg(pattern, window.innerWidth, window.innerHeight); | ||
svg.setAttribute('style', 'margin: auto; position: absolute; inset: 0;'); | ||
document.body.append(svg); | ||
|
||
let modal = Stitch.Browser.Modal.createDownloadModal(pattern, 'csg', 10, 500); | ||
document.body.appendChild(modal.container); | ||
window.addEventListener('click', (e) => { | ||
if (e.target === modal.container) modal.close(); | ||
}); | ||
window.addEventListener('keydown', (e) => { | ||
if (e.code === 'KeyD') modal.open(); | ||
}); | ||
|
||
window.addEventListener("resize", Stitch.Browser.Utils.debounce(() => { | ||
svg.remove(); | ||
svg = Stitch.Graphics.getSvg(pattern, window.innerWidth, window.innerHeight); | ||
svg.setAttribute('style', 'margin: auto; position: absolute; inset: 0;'); | ||
document.body.append(svg); | ||
}, 10)); | ||
</script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { Vector } from '../Math/Vector'; | ||
import { Segment } from './Segment'; | ||
|
||
export class Line { | ||
origin: Vector; | ||
direction: Vector; | ||
normal: Vector; | ||
constructor(origin: Vector, direction: Vector) { | ||
this.origin = origin; | ||
this.direction = direction.normalized(); | ||
this.normal = new Vector(this.direction.y, -1 * this.direction.x); | ||
} | ||
static fromPoints(a: Vector, b: Vector): Line { | ||
return new Line(a, b.subtract(a).normalized()); | ||
} | ||
clone(): Line { | ||
return new Line(this.origin.copy(), this.direction.copy()); | ||
} | ||
flip(): void { | ||
this.direction = this.direction.multiply(-1); | ||
this.normal = this.normal.multiply(-1); | ||
} | ||
splitSegment( | ||
segment: Segment, | ||
collinearRight: Segment[], | ||
collinearLeft: Segment[], | ||
right: Segment[], | ||
left: Segment[], | ||
epsilon = 1e-5, | ||
): void { | ||
const [COLLINEAR, RIGHT, LEFT, SPANNING] = [0, 1, 2, 3]; | ||
|
||
let segmentType = 0; | ||
let t = 0; | ||
const types: number[] = []; | ||
for (let i = 0; i < segment.vertices.length; i++) { | ||
t = this.normal.dot(segment.vertices[i].subtract(this.origin)); | ||
const type = t < -epsilon ? RIGHT : t > epsilon ? LEFT : COLLINEAR; | ||
segmentType |= type; | ||
types.push(type); | ||
} | ||
|
||
switch (segmentType) { | ||
case COLLINEAR: | ||
if (t > 0 || segment.line.origin.x >= this.origin.x) collinearRight.push(segment); | ||
else collinearLeft.push(segment); | ||
break; | ||
case RIGHT: | ||
right.push(segment); | ||
break; | ||
case LEFT: | ||
left.push(segment); | ||
break; | ||
case SPANNING: | ||
const [r, l]: [Vector[], Vector[]] = [[], []]; | ||
const [ti, tj] = [types[0], types[1]]; | ||
const [vi, vj] = [segment.vertices[0], segment.vertices[1]]; | ||
if (ti === RIGHT && tj === RIGHT) { | ||
r.push(vi, vj); | ||
} else if (ti === LEFT && tj === LEFT) { | ||
l.push(vi, vj); | ||
} else if (ti === RIGHT && tj === LEFT) { | ||
const tw = | ||
this.normal.dot(this.origin.subtract(vi)) / this.normal.dot(vj.subtract(vi)); | ||
const v = vi.lerp(vj, tw); | ||
r.push(vi, v); | ||
l.push(v.copy(), vj); | ||
} else if (ti === LEFT && tj === RIGHT) { | ||
const tw = | ||
this.normal.dot(this.origin.subtract(vi)) / this.normal.dot(vj.subtract(vi)); | ||
const v = vi.lerp(vj, tw); | ||
l.push(vi, v); | ||
r.push(v.copy(), vj); | ||
} | ||
if (r.length >= 2) right.push(new Segment(r)); | ||
if (l.length >= 2) left.push(new Segment(l)); | ||
break; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { Segment } from './Segment'; | ||
import { Line } from './Line'; | ||
|
||
export class Node { | ||
line: Line | undefined; | ||
right: Node | undefined; | ||
left: Node | undefined; | ||
segments: Segment[]; | ||
constructor(segments?: Segment[]) { | ||
this.segments = []; | ||
if (segments) this.build(segments); | ||
} | ||
clone(): Node { | ||
const node = new Node(); | ||
if (this.line) node.line = this.line.clone(); | ||
if (this.right) node.right = this.right.clone(); | ||
if (this.left) node.left = this.left.clone(); | ||
node.segments = this.segments.map((p) => p.clone()); | ||
return node; | ||
} | ||
invert(): void { | ||
for (let i = 0; i < this.segments.length; i++) this.segments[i].flip(); | ||
if (this.line) this.line.flip(); | ||
if (this.right) this.right.invert(); | ||
if (this.left) this.left.invert(); | ||
[this.right, this.left] = [this.left, this.right]; | ||
} | ||
clipSegments(segments: Segment[]): Segment[] { | ||
if (!this.line) return segments.slice(); | ||
let right: Segment[] = []; | ||
let left: Segment[] = []; | ||
for (let i = 0; i < segments.length; i++) { | ||
this.line.splitSegment(segments[i], right, left, right, left); | ||
} | ||
if (this.right) right = this.right.clipSegments(right); | ||
if (this.left) left = this.left.clipSegments(left); | ||
else left = []; | ||
return right.concat(left); | ||
} | ||
clipTo(bsp: Node): void { | ||
this.segments = bsp.clipSegments(this.segments); | ||
if (this.right) this.right.clipTo(bsp); | ||
if (this.left) this.left.clipTo(bsp); | ||
} | ||
allSegments(): Segment[] { | ||
let segments = this.segments.slice(); | ||
if (this.right) segments = segments.concat(this.right.allSegments()); | ||
if (this.left) segments = segments.concat(this.left.allSegments()); | ||
return segments; | ||
} | ||
build(segments: Segment[]): void { | ||
if (!segments || segments.length < 1) return; | ||
if (!this.line) this.line = segments[0].line.clone(); | ||
let [right, left] = [[], []]; | ||
for (let i = 0; i < segments.length; i++) { | ||
this.line.splitSegment(segments[i], this.segments, this.segments, right, left); | ||
} | ||
if (right.length > 0) { | ||
if (!this.right) this.right = new Node(); | ||
this.right.build(right); | ||
} | ||
if (left.length > 0) { | ||
if (!this.left) this.left = new Node(); | ||
this.left.build(left); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Vector } from '../Math/Vector'; | ||
import { Line } from './Line'; | ||
|
||
export class Segment { | ||
vertices: Vector[]; | ||
line: Line; | ||
constructor(vertices: Vector[]) { | ||
this.vertices = vertices; | ||
this.line = Line.fromPoints(vertices[0], vertices[1]); | ||
} | ||
clone(): Segment { | ||
return new Segment(this.vertices.map((v) => v.copy())); | ||
} | ||
flip(): void { | ||
this.vertices.reverse().map((v) => v.multiply(-1)); | ||
this.line.flip(); | ||
} | ||
} |
Oops, something went wrong.