void FBehaviorTreeConnectionDrawingPolicy::DrawConnection(int32 LayerId, const FVector2D& Start, const FVector2D& End, const FLinearColor& InColor, float Thickness, bool bDrawBubbles) { const FVector2D& P0 = Start; const FVector2D& P1 = End; const FVector2D Delta = End-Start; const FVector2D NormDelta = Delta.SafeNormal(); const FVector2D P0Tangent = NormDelta; const FVector2D P1Tangent = NormDelta; // Draw the spline itself FSlateDrawElement::MakeDrawSpaceSpline( DrawElementsList, LayerId, P0, P0Tangent, P1, P1Tangent, ClippingRect, Thickness, ESlateDrawEffect::None, InColor ); if (bDrawBubbles) { // This table maps distance along curve to alpha FInterpCurve<float> SplineReparamTable; float SplineLength = MakeSplineReparamTable(P0, P0Tangent, P1, P1Tangent, SplineReparamTable); // Draw bubbles on the spline const float BubbleSpacing = 64.f * ZoomFactor; const float BubbleSpeed = 192.f * ZoomFactor; const FVector2D BubbleSize = BubbleImage->ImageSize * ZoomFactor * 0.1f * Thickness; float Time = (FPlatformTime::Seconds() - GStartTime); const float BubbleOffset = FMath::Fmod(Time * BubbleSpeed, BubbleSpacing); const int32 NumBubbles = FMath::CeilToInt(SplineLength/BubbleSpacing); for (int32 i = 0; i < NumBubbles; ++i) { const float Distance = ((float)i * BubbleSpacing) + BubbleOffset; if (Distance < SplineLength) { const float Alpha = SplineReparamTable.Eval(Distance, 0.f); FVector2D BubblePos = FMath::CubicInterp(P0, P0Tangent, P1, P1Tangent, Alpha); BubblePos -= (BubbleSize * 0.5f); FSlateDrawElement::MakeBox( DrawElementsList, LayerId, FPaintGeometry( BubblePos, BubbleSize, ZoomFactor ), BubbleImage, ClippingRect, ESlateDrawEffect::None, InColor ); } } } }
void FBlueprintProfilerConnectionDrawingPolicy::DrawPerfConnection(int32 LayerId, const FVector2D& Start, const FVector2D& End, const FScriptPerfConnectionParams& Params) { const FVector2D& P0 = Start; const FVector2D& P1 = End; const FVector2D SplineTangent = ComputeSplineTangent(P0, P1); const FVector2D P0Tangent = (Params.StartDirection == EGPD_Output) ? SplineTangent : -SplineTangent; const FVector2D P1Tangent = (Params.EndDirection == EGPD_Input) ? SplineTangent : -SplineTangent; if (Settings->bTreatSplinesLikePins) { // Distance to consider as an overlap const float QueryDistanceTriggerThresholdSquared = FMath::Square(Settings->SplineHoverTolerance + Params.WireThickness * 0.5f); // Distance to pass the bounding box cull test (may want to expand this later on if we want to do 'closest pin' actions that don't require an exact hit) const float QueryDistanceToBoundingBoxSquared = QueryDistanceTriggerThresholdSquared; bool bCloseToSpline = false; { // The curve will include the endpoints but can extend out of a tight bounds because of the tangents // P0Tangent coefficient maximizes to 4/27 at a=1/3, and P1Tangent minimizes to -4/27 at a=2/3. const float MaximumTangentContribution = 4.0f / 27.0f; FBox2D Bounds(ForceInit); Bounds += FVector2D(P0); Bounds += FVector2D(P0 + MaximumTangentContribution * P0Tangent); Bounds += FVector2D(P1); Bounds += FVector2D(P1 - MaximumTangentContribution * P1Tangent); bCloseToSpline = Bounds.ComputeSquaredDistanceToPoint(LocalMousePosition) < QueryDistanceToBoundingBoxSquared; } if (bCloseToSpline) { // Find the closest approach to the spline FVector2D ClosestPoint(ForceInit); float ClosestDistanceSquared = FLT_MAX; const int32 NumStepsToTest = 16; const float StepInterval = 1.0f / (float)NumStepsToTest; FVector2D Point1 = FMath::CubicInterp(P0, P0Tangent, P1, P1Tangent, 0.0f); for (float TestAlpha = 0.0f; TestAlpha < 1.0f; TestAlpha += StepInterval) { const FVector2D Point2 = FMath::CubicInterp(P0, P0Tangent, P1, P1Tangent, TestAlpha + StepInterval); const FVector2D ClosestPointToSegment = FMath::ClosestPointOnSegment2D(LocalMousePosition, Point1, Point2); const float DistanceSquared = (LocalMousePosition - ClosestPointToSegment).SizeSquared(); if (DistanceSquared < ClosestDistanceSquared) { ClosestDistanceSquared = DistanceSquared; ClosestPoint = ClosestPointToSegment; } Point1 = Point2; } // Record the overlap if (ClosestDistanceSquared < QueryDistanceTriggerThresholdSquared) { if (ClosestDistanceSquared < SplineOverlapResult.GetDistanceSquared()) { const float SquaredDistToPin1 = (Params.AssociatedPin1 != nullptr) ? (P0 - ClosestPoint).SizeSquared() : FLT_MAX; const float SquaredDistToPin2 = (Params.AssociatedPin2 != nullptr) ? (P1 - ClosestPoint).SizeSquared() : FLT_MAX; SplineOverlapResult = FGraphSplineOverlapResult(Params.AssociatedPin1, Params.AssociatedPin2, ClosestDistanceSquared, SquaredDistToPin1, SquaredDistToPin2); } } } } // Draw the spline itself const float WireThickness = Params.WireThickness * ZoomFactor; TArray<FSlateGradientStop> Gradients; Gradients.Add(FSlateGradientStop(FVector2D::ZeroVector, Params.WireColor)); Gradients.Add(FSlateGradientStop(FVector2D::ZeroVector, Params.WireColor2)); FSlateDrawElement::MakeDrawSpaceGradientSpline( DrawElementsList, LayerId, P0, P0Tangent, P1, P1Tangent, ClippingRect, Gradients, WireThickness, ESlateDrawEffect::None); if (Params.bDrawBubbles || (MidpointImage != nullptr)) { // This table maps distance along curve to alpha FInterpCurve<float> SplineReparamTable; const float SplineLength = MakeSplineReparamTable(P0, P0Tangent, P1, P1Tangent, SplineReparamTable); // Draw bubbles on the spline if (Params.bDrawBubbles) { const float BubbleSpacing = 64.f * ZoomFactor; const float BubbleSpeed = 192.f * ZoomFactor; float Time = (FPlatformTime::Seconds() - GStartTime); const float BubbleOffset = FMath::Fmod(Time * BubbleSpeed, BubbleSpacing); const int32 NumBubbles = FMath::CeilToInt(SplineLength/BubbleSpacing); const float SizeMin = WireThickness * 0.05f; const float SizeScale = WireThickness * 0.10f; const float SizeA = SizeMin + (Params.PerformanceData1 * SizeScale); const float SizeB = SizeMin + (Params.PerformanceData2 * SizeScale); for (int32 i = 0; i < NumBubbles; ++i) { const float Distance = ((float)i * BubbleSpacing) + BubbleOffset; if (Distance < SplineLength) { const float Alpha = SplineReparamTable.Eval(Distance, 0.f); FVector2D BubblePos = FMath::CubicInterp(P0, P0Tangent, P1, P1Tangent, Alpha); const FVector2D BubbleSize = BubbleImage->ImageSize * FMath::Lerp(SizeA, SizeB, Alpha); const FLinearColor ElementColor = FLinearColor::LerpUsingHSV(Params.WireColor, Params.WireColor2, Alpha); BubblePos -= (BubbleSize * 0.5f); FSlateDrawElement::MakeBox( DrawElementsList, LayerId, FPaintGeometry( BubblePos, BubbleSize, ZoomFactor ), BubbleImage, ClippingRect, ESlateDrawEffect::None, ElementColor ); } } } // Draw the midpoint image if (MidpointImage != nullptr) { // Determine the spline position for the midpoint const float MidpointAlpha = SplineReparamTable.Eval(SplineLength * 0.5f, 0.f); const FVector2D Midpoint = FMath::CubicInterp(P0, P0Tangent, P1, P1Tangent, MidpointAlpha); // Approximate the slope at the midpoint (to orient the midpoint image to the spline) const FVector2D MidpointPlusE = FMath::CubicInterp(P0, P0Tangent, P1, P1Tangent, MidpointAlpha + KINDA_SMALL_NUMBER); const FVector2D MidpointMinusE = FMath::CubicInterp(P0, P0Tangent, P1, P1Tangent, MidpointAlpha - KINDA_SMALL_NUMBER); const FVector2D SlopeUnnormalized = MidpointPlusE - MidpointMinusE; // Draw the arrow const FVector2D MidpointDrawPos = Midpoint - MidpointRadius; const float AngleInRadians = SlopeUnnormalized.IsNearlyZero() ? 0.0f : FMath::Atan2(SlopeUnnormalized.Y, SlopeUnnormalized.X); FLinearColor ElementColor = FLinearColor::LerpUsingHSV(Params.WireColor, Params.WireColor2, 0.5f); FSlateDrawElement::MakeRotatedBox( DrawElementsList, LayerId, FPaintGeometry(MidpointDrawPos, MidpointImage->ImageSize * ZoomFactor, ZoomFactor), MidpointImage, ClippingRect, ESlateDrawEffect::None, AngleInRadians, TOptional<FVector2D>(), FSlateDrawElement::RelativeToElement, ElementColor ); } } }
void FConnectionDrawingPolicy::DrawConnection( int32 LayerId, const FVector2D& Start, const FVector2D& End, const FLinearColor& InColor, float Thickness, bool bDrawBubbles ) { const FVector2D& P0 = Start; const FVector2D& P1 = End; const int32 Tension = FMath::Abs<int32>(Start.X - End.X); const FVector2D P0Tangent = Tension * FVector2D(1.0f, 0); const FVector2D P1Tangent = P0Tangent; // Draw the spline itself FSlateDrawElement::MakeDrawSpaceSpline( DrawElementsList, LayerId, P0, P0Tangent, P1, P1Tangent, ClippingRect, Thickness, ESlateDrawEffect::None, InColor ); if (bDrawBubbles || (MidpointImage != NULL)) { // This table maps distance along curve to alpha FInterpCurve<float> SplineReparamTable; float SplineLength = MakeSplineReparamTable(P0, P0Tangent, P1, P1Tangent, SplineReparamTable); // Draw bubbles on the spline if (bDrawBubbles) { const float BubbleSpacing = 64.f * ZoomFactor; const float BubbleSpeed = 192.f * ZoomFactor; const FVector2D BubbleSize = BubbleImage->ImageSize * ZoomFactor * 0.1f * Thickness; float Time = (FPlatformTime::Seconds() - GStartTime); const float BubbleOffset = FMath::Fmod(Time * BubbleSpeed, BubbleSpacing); const int32 NumBubbles = FMath::Ceil(SplineLength/BubbleSpacing); for (int32 i = 0; i < NumBubbles; ++i) { const float Distance = ((float)i * BubbleSpacing) + BubbleOffset; if (Distance < SplineLength) { const float Alpha = SplineReparamTable.Eval(Distance, 0.f); FVector2D BubblePos = FMath::CubicInterp(P0, P0Tangent, P1, P1Tangent, Alpha); BubblePos -= (BubbleSize * 0.5f); FSlateDrawElement::MakeBox( DrawElementsList, LayerId, FPaintGeometry( BubblePos, BubbleSize, ZoomFactor ), BubbleImage, ClippingRect, ESlateDrawEffect::None, InColor ); } } } // Draw the midpoint image if (MidpointImage != NULL) { // Determine the spline position for the midpoint const float MidpointAlpha = SplineReparamTable.Eval(SplineLength * 0.5f, 0.f); const FVector2D Midpoint = FMath::CubicInterp(P0, P0Tangent, P1, P1Tangent, MidpointAlpha); // Approximate the slope at the midpoint (to orient the midpoint image to the spline) const FVector2D MidpointPlusE = FMath::CubicInterp(P0, P0Tangent, P1, P1Tangent, MidpointAlpha + KINDA_SMALL_NUMBER); const FVector2D MidpointMinusE = FMath::CubicInterp(P0, P0Tangent, P1, P1Tangent, MidpointAlpha - KINDA_SMALL_NUMBER); const FVector2D SlopeUnnormalized = MidpointPlusE - MidpointMinusE; // Draw the arrow const FVector2D MidpointDrawPos = Midpoint - MidpointRadius; const float AngleInRadians = SlopeUnnormalized.IsNearlyZero() ? 0.0f : FMath::Atan2(SlopeUnnormalized.Y, SlopeUnnormalized.X); FSlateDrawElement::MakeRotatedBox( DrawElementsList, LayerId, FPaintGeometry(MidpointDrawPos, MidpointImage->ImageSize * ZoomFactor, ZoomFactor), MidpointImage, ClippingRect, ESlateDrawEffect::None, AngleInRadians, TOptional<FVector2D>(), FSlateDrawElement::RelativeToElement, InColor ); } } }