Skip to content

Commit

Permalink
try to be smarter about polar perimeter detection
Browse files Browse the repository at this point in the history
  • Loading branch information
bobnik committed Sep 20, 2023
1 parent 82ae25d commit f62c0b2
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 32 deletions.
19 changes: 19 additions & 0 deletions src/common/geometry.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,25 @@ export const subsample = (vertices, maxLength) => {
return subsampledVertices
}

export const downsample = (vertices, tolerance = 0.0001) => {
let result = [vertices[0]]

for (let i = 1; i < vertices.length - 1; i++) {
let slope1 =
(vertices[i].y - vertices[i - 1].y) / (vertices[i].x - vertices[i - 1].x)
let slope2 =
(vertices[i + 1].y - vertices[i].y) / (vertices[i + 1].x - vertices[i].x)

if (Math.abs(slope1 - slope2) > tolerance) {
result.push(vertices[i])
}
}

result.push(vertices[vertices.length - 1])

return result
}

// Convert x,y vertices to theta, rho vertices
export const toThetaRho = (subsampledVertices, maxRadius, rhoMax) => {
let vertices = []
Expand Down
2 changes: 1 addition & 1 deletion src/features/effects/Mask.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const options = {
maskBorder: {
title: "Draw border",
type: "checkbox",
}
},
}

export default class Mask extends Effect {
Expand Down
4 changes: 4 additions & 0 deletions src/features/machines/Machine.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ export default class Machine {
if (this.layerInfo.start) this.addStartPoint()
if (this.layerInfo.end) this.addEndPoint()

// remove unnecessary vertices along a straight line; the polar machine, for one, adds
// extra vertices to help with perimeter detection
this.vertices = downsample(this.vertices)

// second call to limit precision for final cleanup
this.limitPrecision()

Expand Down
50 changes: 19 additions & 31 deletions src/features/machines/PolarMachine.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
angle,
onSegment,
arc,
annotateVertex,
Expand Down Expand Up @@ -100,6 +99,20 @@ export default class PolarMachine extends Machine {
}
}

// remove all non-essential perimeter vertices. returns a list of segments
// involving non-perimeter paths
stripExtraPerimeterVertices() {
// Subsampling to protect against the case where there is a straight line connecting two
// perimeter points directly. In this case, we want to register that as a non-perimeter
// move, or it will be incorrectly optimized out of the final vertices. These extra vertices
// will be removed later by the machine if possible.
this.vertices = subsample(
this.vertices,
Math.min(this.sizeX, this.sizeY) / 50,
)
return super.stripExtraPerimeterVertices()
}

// Finds the nearest vertex that is in the bounds of the circle. This will change the
// shape. i.e. this doesn't care about the line segment, only about the point.
nearestVertex(vertex) {
Expand Down Expand Up @@ -132,19 +145,6 @@ export default class PolarMachine extends Machine {
}
}

// Returns the distance along the perimeter between two points.
perimeterDistance(v1, v2) {
const startAngle = angle(v1)
const endAngle = angle(v2)
let deltaAngle = Math.abs(endAngle - startAngle)

if (deltaAngle > Math.PI) {
deltaAngle -= 2.0 * Math.PI
}

return Math.abs(deltaAngle) * this.state.maxRadius
}

// Returns points along the circle from the start to the end, tracing a circle of radius size.
tracePerimeter(start, end) {
return arc(this.state.maxRadius, start.angle(), end.angle())
Expand All @@ -163,23 +163,11 @@ export default class PolarMachine extends Machine {

// Returns whether a given path lies on the perimeter of the circle.
onPerimeter(v1, v2, delta = 1) {
let rm = Math.pow(this.state.maxRadius, 2)
let r1 = Math.pow(v1.x, 2) + Math.pow(v1.y, 2)
let r2 = Math.pow(v2.x, 2) + Math.pow(v2.y, 2)
let d = this.perimeterDistance(v1, v2)

// Delta is purposefully large to accommodate the squaring of the compared values.
// Setting delta too small will result in perimeter moves being miscategorized.
// d is used to guard against the case where there is a straight line connecting two
// perimeter points directly. In this case, we want to register that as a non-perimeter
// move, or it will be incorrectly optimized out of the final vertices. The 3/50
// ratio could likely be refined further (relative to maxRadius), but it seems to produce
// accurate results at various machine sizes.
return (
Math.abs(r1 - rm) < delta &&
Math.abs(r2 - rm) < delta &&
d < (3 * this.state.maxRadius) / this.perimeterConstant
)
const rm = Math.sqrt(Math.pow(this.state.maxRadius, 2))
const r1 = Math.sqrt(Math.pow(v1.x, 2) + Math.pow(v1.y, 2))
const r2 = Math.sqrt(Math.pow(v2.x, 2) + Math.pow(v2.y, 2))

return Math.abs(r1 - rm) < delta && Math.abs(r2 - rm) < delta
}

// The guts of logic for this limits enforcer. It will take a single line (defined by
Expand Down

0 comments on commit f62c0b2

Please sign in to comment.