github.com/richardwilkes/toolbox@v1.121.0/xmath/geom/poly/polygon_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/geom"
    14  	"golang.org/x/exp/constraints"
    15  )
    16  
    17  type vertexNode[T constraints.Float] struct {
    18  	pt   geom.Point[T]
    19  	next *vertexNode[T]
    20  }
    21  
    22  type polygonNode[T constraints.Float] struct {
    23  	left   *vertexNode[T]
    24  	right  *vertexNode[T]
    25  	next   *polygonNode[T]
    26  	proxy  *polygonNode[T]
    27  	active bool
    28  }
    29  
    30  func (p *polygonNode[T]) addLeft(pt geom.Point[T]) {
    31  	p.proxy.left = &vertexNode[T]{
    32  		pt:   pt,
    33  		next: p.proxy.left,
    34  	}
    35  }
    36  
    37  func (p *polygonNode[T]) addRight(pt geom.Point[T]) {
    38  	v := &vertexNode[T]{pt: pt}
    39  	if p.proxy.right != nil {
    40  		p.proxy.right.next = v
    41  	}
    42  	p.proxy.right = v
    43  }
    44  
    45  func (p *polygonNode[T]) mergeLeft(other, list *polygonNode[T]) {
    46  	if p.proxy != other.proxy {
    47  		p.proxy.right.next = other.proxy.left
    48  		other.proxy.left = p.proxy.left
    49  		for target := p.proxy; list != nil; list = list.next {
    50  			if list.proxy == target {
    51  				list.active = false
    52  				list.proxy = other.proxy
    53  			}
    54  		}
    55  	}
    56  }
    57  
    58  func (p *polygonNode[T]) mergeRight(other, list *polygonNode[T]) {
    59  	if p.proxy != other.proxy {
    60  		other.proxy.right.next = p.proxy.left
    61  		other.proxy.right = p.proxy.right
    62  		for target := p.proxy; list != nil; list = list.next {
    63  			if list.proxy == target {
    64  				list.active = false
    65  				list.proxy = other.proxy
    66  			}
    67  		}
    68  	}
    69  }
    70  
    71  func (p *polygonNode[T]) generate() Polygon[T] {
    72  	contourCount := 0
    73  	ptCounts := make([]int, 0, 32)
    74  
    75  	// Count the points of each contour and disable any that don't have enough points.
    76  	for poly := p; poly != nil; poly = poly.next {
    77  		if poly.active {
    78  			var prev *vertexNode[T]
    79  			ptCount := 0
    80  			for v := poly.proxy.left; v != nil; v = v.next {
    81  				if prev == nil || prev.pt != v.pt {
    82  					ptCount++
    83  				}
    84  				prev = v
    85  			}
    86  			if ptCount > 2 {
    87  				ptCounts = append(ptCounts, ptCount)
    88  				contourCount++
    89  			} else {
    90  				poly.active = false
    91  			}
    92  		}
    93  	}
    94  	if contourCount == 0 {
    95  		return Polygon[T]{}
    96  	}
    97  
    98  	// Create the polygon
    99  	result := make([]Contour[T], contourCount)
   100  	ci := 0
   101  	for poly := p; poly != nil; poly = poly.next {
   102  		if !poly.active {
   103  			continue
   104  		}
   105  		var prev *vertexNode[T]
   106  		result[ci] = make([]geom.Point[T], ptCounts[ci])
   107  		v := len(result[ci]) - 1
   108  		for vtx := poly.proxy.left; vtx != nil; vtx = vtx.next {
   109  			if prev == nil || prev.pt != vtx.pt {
   110  				result[ci][v] = vtx.pt
   111  				v--
   112  			}
   113  			prev = vtx
   114  		}
   115  		ci++
   116  	}
   117  	return result
   118  }