@@ -140,22 +140,61 @@ function collect_with_eltype!(result::AbstractVector{T}, iter) where {T}
140140end
141141
142142"""
143- orthogonal_vector(p1, p2, p3 )
143+ orthogonal_vector([target_type = Vec3f], points )
144144
145- Calculates an orthogonal vector `cross(p2 - p1, p3 - p1)` to a plane described
146- by 3 points p1, p2, p3.
145+ Calculates an orthogonal vector to a polygon defined by a vector of ordered
146+ `points`. Note that the orthogonal vector to a collection of 2D points needs to
147+ expand to 3D space.
148+
149+ Note that this vector is not normalized.
147150"""
148- orthogonal_vector (p1, p2, p3) = cross (p2 - p1, p3 - p1)
149- orthogonal_vector (:: Type{VT} , p1, p2, p3) where {VT} = orthogonal_vector (VT (p1), VT (p2), VT (p3))
151+ function orthogonal_vector (:: Type{VT} , vertices) where {VT <: VecTypes{3} }
152+ c = zeros (VT) # Inherit vector type from input
153+ prev = to_ndim (VT, last (coordinates (vertices)), 0 )
154+ @inbounds for p in coordinates (vertices) # Use shoelace approach
155+ v = to_ndim (VT, p, 0 )
156+ c += cross (prev, v) # Add each edge contribution
157+ prev = v
158+ end
159+ return c
160+ end
161+
162+ function orthogonal_vector (:: Type{VT} , vertices:: Tuple ) where {VT <: VecTypes{3} }
163+ c = zeros (VT) # Inherit vector type from input
164+ prev = to_ndim (VT, last (vertices), 0 )
165+ @inbounds for p in vertices # Use shoelace approach
166+ v = to_ndim (VT, p, 0 )
167+ c += cross (prev, v) # Add each edge contribution
168+ prev = v
169+ end
170+ return c
171+ end
172+
173+ # Not sure how useful this fast path is, but it's simple to keep
174+ function orthogonal_vector (:: Type{VT} , triangle:: Triangle ) where {VT <: VecTypes{3} }
175+ a, b, c = triangle
176+ return cross (to_ndim (VT, b- a, 0 ), to_ndim (VT, c- a, 0 ))
177+ end
178+
179+ # derive target type
180+ orthogonal_vector (vertices:: Ngon{D, T} ) where {D, T} = orthogonal_vector (Vec3{T}, vertices)
181+ function orthogonal_vector (vertices:: NTuple{N, VT} ) where {N, D, T, VT <: VecTypes{D, T} }
182+ return orthogonal_vector (Vec3{T}, vertices)
183+ end
184+ function orthogonal_vector (vertices:: AbstractArray{VT} ) where {D, T, VT <: VecTypes{D, T} }
185+ return orthogonal_vector (Vec3{T}, vertices)
186+ end
187+ # fallback to Vec3f if vertices is something else
188+ orthogonal_vector (vertices) = orthogonal_vector (Vec3f, vertices)
150189
151190"""
152191 normals(positions::Vector{Point3{T}}, faces::Vector{<: NgonFace}[; normaltype = Vec3{T}])
153192
154193Compute vertex normals from the given `positions` and `faces`.
155194
156195This runs through all faces, computing a face normal each and adding it to every
157- involved vertex. The direction of the face normal is based on winding direction
158- and assumed counter-clockwise faces. At the end the summed face normals are
196+ involved vertex. The direction of the face normal is based on winding direction
197+ and assumed counter-clockwise faces. At the end the summed face normals are
159198normalized again to produce a vertex normal.
160199"""
161200function normals (vertices:: AbstractVector{Point{3,T}} , faces:: AbstractVector{F} ;
@@ -165,12 +204,12 @@ end
165204
166205function normals (vertices:: AbstractVector{<:Point{3}} , faces:: AbstractVector{<: NgonFace} ,
167206 :: Type{NormalType} ) where {NormalType}
168-
207+
169208 normals_result = zeros (NormalType, length (vertices))
170209 for face in faces
171210 v = vertices[face]
172211 # we can get away with two edges since faces are planar.
173- n = orthogonal_vector (NormalType, v[ 1 ], v[ 2 ], v[ 3 ] )
212+ n = orthogonal_vector (NormalType, v)
174213 for i in 1 : length (face)
175214 fi = face[i]
176215 normals_result[fi] = normals_result[fi] .+ n
@@ -188,15 +227,15 @@ Compute face normals from the given `positions` and `faces` and returns an
188227appropriate `FaceView`.
189228"""
190229function face_normals (
191- positions:: AbstractVector{<:Point3{T}} , fs:: AbstractVector{<: AbstractFace} ;
230+ positions:: AbstractVector{<:Point3{T}} , fs:: AbstractVector{<: AbstractFace} ;
192231 normaltype = Vec3{T}) where {T}
193232 return face_normals (positions, fs, normaltype)
194233end
195-
234+
196235@generated function face_normals (positions:: AbstractVector{<:Point3} , fs:: AbstractVector{F} ,
197236 :: Type{NormalType} ) where {F<: NgonFace ,NormalType}
198-
199- # If the facetype is not concrete it likely varies and we need to query it
237+
238+ # If the facetype is not concrete it likely varies and we need to query it
200239 # doing the iteration
201240 FT = ifelse (isconcretetype (F), :($ F), :(typeof (f)))
202241
206245
207246 for (i, f) in enumerate (fs)
208247 ps = positions[f]
209- n = orthogonal_vector (NormalType, ps[ 1 ], ps[ 2 ], ps[ 3 ] )
248+ n = orthogonal_vector (NormalType, ps)
210249 normals[i] = normalize (n)
211250 faces[i] = $ (FT)(i)
212251 end
0 commit comments