Skip to content

Commit d01c827

Browse files
committed
Add the poly and visibility packages back, using fixed-point math
1 parent 473e333 commit d01c827

23 files changed

+2837
-38
lines changed

fixed/fixed128/fixed128.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ func (f Int[T]) Div(value Int[T]) Int[T] {
188188

189189
// Mod returns the remainder after subtracting all full multiples of the passed-in value.
190190
func (f Int[T]) Mod(value Int[T]) Int[T] {
191-
return f.Sub(value.Mul(f.Div(value).Trunc()))
191+
return f.Sub(value.Mul(f.Div(value).Floor()))
192192
}
193193

194194
// Neg negates this value, returning a new value.
@@ -231,15 +231,15 @@ func (f Int[T]) LessThanOrEqual(n Int[T]) bool {
231231
return f.data.LessThanOrEqual(n.data)
232232
}
233233

234-
// Trunc returns a new value which has everything to the right of the decimal place truncated.
235-
func (f Int[T]) Trunc() Int[T] {
234+
// Floor returns the value rounded down to the nearest whole number.
235+
func (f Int[T]) Floor() Int[T] {
236236
m := multiplier[T]()
237237
return Int[T]{data: f.data.Div(m).Mul(m)}
238238
}
239239

240240
// Ceil returns the value rounded up to the nearest whole number.
241241
func (f Int[T]) Ceil() Int[T] {
242-
v := f.Trunc()
242+
v := f.Floor()
243243
if f.GreaterThan(Int[T]{}) && f != v {
244244
v = v.Add(Multiplier[T]())
245245
}
@@ -251,7 +251,7 @@ func (f Int[T]) Round() Int[T] {
251251
one := Multiplier[T]()
252252
half := Int[T]{data: one.data.Div(num128.IntFrom64(2))}
253253
negHalf := half.Neg()
254-
value := f.Trunc()
254+
value := f.Floor()
255255
rem := f.Sub(value)
256256
if rem.GreaterThanOrEqual(half) {
257257
value = value.Add(one)

fixed/fixed128/fixed128_test.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -138,23 +138,23 @@ func testMod[T fixed.Dx](t *testing.T) {
138138
c.Equal(fixed128.FromStringForced[T]("0.1"), fixed128.FromStringForced[T]("3.1").Mod(fixed128.FromStringForced[T]("0.2")))
139139
}
140140

141-
func TestTrunc(t *testing.T) {
142-
testTrunc[fixed.D1](t)
143-
testTrunc[fixed.D2](t)
144-
testTrunc[fixed.D3](t)
145-
testTrunc[fixed.D4](t)
146-
testTrunc[fixed.D5](t)
147-
testTrunc[fixed.D6](t)
141+
func TestFloor(t *testing.T) {
142+
testFloor[fixed.D1](t)
143+
testFloor[fixed.D2](t)
144+
testFloor[fixed.D3](t)
145+
testFloor[fixed.D4](t)
146+
testFloor[fixed.D5](t)
147+
testFloor[fixed.D6](t)
148148
}
149149

150-
func testTrunc[T fixed.Dx](t *testing.T) {
150+
func testFloor[T fixed.Dx](t *testing.T) {
151151
c := check.New(t)
152-
c.Equal(fixed128.FromInteger[T](0), fixed128.FromStringForced[T]("0.3333").Trunc())
153-
c.Equal(fixed128.FromInteger[T](2), fixed128.FromStringForced[T]("2.6789").Trunc())
154-
c.Equal(fixed128.FromInteger[T](3), fixed128.FromInteger[T](3).Trunc())
155-
c.Equal(fixed128.FromInteger[T](0), fixed128.FromStringForced[T]("-0.3333").Trunc())
156-
c.Equal(fixed128.FromInteger[T](-2), fixed128.FromStringForced[T]("-2.6789").Trunc())
157-
c.Equal(fixed128.FromInteger[T](-3), fixed128.FromInteger[T](-3).Trunc())
152+
c.Equal(fixed128.FromInteger[T](0), fixed128.FromStringForced[T]("0.3333").Floor())
153+
c.Equal(fixed128.FromInteger[T](2), fixed128.FromStringForced[T]("2.6789").Floor())
154+
c.Equal(fixed128.FromInteger[T](3), fixed128.FromInteger[T](3).Floor())
155+
c.Equal(fixed128.FromInteger[T](0), fixed128.FromStringForced[T]("-0.3333").Floor())
156+
c.Equal(fixed128.FromInteger[T](-2), fixed128.FromStringForced[T]("-2.6789").Floor())
157+
c.Equal(fixed128.FromInteger[T](-3), fixed128.FromInteger[T](-3).Floor())
158158
}
159159

160160
func TestCeil(t *testing.T) {

fixed/fixed64/int.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ func (f Int[T]) Div(value Int[T]) Int[T] {
185185

186186
// Mod returns the remainder after subtracting all full multiples of the passed-in value.
187187
func (f Int[T]) Mod(value Int[T]) Int[T] {
188-
return f - (value.Mul(f.Div(value).Trunc()))
188+
return f - (value.Mul(f.Div(value).Floor()))
189189
}
190190

191191
// Abs returns the absolute value of this value.
@@ -196,15 +196,15 @@ func (f Int[T]) Abs() Int[T] {
196196
return f
197197
}
198198

199-
// Trunc returns a new value which has everything to the right of the decimal place truncated.
200-
func (f Int[T]) Trunc() Int[T] {
199+
// Floor returns the value rounded down to the nearest whole number.
200+
func (f Int[T]) Floor() Int[T] {
201201
mult := Int[T](Multiplier[T]())
202202
return f / mult * mult
203203
}
204204

205205
// Ceil returns the value rounded up to the nearest whole number.
206206
func (f Int[T]) Ceil() Int[T] {
207-
v := f.Trunc()
207+
v := f.Floor()
208208
if f > 0 && f != v {
209209
v += Int[T](Multiplier[T]())
210210
}
@@ -214,7 +214,7 @@ func (f Int[T]) Ceil() Int[T] {
214214
// Round returns the nearest integer, rounding half away from zero.
215215
func (f Int[T]) Round() Int[T] {
216216
one := Int[T](Multiplier[T]())
217-
value := f.Trunc()
217+
value := f.Floor()
218218
rem := f - value
219219
if rem >= one/2 {
220220
value += one

fixed/fixed64/int_test.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -141,23 +141,23 @@ func testMod[T fixed.Dx](t *testing.T) {
141141
c.Equal(fixed64.FromStringForced[T]("0.1"), fixed64.FromStringForced[T]("3.1").Mod(fixed64.FromStringForced[T]("0.2")))
142142
}
143143

144-
func TestTrunc(t *testing.T) {
145-
testTrunc[fixed.D1](t)
146-
testTrunc[fixed.D2](t)
147-
testTrunc[fixed.D3](t)
148-
testTrunc[fixed.D4](t)
149-
testTrunc[fixed.D5](t)
150-
testTrunc[fixed.D6](t)
144+
func TestFloor(t *testing.T) {
145+
testFloor[fixed.D1](t)
146+
testFloor[fixed.D2](t)
147+
testFloor[fixed.D3](t)
148+
testFloor[fixed.D4](t)
149+
testFloor[fixed.D5](t)
150+
testFloor[fixed.D6](t)
151151
}
152152

153-
func testTrunc[T fixed.Dx](t *testing.T) {
153+
func testFloor[T fixed.Dx](t *testing.T) {
154154
c := check.New(t)
155-
c.Equal(fixed64.FromInteger[T](0), fixed64.FromStringForced[T]("0.3333").Trunc())
156-
c.Equal(fixed64.FromInteger[T](2), fixed64.FromStringForced[T]("2.6789").Trunc())
157-
c.Equal(fixed64.FromInteger[T](3), fixed64.FromInteger[T](3).Trunc())
158-
c.Equal(fixed64.FromInteger[T](0), fixed64.FromStringForced[T]("-0.3333").Trunc())
159-
c.Equal(fixed64.FromInteger[T](-2), fixed64.FromStringForced[T]("-2.6789").Trunc())
160-
c.Equal(fixed64.FromInteger[T](-3), fixed64.FromInteger[T](-3).Trunc())
155+
c.Equal(fixed64.FromInteger[T](0), fixed64.FromStringForced[T]("0.3333").Floor())
156+
c.Equal(fixed64.FromInteger[T](2), fixed64.FromStringForced[T]("2.6789").Floor())
157+
c.Equal(fixed64.FromInteger[T](3), fixed64.FromInteger[T](3).Floor())
158+
c.Equal(fixed64.FromInteger[T](0), fixed64.FromStringForced[T]("-0.3333").Floor())
159+
c.Equal(fixed64.FromInteger[T](-2), fixed64.FromStringForced[T]("-2.6789").Floor())
160+
c.Equal(fixed64.FromInteger[T](-3), fixed64.FromInteger[T](-3).Floor())
161161
}
162162

163163
func TestCeil(t *testing.T) {

geom/poly/contour.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright (c) 2016-2025 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/v2/geom"
16+
)
17+
18+
// Contour is a sequence of vertices connected by line segments, forming a closed shape.
19+
type Contour []Point
20+
21+
// Points converts this Contour into a slice of geom.Point.
22+
func (c Contour) Points() []geom.Point {
23+
points := make([]geom.Point, len(c))
24+
for i, p := range c {
25+
points[i] = p.Point()
26+
}
27+
return points
28+
}
29+
30+
// Clone returns a copy of this contour.
31+
func (c Contour) Clone() Contour {
32+
if len(c) == 0 {
33+
return nil
34+
}
35+
clone := make(Contour, len(c))
36+
copy(clone, c)
37+
return clone
38+
}
39+
40+
// Bounds returns the bounding rectangle of the contour.
41+
func (c Contour) Bounds() Rect {
42+
if len(c) == 0 {
43+
return Rect{}
44+
}
45+
minX := c[0].X
46+
minY := c[0].Y
47+
maxX := minX
48+
maxY := minY
49+
for _, p := range c[1:] {
50+
if p.X > maxX {
51+
maxX = p.X
52+
}
53+
if p.X < minX {
54+
minX = p.X
55+
}
56+
if p.Y > maxY {
57+
maxY = p.Y
58+
}
59+
if p.Y < minY {
60+
minY = p.Y
61+
}
62+
}
63+
return NewRect(minX, minY, maxX-minX, maxY-minY)
64+
}
65+
66+
// Contains returns true if the point is contained by the contour.
67+
func (c Contour) Contains(pt Point) bool {
68+
if len(c) < 3 {
69+
return false // A contour needs at least 3 points to contain anything
70+
}
71+
var count int
72+
for i := range c {
73+
cur := c[i]
74+
bottom := cur
75+
next := c[(i+1)%len(c)]
76+
top := next
77+
if bottom.Y > top.Y {
78+
bottom, top = top, bottom
79+
}
80+
if pt.Y >= bottom.Y &&
81+
pt.Y < top.Y &&
82+
pt.X < max(cur.X, next.X) &&
83+
next.Y != cur.Y &&
84+
(cur.X == next.X ||
85+
pt.X <= (pt.Y-cur.Y).Mul(next.X-cur.X).Div(next.Y-cur.Y)+cur.X) {
86+
count++
87+
}
88+
}
89+
return count%2 == 1
90+
}
91+
92+
func (c Contour) String() string {
93+
var buffer strings.Builder
94+
buffer.WriteByte('{')
95+
for j, pt := range c {
96+
if j != 0 {
97+
buffer.WriteByte(',')
98+
}
99+
buffer.WriteByte('{')
100+
buffer.WriteString(pt.String())
101+
buffer.WriteByte('}')
102+
}
103+
buffer.WriteByte('}')
104+
return buffer.String()
105+
}

0 commit comments

Comments
 (0)