Une solution analytique pour la longueur de Bézier cubique Ne semble pas exister, mais cela ne signifie pas que Codant pour une solution bon marché n'existe pas. Par pas cher, je veux dire quelque chose comme dans la gamme de 50-100 ns (ou moins).
Est-ce que quelqu'un sait quelque chose comme ça? Peut-être dans deux catégories:
1) moins d'erreur comme 1% mais un code plus lent 2) plus d'erreur comme 20% mais plus rapide?
J'ai parcouru un peu Google, mais il N'a rien trouvé qui ressemble à une solution de Nice. Seulement quelque chose comme diviser sur N segments de ligne Et additionner le N sqrt - trop lent pour plus de précision, Et probablement trop imprécis pour 2 ou 3 segments.
Y a-t-il quelque chose de mieux?
Une autre option consiste à estimer la longueur de l’arc comme la moyenne entre l’accord et le réseau de contrôle. En pratique:
Bezier bezier = Bezier (p0, p1, p2, p3);
chord = (p3-p0).Length;
cont_net = (p0 - p1).Length + (p2 - p1).Length + (p3 - p2).Length;
app_arc_length = (cont_net + chord) / 2;
Vous pouvez ensuite fractionner de manière récursive votre segment de spline en deux segments et calculer la longueur de l'arc jusqu'à la convergence. Je me suis testé et il converge assez rapidement. J'ai eu l'idée de ce forum .
Algorithme le plus simple: aplatir la courbe et calculer la distance euclidienne. Tant que vous voulez une longueur d'arc approximative, cette solution est rapide et bon marché. Étant donné les coordonnées LUT de votre courbe (vous parlez de vitesse, je suppose donc que vous les utilisez et que vous ne calculez pas constamment les coordonnées), il s'agit d'une simple boucle for avec un décompte. En code générique, avec une fonction dist
qui calcule la distance euclidienne entre deux points:
var arclength = 0,
last=LUT.length-1,
i;
for (i=0; i<last; i++) {
arclength += dist(LUT[i], LUT[i+1]);
}
Terminé. arclength
est maintenant la longueur approximative de l’arc basée sur le nombre maximum de segments que vous pouvez former dans la courbe en fonction de votre table de correspondance. Besoin de choses plus rapidement avec une plus grande erreur potentielle? Contrôler le nombre de segments.
var arclength = 0,
segCount = ...,
last=LUT.length-2,
step = last/segCount,
s, i;
for (s=0; s<=segCount; s++) {
i = (s*step/last)|0;
arclength += dist(LUT[i], LUT[i+1]);
}
C'est à peu près l'algorithme le plus simple possible qui génère encore des valeurs qui s'approchent même de la vraie longueur d'arc. Pour quelque chose de mieux, vous allez devoir utiliser des approches numériques plus coûteuses (comme la technique de quadrature de Legendre-Gauss).
Si vous voulez savoir pourquoi, tapez la section de longueur d'arc de "Introduction aux courbes de Bézier".
J'ai élaboré l'expression de longueur sous forme fermée pour un Bézier à 3 points (ci-dessous). Je n'ai pas essayé d'élaborer un formulaire fermé pour plus de 4 points. Ce serait probablement difficile ou compliqué à représenter et à gérer. Cependant, une technique d'approximation numérique telle qu'un algorithme d'intégration de Runge-Kutta ( voir mon Q & A ici pour plus de détails ) fonctionnerait plutôt bien en intégrant la formule longueur d'arc .
Voici du code Java pour la longueur d'arc d'un Bézier à 3 points, avec les points a
, b
et c
.
v.x = 2*(b.x - a.x);
v.y = 2*(b.y - a.y);
w.x = c.x - 2*b.x + a.x;
w.y = c.y - 2*b.y + a.y;
uu = 4*(w.x*w.x + w.y*w.y);
if(uu < 0.00001)
{
return (float) Math.sqrt((c.x - a.x)*(c.x - a.x) + (c.y - a.y)*(c.y - a.y));
}
vv = 4*(v.x*w.x + v.y*w.y);
ww = v.x*v.x + v.y*v.y;
t1 = (float) (2*Math.sqrt(uu*(uu + vv + ww)));
t2 = 2*uu+vv;
t3 = vv*vv - 4*uu*ww;
t4 = (float) (2*Math.sqrt(uu*ww));
return (float) ((t1*t2 - t3*Math.log(t2+t1) -(vv*t4 - t3*Math.log(vv+t4))) / (8*Math.pow(uu, 1.5)));
public float FastArcLength()
{
float arcLength = 0.0f;
ArcLengthUtil(cp0.position, cp1.position, cp2.position, cp3.position, 5, ref arcLength);
return arcLength;
}
private void ArcLengthUtil(Vector3 A, Vector3 B, Vector3 C, Vector3 D, uint subdiv, ref float L)
{
if (subdiv > 0)
{
Vector3 a = A + (B - A) * 0.5f;
Vector3 b = B + (C - B) * 0.5f;
Vector3 c = C + (D - C) * 0.5f;
Vector3 d = a + (b - a) * 0.5f;
Vector3 e = b + (c - b) * 0.5f;
Vector3 f = d + (e - d) * 0.5f;
// left branch
ArcLengthUtil(A, a, d, f, subdiv - 1, ref L);
// right branch
ArcLengthUtil(f, e, c, D, subdiv - 1, ref L);
}
else
{
float controlNetLength = (B-A).magnitude + (C - B).magnitude + (D - C).magnitude;
float chordLength = (D - A).magnitude;
L += (chordLength + controlNetLength) / 2.0f;
}
}