Skip to content

Commit

Permalink
[Clipping] Fixing issue with edge-vertex tolerance detection
Browse files Browse the repository at this point in the history
  • Loading branch information
LuizZak committed Dec 11, 2024
1 parent 3d5870a commit c990252
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 10 deletions.
9 changes: 9 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@
"version": "0.8.1"
}
},
{
"package": "MiniGraphviz",
"repositoryURL": "https://github.com/LuizZak/MiniGraphviz.git",
"state": {
"branch": null,
"revision": "aa15c50cf8027dae6dde860a40987c238070d849",
"version": "0.1.0"
}
},
{
"package": "MiniP5Printer",
"repositoryURL": "https://github.com/LuizZak/MiniP5Printer",
Expand Down
2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ let testCommons: Target = .target(
name: "TestCommons",
dependencies: [
.product(name: "MiniP5Printer", package: "MiniP5Printer"),
.product(name: "MiniGraphviz", package: "MiniGraphviz"),
"Geometria",
"GeometriaAlgorithms",
"GeometriaClipping",
Expand Down Expand Up @@ -95,6 +96,7 @@ let package = Package(
.package(url: "https://github.com/apple/swift-collections.git", from: "1.1.2"),
.package(url: "https://github.com/LuizZak/MiniP5Printer.git", .exactItem("0.0.2")),
.package(url: "https://github.com/LuizZak/MiniDigraph.git", .exactItem("0.8.1")),
.package(url: "https://github.com/LuizZak/MiniGraphviz.git", .exactItem("0.1.0")),
],
targets: [
geometriaTarget.applyReportBuildTime(),
Expand Down
6 changes: 6 additions & 0 deletions Sources/Geometria/Angles/Angle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ public struct Angle<Scalar: FloatingPoint & ElementaryFunctions>: Hashable, Cust
/// Gets the radian value associated with this angle.
public let radians: Scalar

/// Gets the degrees value associated with this angle.
@inlinable
public var degrees: Scalar {
radians * (180 / .pi)
}

public var description: String {
"\(type(of: self))(radians: \(radians))"
}
Expand Down
27 changes: 17 additions & 10 deletions Sources/GeometriaClipping/2D/Graph/Simplex2Graph+Creation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,8 @@ extension Simplex2Graph {
var hasMergedNodes = false
var hasMergedEdges = false

var nodesToMerge: OrderedSet<OrderedSet<Node>> = []

// MARK: Edge-vertex interferences
for node in nodes {
let nodeAABB = AABB2(center: node.location, size: .init(repeating: tolerance * 2))
Expand All @@ -349,8 +351,10 @@ extension Simplex2Graph {
return
}

if distanceSquared.squareRoot() < tolerance {
splitEdge(edge, ratio: ratio)
if distanceSquared < tolerance {
let midNode = splitEdge(edge, ratio: ratio)

nodesToMerge.append([node, midNode])
}
}
}
Expand All @@ -365,8 +369,6 @@ extension Simplex2Graph {
areClose(n1.location, n2.location)
}

var nodesToMerge: OrderedSet<OrderedSet<Node>> = []

for node in nodes {
let neighbors = nodeTree.nearestNeighbors(
to: node.location,
Expand Down Expand Up @@ -582,13 +584,17 @@ extension Simplex2Graph {
///
/// If `period` matches the edge's `startPeriod` or `endPeriod`, then the edge
/// is not split and nothing is done.
@discardableResult
@inlinable
mutating func splitEdge(
_ edge: Edge,
ratio: Scalar
) {
guard ratio > 0 && ratio < 1 else {
return
) -> Node {
guard ratio > 0 else {
return edge.start
}
guard ratio < 1 else {
return edge.end
}

let kind: Node.Kind
Expand Down Expand Up @@ -617,6 +623,8 @@ extension Simplex2Graph {

addNode(midNode)
splitEdge(edge, ratio: ratio, midNode: midNode)

return midNode
}

/// Splits an edge into two sub-edges, covering the same period range, but with
Expand Down Expand Up @@ -844,9 +852,8 @@ extension Parametric2Contour {

for selfSimplex in self.allSimplexes() {
for otherSimplex in other.allSimplexes() {
atoms.append(
contentsOf: selfSimplex.intersectionPeriods(with: otherSimplex)
)
let periods = selfSimplex.intersectionPeriods(with: otherSimplex)
atoms.append(contentsOf: periods)
}
}

Expand Down
29 changes: 29 additions & 0 deletions Sources/TestCommons/MiniGraphviz+Clipping.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Geometria
import GeometriaClipping
import MiniGraphviz

extension GraphViz {
static func printGraph<Vector>(_ graph: Simplex2Graph<Vector>) where Vector.Scalar: CustomStringConvertible {
let nodes = graph.nodes
let edges = graph.edges

let graph = GraphViz()

for node in nodes {
graph.createNode(label: "\(node.id)")
}

for edge in edges {
let label: String
switch edge.kind {
case .line:
label = "line"
case .circleArc(_, _, _, let sweepAngle):
label = "arc (\(sweepAngle.degrees)°)"
}
graph.addConnection(fromLabel: "\(edge.start.id)", toLabel: "\(edge.end.id)", label: label)
}

print(graph.generateFile())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,22 @@ class Union2ParametricTests: XCTestCase {
}
}

func testUnion_capsuleSequence_long_3() {
let inputs: [Capsule2Parametric<Vector2D>] = [Capsule2Parametric<Vector2<Double>>(start: Vector2<Double>(x: 110.9805095512569, y: 498.69591675104044), startRadius: 48.0317197264079, end: Vector2<Double>(x: 416.52708500302657, y: 233.8149454674293), endRadius: 36.588642093089966, startPeriod: 0.0, endPeriod: 1.0), Capsule2Parametric<Vector2<Double>>(start: Vector2<Double>(x: 416.52708500302657, y: 233.8149454674293), startRadius: 36.588642093089966, end: Vector2<Double>(x: 319.21714610719965, y: 338.621547076579), endRadius: 47.257943707318276, startPeriod: 0.0, endPeriod: 1.0), Capsule2Parametric<Vector2<Double>>(start: Vector2<Double>(x: 319.21714610719965, y: 338.621547076579), startRadius: 47.257943707318276, end: Vector2<Double>(x: 598.0389757446262, y: 414.0995939891976), endRadius: 48.88305781959036, startPeriod: 0.0, endPeriod: 1.0), Capsule2Parametric<Vector2<Double>>(start: Vector2<Double>(x: 598.0389757446262, y: 414.0995939891976), startRadius: 48.88305781959036, end: Vector2<Double>(x: 918.9250986336896, y: 115.50051959924077), endRadius: 33.94632559108497, startPeriod: 0.0, endPeriod: 1.0), Capsule2Parametric<Vector2<Double>>(start: Vector2<Double>(x: 918.9250986336896, y: 115.50051959924077), startRadius: 33.94632559108497, end: Vector2<Double>(x: 358.181382248346, y: 335.7497615451311), endRadius: 38.74178996766864, startPeriod: 0.0, endPeriod: 1.0), Capsule2Parametric<Vector2<Double>>(start: Vector2<Double>(x: 358.181382248346, y: 335.7497615451311), startRadius: 38.74178996766864, end: Vector2<Double>(x: 189.35176796497183, y: 331.4399380733415), endRadius: 29.50247300985871, startPeriod: 0.0, endPeriod: 1.0)]

let sut = Union2Parametric(contours: inputs.flatMap({ $0.allContours() }), tolerance: 1e-5)

TestFixture.beginFixture(lineScale: 1.0, renderScale: 0.45) { fixture in
fixture.add(inputs, category: "inputs")

fixture.assertions(on: sut)
.assertAllSimplexes(
accuracy: accuracy,
[[Parametric2GeometrySimplex<Vector2<Double>>.circleArc2(CircleArc2Simplex<Vector2<Double>>(center: Vector2<Double>(x: 110.9805095512569, y: 498.69591675104044), radius: 48.0317197264079, startAngle: Angle<Double>(radians: 0.8282656212596273), sweepAngle: Angle<Double>(radians: 1.5707963267948966), startPeriod: 0.0, endPeriod: 0.031958306431205064)), Parametric2GeometrySimplex<Vector2<Double>>.circleArc2(CircleArc2Simplex<Vector2<Double>>(center: Vector2<Double>(x: 110.9805095512569, y: 498.69591675104044), radius: 48.0317197264079, startAngle: Angle<Double>(radians: 2.399061948054524), sweepAngle: Angle<Double>(radians: 1.5707963267948966), startPeriod: 0.031958306431205064, endPeriod: 0.06391661286241013)), Parametric2GeometrySimplex<Vector2<Double>>.circleArc2(CircleArc2Simplex<Vector2<Double>>(center: Vector2<Double>(x: 110.9805095512569, y: 498.69591675104044), radius: 48.0317197264079, startAngle: Angle<Double>(radians: 3.9698582748494204), sweepAngle: Angle<Double>(radians: 0.05660366239195147), startPeriod: 0.06391661286241013, endPeriod: 0.0650682307755622)), Parametric2GeometrySimplex<Vector2<Double>>.lineSegment2(LineSegment2Simplex<Vector2<Double>>(start: Vector2<Double>(x: 80.55766692072845, y: 461.5274254424831), end: Vector2<Double>(x: 202.07840514413593, y: 362.0612871850714), startPeriod: 0.0650682307755622, endPeriod: 0.1315862557108902)), Parametric2GeometrySimplex<Vector2<Double>>.lineSegment2(LineSegment2Simplex<Vector2<Double>>(start: Vector2<Double>(x: 202.07840514413593, y: 362.0612871850714), end: Vector2<Double>(x: 186.98652020697256, y: 360.84744590281804), startPeriod: 0.1315862557108902, endPeriod: 0.13799952430606413)), Parametric2GeometrySimplex<Vector2<Double>>.circleArc2(CircleArc2Simplex<Vector2<Double>>(center: Vector2<Double>(x: 189.35176796497183, y: 331.4399380733415), radius: 29.50247300985871, startAngle: Angle<Double>(radians: 1.651053627682679), sweepAngle: Angle<Double>(radians: 1.5707963267948966), startPeriod: 0.13799952430606413, endPeriod: 0.15762924146584847)), Parametric2GeometrySimplex<Vector2<Double>>.circleArc2(CircleArc2Simplex<Vector2<Double>>(center: Vector2<Double>(x: 189.35176796497183, y: 331.4399380733415), radius: 29.50247300985871, startAngle: Angle<Double>(radians: 3.221849954477576), sweepAngle: Angle<Double>(radians: 1.4613259411032358), startPeriod: 0.15762924146584847, endPeriod: 0.1758909437711008)), Parametric2GeometrySimplex<Vector2<Double>>.lineSegment2(LineSegment2Simplex<Vector2<Double>>(start: Vector2<Double>(x: 188.4900322993723, y: 301.95005293724654), end: Vector2<Double>(x: 278.7400722555543, y: 299.3128206152788), startPeriod: 0.1758909437711008, endPeriod: 0.21413539968955667)), Parametric2GeometrySimplex<Vector2<Double>>.lineSegment2(LineSegment2Simplex<Vector2<Double>>(start: Vector2<Double>(x: 278.7400722555543, y: 299.3128206152788), end: Vector2<Double>(x: 393.3521808604459, y: 205.50147608979154), startPeriod: 0.21413539968955667, endPeriod: 0.27687177875972757)), Parametric2GeometrySimplex<Vector2<Double>>.circleArc2(CircleArc2Simplex<Vector2<Double>>(center: Vector2<Double>(x: 416.52708500302657, y: 233.8149454674293), radius: 36.588642093089966, startAngle: Angle<Double>(radians: -2.256723369938214), sweepAngle: Angle<Double>(radians: 1.5089228046683982), startPeriod: 0.27687177875972757, endPeriod: 0.30025740785407506)), Parametric2GeometrySimplex<Vector2<Double>>.circleArc2(CircleArc2Simplex<Vector2<Double>>(center: Vector2<Double>(x: 416.52708500302657, y: 233.8149454674293), radius: 36.588642093089966, startAngle: Angle<Double>(radians: -0.7478005652698156), sweepAngle: Angle<Double>(radians: 1.421453534874056), startPeriod: 0.30025740785407506, endPeriod: 0.32228741829501273)), Parametric2GeometrySimplex<Vector2<Double>>.lineSegment2(LineSegment2Simplex<Vector2<Double>>(start: Vector2<Double>(x: 445.1228649901167, y: 256.640591069387), end: Vector2<Double>(x: 440.219457475034, y: 262.78353944129736), startPeriod: 0.32228741829501273, endPeriod: 0.3256167491331845)), Parametric2GeometrySimplex<Vector2<Double>>.lineSegment2(LineSegment2Simplex<Vector2<Double>>(start: Vector2<Double>(x: 440.219457475034, y: 262.78353944129736), end: Vector2<Double>(x: 906.7665394903199, y: 83.80631727936634), startPeriod: 0.3256167491331845, endPeriod: 0.537279368940178)), Parametric2GeometrySimplex<Vector2<Double>>.circleArc2(CircleArc2Simplex<Vector2<Double>>(center: Vector2<Double>(x: 918.9250986336896, y: 115.50051959924077), radius: 33.94632559108497, startAngle: Angle<Double>(radians: -1.9371035626186832), sweepAngle: Angle<Double>(radians: 1.2217500110984842), startPeriod: 0.537279368940178, endPeriod: 0.5548469055500806)), Parametric2GeometrySimplex<Vector2<Double>>.circleArc2(CircleArc2Simplex<Vector2<Double>>(center: Vector2<Double>(x: 918.9250986336896, y: 115.50051959924077), radius: 33.94632559108497, startAngle: Angle<Double>(radians: -0.7153535515201987), sweepAngle: Angle<Double>(radians: 1.5026295331423438), startPeriod: 0.5548469055500806, endPeriod: 0.5764532072442095)), Parametric2GeometrySimplex<Vector2<Double>>.lineSegment2(LineSegment2Simplex<Vector2<Double>>(start: Vector2<Double>(x: 942.8836588189453, y: 139.54922881585847), end: Vector2<Double>(x: 632.5395487055349, y: 448.7299824820002), startPeriod: 0.5764532072442095, endPeriod: 0.762011450387202)), Parametric2GeometrySimplex<Vector2<Double>>.circleArc2(CircleArc2Simplex<Vector2<Double>>(center: Vector2<Double>(x: 598.0389757446262, y: 414.0995939891976), radius: 48.88305781959036, startAngle: Angle<Double>(radians: 0.7872759816221461), sweepAngle: Angle<Double>(radians: 1.0422617292624414), startPeriod: 0.762011450387202, endPeriod: 0.7835924173205498)), Parametric2GeometrySimplex<Vector2<Double>>.circleArc2(CircleArc2Simplex<Vector2<Double>>(center: Vector2<Double>(x: 598.0389757446262, y: 414.0995939891976), radius: 48.88305781959036, startAngle: Angle<Double>(radians: 1.8295377108845876), sweepAngle: Angle<Double>(radians: 0.01125207916006099), startPeriod: 0.7835924173205498, endPeriod: 0.7838254017441998)), Parametric2GeometrySimplex<Vector2<Double>>.lineSegment2(LineSegment2Simplex<Vector2<Double>>(start: Vector2<Double>(x: 585.0006354627674, y: 461.21174767047046), end: Vector2<Double>(x: 306.7651781612669, y: 384.20950313645886), startPeriod: 0.7838254017441998, endPeriod: 0.9061105557779229)), Parametric2GeometrySimplex<Vector2<Double>>.lineSegment2(LineSegment2Simplex<Vector2<Double>>(start: Vector2<Double>(x: 306.7651781612669, y: 384.20950313645886), end: Vector2<Double>(x: 143.4573776400863, y: 534.0837556087183), startPeriod: 0.9061105557779229, endPeriod: 1.0))], [Parametric2GeometrySimplex<Vector2<Double>>.lineSegment2(LineSegment2Simplex<Vector2<Double>>(start: Vector2<Double>(x: 474.36380532422453, y: 330.79968554139725), end: Vector2<Double>(x: 585.5196060376405, y: 360.21977007895777), startPeriod: 0.0, endPeriod: 0.18662569292398146)), Parametric2GeometrySimplex<Vector2<Double>>.lineSegment2(LineSegment2Simplex<Vector2<Double>>(start: Vector2<Double>(x: 585.5196060376405, y: 360.21977007895777), end: Vector2<Double>(x: 744.2413691085113, y: 222.30952735308094), startPeriod: 0.18662569292398146, endPeriod: 0.5279019083759146)), Parametric2GeometrySimplex<Vector2<Double>>.lineSegment2(LineSegment2Simplex<Vector2<Double>>(start: Vector2<Double>(x: 744.2413691085113, y: 222.30952735308094), end: Vector2<Double>(x: 474.36380532422453, y: 330.79968554139725), startPeriod: 0.5279019083759146, endPeriod: 1.0))]]
)
}
}

#if GEOMETRIA_PERFORMANCE_TESTS

func testPerformance_overlapping_circles() {
Expand Down

0 comments on commit c990252

Please sign in to comment.