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  }