github.com/richardwilkes/toolbox@v1.121.0/xmath/geom/poly/contour.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  	"strings"
    14  
    15  	"github.com/richardwilkes/toolbox/xmath"
    16  	"github.com/richardwilkes/toolbox/xmath/geom"
    17  	"golang.org/x/exp/constraints"
    18  )
    19  
    20  // Contour is a sequence of vertices connected by line segments, forming a closed shape.
    21  type Contour[T constraints.Float] []geom.Point[T]
    22  
    23  // Clone returns a copy of this contour.
    24  func (c Contour[T]) Clone() Contour[T] {
    25  	if len(c) == 0 {
    26  		return nil
    27  	}
    28  	clone := make(Contour[T], len(c))
    29  	copy(clone, c)
    30  	return clone
    31  }
    32  
    33  // Bounds returns the bounding rectangle of the contour.
    34  func (c Contour[T]) Bounds() geom.Rect[T] {
    35  	if len(c) == 0 {
    36  		return geom.Rect[T]{}
    37  	}
    38  	minX := xmath.MaxValue[T]()
    39  	minY := minX
    40  	maxX := xmath.MinValue[T]()
    41  	maxY := maxX
    42  	for _, p := range c {
    43  		if p.X > maxX {
    44  			maxX = p.X
    45  		}
    46  		if p.X < minX {
    47  			minX = p.X
    48  		}
    49  		if p.Y > maxY {
    50  			maxY = p.Y
    51  		}
    52  		if p.Y < minY {
    53  			minY = p.Y
    54  		}
    55  	}
    56  	return geom.NewRect(minX, minY, 1+maxX-minX, 1+maxY-minY)
    57  }
    58  
    59  // Contains returns true if the point is contained by the contour.
    60  func (c Contour[T]) Contains(pt geom.Point[T]) bool {
    61  	var count int
    62  	for i := range c {
    63  		cur := c[i]
    64  		bottom := cur
    65  		next := c[(i+1)%len(c)]
    66  		top := next
    67  		if bottom.Y > top.Y {
    68  			bottom, top = top, bottom
    69  		}
    70  		if pt.Y >= bottom.Y && pt.Y < top.Y && pt.X < max(cur.X, next.X) && next.Y != cur.Y &&
    71  			(cur.X == next.X || pt.X <= (pt.Y-cur.Y)*(next.X-cur.X)/(next.Y-cur.Y)+cur.X) {
    72  			count++
    73  		}
    74  	}
    75  	return count%2 == 1
    76  }
    77  
    78  func (c Contour[T]) String() string {
    79  	var buffer strings.Builder
    80  	buffer.WriteByte('{')
    81  	for j, pt := range c {
    82  		if j != 0 {
    83  			buffer.WriteByte(',')
    84  		}
    85  		buffer.WriteByte('{')
    86  		buffer.WriteString(pt.String())
    87  		buffer.WriteByte('}')
    88  	}
    89  	buffer.WriteByte('}')
    90  	return buffer.String()
    91  }