UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
CurveUtil.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "VectorTypes.h"
6#include "SegmentTypes.h"
7#include "LineTypes.h"
8#include "BoxTypes.h"
9#include "SegmentTypes.h"
10
11namespace UE
12{
13namespace Geometry
14{
15namespace CurveUtil
16{
17
18using namespace UE::Math;
19
28 template<typename RealType, typename VectorType, bool bLoop>
29 inline void GetPrevNext(const TArrayView<const VectorType>& Vertices, int32 Idx, VectorType& OutPrev, VectorType& OutNext)
30 {
31 int32 NextIdx = Idx + 1;
32 int32 PrevIdx = Idx - 1;
33 int32 NV = Vertices.Num();
34 if constexpr (bLoop)
35 {
36 NextIdx = NextIdx % NV;
37 PrevIdx = (PrevIdx + NV) % NV;
38 }
39 else
40 {
41 NextIdx = FMath::Min(NV - 1, NextIdx);
42 PrevIdx = FMath::Max(0, PrevIdx);
43 }
44 OutNext = Vertices[NextIdx];
45 OutPrev = Vertices[PrevIdx];
46 }
47
52 template<typename RealType, typename VectorType, bool bLoop>
53 inline void GetVectorsToPrevNext(const TArrayView<const VectorType>& Vertices, int32 VertexIndex, VectorType& OutToPrev, VectorType& OutToNext, bool bNormalize)
54 {
56 OutToNext -= Vertices[VertexIndex];
57 OutToPrev -= Vertices[VertexIndex];
58 if (bNormalize)
59 {
62 }
63 }
64
69 template<typename RealType, typename VectorType>
70 inline void GetVectorsToPrevNext(const TArrayView<const VectorType>& Vertices, int32 VertexIndex, VectorType& OutToPrev, VectorType& OutToNext, bool bNormalize, bool bLoop)
71 {
72 if (bLoop)
73 {
75 }
76 else
77 {
79 }
80 }
81
85 template<typename RealType, typename VectorType, bool bLoop>
86 inline VectorType Tangent(const TArrayView<const VectorType>& Vertices, int32 Idx)
87 {
88 VectorType Prev, Next;
90 return Normalized(Next - Prev);
91 }
92
96 template<typename RealType, typename VectorType>
97 inline VectorType Tangent(const TArrayView<const VectorType>& Vertices, int32 Idx, bool bLoop = false)
98 {
99 return bLoop ?
102 }
103
110 template<typename RealType, typename VectorType, bool bLoop>
111 VectorType GetNormal_FaceAvg2(const TArrayView<const VectorType>& Vertices, int VertexIndex)
112 {
115
117 RealType Len = Normalize(N);
118 if (Len == 0)
119 {
120 return Normalized(ToNext + ToPrev); // this gives right direction for degenerate angle
121 }
122 else
123 {
124 return N;
125 }
126 }
127
131 template<typename RealType, typename VectorType>
133 {
134 RealType Area = 0;
135 int N = Vertices.Num();
136 if (N == 0)
137 {
138 return 0;
139 }
140 for (int Idx = 0, PrevIdx = N-1; Idx < N; PrevIdx=Idx++)
141 {
142 const TVector2<RealType>& V1 = Vertices[PrevIdx];
143 const TVector2<RealType>& V2 = Vertices[Idx];
144 Area += V1.X * V2.Y - V1.Y * V2.X;
145 }
146 return static_cast<RealType>(Area * 0.5);
147 }
148
152 template<typename RealType, typename VectorType>
153 RealType WindingIntegral2(const TArrayView<const VectorType>& Vertices, const VectorType& QueryPoint)
154 {
155 RealType Sum = 0;
156 int N = Vertices.Num();
157 VectorType A = Vertices[N-1] - QueryPoint, B = VectorType::Zero();
158 for (int Idx = 0; Idx < N; ++Idx)
159 {
160 B = Vertices[Idx] - QueryPoint;
161 // TODO: Consider computing closed curve winding w/out trig functions, i.e. see Contains2 below
162 Sum += TMathUtil<RealType>::Atan2(A.X * B.Y - A.Y * B.X, A.X * B.X + A.Y * B.Y);
163 A = B;
164 }
165 return Sum / FMathd::TwoPi;
166 }
167
172 template<typename RealType, typename VectorType>
173 bool Contains2(const TArrayView<const VectorType>& Vertices, const VectorType& QueryPoint)
174 {
175 int WindingNumber = 0;
176
177 int N = Vertices.Num();
178 if (N == 0)
179 {
180 return false;
181 }
182 VectorType A = Vertices[N - 1], B = VectorType::Zero();
183 for (int Idx = 0; Idx < N; ++Idx)
184 {
185 B = Vertices[Idx];
186
187 if (A.Y <= QueryPoint.Y) // y <= P.Y (below)
188 {
189 if (B.Y > QueryPoint.Y) // an upward crossing
190 {
191 if (Orient(A, B, QueryPoint) > 0) // P left of edge
192 {
193 ++WindingNumber; // have a valid up intersect
194 }
195 }
196 }
197 else // y > P.Y (above)
198 {
199 if (B.Y <= QueryPoint.Y) // a downward crossing
200 {
201 if (Orient(A, B, QueryPoint) < 0) // P right of edge
202 {
203 --WindingNumber; // have a valid down intersect
204 }
205 }
206 }
207 A = B;
208 }
209 return WindingNumber != 0;
210 }
211
212 template<typename RealType, typename VectorType>
213 static RealType ArcLength(const TArrayView<const VectorType>& Vertices, bool bLoop = false) {
214 RealType Sum = 0;
215 int32 NV = Vertices.Num();
216 for (int i = 1; i < NV; ++i)
217 {
218 Sum += Distance(Vertices[i], Vertices[i-1]);
219 }
220 if (bLoop && NV > 1)
221 {
222 Sum += Distance(Vertices[NV-1], Vertices[0]);
223 }
224 return Sum;
225 }
226
227 template<typename RealType, typename VectorType>
228 int FindNearestIndex(const TArrayView<const VectorType>& Vertices, const VectorType& V)
229 {
230 int iNearest = -1;
232 int N = Vertices.Num();
233 for ( int i = 0; i < N; ++i )
234 {
235 RealType dSqr = DistanceSquared(Vertices[i], V);
236 if (dSqr < dNear)
237 {
238 dNear = dSqr;
239 iNearest = i;
240 }
241 }
242 return iNearest;
243 }
244
253 template<typename RealType, typename VectorType, bool bLoop = true, int ClipDims = 2>
254 static void ClipConvexToBounds(TArray<VectorType>& Vertices, VectorType Min, VectorType Max)
255 {
257 Clipped.Reserve(Vertices.Num());
258 VectorType SidePt = Min;
259 for (int32 SideIdx = 0; SideIdx < 2; ++SideIdx, SidePt = Max)
260 {
261 int32 SideSign = -SideIdx * 2 + 1;
262 for (int32 ClipDim = 0; ClipDim < ClipDims; ++ClipDim)
263 {
264 RealType ClipCoord = SidePt[ClipDim];
265 int32 VertNum = Vertices.Num();
267 if constexpr (bLoop)
268 {
269 StartCur = 0;
270 StartPrev = VertNum - 1;
271 }
272 else
273 {
274 StartCur = 1;
275 StartPrev = 0;
276 }
277 RealType PrevDist = (Vertices[StartPrev][ClipDim] - ClipCoord) * RealType(SideSign);
278 if constexpr (!bLoop)
279 {
280 if (PrevDist >= 0)
281 {
282 Clipped.Add(Vertices[0]);
283 }
284 }
285 for (int32 CurIdx = StartCur, PrevIdx = StartPrev; CurIdx < VertNum; PrevIdx = CurIdx++)
286 {
287 RealType CurDist = (Vertices[CurIdx][ClipDim] - ClipCoord) * RealType(SideSign);
288 if (CurDist >= 0)
289 {
291 {
292 RealType T = CurDist / (CurDist - PrevDist);
293 VectorType LerpVec = Lerp(Vertices[CurIdx], Vertices[PrevIdx], T);
294 LerpVec[ClipDim] = ClipCoord; // snap to exact bounds
295 Clipped.Add(LerpVec);
296 }
297 Clipped.Add(Vertices[CurIdx]);
298 }
299 else if (PrevDist > 0)
300 {
301 RealType T = CurDist / (CurDist - PrevDist);
302 VectorType LerpVec = Lerp(Vertices[CurIdx], Vertices[PrevIdx], T);
303 LerpVec[ClipDim] = ClipCoord; // snap to exact bounds
304 Clipped.Add(LerpVec);
305 }
307 }
308 Swap(Vertices, Clipped);
309 if (Vertices.IsEmpty())
310 {
311 return;
312 }
313 Clipped.Reset();
314 }
315 }
316 }
317
328 template<typename RealType, typename VectorType, bool bLoop = true>
329 static bool ClipConvexToPlane(TArray<VectorType>& Vertices, const TPlane<RealType>& Plane, TArray<VectorType>& OutClipped, bool bKeepPositiveSide = false)
330 {
331 OutClipped.Reset(Vertices.Num());
332 bool bWasClipped = false;
333 int32 VertNum = Vertices.Num();
335 if constexpr (bLoop)
336 {
337 StartCur = 0;
338 StartPrev = VertNum - 1;
339 }
340 else
341 {
342 StartCur = 1;
343 StartPrev = 0;
344 }
345 RealType SideSign = bKeepPositiveSide ? (RealType)1 : (RealType)-1;
346 RealType PrevDist = Plane.PlaneDot(Vertices[StartPrev]) * RealType(SideSign);
347 if constexpr (!bLoop)
348 {
349 if (PrevDist >= 0)
350 {
351 OutClipped.Add(Vertices[0]);
352 }
353 else
354 {
355 bWasClipped = true;
356 }
357 }
358 for (int32 CurIdx = StartCur, PrevIdx = StartPrev; CurIdx < VertNum; PrevIdx = CurIdx++)
359 {
360 RealType CurDist = Plane.PlaneDot(Vertices[CurIdx]) * RealType(SideSign);
361 if (CurDist >= 0)
362 {
364 {
365 RealType T = CurDist / (CurDist - PrevDist);
366 VectorType LerpVec = Lerp(Vertices[CurIdx], Vertices[PrevIdx], T);
367 OutClipped.Add(LerpVec);
368 bWasClipped = true;
369 }
370 OutClipped.Add(Vertices[CurIdx]);
371 }
372 else
373 {
374 bWasClipped = true;
375 if (PrevDist > 0)
376 {
377 RealType T = CurDist / (CurDist - PrevDist);
378 VectorType LerpVec = Lerp(Vertices[CurIdx], Vertices[PrevIdx], T);
379 OutClipped.Add(LerpVec);
380 }
381 }
383 }
384 return bWasClipped;
385 }
386
395 template<typename RealType, typename VectorType>
397 bool bDegenerateIsConvex = true)
398 {
399 int32 N = Vertices.Num();
400 if (N < 3)
401 {
402 // degenerate case, less than 3 points
403 return bDegenerateIsConvex;
404 }
405 VectorType FromPrev = Vertices[N - 1] - Vertices[N - 2];
406 int32 UsedPrevIdx = N - 2;
407 while (!FromPrev.Normalize(0))
408 {
409 UsedPrevIdx--;
410 if (UsedPrevIdx < 0)
411 {
412 return bDegenerateIsConvex; // degenerate case: all points coincident
413 }
414 FromPrev = Vertices[N - 1] - Vertices[UsedPrevIdx];
415 }
416 bool bSeenNeg = false, bSeenPos = false;
417 RealType AngleSum = 0;
419 for (int32 Cur = N-1, Next = 0; Next < N; Next++)
420 {
421 VectorType ToNext = Vertices[Next] - Vertices[Cur];
422 if (!ToNext.Normalize(0)) // skip duplicates
423 {
424 continue;
425 }
426 RealType Angle = SignedAngleR(FromPrev, ToNext);
427 bSeenNeg |= (Angle < -Tolerance);
428 bSeenPos |= (Angle > Tolerance);
429 AngleSum += Angle;
431 Cur = Next;
432 NonZeroEdges++;
433 }
434 if (NonZeroEdges < 3)
435 {
436 return bDegenerateIsConvex;
437 }
438 return !(bSeenNeg && bSeenPos) // curve should only turn one way, and
439 // curve should loop only once, turning 2*Pi radians in that one direction
441 }
442
450 template<typename RealType>
451 bool ProjectPointInsideConvexPolygon(const TArrayView<const TVector2<RealType>> Vertices, TVector2<RealType>& ProjPt, bool bReverseOrientation = false)
452 {
453 bool bProjected = false;
454 const int32 N = Vertices.Num();
455 for (int32 Idx = 0, PrevIdx = N - 1; Idx < N; PrevIdx = Idx++)
456 {
457 const TVector2<RealType>& V1 = Vertices[PrevIdx];
458 const TVector2<RealType>& V2 = Vertices[Idx];
461 RealType Orient = ToPt.Dot(Normal);
462 bool bOutside = bReverseOrientation ? Orient < 0 : Orient > 0;
463 if (bOutside)
464 {
465 RealType SqLen = Normal.SquaredLength();
466 if (SqLen > (RealType)FLT_MIN)
467 {
468 bProjected = true;
469 ProjPt -= Normal * Orient / SqLen;
470 }
471 }
472 }
473 return bProjected;
474 }
475
476
480 template<typename RealType, typename VectorType>
481 void InPlaceSmooth(TArrayView<VectorType> Vertices, int StartIdx, int EndIdx, double Alpha, int NumIterations, bool bClosed)
482 {
483 int N = Vertices.Num();
484 if ( bClosed )
485 {
486 for (int Iter = 0; Iter < NumIterations; ++Iter)
487 {
488 for (int ii = StartIdx; ii < EndIdx; ++ii)
489 {
490 int i = (ii % N);
491 int iPrev = (ii == 0) ? N - 1 : ii - 1;
492 int iNext = (ii + 1) % N;
493 TVector<RealType> prev = Vertices[iPrev], next = Vertices[iNext];
494 TVector<RealType> c = (prev + next) * 0.5f;
495 Vertices[i] = (1 - Alpha) * Vertices[i] + (Alpha) * c;
496 }
497 }
498 }
499 else
500 {
501 for (int Iter = 0; Iter < NumIterations; ++Iter)
502 {
503 for (int i = StartIdx; i < EndIdx; ++i)
504 {
505 if (i == 0 || i >= N - 1)
506 {
507 continue;
508 }
509 TVector<RealType> prev = Vertices[i - 1], next = Vertices[i + 1];
510 TVector<RealType> c = (prev + next) * 0.5f;
511 Vertices[i] = (1 - Alpha) * Vertices[i] + (Alpha) * c;
512 }
513 }
514 }
515 }
516
517
518
522 template<typename RealType, typename VectorType>
523 void IterativeSmooth(TArrayView<VectorType> Vertices, int StartIdx, int EndIdx, double Alpha, int NumIterations, bool bClosed)
524 {
525 int N = Vertices.Num();
528
529 if (bClosed)
530 {
531 for (int Iter = 0; Iter < NumIterations; ++Iter)
532 {
533 for (int ii = StartIdx; ii < EndIdx; ++ii)
534 {
535 int i = (ii % N);
536 int iPrev = (ii == 0) ? N - 1 : ii - 1;
537 int iNext = (ii + 1) % N;
538 TVector<RealType> prev = Vertices[iPrev], next = Vertices[iNext];
539 TVector<RealType> c = (prev + next) * 0.5f;
540 Buffer[i] = (1 - Alpha) * Vertices[i] + (Alpha) * c;
541 }
542 for (int ii = StartIdx; ii < EndIdx; ++ii)
543 {
544 int i = (ii % N);
545 Vertices[i] = Buffer[i];
546 }
547 }
548 }
549 else
550 {
551 for (int Iter = 0; Iter < NumIterations; ++Iter)
552 {
553 for (int i = StartIdx; i < EndIdx && i < N; ++i)
554 {
555 if (i == 0 || i == N - 1)
556 {
557 continue;
558 }
559 TVector<RealType> prev = Vertices[i - 1], next = Vertices[i + 1];
560 TVector<RealType> c = (prev + next) * 0.5f;
561 Buffer[i] = (1 - Alpha) * Vertices[i] + (Alpha) * c;
562 }
563 for (int i = StartIdx; i < EndIdx && i < N; ++i)
564 {
565 if (i == 0 || i == N - 1)
566 {
567 continue;
568 }
569 Vertices[i] = Buffer[i];
570 }
571 }
572 }
573 }
574
575
576} // end namespace UE::Geometry::CurveUtil
577} // end namespace UE::Geometry
578} // end namespace UE
@ Normal
Definition AndroidInputInterface.h:116
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
int prev(int i, int n)
Definition RecastMesh.cpp:163
int next(int i, int n)
Definition RecastMesh.cpp:164
Definition ArrayView.h:139
UE_FORCEINLINE_HINT constexpr SizeType Num() const
Definition ArrayView.h:380
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
void SetNumZeroed(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2340
UE_REWRITE bool IsEmpty() const
Definition Array.h:1133
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition MathUtil.h:150
static RealType Atan2(const RealType ValueY, const RealType ValueX)
Definition MathUtil.h:360
bool ProjectPointInsideConvexPolygon(const TArrayView< const TVector2< RealType > > Vertices, TVector2< RealType > &ProjPt, bool bReverseOrientation=false)
Definition CurveUtil.h:451
int FindNearestIndex(const TArrayView< const VectorType > &Vertices, const VectorType &V)
Definition CurveUtil.h:228
VectorType GetNormal_FaceAvg2(const TArrayView< const VectorType > &Vertices, int VertexIndex)
Definition CurveUtil.h:111
bool IsConvex2(const TArrayView< const VectorType > Vertices, RealType Tolerance=TMathUtil< RealType >::ZeroTolerance, bool bDegenerateIsConvex=true)
Definition CurveUtil.h:396
RealType WindingIntegral2(const TArrayView< const VectorType > &Vertices, const VectorType &QueryPoint)
Definition CurveUtil.h:153
RealType SignedArea2(const TArrayView< const VectorType > &Vertices)
Definition CurveUtil.h:132
void GetVectorsToPrevNext(const TArrayView< const VectorType > &Vertices, int32 VertexIndex, VectorType &OutToPrev, VectorType &OutToNext, bool bNormalize)
Definition CurveUtil.h:53
void GetPrevNext(const TArrayView< const VectorType > &Vertices, int32 Idx, VectorType &OutPrev, VectorType &OutNext)
Definition CurveUtil.h:29
bool Contains2(const TArrayView< const VectorType > &Vertices, const VectorType &QueryPoint)
Definition CurveUtil.h:173
void InPlaceSmooth(TArrayView< VectorType > Vertices, int StartIdx, int EndIdx, double Alpha, int NumIterations, bool bClosed)
Definition CurveUtil.h:481
void IterativeSmooth(TArrayView< VectorType > Vertices, int StartIdx, int EndIdx, double Alpha, int NumIterations, bool bClosed)
Definition CurveUtil.h:523
constexpr UE::Math::TVector2< T > PerpCW(const UE::Math::TVector2< T > &V)
Definition VectorTypes.h:26
T DistanceSquared(const UE::Math::TVector2< T > &V1, const UE::Math::TVector2< T > &V2)
Definition VectorTypes.h:82
T SignedAngleR(const UE::Math::TVector2< T > &V1, const UE::Math::TVector2< T > &V2)
Definition VectorTypes.h:110
T Orient(const UE::Math::TVector2< T > &A, const UE::Math::TVector2< T > &B, const UE::Math::TVector2< T > &C)
Definition VectorTypes.h:33
@ Area
Definition FitOrientedBox2.h:17
T Normalize(UE::Math::TVector2< T > &Vector, const T Epsilon=0)
Definition VectorTypes.h:46
Definition Sphere.cpp:10
Definition AdvancedWidgetsModule.cpp:13
Definition Plane.h:35
Definition Vector2D.h:38
Definition Vector.h:51