github.com/richardwilkes/toolbox@v1.121.0/xmath/geom/poly/edge_node.go (about) 1 // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved. 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, version 2.0. If a copy of the MPL was not distributed with 5 // this file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 // 7 // This Source Code Form is "Incompatible With Secondary Licenses", as 8 // defined by the Mozilla Public License, version 2.0. 9 10 package poly 11 12 import ( 13 "github.com/richardwilkes/toolbox/xmath" 14 "github.com/richardwilkes/toolbox/xmath/geom" 15 "golang.org/x/exp/constraints" 16 ) 17 18 const epsilon = 0.00001 // 2.220446e-16 19 20 type horizontalEdgeStates int 21 22 const ( 23 noHorizontalEdge horizontalEdgeStates = iota 24 bottomHorizontalEdge 25 topHorizontalEdge 26 ) 27 28 var nextHorizontalEdgeStates = [3][6]horizontalEdgeStates{ 29 {bottomHorizontalEdge, topHorizontalEdge, topHorizontalEdge, bottomHorizontalEdge, noHorizontalEdge, noHorizontalEdge}, 30 {noHorizontalEdge, noHorizontalEdge, noHorizontalEdge, noHorizontalEdge, topHorizontalEdge, topHorizontalEdge}, 31 {noHorizontalEdge, noHorizontalEdge, noHorizontalEdge, noHorizontalEdge, bottomHorizontalEdge, bottomHorizontalEdge}, 32 } 33 34 type bundleState int 35 36 const ( 37 unbundled bundleState = iota 38 bundleHead 39 bundleTail 40 ) 41 42 type edgeNode[T constraints.Float] struct { 43 vertex geom.Point[T] 44 bot geom.Point[T] 45 top geom.Point[T] 46 xb T 47 xt T 48 dx T 49 outAbove *polygonNode[T] 50 outBelow *polygonNode[T] 51 prev *edgeNode[T] 52 next *edgeNode[T] 53 pred *edgeNode[T] 54 successor *edgeNode[T] 55 nextBound *edgeNode[T] 56 aboveState bundleState 57 belowState bundleState 58 which int 59 bundleAbove [2]bool 60 bundleBelow [2]bool 61 subjectSide bool 62 clipSide bool 63 } 64 65 type sortedEdge[T constraints.Float] struct { 66 edge *edgeNode[T] 67 xb T 68 xt T 69 dx T 70 prev *sortedEdge[T] 71 } 72 73 func (e *edgeNode[T]) insertInto(b **edgeNode[T]) { 74 switch { 75 case *b == nil: 76 *b = e 77 case e.bot.X < (*b).bot.X || (e.bot.X == (*b).bot.X && e.dx < (*b).dx): 78 e.nextBound = *b 79 *b = e 80 default: 81 e.insertInto(&(*b).nextBound) 82 } 83 } 84 85 func (e *edgeNode[T]) addEdgeToActiveEdgeTable(aet, prev *edgeNode[T]) *edgeNode[T] { 86 switch { 87 case aet == nil: 88 aet = e 89 e.prev = prev 90 e.next = nil 91 case e.xb < aet.xb || (e.xb == aet.xb && e.dx < aet.dx): 92 e.prev = prev 93 e.next = aet 94 aet.prev = e 95 aet = e 96 default: 97 aet.next = e.addEdgeToActiveEdgeTable(aet.next, aet) 98 } 99 return aet 100 } 101 102 func (e *edgeNode[T]) addLocalMin(p *polygonNode[T], pt geom.Point[T]) *polygonNode[T] { 103 v := &vertexNode[T]{pt: pt} 104 result := &polygonNode[T]{ 105 left: v, 106 right: v, 107 next: p, 108 active: true, 109 } 110 result.proxy = result 111 e.outAbove = result 112 return result 113 } 114 115 func (e *edgeNode[T]) buildIntersections(dy T) *intersection[T] { 116 var se *sortedEdge[T] 117 var it *intersection[T] 118 for edge := e; edge != nil; edge = edge.next { 119 if edge.aboveState == bundleHead || edge.bundleAbove[clipping] || edge.bundleAbove[subject] { 120 edge.addToSortedEdgeTable(&se, &it, dy) 121 } 122 } 123 return it 124 } 125 126 func (e *edgeNode[T]) addToSortedEdgeTable(se **sortedEdge[T], it **intersection[T], dy T) { 127 if *se == nil { 128 *se = &sortedEdge[T]{ 129 edge: e, 130 xb: e.xb, 131 xt: e.xt, 132 dx: e.dx, 133 } 134 } else { 135 den := ((*se).xt - (*se).xb) - (e.xt - e.xb) 136 if e.xt >= (*se).xt || e.dx == (*se).dx || xmath.Abs(den) <= epsilon { 137 *se = &sortedEdge[T]{ 138 edge: e, 139 xb: e.xb, 140 xt: e.xt, 141 dx: e.dx, 142 prev: *se, 143 } 144 } else { 145 r := (e.xb - (*se).xb) / den 146 addIntersection(it, (*se).edge, e, geom.Point[T]{ 147 X: (*se).xb + r*((*se).xt-(*se).xb), 148 Y: r * dy, 149 }) 150 e.addToSortedEdgeTable(&(*se).prev, it, dy) 151 } 152 } 153 } 154 155 func addIntersection[T constraints.Float](it **intersection[T], edge0, edge1 *edgeNode[T], pt geom.Point[T]) { 156 switch { 157 case *it == nil: 158 *it = &intersection[T]{ 159 edge0: edge0, 160 edge1: edge1, 161 point: pt, 162 } 163 case (*it).point.Y > pt.Y: 164 *it = &intersection[T]{ 165 edge0: edge0, 166 edge1: edge1, 167 point: pt, 168 next: *it, 169 } 170 default: 171 addIntersection(&(*it).next, edge0, edge1, pt) 172 } 173 } 174 175 func (e *edgeNode[T]) bundleFields(pt geom.Point[T]) { 176 updated := e 177 e.bundleAbove[e.which] = e.top.Y != pt.Y 178 e.bundleAbove[1-e.which] = false 179 e.aboveState = unbundled 180 for nextEdge := e.next; nextEdge != nil; nextEdge = nextEdge.next { 181 nextEdge.bundleAbove[nextEdge.which] = nextEdge.top.Y != pt.Y 182 nextEdge.bundleAbove[1-nextEdge.which] = false 183 nextEdge.aboveState = unbundled 184 if nextEdge.bundleAbove[nextEdge.which] { 185 if mostlyEqual(updated.xb, nextEdge.xb) && mostlyEqual(updated.dx, nextEdge.dx) && updated.top.Y != pt.Y { 186 nextEdge.bundleAbove[nextEdge.which] = nextEdge.bundleAbove[nextEdge.which] != updated.bundleAbove[nextEdge.which] 187 nextEdge.bundleAbove[1-nextEdge.which] = updated.bundleAbove[1-nextEdge.which] 188 nextEdge.aboveState = bundleHead 189 updated.bundleAbove[clipping] = false 190 updated.bundleAbove[subject] = false 191 updated.aboveState = bundleTail 192 } 193 updated = nextEdge 194 } 195 } 196 } 197 198 func (e *edgeNode[T]) process(op clipOp, pt geom.Point[T], inPoly *polygonNode[T]) (bPt geom.Point[T], outPoly *polygonNode[T]) { 199 bPt = pt 200 outPoly = inPoly 201 var parityClipRight, paritySubjRight bool 202 if op == subtractOp { 203 parityClipRight = true 204 } 205 var horiz [2]horizontalEdgeStates 206 var cf *polygonNode[T] 207 px := xmath.MinValue[T]() 208 for edge := e; edge != nil; edge = edge.next { 209 clipExistsState, clipExists := edge.existsState(clipping) 210 subjExistsState, subjExists := edge.existsState(subject) 211 if clipExists || subjExists { 212 // Set bundle side 213 edge.clipSide = parityClipRight 214 edge.subjectSide = paritySubjRight 215 216 // Determine contributing status and quadrant occupancies 217 var br, bl, tr, tl, contributing bool 218 pcb := parityClipRight != edge.bundleAbove[clipping] 219 psb := paritySubjRight != edge.bundleAbove[subject] 220 hc := horiz[clipping] != noHorizontalEdge 221 hs := horiz[subject] != noHorizontalEdge 222 phc := parityClipRight != hc 223 phs := paritySubjRight != hs 224 phcb := phc != edge.bundleBelow[clipping] 225 phsb := phs != edge.bundleBelow[subject] 226 switch op { 227 case subtractOp, intersectOp: 228 if contributing = (clipExists && (paritySubjRight || hs)) || (subjExists && (parityClipRight || hc)) || (clipExists && subjExists && parityClipRight == paritySubjRight); contributing { 229 br = parityClipRight && paritySubjRight 230 bl = pcb && psb 231 tr = phc && phs 232 tl = phcb && phsb 233 } 234 case xorOp: 235 if contributing = clipExists || subjExists; contributing { 236 br = parityClipRight != paritySubjRight 237 bl = pcb != psb 238 tr = phc != phs 239 tl = phcb != phsb 240 } 241 case unionOp: 242 if contributing = (clipExists && (!paritySubjRight || hs)) || (subjExists && (!parityClipRight || hc)) || (clipExists && subjExists && parityClipRight == paritySubjRight); contributing { 243 br = parityClipRight || paritySubjRight 244 bl = pcb || psb 245 tr = phc || phs 246 tl = phcb || phsb 247 } 248 default: 249 } 250 251 // Update parity 252 parityClipRight = pcb 253 paritySubjRight = psb 254 255 // Update horizontal state 256 if clipExists { 257 horiz[clipping] = calcNextHState(clipExistsState, horiz[clipping], parityClipRight) 258 } 259 if subjExists { 260 horiz[subject] = calcNextHState(subjExistsState, horiz[subject], paritySubjRight) 261 } 262 263 if contributing { 264 bPt.X = edge.xb 265 switch calcVertexType(tr, tl, br, bl) { 266 case externalMinimum, internalMinimum: 267 outPoly = edge.addLocalMin(outPoly, bPt) 268 px = bPt.X 269 cf = edge.outAbove 270 case externalRightIntermediate: 271 if cf != nil { 272 if bPt.X != px { 273 cf.addRight(bPt) 274 px = bPt.X 275 } 276 edge.outAbove = cf 277 cf = nil 278 } 279 case externalLeftIntermediate: 280 edge.outBelow.addLeft(bPt) 281 px = bPt.X 282 cf = edge.outBelow 283 case externalMaximum: 284 if cf != nil { 285 if bPt.X != px { 286 cf.addLeft(bPt) 287 px = bPt.X 288 } 289 cf.mergeRight(edge.outBelow, outPoly) 290 cf = nil 291 } 292 case internalLeftIntermediate: 293 if cf != nil { 294 if bPt.X != px { 295 cf.addLeft(bPt) 296 px = bPt.X 297 } 298 edge.outAbove = cf 299 cf = nil 300 } 301 case internalRightIntermediate: 302 edge.outBelow.addRight(bPt) 303 px = bPt.X 304 cf = edge.outBelow 305 edge.outBelow = nil 306 case internalMaximum: 307 if cf != nil { 308 if bPt.X != px { 309 cf.addRight(bPt) 310 px = bPt.X 311 } 312 cf.mergeLeft(edge.outBelow, outPoly) 313 cf = nil 314 edge.outBelow = nil 315 } 316 case internalMaximumAndMinimum: 317 if cf != nil { 318 if bPt.X != px { 319 cf.addRight(bPt) 320 px = bPt.X 321 } 322 cf.mergeLeft(edge.outBelow, outPoly) 323 edge.outBelow = nil 324 outPoly = edge.addLocalMin(outPoly, bPt) 325 cf = edge.outAbove 326 } 327 case externalMaximumAndMinimum: 328 if cf != nil { 329 if bPt.X != px { 330 cf.addLeft(bPt) 331 px = bPt.X 332 } 333 cf.mergeRight(edge.outBelow, outPoly) 334 edge.outBelow = nil 335 outPoly = edge.addLocalMin(outPoly, bPt) 336 cf = edge.outAbove 337 } 338 case leftEdge: 339 if edge.bot.Y == bPt.Y { 340 if edge.outBelow == nil { 341 edge.outBelow = &polygonNode[T]{ 342 left: &vertexNode[T]{ 343 pt: pt, 344 }, 345 } 346 edge.outBelow.proxy = edge.outBelow 347 } else { 348 edge.outBelow.addLeft(bPt) 349 } 350 } 351 edge.outAbove = edge.outBelow 352 px = bPt.X 353 case rightEdge: 354 if edge.bot.Y == bPt.Y { 355 if edge.outBelow == nil { 356 edge.outBelow = &polygonNode[T]{ 357 right: &vertexNode[T]{ 358 pt: pt, 359 }, 360 } 361 edge.outBelow.proxy = edge.outBelow 362 } else { 363 edge.outBelow.addRight(bPt) 364 } 365 } 366 edge.outAbove = edge.outBelow 367 px = bPt.X 368 default: 369 } 370 } 371 } 372 } 373 return 374 } 375 376 func (e *edgeNode[T]) deleteTerminatingEdges(pt geom.Point[T], yt T) *edgeNode[T] { 377 updated := e 378 for edge := e; edge != nil; edge = edge.next { 379 switch { 380 case edge.top.Y == pt.Y: 381 prevEdge := edge.prev 382 nextEdge := edge.next 383 if prevEdge != nil { 384 prevEdge.next = nextEdge 385 } else { 386 updated = nextEdge 387 } 388 if nextEdge != nil { 389 nextEdge.prev = prevEdge 390 } 391 if edge.belowState == bundleHead && prevEdge != nil && prevEdge.belowState == bundleTail { 392 prevEdge.outBelow = edge.outBelow 393 prevEdge.belowState = unbundled 394 if prevEdge.prev != nil && prevEdge.prev.belowState == bundleTail { 395 prevEdge.belowState = bundleHead 396 } 397 } 398 case edge.top.Y == yt: 399 edge.xt = edge.top.X 400 default: 401 edge.xt = edge.bot.X + edge.dx*(yt-edge.bot.Y) 402 } 403 } 404 return updated 405 } 406 407 func (e *edgeNode[T]) prepareForNextScanBeam(yt T) *edgeNode[T] { 408 updated := e 409 for edge := e; edge != nil; edge = edge.next { 410 successorEdge := edge.successor 411 if edge.top.Y == yt && successorEdge != nil { 412 successorEdge.outBelow = edge.outAbove 413 successorEdge.belowState = edge.aboveState 414 successorEdge.bundleBelow[clipping] = edge.bundleAbove[clipping] 415 successorEdge.bundleBelow[subject] = edge.bundleAbove[subject] 416 prevEdge := edge.prev 417 if prevEdge != nil { 418 prevEdge.next = successorEdge 419 } else { 420 updated = successorEdge 421 } 422 if edge.next != nil { 423 edge.next.prev = successorEdge 424 } 425 successorEdge.prev = prevEdge 426 successorEdge.next = edge.next 427 } else { 428 edge.outBelow = edge.outAbove 429 edge.belowState = edge.aboveState 430 edge.bundleBelow[clipping] = edge.bundleAbove[clipping] 431 edge.bundleBelow[subject] = edge.bundleAbove[subject] 432 edge.xb = edge.xt 433 } 434 edge.outAbove = nil 435 } 436 return updated 437 } 438 439 func (e *edgeNode[T]) swapIntersectingEdgeBundles(inter *intersection[T]) *edgeNode[T] { 440 result := e 441 e0 := inter.edge0 442 e1 := inter.edge1 443 e0t := e0 444 e1t := e1 445 e0n := e0.next 446 e1n := e1.next 447 448 e0p := e0.prev 449 if e0.aboveState == bundleHead { 450 for { 451 e0t = e0p 452 e0p = e0p.prev 453 if e0p == nil || e0p.aboveState != bundleTail { 454 break 455 } 456 } 457 } 458 459 e1p := e1.prev 460 if e1.aboveState == bundleHead { 461 for { 462 e1t = e1p 463 e1p = e1p.prev 464 if e1p == nil || e1p.aboveState != bundleTail { 465 break 466 } 467 } 468 } 469 470 if e0p != nil { 471 if e1p != nil { 472 if e0p != e1 { 473 e0p.next = e1t 474 e1t.prev = e0p 475 } 476 if e1p != e0 { 477 e1p.next = e0t 478 e0t.prev = e1p 479 } 480 } else { 481 if e0p != e1 { 482 e0p.next = e1t 483 e1t.prev = e0p 484 } 485 result = e0t 486 e0t.prev = nil 487 } 488 } else { 489 if e1p != e0 { 490 if e1p != nil { 491 e1p.next = e0t 492 } 493 e0t.prev = e1p 494 } 495 result = e1t 496 e1t.prev = nil 497 } 498 499 if e0p != e1 { 500 e0.next = e1n 501 if e1n != nil { 502 e1n.prev = e0 503 } 504 } else { 505 e0.next = e1t 506 e1t.prev = e0 507 } 508 509 if e1p != e0 { 510 e1.next = e0n 511 if e0n != nil { 512 e0n.prev = e1 513 } 514 } else { 515 e1.next = e0t 516 e0t.prev = e1 517 } 518 519 return result 520 } 521 522 func (e *edgeNode[T]) existsState(which int) (int, bool) { 523 state := 0 524 if e.bundleAbove[which] { 525 state = 1 526 } 527 if e.bundleBelow[which] { 528 state |= 2 529 } 530 return state, e.bundleAbove[which] || e.bundleBelow[which] 531 } 532 533 func mostlyEqual[T constraints.Float](a, b T) bool { 534 return xmath.Abs(a-b) <= epsilon 535 } 536 537 func calcNextHState(existsState int, current horizontalEdgeStates, parityRight bool) horizontalEdgeStates { 538 i := (existsState - 1) << 1 539 if parityRight { 540 i++ 541 } 542 return nextHorizontalEdgeStates[current][i] 543 }