UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
TangentBezierSpline.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
6namespace UE
7{
8namespace Geometry
9{
10namespace Spline
11{
12
13template<typename VALUETYPE> struct UE_EXPERIMENTAL(5.7, "New spline APIs are experimental.") TTangentBezierControlPoint;
14template<typename VALUETYPE> class UE_EXPERIMENTAL(5.7, "New spline APIs are experimental.") TTangentBezierSpline;
15
19enum class UE_EXPERIMENTAL(5.7, "New spline APIs are experimental.") ETangentMode : uint8
20{
22 Auto,
23
26
28 User,
29
31 Broken,
32
34 Linear,
35
38
41};
42
46template<typename ValueType>
48{
50 ValueType Position;
51
53 ValueType TangentIn;
54
56 ValueType TangentOut;
57
60
63 : Position(ValueType())
64 , TangentIn(ValueType())
65 , TangentOut(ValueType())
67 {
68 }
69
71 explicit TTangentBezierControlPoint (const ValueType& InPosition)
73 , TangentIn(ValueType())
74 , TangentOut(ValueType())
76 {
77 }
78
81 const ValueType& InPosition,
82 const ValueType& InTangentIn,
83 const ValueType& InTangentOut,
89 {
90 }
91
93 {
94 Ar << Position;
95 Ar << TangentIn;
96 Ar << TangentOut;
97 // Serialize as uint8 for compatibility
99 Ar << TangentModeValue;
100
101 if (Ar.IsLoading())
102 {
104 }
105
106 }
107
109 {
110 Point.Serialize(Ar);
111 return Ar;
112 }
113
115 {
116 return Position == Other.Position
117 && TangentIn == Other.TangentIn
118 && TangentOut == Other.TangentOut
119 && TangentMode == Other.TangentMode;
120 }
121};
122
128template<typename VALUETYPE>
130 public TSplineWrapper<TPolyBezierSpline<VALUETYPE>>,
131 private TSelfRegisteringSpline<TTangentBezierSpline<VALUETYPE>, VALUETYPE>
132{
133public:
137
138 // Generate compile-time type ID for TangentBezier
140 TEXT("TangentBezier"),
142 );
143
145 virtual ~TTangentBezierSpline() override = default;
146
148 TTangentBezierSpline(const ValueType& StartPoint, const ValueType& EndPoint)
149 : Tension(0.0f)
150 , TangentModes({ETangentMode::Auto, ETangentMode::Auto})
151 {
152 InternalSpline = FPolyBezierSpline3d::CreateLine(StartPoint, EndPoint);
153 }
154
159 const ValueType& StartPoint,
160 const ValueType& EndPoint,
161 const ValueType& StartTangent,
162 const ValueType& EndTangent,
163 bool bAutoTangents = false)
164 : Tension(0.0f)
165 , TangentModes({bAutoTangents ? ETangentMode::Auto : ETangentMode::User,
166 bAutoTangents ? ETangentMode::Auto : ETangentMode::User})
167 {
169 StartPoint + StartTangent / 3.0f,
170 EndPoint - (EndTangent / 3.0f),
171 EndPoint);
172 }
173
181
184 {
185 if (this != &Other)
186 {
188 TangentModes = Other.TangentModes;
189 bStationaryEndpoint = Other.bStationaryEndpoint;
190 InternalSpline = Other.InternalSpline;
191 }
192 return *this;
193 }
194
195 virtual bool IsEqual(const ISplineInterface* OtherSpline) const override
196 {
197 if (OtherSpline->GetTypeId() == GetTypeId())
198 {
199 const TTangentBezierSpline* Other = static_cast<const TTangentBezierSpline*>(OtherSpline);
200 return operator==(*Other);
201 }
202
203 return false;
204 }
205
206 virtual bool Serialize(FArchive& Ar) override
207 {
208 // Call immediate parent's Serialize (TSplineWrapper)
210 {
211 return false;
212 }
213
214 Ar << Tension;
215 // Serialize as uint8 for compatibility
217 Ar << NumTangentModes;
218
219 if (Ar.IsLoading())
220 {
222
223 // It was once valid for a spline to have a single bezier point, but we now expect 3 for this case.
224 // It is never valid to have 1 or 2 points.
225 switch (InternalSpline.NumKeys())
226 {
227 case 1: InternalSpline.AddValue(ValueType()); // fallthrough
228 case 2: InternalSpline.AddValue(ValueType()); // fallthrough
229 default: break;
230 }
231 }
232
233 for (int32 i = 0; i < NumTangentModes; ++i)
234 {
235 uint8 TangentModeValue = static_cast<uint8>(TangentModes[i]);
236 Ar << TangentModeValue;
237
238 if (Ar.IsLoading())
239 {
240 TangentModes[i] = static_cast<ETangentMode>(TangentModeValue);
241 }
242 }
244
245 return true;
246 }
247
249 {
250 Spline.Serialize(Ar);
251 return Ar;
252 }
253
255 {
256 return InternalSpline == Other.InternalSpline &&
257 Tension == Other.Tension &&
258 TangentModes == Other.TangentModes &&
259 bStationaryEndpoint == Other.bStationaryEndpoint;
260 }
261
262 // Static shape generators
263
271 const ValueType& StartPoint,
272 const ValueType& EndPoint)
273 {
274 // Create empty spline - we'll manually build it
276
277 // Clear the default initialization
278 Result.InternalSpline = FPolyBezierSpline3d::CreateLine(StartPoint, EndPoint);
279
280 Result.Reparameterize(EParameterizationPolicy::Uniform);
281 return Result;
282 }
283
294 const ValueType& Center,
295 float Radius,
296 float StartAngle,
297 float EndAngle,
298 int32 NumSegments = 4)
299 {
300 // Create empty spline - we'll manually build it
302
303 // Clear the default initialization
304 Result.InternalSpline = FPolyBezierSpline3d::CreateCircleArc(
305 Center, Radius, StartAngle, EndAngle, NumSegments);
306
307 // Set up tangent status for all points
308 Result.TangentModes.Init(ETangentMode::Auto, Result.GetNumPoints());
309
310 // Apply consistent parameterization - even though the internal PolyBezier already calls
311 // Reparameterize, we need to do it again here to ensure consistency
312 Result.Reparameterize(EParameterizationPolicy::Centripetal);
313
314 return Result;
315 }
316
325 const ValueType& Center,
326 float Radius,
327 int32 NumSegments = 4)
328 {
329 // Create the circle
331 Result.InternalSpline = FPolyBezierSpline3d::CreateCircle(
332 ValueType(Center), Radius, NumSegments);
333
334 // Set up tangent status for all points
335 Result.TangentModes.Init(ETangentMode::Auto, Result.GetNumPoints());
336
337 // Apply consistent parameterization
338 Result.Reparameterize(EParameterizationPolicy::Centripetal);
339
340 return Result;
341 }
342
352 const ValueType& Center,
353 float RadiusX,
354 float RadiusY,
355 int32 NumSegments = 4)
356 {
357 // Create the ellipse
359 Result.InternalSpline = FPolyBezierSpline3d::CreateEllipse(
360 Center, RadiusX, RadiusY, NumSegments);
361
362 // Set up tangent status for all points
363 Result.TangentModes.Init(ETangentMode::Auto, Result.GetNumPoints());
364
365 // Apply consistent parameterization
366 Result.Reparameterize(EParameterizationPolicy::Centripetal);
367
368 return Result;
369 }
370 virtual TUniquePtr<ISplineInterface> Clone() const override
371 {
373
374 // Copy internal spline
375 Clone->InternalSpline = this->InternalSpline;
376
377 // Copy tangent settings
378 Clone->Tension = this->Tension;
379 Clone->TangentModes = this->TangentModes;
380 Clone->bStationaryEndpoint = this->bStationaryEndpoint;
381
382 // Copy infinity modes
383 Clone->PreInfinityMode = this->PreInfinityMode;
384 Clone->PostInfinityMode = this->PostInfinityMode;
385
386 return Clone;
387 }
388
389 virtual bool IsClosedLoop() const override
390 {
392 }
393
394 virtual void SetClosedLoop(bool bInClosedLoop) override
395 {
396 // Skip if state isn't changing
397 if (bInClosedLoop == InternalSpline.IsClosedLoop())
398 return;
399
400 if (bInClosedLoop)
401 {
402 const int32 NumPoints = GetNumPoints();
403 if (NumPoints >= 2)
404 {
405 // Get first and last points
406 const ValueType FirstPos = GetValue(0);
407 const ValueType LastPos = GetValue(NumPoints - 1);
408
409 // Compute tangents for the closure segment
411
412 if (IsAutoTangent(NumPoints - 1) && IsAutoTangent(0))
413 {
414 // Auto tangents for closure
418 }
419 else
420 {
421 // Use existing tangents
422 LastOutTangent = GetTangentOut(NumPoints - 1);
424 }
425
426 // Calculate control points for the closing segment
427 ValueType P0 = LastPos;
428 ValueType P3 = FirstPos;
429 ValueType P1 = P0 + (LastOutTangent / 3.0f);
430 ValueType P2 = P3 - (FirstInTangent / 3.0f);
431
432 // Add the closing segment to the internal spline
434
436 }
437 }
438 else
439 {
440 // For an open spline, remove the closing segment first
441 // then update the internal flag
442 const int32 NumSegments = GetNumberOfSegments();
443 if (NumSegments > 0)
444 {
445 // Remove the last segment
446 InternalSpline.RemoveSegment(NumSegments - 1);
447 }
448
449 }
450
451 // update flag on the internal spline
453
454
455 // Ensure auto tangents are updated after changing loop state
457 }
458
466 {
467 const int32 NumPoints = Points.Num();
468
469 Clear();
470
471 // Special case for less than 2 points
472 if (NumPoints < 2)
473 {
474 if (NumPoints == 1)
475 {
476 // Initialize the spline with a single point
478 return InitializeFirstSegment(Points[0], OutNewIndex);
479 }
480 return true;
481 }
482
483 // Convert to cubic Bezier control points for the internal spline
484 TArray<ValueType> BezierPoints;
485
486 // For each segment (between consecutive points), we need 4 control points
487 // Reserve space based on number of segments
488 const int32 NumSegments = NumPoints - 1;
489 BezierPoints.Reserve(NumSegments * 4);
490
491 for (int32 i = 0; i < NumSegments; ++i)
492 {
493 const FTangentBezierControlPoint& StartPoint = Points[i];
494 const FTangentBezierControlPoint& EndPoint = Points[i + 1];
495
496 ValueType P0 = StartPoint.Position;
497 ValueType P3 = EndPoint.Position;
498
499 // Calculate tangents based on the points' tangent modes
501
502 // Process start point's outgoing tangent
503 if (StartPoint.TangentMode == ETangentMode::Auto ||
504 StartPoint.TangentMode == ETangentMode::AutoClamped)
505 {
506 // Compute auto tangent
507 if (i == 0 && NumPoints > 2)
508 {
509 // First point - use forward difference
510 OutTangent = (1.0f - Tension) * (Points[i + 1].Position - Points[i].Position);
511 }
512 else if (i == NumPoints - 2 && NumPoints > 2)
513 {
514 // Point before last - use central difference
515 OutTangent = (1.0f - Tension) * (Points[i + 1].Position - Points[i - 1].Position);
516 }
517 else if (NumPoints > 2)
518 {
519 // Normal case - use central difference
520 OutTangent = (1.0f - Tension) * (Points[i + 1].Position - Points[i - 1].Position);
521 }
522 else
523 {
524 // Only two points - use forward difference
525 OutTangent = (1.0f - Tension) * (Points[i + 1].Position - Points[i].Position);
526 }
527
528 // Apply clamping for AutoClamped
529 if (StartPoint.TangentMode == ETangentMode::AutoClamped)
530 {
531 const float SegmentLength = Math::Distance(P3, P0);
532 const float MaxTangentSize = SegmentLength * 0.33f;
533
535 {
537 }
538 }
539 }
540 else if (StartPoint.TangentMode == ETangentMode::Linear)
541 {
542 // Linear tangent
543 OutTangent = (P3 - P0) * 0.33f;
544 }
545 else if (StartPoint.TangentMode == ETangentMode::Constant)
546 {
547 // Zero tangent for constant interpolation
549 }
550 else
551 {
552 // User or Broken mode - use provided tangent
553 OutTangent = StartPoint.TangentOut;
554 }
555
556 // Process end point's incoming tangent
557 if (EndPoint.TangentMode == ETangentMode::Auto ||
558 EndPoint.TangentMode == ETangentMode::AutoClamped)
559 {
560 // Compute auto tangent
561 if (i + 1 == 0 && NumPoints > 2)
562 {
563 // First point - use central difference
564 InTangent = (1.0f - Tension) * (Points[i + 2].Position - Points[i].Position);
565 }
566 else if (i + 1 == NumPoints - 1 && NumPoints > 2)
567 {
568 // Last point - use backward difference
569 InTangent = (1.0f - Tension) * (Points[i + 1].Position - Points[i].Position);
570 }
571 else if (NumPoints > 2)
572 {
573 // Normal case - use central difference
574 InTangent = (1.0f - Tension) * (Points[i + 2].Position - Points[i].Position);
575 }
576 else
577 {
578 // Only two points - use backward difference
579 InTangent = (1.0f - Tension) * (Points[i + 1].Position - Points[i].Position);
580 }
581
582 // Apply clamping for AutoClamped
583 if (EndPoint.TangentMode == ETangentMode::AutoClamped)
584 {
585 const float SegmentLength = Math::Distance(P3, P0);
586 const float MaxTangentSize = SegmentLength * 0.33f;
587
589 {
591 }
592 }
593 }
594 else if (EndPoint.TangentMode == ETangentMode::Linear)
595 {
596 // Linear tangent
597 InTangent = (P3 - P0) * 0.33f;
598 }
599 else if (EndPoint.TangentMode == ETangentMode::Constant)
600 {
601 // Zero tangent for constant interpolation
603 }
604 else
605 {
606 // User or Broken mode - use provided tangent
607 InTangent = EndPoint.TangentIn;
608 }
609
610 // Calculate bezier control points
611 ValueType P1 = P0 + OutTangent / 3.0f;
612 ValueType P2 = P3 - InTangent / 3.0f;
613
614 // Add all 4 control points for this segment
615 BezierPoints.Add(P0);
616 BezierPoints.Add(P1);
617 BezierPoints.Add(P2);
618 BezierPoints.Add(P3);
619 }
620
621 // Set the control points in the internal spline
622 InternalSpline.SetControlPoints(BezierPoints, EParameterizationPolicy::Centripetal);
623
624 // Store tangent modes for later use
625 TangentModes.Reset(NumPoints);
626 for (const FTangentBezierControlPoint& Point : Points)
627 {
628 TangentModes.Add(Point.TangentMode);
629 }
630
631 // Update tangents to ensure proper curve continuity
633
634 return true;
635 }
636
646 bool bAppend = true)
647 {
648 const int32 NumPoints = Points.Num();
649
650 // Need at least 2 points to add a valid segment
651 if (NumPoints < 2)
652 {
653 UE_LOG(LogSpline, Warning, TEXT("AddControlPoints requires at least 2 points to add a valid segment. Got %d points."), NumPoints);
654 return false;
655 }
656
657 // If the spline is currently empty (though it shouldn't be based on our design),
658 // this is equivalent to SetControlPoints
659 if (GetNumPoints() == 0)
660 {
661 return SetControlPoints(Points);
662 }
663
664 if (bAppend)
665 {
666 // For each point, use AddPoint
667 for (int32 i = 0; i < NumPoints; ++i)
668 {
669 // Skip the first point if it's the same as our last existing point
670 // (to avoid duplicates at connection point)
671 if (i == 0)
672 {
675
676 // Check if connection points are reasonably close
677 constexpr double ConnectionTolerance = 1e-4;
679
681 {
682 // Points are close - the first existing point is the same as our first new point
683 // So update the last point's tangent mode if needed
684 if (Points[0].TangentMode != ETangentMode::User && Points[0].TangentMode != ETangentMode::Broken)
685 {
686 SetPointTangentMode(ExistingNumPoints - 1, Points[0].TangentMode);
687 }
688 continue; // Skip adding this point
689 }
690 }
691
692 // Add each point normally
693 AppendPoint(Points[i]);
694 }
695 }
696 else
697 {
698 // For prepending, we need to add points in reverse order
699 for (int32 i = NumPoints - 1; i >= 0; --i)
700 {
701 // Skip the last point if it's the same as our first existing point
702 if (i == NumPoints - 1)
703 {
705
706 // Check if connection points are reasonably close
707 constexpr double ConnectionTolerance = 1e-4;
709
711 {
712 // Points are close - update first point's tangent mode if needed
713 if (Points[i].TangentMode != ETangentMode::User && Points[i].TangentMode != ETangentMode::Broken)
714 {
715 SetPointTangentMode(0, Points[i].TangentMode);
716 }
717 continue; // Skip adding this point
718 }
719 }
720
721 // Prepend each point
722 PrependPoint(Points[i]);
723 }
724 }
725
726 // Reparameterize for consistent parameter distribution
728
729 return true;
730 }
731
732
733
742 const FTangentBezierControlPoint& ControlPoint)
743 {
744 int32 SegmentIndex;
745 float LocalT;
747
748 return InsertPointAtSegmentParam(SegmentIndex, LocalT, ControlPoint);
749 }
750
763 int32 SegmentIndex,
764 float LocalT,
765 const FTangentBezierControlPoint& ControlPoint,
766 EParameterizationPolicy ParameterizationPolicy = EParameterizationPolicy::Centripetal)
767 {
769 // Special case: handle first two points
770 if (InitializeFirstSegment(ControlPoint, OutNewIndex))
771 {
772 return OutNewIndex;
773 }
774
775 // Insert at that parameter with the exact position
776 // Have the internal spline handle the insertion
777 int32 InsertedPointIndex = InternalSpline.InsertPointAtSegmentParam(SegmentIndex, LocalT, ControlPoint.Position);
778
779 // If insertion was successful, update tangents if not auto-computed
780 if (InsertedPointIndex >= 0 &&
781 (ControlPoint.TangentMode == ETangentMode::User ||
782 ControlPoint.TangentMode == ETangentMode::Broken))
783 {
784 SetTangentIn(InsertedPointIndex, ControlPoint.TangentIn);
785 SetTangentOut(InsertedPointIndex, ControlPoint.TangentOut);
786 }
787
789 SetPointTangentMode(InsertedPointIndex, ControlPoint.TangentMode);
790
791 // Update adjacent points if they have automatic tangents
792 if (InsertedPointIndex > 0 &&
793 (TangentModes[InsertedPointIndex-1] == ETangentMode::Auto ||
794 TangentModes[InsertedPointIndex-1] == ETangentMode::AutoClamped))
795 {
797 }
798
799 if (InsertedPointIndex+1 < GetNumPoints() &&
800 (TangentModes[InsertedPointIndex+1] == ETangentMode::Auto ||
801 TangentModes[InsertedPointIndex+1] == ETangentMode::AutoClamped))
802 {
804 }
805 return InsertedPointIndex;
806 }
807
816 const FTangentBezierControlPoint& ControlPoint)
817 {
819 if (InitializeFirstSegment(ControlPoint, NewPointIndex)) return NewPointIndex;
820
821 const int32 NumPoints = GetNumPoints();
822 // need to convert point index to segment index while also clamping,
823 // also keeping in mind that we could append or prepend a segment
824 const int32 SegmentIndex = FMath::Clamp(PointIndex - 1, 0, NumPoints - 1);
825 // Insert at that parameter with the exact position
826 // Have the internal spline handle the insertion
827 int32 InsertedPointIndex = InternalSpline.InsertPointAtPosition(SegmentIndex, ControlPoint.Position);
828
829 // If insertion was successful, update tangents if not auto-computed
830 if (InsertedPointIndex >= 0 &&
831 (ControlPoint.TangentMode == ETangentMode::User ||
832 ControlPoint.TangentMode == ETangentMode::Broken))
833 {
834 SetTangentIn(InsertedPointIndex, ControlPoint.TangentIn);
835 SetTangentOut(InsertedPointIndex, ControlPoint.TangentOut);
836 }
837
839 SetPointTangentMode(InsertedPointIndex, ControlPoint.TangentMode);
840
841 // Update adjacent points if they have automatic tangents
842 if (InsertedPointIndex > 0 &&
843 (TangentModes[InsertedPointIndex-1] == ETangentMode::Auto ||
844 TangentModes[InsertedPointIndex-1] == ETangentMode::AutoClamped))
845 {
847 }
848
849 if (InsertedPointIndex + 1 < GetNumPoints() &&
850 (TangentModes[InsertedPointIndex+1] == ETangentMode::Auto ||
851 TangentModes[InsertedPointIndex+1] == ETangentMode::AutoClamped))
852 {
854 }
855 return InsertedPointIndex;
856 }
857
864 void PrependPoint(const FTangentBezierControlPoint& ControlPoint)
865 {
866 bool bWasClosed = IsClosedLoop();
867 SetClosedLoop(false);
869 {
870 if (bWasClosed)
871 {
872 SetClosedLoop(true);
873 }
874 };
875
877
878 // Special case: handle first two points
879 if (InitializeFirstSegment(ControlPoint, OutNewIndex))
880 {
881 return;
882 }
883
884 // Calculate control points for the cubic Bezier segment
885 ValueType P0 = ControlPoint.Position;
886
887 // Handle tangent computation based on mode
889
890 if (ControlPoint.TangentMode == ETangentMode::User ||
891 ControlPoint.TangentMode == ETangentMode::Broken)
892 {
893 // Use the provided tangent for user-specified mode
894 OutTangent = ControlPoint.TangentOut;
895 }
896 else
897 {
898 // For auto modes, use a reasonable default that will be updated later
899 OutTangent = GetValue(0) - ControlPoint.Position;
900 }
901
902 ValueType P1 = P0 + (OutTangent / 3.0f);
903 ValueType P2 = GetValue(0) - (GetTangentIn(0) / 3.0f);
904
905 // Let InternalSpline handle the prepending efficiently
907
909 SetPointTangentMode(0, ControlPoint.TangentMode);
910
911 // Update tangents for the next point if it's auto
912 if (GetNumPoints() > 0 &&
913 (TangentModes[1] == ETangentMode::Auto || TangentModes[1] == ETangentMode::AutoClamped))
914 {
916 }
917 }
922 void AppendPoint(const FTangentBezierControlPoint& ControlPoint)
923 {
924 bool bWasClosed = IsClosedLoop();
925 SetClosedLoop(false);
927 {
928 if (bWasClosed)
929 {
930 SetClosedLoop(true);
931 }
932 };
933
934 int32 NewIndex = GetNumPoints();
935
936 // Special case: handle first two points
937 if (InitializeFirstSegment(ControlPoint, NewIndex))
938 {
939 return;
940 }
941
942
943 // Normal case - add segment to existing spline
944 ValueType P0 = GetValue(NewIndex - 1);
945
946
947 ValueType P3 = ControlPoint.Position;
948
949 // Update the previous point's tangents if needed
950 if (TangentModes[NewIndex - 1] == ETangentMode::Auto ||
951 TangentModes[NewIndex - 1] == ETangentMode::AutoClamped)
952 {
953 UpdatePointTangents(NewIndex - 1, TangentModes[NewIndex - 1]);
954 }
955
956 // Get its outgoing tangent (may have been updated above)
957 ValueType OutTangentPrev = GetTangentOut(NewIndex - 1);
958
959 // Calculate control points for the new segment
960 ValueType P1 = P0 + (OutTangentPrev / 3.0f);
961
962 // For the incoming tangent of the new point
963 ValueType P2;
964
965 if (ControlPoint.TangentMode == ETangentMode::User ||
966 ControlPoint.TangentMode == ETangentMode::Broken)
967 {
968 // For user-defined tangents, use the provided tangent
969 P2 = P3 - (ControlPoint.TangentIn / 3.0f);
970 }
971 else
972 {
973 // For other modes, we'll set up a reasonable default
974 // The UpdatePointTangents call below will update it properly
975 ValueType DefaultTangent = (P3 - P0) * (1.0f - Tension);
976 P2 = P3 - (DefaultTangent / 3.0f);
977 }
978
979 // Add the segment
981
983 SetPointTangentMode(NewIndex, ControlPoint.TangentMode);
984
985 // If this is auto mode, update tangents for adjacent points
986 if (ControlPoint.TangentMode == ETangentMode::Auto ||
987 ControlPoint.TangentMode == ETangentMode::AutoClamped)
988 {
989 // Update previous point's tangents again to ensure continuity
990 if (TangentModes[NewIndex - 1] == ETangentMode::Auto ||
991 TangentModes[NewIndex - 1] == ETangentMode::AutoClamped)
992 {
993 UpdatePointTangents(NewIndex - 1, TangentModes[NewIndex - 1]);
994 }
995 }
996 }
997
1004 {
1005 const int32 NumPoints = GetNumPoints();
1006 if (Index < 0 || Index >= NumPoints)
1007 {
1009 }
1010
1012 Result.Position = GetValue(Index);
1013 Result.TangentIn = GetTangentIn(Index);
1014 Result.TangentOut = GetTangentOut(Index);
1015
1016 // Get tangent mode
1018 {
1019 Result.TangentMode = TangentModes[Index];
1020 }
1021 else
1022 {
1023 Result.TangentMode = ETangentMode::Auto;
1024 }
1025
1026 return Result;
1027 }
1028
1034 {
1035 const int32 NumPoints = GetNumPoints();
1036 if (Index < 0 || Index >= NumPoints)
1037 {
1038 return;
1039 }
1040 // special case for handling the last point and second last point
1041 if (NumPoints == 1)
1042 {
1043 // Removing the only point
1046 return;
1047 }
1048 else if (NumPoints == 2 && !IsClosedLoop())
1049 {
1051
1052 // Removing the last point and tangents
1053 // For CP = 0, we remove P0, P1, P2
1054 // For CP = 1, we remove P1, P2, P3
1059
1060 // If we remove point 0:
1061 // Values = < p3, p2, p2 >
1062
1063 // Ff we remove point 1:
1064 // Values = < p0, p1, p1 >
1065
1068
1069 return;
1070 }
1071
1072 // Remove the corresponding tangent mode
1074 {
1076 }
1077
1079
1080 // Ensure tangent modes array size matches number of points after removal
1081 if (TangentModes.Num() > GetNumPoints())
1082 {
1084 }
1085
1086 // Update tangents for adjacent points
1088
1089 }
1090
1097 {
1098 const int32 NumPoints = GetNumPoints();
1099 if (Index < 0 || Index >= NumPoints)
1100 {
1101 return;
1102 }
1103
1104 const ValueType OldPosition = GetValue(Index);
1105 bool bPositionChanged = (OldPosition != ControlPoint.Position);
1106
1107 SetPointTangentMode(Index, ControlPoint.TangentMode);
1108
1109 // Update position
1110 if (Index == 0)
1111 {
1112 // First point
1113 UpdateBezierControlPoint(Index * 4, ControlPoint.Position);
1114
1115 if (IsClosedLoop())
1116 {
1117 UpdateBezierControlPoint((NumPoints - 1) * 4 + 3, ControlPoint.Position);
1118 }
1119 }
1120 else if (Index == NumPoints - 1)
1121 {
1122 // Last point
1123 UpdateBezierControlPoint(Index * 4 - 1, ControlPoint.Position);
1124 if (IsClosedLoop())
1125 {
1126 UpdateBezierControlPoint(Index * 4, ControlPoint.Position);
1127 }
1128 }
1129 else
1130 {
1131 // Middle point - affects two segments
1132 UpdateBezierControlPoint((Index - 1) * 4 + 3, ControlPoint.Position);
1133 UpdateBezierControlPoint(Index * 4, ControlPoint.Position);
1134 }
1135
1136 // If not auto tangents, apply the specified tangents
1137 if (ControlPoint.TangentMode != ETangentMode::Auto &&
1138 ControlPoint.TangentMode != ETangentMode::AutoClamped &&
1139 ControlPoint.TangentMode != ETangentMode::Linear &&
1140 ControlPoint.TangentMode != ETangentMode::Constant)
1141 {
1142 SetTangentIn(Index, ControlPoint.TangentIn);
1143 SetTangentOut(Index, ControlPoint.TangentOut);
1144 }
1145
1146 // Update tangents based on tangent mode
1147 UpdatePointTangents(Index, ControlPoint.TangentMode);
1148
1149 // Update surrounding points if position changed and they're auto tangents
1150 if (bPositionChanged)
1151 {
1152 if (Index > 0 && (TangentModes[Index-1] == ETangentMode::Auto ||
1153 TangentModes[Index-1] == ETangentMode::AutoClamped))
1154 {
1156 }
1157
1158 if (Index < NumPoints-1 && (TangentModes[Index+1] == ETangentMode::Auto ||
1159 TangentModes[Index+1] == ETangentMode::AutoClamped))
1160 {
1162 }
1163 }
1164 }
1165
1166 void SetValue(int32 Index, const ValueType& NewValue)
1167 {
1169 NewPoint.Position = NewValue;
1171 }
1172
1174 ValueType GetValue(float Parameter) const
1175 {
1176 return InternalSpline.Evaluate(Parameter);
1177 }
1178
1181 {
1182 const int32 NumKeys = InternalSpline.NumKeys();
1183
1184 if (NumKeys == 0 || Index < 0 || Index >= GetNumPoints())
1185 return ValueType();
1186
1187 if (NumKeys == 1)
1188 {
1189 // Special case when we only have one point
1190 if (Index == 0)
1191 return InternalSpline.GetValue(0);
1192
1193 return ValueType();
1194 }
1195
1196 // Regular case - convert from point index to bezier control point index
1197 if (Index == 0)
1198 {
1199 // First point is P0 of first segment
1200 return InternalSpline.GetValue(0);
1201 }
1202 else
1203 {
1204 // Other points are P3 of previous segments
1205 const int32 ControlPointIndex = (Index - 1) * 4 + 3;
1206 if (ControlPointIndex < NumKeys)
1208 }
1209
1210 return ValueType();
1211 }
1212
1214 ValueType GetTangent(float Parameter) const
1215 {
1216 return InternalSpline.template EvaluateDerivative<1>(Parameter);
1217 }
1218
1221 {
1222 const int32 NumPoints = GetNumPoints();
1223
1224 if (NumPoints <= 1)
1225 {
1226 return ValueType();
1227 }
1228
1229 if (Index == 0)
1230 {
1231 if (IsClosedLoop())
1232 {
1233 // The point at NumPoints when the spline is closed is the point that is coincident with point 0.
1234 return GetTangentIn(NumPoints);
1235 }
1236 else
1237 {
1238 // If spline is unclosed, in tangent == out tangent at first point.
1239 return GetTangentOut(Index);
1240 }
1241 }
1242
1243 // Normal case - get from previous segment's control points
1244 const int32 BezierIndex = (Index - 1) * 4;
1246 {
1249 return (P3 - P2) * 3.0f;
1250 }
1251 return ValueType();
1252 }
1253
1256 {
1257 const int32 NumPoints = GetNumPoints();
1258
1259 if (NumPoints <= 1)
1260 {
1261 return ValueType();
1262 }
1263
1264 if (Index == NumPoints - 1 && !IsClosedLoop())
1265 {
1266 // If spline is unclosed, out tangent == in tangent at final point.
1267 return GetTangentIn(Index);
1268 }
1269
1270 // Normal case - get from next segment's control points
1271 const int32 BezierIndex = Index * 4;
1273 {
1276 return (P1 - P0) * 3.0f;
1277 }
1278 return ValueType();
1279 }
1280
1283 {
1285 {
1286 return;
1287 }
1288
1289 if (Index == 0)
1290 {
1291 // Special case for first point in closed loop
1293 {
1296 ValueType P2 = P3 - (NewTangent / 3.0f);
1298 }
1299 return;
1300 }
1301
1302 // Normal case - update previous segment's P2 control point
1303 const int32 BezierIndex = (Index - 1) * 4 + 2;
1305 {
1307 ValueType P2 = P3 - (NewTangent / 3.0f);
1309 }
1310 }
1311
1314 {
1315 const int32 NumPoints = GetNumPoints();
1316 if (Index == NumPoints - 1 && !IsClosedLoop())
1317 {
1318 return;
1319 }
1320
1321 // Normal case - update next segment's P1 control point
1322 const int32 BezierIndex = Index * 4 + 1;
1324 {
1326 ValueType P1 = P0 + (NewTangent / 3.0f);
1328 }
1329 }
1330
1333 {
1334 const int32 NumKeys = InternalSpline.NumKeys();
1335
1336 switch (NumKeys)
1337 {
1338 case 0:
1339 return 0;
1340 case 1:
1341 // intentional fallthrough!
1342 case 2:
1343 ensureAlwaysMsgf(false, TEXT("Invalid number of bezier points!"));
1344 case 3:
1345 return 1;
1346 default:
1348 }
1349 }
1350
1351 virtual int32 GetNumberOfSegments() const override
1352 {
1354 }
1355
1356 virtual FInterval1f GetSegmentParameterRange(int32 SegmentIndex) const override
1357 {
1358 return InternalSpline.GetSegmentParameterRange(SegmentIndex);
1359 }
1360
1363 {
1364 return TangentModes.IsValidIndex(Index) && (TangentModes[Index] == ETangentMode::Auto || TangentModes[Index] == ETangentMode::AutoClamped);
1365 }
1366
1373 {
1375 {
1376 return;
1377 }
1378
1379 TangentModes[Index] = Mode;
1380
1381 // Update tangents based on new mode
1383 }
1384
1385 virtual void Clear() override
1386 {
1389 }
1390
1392 {
1393 const int32 ControlPointIndex = (Index == 0) ? 0 : ((Index - 1) * 4 + 3);
1395 }
1396
1398 {
1399 const int32 ControlPointIndex = (Index == 0) ? 0 : ((Index - 1) * 4 + 3);
1400 const int32 NewIndex = (InternalSpline.SetParameter(ControlPointIndex, NewParameter) + 1) / 4;
1401
1402 // We likely need to do better in PolyBezier's SetParameter to account for user tangents, but this works for now.
1403 if (NewIndex != Index)
1404 {
1406
1407 UE_LOG(LogSpline, Verbose, TEXT("\t"));
1408 UE_LOG(LogSpline, Verbose, TEXT("After Tangent Recompute:"));
1410 }
1411
1412 return NewIndex;
1413 }
1414
1415 virtual FInterval1f GetParameterSpace() const override
1416 {
1417 if (GetNumPoints() == 1)
1418 {
1419 return FInterval1f(0.f, 0.f);
1420 }
1421
1423 }
1424
1425 int32 FindSegmentIndex(float Parameter, float &OutLocalParam) const
1426 {
1428 }
1429
1434 {
1435 const int32 NumPoints = GetNumPoints();
1436 if (NumPoints < 2)
1437 {
1438 return;
1439 }
1440 // Update tangents for each point according to its mode
1441 for (int32 i = 0; i < NumPoints; ++i)
1442 {
1444 }
1445 }
1446
1453 {
1454 const int32 NumPoints = GetNumPoints();
1455 if (Index < 0 || Index >= NumPoints || Mode == ETangentMode::User || Mode == ETangentMode::Broken)
1456 {
1457 // Invalid index or user-defined mode, no action needed
1458 return;
1459 }
1460
1461
1462 // Get the position of the current point
1463 const ValueType& Current = GetValue(Index);
1464
1465 // Get previous and next points (handling closed loop case)
1466 const ValueType& Prev = (Index > 0) ? GetValue(Index - 1) : (IsClosedLoop() ? GetValue(NumPoints - 1) : Current);
1467
1468 const ValueType& Next = (Index < NumPoints - 1) ? GetValue(Index + 1) : (IsClosedLoop() ? GetValue(0) : Current);
1469
1470 // Get actual parameter values for points
1471 float PrevParam = (Index > 0)
1472 ? static_cast<float>(Index - 1)
1473 : (IsClosedLoop() ? static_cast<float>(NumPoints - 1) : static_cast<float>(Index));
1474
1475 float CurrentParam = static_cast<float>(Index);
1476
1477 float NextParam = (Index < NumPoints - 1) ? static_cast<float>(Index + 1) : (IsClosedLoop() ? 0.0f : static_cast<float>(Index));
1478
1479 // Adjust params for closed loop to ensure proper weighting
1480 if (IsClosedLoop() && Index == 0)
1481 {
1482 PrevParam -= static_cast<float>(NumPoints); // Make previous parameter negative for proper delta
1483 }
1484 else if (IsClosedLoop() && Index == NumPoints - 1)
1485 {
1486 NextParam += static_cast<float>(NumPoints); // Make next parameter greater than NumPoints for proper delta
1487 }
1488
1489 // Calculate tangents based on mode
1490 ValueType InTangent = GetTangentIn(Index); // Keep existing if not changing
1492
1493 switch (Mode)
1494 {
1495 case ETangentMode::Auto:
1496 case ETangentMode::AutoClamped:
1497 {
1498 // Legacy-style auto tangent calculation with parameter weighting
1499 float PrevToNextParamDiff = FMath::Max<float>(UE_KINDA_SMALL_NUMBER, NextParam - PrevParam);
1500
1501 // Calculate basic tangent - matches legacy formula
1502 ValueType AutoTangent = ((Next - Prev) / PrevToNextParamDiff) * (1.0f - Tension);
1503
1504 // For AutoClamped mode, apply clamping like the legacy implementation
1505 if (Mode == ETangentMode::AutoClamped)
1506 {
1507 float PrevToCurParamDiff = FMath::Max<float>(UE_KINDA_SMALL_NUMBER, CurrentParam - PrevParam);
1508 float NextToCurParamDiff = FMath::Max<float>(UE_KINDA_SMALL_NUMBER, NextParam - CurrentParam);
1509
1512
1513 float PrevToCurLength = static_cast<float>(Math::Size(PrevToCurTangent));
1514 float NextToCurLength = static_cast<float>(Math::Size(NextToCurTangent));
1515
1516 // Clamp tangent magnitude
1517 float MaxScale = FMath::Min(PrevToCurLength, NextToCurLength);
1518
1520 {
1522 }
1523 }
1524
1525 // Handle stationary endpoints
1526 if (bStationaryEndpoint && (Index == 0 || Index == NumPoints - 1) && !IsClosedLoop())
1527 {
1529 }
1530
1533 }
1534 break;
1535
1536 case ETangentMode::Linear:
1537 {
1538 // Previous segment tangent (incoming)
1539 if (Index > 0 || IsClosedLoop())
1540 {
1541 InTangent = Current - Prev;
1542 }
1543 else
1544 {
1545 InTangent = ValueType();
1546 }
1547
1548 // Next segment tangent (outgoing)
1549 if (Index < NumPoints - 1 || IsClosedLoop())
1550 {
1552 }
1553 else
1554 {
1556 }
1557 }
1558 break;
1559
1560 case ETangentMode::Constant:
1561 {
1562 // Zero tangents for constant interpolation
1563 InTangent = ValueType();
1565 }
1566 break;
1567
1568 case ETangentMode::User:
1569 break;
1570
1571 case ETangentMode::Unknown:
1572 default:
1573 // Default to Auto mode if unknown
1574 {
1575 ValueType Tangent = (Next - Prev) * (1.0f - Tension);
1578 }
1579 break;
1580 }
1581
1582 // Apply the calculated tangents
1585
1586 }
1587
1588 virtual float FindNearest(const ValueType& Point, float& OutSquaredDistance) const override
1589 {
1590 // Delegate to internal spline
1592 }
1593
1594 void Reparameterize(EParameterizationPolicy Policy = EParameterizationPolicy::Centripetal)
1595 {
1597
1598 // Update tangents to maintain proper curve shape
1600 }
1601
1608
1610 {
1612 }
1613
1616 {
1617 return InternalSpline;
1618 }
1619
1620 float GetTension() const
1621 {
1622 return Tension;
1623 }
1624
1625 void SetTension(const float InTension)
1626 {
1627 this->Tension = InTension;
1628 }
1629
1631 {
1633 {
1634 return ETangentMode::Unknown;
1635 }
1636 return TangentModes[Index];
1637 }
1638
1643
1645 {
1646 return TangentModes;
1647 }
1648
1650 {
1652 UpdateTangents(); // Update tangents when this flag changes
1653 }
1654
1656 {
1657 return bStationaryEndpoint;
1658 }
1659
1660 float FindNearestOnSegment(const ValueType& Point, int32 SegmentIndex, float& OutSquaredDistance) const
1662
1663protected:
1666 {
1668 {
1669 InternalSpline.SetValue(Index, NewValue);
1670 }
1671 }
1672private:
1673
1674 static bool IsCurveKey(ETangentMode Mode)
1675 {
1676 return ((Mode == ETangentMode::Auto) || (Mode == ETangentMode::AutoClamped) || (Mode == ETangentMode::User) || (Mode == ETangentMode::Broken));
1677 }
1684 bool InitializeFirstSegment(const FTangentBezierControlPoint& ControlPoint, int32& OutPointIndex)
1685 {
1686 const int32 NumPoints = GetNumPoints();
1687
1688 if (NumPoints == 0)
1689 {
1690 // First point - just store it directly with both tangents, these are not used until the first segment is completed
1691 InternalSpline.AddValue(ControlPoint.Position);
1692 InternalSpline.AddValue(ControlPoint.Position + (ControlPoint.TangentOut / 3.0f));
1693 InternalSpline.AddValue(ControlPoint.Position - (ControlPoint.TangentIn / 3.0f));
1694
1695 // update tangent mode
1696 if (TangentModes.IsEmpty())
1697 {
1698 TangentModes.Add(ControlPoint.TangentMode);
1699 }
1700 else
1701 {
1702 TangentModes[0] = ControlPoint.TangentMode;
1703 }
1704
1705 OutPointIndex = 0;
1706 return true;
1707 }
1708 else if (NumPoints == 1)
1709 {
1710 constexpr bool bAppend = true;
1711 if (bAppend)
1712 {
1713 // We have exactly one point stored - this is our second point
1714 // Get the first point, then clear to start building segments
1715
1716 // Calculate control points
1718 ValueType P1, P2;
1719 ValueType P3 = ControlPoint.Position;
1720
1721 ValueType AutoTangent = (P3 - P0) * (1.0f - Tension);
1722
1723 if (ControlPoint.TangentMode == ETangentMode::Auto ||
1724 ControlPoint.TangentMode == ETangentMode::AutoClamped)
1725 {
1726 P2 = P3 - AutoTangent / 3.0f;
1727 }
1728 else
1729 {
1730 // Use provided tangents
1731 P2 = P3 - ControlPoint.TangentIn / 3.0f;
1732 }
1733
1734 if (IsAutoTangent(0))
1735 {
1736 P1 = P0 + AutoTangent / 3.0f;
1737 }
1738 else
1739 {
1740 // Values[1] is already the proper out tangent value (Values[2] is the in tangent we'd use if prepending instead of appending here)
1741 P1 = InternalSpline.GetValue(1);
1742 }
1743
1744 // Add the first segment
1745 InternalSpline.SetControlPoints( {P0, P1, P2, P3}, EParameterizationPolicy::Centripetal);
1746
1747 // update tangent mode
1749 SetPointTangentMode(1, ControlPoint.TangentMode);
1750
1751 OutPointIndex = 1;
1752 return true;
1753 }
1754 else
1755 {
1756 // todo: prepend
1757 }
1758 }
1759
1760 return false;
1761 }
1762
1763protected:
1764 /* Tension parameter for auto-computed tangents [0,1] */
1765 float Tension = 0.0f;
1766
1767 /* How tangents should be computed for each point */
1769
1771};
1772
1775} // end namespace UE::Geometry::Spline
1776} // end namespace UE::Geometry
1777} // end namespace UE
#define ensureAlwaysMsgf(InExpression, InFormat,...)
Definition AssertionMacros.h:467
#define UE_EXPERIMENTAL(Version, Message)
Definition CoreMiscDefines.h:369
#define TEXT(x)
Definition Platform.h:1272
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
#define UE_LOG(CategoryName, Verbosity, Format,...)
Definition LogMacros.h:270
#define ON_SCOPE_EXIT
Definition ScopeExit.h:73
#define UE_KINDA_SMALL_NUMBER
Definition UnrealMathUtility.h:131
uint8_t uint8
Definition binka_ue_file_header.h:8
Definition Archive.h:1208
virtual void Serialize(void *V, int64 Length)
Definition Archive.h:1689
UE_FORCEINLINE_HINT bool IsLoading() const
Definition Archive.h:236
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
void RemoveAt(SizeType Index, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2083
void Reset(SizeType NewSize=0)
Definition Array.h:2246
SizeType AddDefaulted()
Definition Array.h:2795
void InsertDefaulted(SizeType Index)
Definition Array.h:1841
UE_REWRITE bool IsEmpty() const
Definition Array.h:1133
void SetNum(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2308
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
UE_NODEBUG UE_FORCEINLINE_HINT bool IsValidIndex(SizeType Index) const
Definition Array.h:1122
void Empty(SizeType Slack=0)
Definition Array.h:2273
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition UniquePtr.h:107
Definition SplineInterfaces.h:35
virtual bool IsClosedLoop() const override
Definition BSpline.h:233
virtual bool RemoveValue(int32 Index)
Definition BSpline.h:329
int32 NumKeys() const
Definition BSpline.h:295
bool SetValue(int32 Idx, const ValueType &NewValue)
Definition BSpline.h:310
const ValueType & GetValue(int32 Idx) const
Definition BSpline.h:300
const TArray< FKnot > & GetKnotVector() const
Definition BSpline.h:422
int32 AddValue(const ValueType &NewValue)
Definition BSpline.h:305
void Dump() const
Definition BSpline.h:152
Definition PolyBezierSpline.h:22
int32 AppendBezierSegment(const ValueType &P1, const ValueType &P2, const ValueType &P3, EParameterizationPolicy ParameterizationPolicy=EParameterizationPolicy::Centripetal)
Definition PolyBezierSpline.h:633
virtual int32 GetNumberOfSegments() const override
Definition PolyBezierSpline.h:1051
virtual void Clear() override
Definition PolyBezierSpline.h:262
virtual FInterval1f GetSegmentParameterRange(int32 SegmentIndex) const override
Definition PolyBezierSpline.h:1062
int32 FindSegmentIndex(float Parameter, float &OutLocalParam) const
Definition PolyBezierSpline.h:1486
bool RemoveSegment(const int32 SegmentIndex)
Definition PolyBezierSpline.h:902
virtual FInterval1f GetParameterSpace() const override
Definition PolyBezierSpline.h:1094
void SetClosedLoopFlag(bool bClosed)
Definition PolyBezierSpline.h:1326
virtual int32 SetParameter(int32 Index, float NewParameter) override
Definition PolyBezierSpline.h:1118
virtual float GetParameter(int32 Index) const override
Definition PolyBezierSpline.h:1101
static TPolyBezierSpline< ValueType > CreateLine(const ValueType &Start, const ValueType &End)
Definition PolyBezierSpline.h:66
bool MapGlobalParameterToLocalSegment(float GlobalParam, int32 &OutSegmentIndex, float &OutLocalParam) const
Definition PolyBezierSpline.h:1395
virtual void Reparameterize(EParameterizationPolicy Mode=EParameterizationPolicy::Centripetal) override
Definition PolyBezierSpline.h:1081
int32 InsertPointAtPosition(int32 SegmentIndex, const ValueType &Position, EParameterizationPolicy ParameterizationPolicy=EParameterizationPolicy::Centripetal)
Definition PolyBezierSpline.h:701
bool RemovePoint(int32 PointIndex)
Definition PolyBezierSpline.h:930
static TPolyBezierSpline< ValueType > CreateCircleArc(const ValueType &Center, float Radius, float StartAngle, float EndAngle, int32 NumSegments=4)
Definition PolyBezierSpline.h:84
int32 InsertPointAtSegmentParam(int32 SegmentIndex, float LocalT, const ValueType &Position, EParameterizationPolicy ParameterizationPolicy=EParameterizationPolicy::Centripetal)
Definition PolyBezierSpline.h:725
static TPolyBezierSpline< ValueType > CreateEllipse(const ValueType &Center, float RadiusX, float RadiusY, int32 NumSegments=4)
Definition PolyBezierSpline.h:192
static TPolyBezierSpline< ValueType > CreateCircle(const ValueType &Center, float Radius, int32 NumSegments=4)
Definition PolyBezierSpline.h:172
float FindNearestOnSegment(const ValueType &Point, int32 SegmentIndex, float &OutSquaredDistance) const
Definition PolyBezierSpline.h:280
void SetKnotVector(const TArray< FKnot > &NewKnots)
Definition PolyBezierSpline.h:1506
virtual float FindNearest(const ValueType &Point, float &OutSquaredDistance) const override
Definition PolyBezierSpline.h:308
int32 PrependBezierSegment(const ValueType &P0, const ValueType &P1, const ValueType &P2, EParameterizationPolicy ParameterizationPolicy=EParameterizationPolicy::Centripetal)
Definition PolyBezierSpline.h:668
bool SetControlPoints(const TArray< ValueType > &Points, EParameterizationPolicy ParameterizationPolicy)
Definition PolyBezierSpline.h:379
Definition SplineTypeRegistry.h:236
EOutOfBoundsHandlingMode PostInfinityMode
Definition SplineInterfaces.h:191
virtual FSplineTypeId::IdType GetTypeId() const override
Definition SplineInterfaces.h:168
EOutOfBoundsHandlingMode PreInfinityMode
Definition SplineInterfaces.h:190
ValueType Evaluate(float Parameter) const
Definition SplineInterfaces.h:117
Definition TangentBezierSpline.h:132
TTangentBezierControlPoint< ValueType > FTangentBezierControlPoint
Definition TangentBezierSpline.h:136
TTangentBezierSpline(const TTangentBezierSpline &Other)
Definition TangentBezierSpline.h:175
DECLARE_SPLINE_TYPE_ID(TEXT("TangentBezier"), *TSplineValueTypeTraits< VALUETYPE >::Name)
ValueType GetValue(float Parameter) const
Definition TangentBezierSpline.h:1174
ValueType GetTangent(float Parameter) const
Definition TangentBezierSpline.h:1214
void SetTangentIn(int32 Index, const ValueType &NewTangent)
Definition TangentBezierSpline.h:1282
virtual bool IsClosedLoop() const override
Definition TangentBezierSpline.h:389
void RemovePoint(int32 Index)
Definition TangentBezierSpline.h:1033
const TArray< ETangentMode > & GetTangentModes() const
Definition TangentBezierSpline.h:1644
void SetPointTangentMode(int32 Index, ETangentMode Mode)
Definition TangentBezierSpline.h:1372
virtual bool Serialize(FArchive &Ar) override
Definition TangentBezierSpline.h:206
ValueType GetValue(int32 Index) const
Definition TangentBezierSpline.h:1180
float FindNearestOnSegment(const ValueType &Point, int32 SegmentIndex, float &OutSquaredDistance) const
Definition TangentBezierSpline.h:1660
static TTangentBezierSpline CreateLine(const ValueType &StartPoint, const ValueType &EndPoint)
Definition TangentBezierSpline.h:270
FTangentBezierControlPoint GetControlPoint(int32 Index) const
Definition TangentBezierSpline.h:1003
bool AddControlPoints(const TArray< FTangentBezierControlPoint > &Points, bool bAppend=true)
Definition TangentBezierSpline.h:644
bool IsAutoTangent(int32 Index) const
Definition TangentBezierSpline.h:1362
int32 SetParameter(int32 Index, float NewParameter)
Definition TangentBezierSpline.h:1397
const TArray< FKnot > & GetKnotVector() const
Definition TangentBezierSpline.h:1609
int32 FindSegmentIndex(float Parameter, float &OutLocalParam) const
Definition TangentBezierSpline.h:1425
virtual TUniquePtr< ISplineInterface > Clone() const override
Definition TangentBezierSpline.h:370
void PrependPoint(const FTangentBezierControlPoint &ControlPoint)
Definition TangentBezierSpline.h:864
int32 InsertPointAtPosition(int32 PointIndex, const FTangentBezierControlPoint &ControlPoint)
Definition TangentBezierSpline.h:815
ETangentMode GetTangentMode(int32 Index) const
Definition TangentBezierSpline.h:1630
virtual int32 GetNumberOfSegments() const override
Definition TangentBezierSpline.h:1351
void SetStationaryEndpoints(bool bInStationaryEndpoints)
Definition TangentBezierSpline.h:1649
bool SetControlPoints(const TArray< FTangentBezierControlPoint > &Points)
Definition TangentBezierSpline.h:465
void SetKnotVector(const TArray< FKnot > &InKnots)
Definition TangentBezierSpline.h:1602
float Tension
Definition TangentBezierSpline.h:1765
float GetTension() const
Definition TangentBezierSpline.h:1620
void SetTangentOut(int32 Index, const ValueType &NewTangent)
Definition TangentBezierSpline.h:1313
TArray< ETangentMode > TangentModes
Definition TangentBezierSpline.h:1768
virtual FInterval1f GetParameterSpace() const override
Definition TangentBezierSpline.h:1415
virtual void SetClosedLoop(bool bInClosedLoop) override
Definition TangentBezierSpline.h:394
bool IsStationaryEndpoints() const
Definition TangentBezierSpline.h:1655
void SetTangentModes(const TArray< ETangentMode > &InTangentModes)
Definition TangentBezierSpline.h:1639
static TTangentBezierSpline CreateCircleArc(const ValueType &Center, float Radius, float StartAngle, float EndAngle, int32 NumSegments=4)
Definition TangentBezierSpline.h:293
ValueType GetTangentIn(int32 Index) const
Definition TangentBezierSpline.h:1220
TTangentBezierSpline & operator=(const TTangentBezierSpline &Other)
Definition TangentBezierSpline.h:183
TTangentBezierSpline(const ValueType &StartPoint, const ValueType &EndPoint, const ValueType &StartTangent, const ValueType &EndTangent, bool bAutoTangents=false)
Definition TangentBezierSpline.h:158
bool bStationaryEndpoint
Definition TangentBezierSpline.h:1770
void UpdateBezierControlPoint(int32 Index, const ValueType &NewValue)
Definition TangentBezierSpline.h:1665
void Reparameterize(EParameterizationPolicy Policy=EParameterizationPolicy::Centripetal)
Definition TangentBezierSpline.h:1594
float GetParameter(int32 Index) const
Definition TangentBezierSpline.h:1391
bool operator==(const TTangentBezierSpline< ValueType > &Other) const
Definition TangentBezierSpline.h:254
TTangentBezierSpline(const ValueType &StartPoint, const ValueType &EndPoint)
Definition TangentBezierSpline.h:148
TSplineWrapper< TPolyBezierSpline< VALUETYPE > >::ValueType ValueType
Definition TangentBezierSpline.h:134
void SetValue(int32 Index, const ValueType &NewValue)
Definition TangentBezierSpline.h:1166
static TTangentBezierSpline CreateEllipse(const ValueType &Center, float RadiusX, float RadiusY, int32 NumSegments=4)
Definition TangentBezierSpline.h:351
int32 InsertPointAtGlobalParam(float Parameter, const FTangentBezierControlPoint &ControlPoint)
Definition TangentBezierSpline.h:741
void UpdateTangents()
Definition TangentBezierSpline.h:1433
static TTangentBezierSpline CreateCircle(const ValueType &Center, float Radius, int32 NumSegments=4)
Definition TangentBezierSpline.h:324
const FPolyBezierSpline3d & GetInternalSpline() const
Definition TangentBezierSpline.h:1615
void ModifyPoint(int32 Index, const FTangentBezierControlPoint &ControlPoint)
Definition TangentBezierSpline.h:1096
int32 InsertPointAtSegmentParam(int32 SegmentIndex, float LocalT, const FTangentBezierControlPoint &ControlPoint, EParameterizationPolicy ParameterizationPolicy=EParameterizationPolicy::Centripetal)
Definition TangentBezierSpline.h:762
virtual bool IsEqual(const ISplineInterface *OtherSpline) const override
Definition TangentBezierSpline.h:195
ValueType GetTangentOut(int32 Index) const
Definition TangentBezierSpline.h:1255
virtual void Clear() override
Definition TangentBezierSpline.h:1385
void AppendPoint(const FTangentBezierControlPoint &ControlPoint)
Definition TangentBezierSpline.h:922
int32 GetNumPoints() const
Definition TangentBezierSpline.h:1332
void UpdatePointTangents(int32 Index, ETangentMode Mode)
Definition TangentBezierSpline.h:1452
virtual float FindNearest(const ValueType &Point, float &OutSquaredDistance) const override
Definition TangentBezierSpline.h:1588
void SetTension(const float InTension)
Definition TangentBezierSpline.h:1625
virtual ~TTangentBezierSpline() override=default
virtual FInterval1f GetSegmentParameterRange(int32 SegmentIndex) const override
Definition TangentBezierSpline.h:1356
friend FArchive & operator<<(FArchive &Ar, TTangentBezierSpline &Spline)
Definition TangentBezierSpline.h:248
@ ControlPoint
Definition Visu.h:27
double Size(const T &Value)
Definition SplineMath.h:158
T GetSafeNormal(const T &Value)
Definition SplineMath.h:221
double Distance(const T &A, const T &B)
Definition SplineMath.h:186
double SizeSquared(const T &Value)
Definition SplineMath.h:172
TPolyBezierSpline< FVector3d > FPolyBezierSpline3d
Definition PolyBezierSpline.h:1947
TInterval1< float > FInterval1f
Definition BoxTypes.h:241
T DistanceSquared(const UE::Math::TVector2< T > &V1, const UE::Math::TVector2< T > &V2)
Definition VectorTypes.h:82
Definition AdvancedWidgetsModule.cpp:13
U16 Index
Definition radfft.cpp:71
static constexpr UE_FORCEINLINE_HINT T Clamp(const T X, const T MinValue, const T MaxValue)
Definition UnrealMathUtility.h:592
static UE_FORCEINLINE_HINT bool IsNearlyZero(float Value, float ErrorTolerance=UE_SMALL_NUMBER)
Definition UnrealMathUtility.h:407
Definition TangentBezierSpline.h:48
ETangentMode TangentMode
Definition TangentBezierSpline.h:59
TTangentBezierControlPoint()
Definition TangentBezierSpline.h:62
bool operator==(const TTangentBezierControlPoint &Other) const
Definition TangentBezierSpline.h:114
TTangentBezierControlPoint(const ValueType &InPosition)
Definition TangentBezierSpline.h:71
friend FArchive & operator<<(FArchive &Ar, TTangentBezierControlPoint &Point)
Definition TangentBezierSpline.h:108
ValueType TangentOut
Definition TangentBezierSpline.h:56
void Serialize(FArchive &Ar)
Definition TangentBezierSpline.h:92
ValueType TangentIn
Definition TangentBezierSpline.h:53
ValueType Position
Definition TangentBezierSpline.h:50
TTangentBezierControlPoint(const ValueType &InPosition, const ValueType &InTangentIn, const ValueType &InTangentOut, ETangentMode InTangentMode)
Definition TangentBezierSpline.h:80