Skip to content

Instantly share code, notes, and snippets.

@samloeschen
Last active November 29, 2021 21:54
Show Gist options
  • Save samloeschen/129c92e01a99aa306ddf11681ee6234c to your computer and use it in GitHub Desktop.
Save samloeschen/129c92e01a99aa306ddf11681ee6234c to your computer and use it in GitHub Desktop.
Bezier Length Estimation
using UnityEngine;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[ExecuteInEditMode]
public class Bezier : MonoBehaviour {
public Vector3 a;
public Vector3 b;
public Vector3 c;
public Vector3 Evaluate(float t) {
Vector3 b = this.b - a;
Vector3 c = this.c - a;
return Vector3.Lerp(b*t,
Vector3.Lerp(b, c, t),
t) + a;
}
public void OnDrawGizmos() {
const float DENSITY = 100f;
Vector3 p0, p1;
for (int i = 1; i < DENSITY; i++) {
p0 = Evaluate(i / DENSITY);
p1 = Evaluate((i - 1f) / DENSITY);
Gizmos.DrawLine(p0, p1);
}
}
public float ComputeLengthAnalytic(float3 p0, float3 p1, float3 p2) {
float3 v0 = p0 - 2f*p1 + p2;
float3 v1 = 2f*p1 - 2f*p0;
float A = 4f*lengthsq(v0);
float B = 4f*dot(v0, v1);
float C = lengthsq(v1);
float sqrtABC = 2f*sqrt(A + B + C);
float A2 = sqrt(A);
float A32 = 2f*A*A2;
float C2 = 2f*sqrt(C);
float BA = B / A2;
const float EPSILON = 1e-4f;
bool isLinear = A2 <= EPSILON ||
C2 <= EPSILON ||
BA + C2 <= EPSILON;
if (isLinear) {
return length(p0 - p1) + length(p2 - p1);
}
else {
return (A32 * sqrtABC +
A2 * B * (sqrtABC - C2) +
(4f * C * A - B * B) * log((2f*A2 + BA + sqrtABC) / (BA + C2))) /
(4f*A32);
}
}
public float ComputeLengthDiscrete() {
float discreteLength = 0f;
const float DENSITY = 200f;
for (int i = 1; i <= DENSITY; i++) {
discreteLength += (Evaluate(i / DENSITY) - Evaluate((i - 1f) / DENSITY)).magnitude;
}
return discreteLength;
}
public void Update() {
Debug.Log($"analytic length: {ComputeLengthAnalytic(a, b, c)} discreteLength: {ComputeLengthDiscrete()}");
}
}
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(Bezier)), CanEditMultipleObjects]
public class BezierEditor : Editor {
protected virtual void OnSceneGUI() {
Bezier bezier = (Bezier)target;
EditorGUI.BeginChangeCheck();
bezier.a = Handles.PositionHandle(bezier.a, Quaternion.identity);
bezier.b = Handles.PositionHandle(bezier.b, Quaternion.identity);
bezier.c = Handles.PositionHandle(bezier.c, Quaternion.identity);
if (EditorGUI.EndChangeCheck()) {
Undo.RecordObject(bezier, "Moved Bezier Handle");
bezier.Update();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment