github.com/richardwilkes/toolbox@v1.121.0/xmath/geom/poly/intersection.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/geom"
    14  	"golang.org/x/exp/constraints"
    15  )
    16  
    17  type intersection[T constraints.Float] struct {
    18  	edge0 *edgeNode[T]
    19  	edge1 *edgeNode[T]
    20  	point geom.Point[T]
    21  	next  *intersection[T]
    22  }
    23  
    24  func (i *intersection[T]) process(op clipOp, pt geom.Point[T], outPoly *polygonNode[T]) *polygonNode[T] {
    25  	e0 := i.edge0
    26  	e1 := i.edge1
    27  
    28  	// Only generate output for contributing intersections
    29  	if (e0.bundleAbove[clipping] || e0.bundleAbove[subject]) && (e1.bundleAbove[clipping] || e1.bundleAbove[subject]) {
    30  		n0 := e0.outAbove
    31  		n1 := e1.outAbove
    32  		iPt := i.point
    33  		iPt.Y += pt.Y
    34  		inClip := (e0.bundleAbove[clipping] && !e0.clipSide) ||
    35  			(e1.bundleAbove[clipping] && e1.clipSide) ||
    36  			(!e0.bundleAbove[clipping] && !e1.bundleAbove[clipping] && e0.clipSide && e1.clipSide)
    37  		inSubj := (e0.bundleAbove[subject] && !e0.subjectSide) ||
    38  			(e1.bundleAbove[subject] && e1.subjectSide) ||
    39  			(!e0.bundleAbove[subject] && !e1.bundleAbove[subject] && e0.subjectSide && e1.subjectSide)
    40  
    41  		// Determine quadrant occupancies
    42  		var br, bl, tr, tl bool
    43  		e0InClip := inClip != e0.bundleAbove[clipping]
    44  		e1InClip := inClip != e1.bundleAbove[clipping]
    45  		e0InSubj := inSubj != e0.bundleAbove[subject]
    46  		e1InSubj := inSubj != e1.bundleAbove[subject]
    47  		e10InClip := e1InClip != e0.bundleAbove[clipping]
    48  		e10InSubj := e1InSubj != e0.bundleAbove[subject]
    49  		switch op {
    50  		case subtractOp, intersectOp:
    51  			tr = inClip && inSubj
    52  			tl = e1InClip && e1InSubj
    53  			br = e0InClip && e0InSubj
    54  			bl = e10InClip && e10InSubj
    55  		case xorOp:
    56  			tr = inClip != inSubj
    57  			tl = e1InClip != e1InSubj
    58  			br = e0InClip != e0InSubj
    59  			bl = e10InClip != e10InSubj
    60  		case unionOp:
    61  			tr = inClip || inSubj
    62  			tl = e1InClip || e1InSubj
    63  			br = e0InClip || e0InSubj
    64  			bl = e10InClip || e10InSubj
    65  		}
    66  		switch calcVertexType(tr, tl, br, bl) {
    67  		case externalMinimum:
    68  			outPoly = e0.addLocalMin(outPoly, iPt)
    69  			e1.outAbove = e0.outAbove
    70  		case externalRightIntermediate:
    71  			if n0 != nil {
    72  				n0.addRight(iPt)
    73  				e1.outAbove = n0
    74  				e0.outAbove = nil
    75  			}
    76  		case externalLeftIntermediate:
    77  			if n1 != nil {
    78  				n1.addLeft(iPt)
    79  				e0.outAbove = n1
    80  				e1.outAbove = nil
    81  			}
    82  		case externalMaximum:
    83  			if n0 != nil && n1 != nil {
    84  				n0.addLeft(iPt)
    85  				n0.mergeRight(n1, outPoly)
    86  				e0.outAbove = nil
    87  				e1.outAbove = nil
    88  			}
    89  		case internalMinimum:
    90  			outPoly = e0.addLocalMin(outPoly, iPt)
    91  			e1.outAbove = e0.outAbove
    92  		case internalLeftIntermediate:
    93  			if n0 != nil {
    94  				n0.addLeft(iPt)
    95  				e1.outAbove = n0
    96  				e0.outAbove = nil
    97  			}
    98  		case internalRightIntermediate:
    99  			if n1 != nil {
   100  				n1.addRight(iPt)
   101  				e0.outAbove = n1
   102  				e1.outAbove = nil
   103  			}
   104  		case internalMaximum:
   105  			if n0 != nil && n1 != nil {
   106  				n0.addRight(iPt)
   107  				n0.mergeLeft(n1, outPoly)
   108  				e0.outAbove = nil
   109  				e1.outAbove = nil
   110  			}
   111  		case internalMaximumAndMinimum:
   112  			if n0 != nil && n1 != nil {
   113  				n0.addRight(iPt)
   114  				n0.mergeLeft(n1, outPoly)
   115  				outPoly = e0.addLocalMin(outPoly, iPt)
   116  				e1.outAbove = e0.outAbove
   117  			}
   118  		case externalMaximumAndMinimum:
   119  			if n0 != nil && n1 != nil {
   120  				n0.addLeft(iPt)
   121  				n0.mergeRight(n1, outPoly)
   122  				outPoly = e0.addLocalMin(outPoly, iPt)
   123  				e1.outAbove = e0.outAbove
   124  			}
   125  		default:
   126  		}
   127  	}
   128  
   129  	// Swap bundle sides in response to edge crossing
   130  	if e0.bundleAbove[clipping] {
   131  		e1.clipSide = !e1.clipSide
   132  	}
   133  	if e1.bundleAbove[clipping] {
   134  		e0.clipSide = !e0.clipSide
   135  	}
   136  	if e0.bundleAbove[subject] {
   137  		e1.subjectSide = !e1.subjectSide
   138  	}
   139  	if e1.bundleAbove[subject] {
   140  		e0.subjectSide = !e0.subjectSide
   141  	}
   142  
   143  	return outPoly
   144  }