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 }