@@ -2,6 +2,7 @@ package ms2
22
33import (
44 math "github.com/chewxy/math32"
5+ "github.com/soypat/geometry/internal"
56)
67
78// Triangle represents a triangle in 2D space and
@@ -17,6 +18,17 @@ func (t Triangle) Centroid() Vec {
1718 return Scale (1.0 / 3.0 , Add (Add (t [0 ], t [1 ]), t [2 ]))
1819}
1920
21+ // Sides returns the triangle's sides as lines:
22+ //
23+ // [(t[0],t[1]), (t[1],t[2]), (t[2],t[0])]
24+ func (t Triangle ) Sides () [3 ]Line {
25+ return [3 ]Line {
26+ {t [0 ], t [1 ]},
27+ {t [1 ], t [2 ]},
28+ {t [2 ], t [0 ]},
29+ }
30+ }
31+
2032// Area returns the surface area of the triangle.
2133func (t Triangle ) Area () float32 {
2234 // Heron's Formula, see https://en.wikipedia.org/wiki/Heron%27s_formula.
@@ -64,33 +76,10 @@ func (t Triangle) IsDegenerate(tol float32) bool {
6476 }
6577 // calculate vertex distance from longest side
6678 ln := Line {t [longIdx ], t [(longIdx + 1 )% 3 ]}
67- dist := ln .Distance (t [(longIdx + 2 )% 3 ])
79+ dist := ln .DistanceInfinite (t [(longIdx + 2 )% 3 ])
6880 return dist <= tol
6981}
7082
71- // Line is an infinite 3D Line
72- // defined by two points on the Line.
73- type Line [2 ]Vec
74-
75- // Interpolate takes a value between 0 and 1 to linearly
76- // interpolate a point on the line.
77- //
78- // Interpolate(0) returns l[0]
79- // Interpolate(1) returns l[1]
80- func (l Line ) Interpolate (t float32 ) Vec {
81- lineDir := Sub (l [1 ], l [0 ])
82- return Add (l [0 ], Scale (t , lineDir ))
83- }
84-
85- // Distance returns the minimum euclidean Distance of point p to the line.
86- func (l Line ) Distance (p Vec ) float32 {
87- // https://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
88- p1 := l [0 ]
89- p2 := l [1 ]
90- num := math .Abs ((p2 .X - p1 .X )* (p1 .Y - p .Y ) - (p1 .X - p .X )* (p2 .Y - p1 .Y ))
91- return num / math .Hypot (p2 .X - p1 .X , p2 .Y - p1 .Y )
92- }
93-
9483// sort performs the sort-3 algorithm and returns
9584// l1, l2, l3 such that l1 ≤ l2 ≤ l3.
9685func sort (a , b , c float32 ) (l1 , l2 , l3 float32 ) {
@@ -110,14 +99,57 @@ func sort(a, b, c float32) (l1, l2, l3 float32) {
11099// orderedLengths returns the lengths of the sides of the triangle such that
111100// a ≤ b ≤ c.
112101func (t Triangle ) orderedLengths () (a , b , c float32 ) {
113- s1 , s2 , s3 := t .sides ()
102+ s1 , s2 , s3 := t .edges ()
114103 l1 := Norm (s1 )
115104 l2 := Norm (s2 )
116105 l3 := Norm (s3 )
117106 return sort (l1 , l2 , l3 )
118107}
119108
120- // sides returns vectors for each of the sides of t.
121- func (t Triangle ) sides () (Vec , Vec , Vec ) {
109+ // edges returns vectors for each of the edges of t.
110+ func (t Triangle ) edges () (Vec , Vec , Vec ) {
122111 return Sub (t [1 ], t [0 ]), Sub (t [2 ], t [1 ]), Sub (t [0 ], t [2 ])
123112}
113+
114+ // Contains returns true if point is contained within the triangle's surface.
115+ func (t Triangle ) Contains (point Vec ) bool {
116+ d1 := d2Sign (point , t [0 ], t [1 ])
117+ d2 := d2Sign (point , t [1 ], t [2 ])
118+ d3 := d2Sign (point , t [2 ], t [0 ])
119+ has_neg := (d1 < 0 ) || (d2 < 0 ) || (d3 < 0 )
120+ has_pos := (d1 > 0 ) || (d2 > 0 ) || (d3 > 0 )
121+ return ! (has_neg && has_pos )
122+ }
123+
124+ func d2Sign (p1 , p2 , p3 Vec ) float32 {
125+ // TODO: replace with CopyOrientation?
126+ return (p1 .X - p3 .X )* (p2 .Y - p3 .Y ) - (p2 .X - p3 .X )* (p1 .Y - p3 .Y )
127+ }
128+
129+ // Closest returns the point on the triangle closest to the argument point p.
130+ // side and vertex are non negative to flag the point is closest the non-negative side or vertex index.
131+ // If the point lies on the triangle it returns the same point and side=vertex=-1. side and vertex cannot be both non-negative.
132+ func (t Triangle ) Closest (p Vec ) (closest Vec , side int8 , vertex int8 ) {
133+ if t .Contains (p ) {
134+ return p , - 1 , - 1
135+ }
136+ minDist := internal .Largefloat32
137+ for j := range t {
138+ nxt := (j + 1 ) % 3
139+ edge := Line {{X : t [j ].X , Y : t [j ].Y }, {X : t [nxt ].X , Y : t [nxt ].Y }}
140+ pointOnTriangle , maybeVertex := edge .Closest (p )
141+ d2 := Norm2 (Sub (p , pointOnTriangle ))
142+ if d2 < minDist {
143+ if vertex < 0 {
144+ vertex = - 1
145+ side = int8 (j )
146+ } else {
147+ side = - 1
148+ vertex = (int8 (j ) + maybeVertex ) % 3
149+ }
150+ minDist = d2
151+ closest = pointOnTriangle
152+ }
153+ }
154+ return closest , side , vertex
155+ }
0 commit comments