2013-04-17 12 views
16

Wprowadzam sześcienną logikę krzywej Beziera w jednej z aplikacji Android.Znajdź nowy punkt kontrolny, gdy zmiana punktu końcowego w sześciennej krzywej Beziera

Zaimplementowałem sześcienny kod krzywej Beziera na płótnie w onDraw widoku niestandardowego.

// Path to draw cubic bezier curve 
Path cubePath = new Path(); 

// Move to startPoint(200,200) (P0) 
cubePath.moveTo(200,200); 

// Cubic to with ControlPoint1(200,100) (C1), ControlPoint2(300,100) (C2) , EndPoint(300,200) (P1) 
cubePath.cubicTo(200,100,300,100,300,200); 

// Draw on Canvas 
canvas.drawPath(cubePath, paint); 

Wizualizuję powyższy kod na poniższym obrazku.

Output of above code

[Aktualizacja]

Logic for selecting first control points, I've taken , 
baseX = 200 , baseY = 200 and curve_size = X of Endpoint - X of Start Point 

Start Point  : x = baseX and y = baseY 
Control Point 1 : x = baseX and y = baseY - curve_size 
Control Point 2 : x = baseX + curve_size and y = baseY - curve_size 
End Point  : x = baseX + curve_size and y = baseY 

Chcę umożliwić zmianę punktu końcowego powyżej krzywej, a na podstawie nowych punktów końcowych, ja unieważnić płótna.

Problem polega na tym, że Krzywa jest utrzymywana przez dwa punkty kontrolne, które należy ponownie przeliczyć po zmianie w punkcie końcowym.

jak, po prostu chcę, aby znaleźć nowe punkty kontrolne przy EndPoint zmiana (300,200) do (250,250)

jak w poniższym obrazie:

New Image

Proszę mi pomóc obliczyć dwa nowe Punkty kontrolne oparte na nowym punkcie końcowym, którego krzywa kształtu będzie zachowywać się tak samo jak poprzedni punkt końcowy.

odsyłam poniższych linków referencyjnych podczas poszukiwań:

http://pomax.github.io/bezierinfo/

http://jsfiddle.net/hitesh24by365/jHbVE/3/

http://en.wikipedia.org/wiki/B%C3%A9zier_curve

http://cubic-bezier.com/

dowolny link referencyjny docenione również w odpowiedzi na to pytanie.

+1

jak ty obliczania pierwsze dwa punkty kontrolne? Czy próbujesz rysować zgodnie ze zdarzeniem ruchu użytkownika? –

+1

@ArunCThomas: Zaktualizowałem Pytanie z logiką wyboru domyślnego punktu kontrolnego –

+0

Jestem prawie pewien, że omówię to w http: //pomax.github.io/bezierinfo/# polybezier =) –

Odpowiedz

12

zmiana punktu końcowego oznacza dwie rzeczy, obrót wzdłuż P1 i współczynnik skalowania.

Współczynnik skalowania (nazwijmy go S) DŁ (P1 - P0)/len (p2 - P0)

dla współczynnika rotacji (pozwala wywołać r) i odłożyć do Calculating the angle between three points in android, co daje również implementacja specyficzna dla platformy, ale możesz sprawdzić poprawność, skalując/rotationg p1 w stosunku do p0, i powinieneś otrzymać p2 jako wynik.

następnie zastosować skalowanie i obrót względem p0 do c1 i c2. dla wygody zadzwonię do nowego c1 'd1' i nowego d2.

d1 = rot(c1 - p0, factor) * s + p0 
d2 = rot(c2 - p0, factor) * s + p0 

zdefiniować kilka Pseudokod Rot() (obrót http://en.wikipedia.org/wiki/Rotation_%28mathematics%29)

rot(point p, double angle){ 
    point q; 
    q.x = p.x * cos(angle) - p.y * sin(angle); 
    q.y = p.x * sin(angle) + p.y * cos(angle); 
} 

krzywą beziera teraz skalowane i obracany w stosunku do P0, P1 zmieniono na P2

+1

Proszę, popraw mnie, że współczynnik = r. –

+0

"współczynnik" jak w mojej odpowiedzi jest czynnikiem skalującym, więc kwantyfikuje motykę wielokrotnie dłuższą/krótszą linię p1-p3 to linia p1-p2. – nido

+0

Ok, współczynnik służy tylko do zwiększenia lub zmniejszenia powierzchni krzywej w oparciu o skalę nowego punktu. –

6

Najpierw Chciałbym poprosić o zapoznanie się z następującymi artykułami:

  1. Bezier Curves
  2. Why B-Spline Curve
  3. B-Spline Curve Summary

Co próbujesz wdrożenia jest odcinkowo kompozytowych krzywa Béziera. Ze strony Podsumowanie dla n punktów kontrolnych (włączając początek/koniec) otrzymujesz (n - 1)/3 fragmenty krzywych Béziera.

Punkty kontrolne kształtują krzywą dosłownie. Jeśli nie dasz odpowiednich punktów kontrolnych z nowym punktem, nie będziesz w stanie stworzyć płynnie połączonej krzywej Beziera. Generowanie ich nie będzie działać, ponieważ jest zbyt skomplikowane i nie ma powszechnie akceptowanego sposobu.

Jeśli nie masz/chcesz podawać dodatkowych punktów kontrolnych, powinieneś użyć splajnu Catmull-Rom, który przechodzi przez wszystkie punkty kontrolne i będzie miał ciągłą wartość C1 (pochodna jest ciągła w dowolnym punkcie krzywej).

Odnośniki dla Catmull Rom Spline w języku Java/Android:

Konkluzja jest jeśli nie masz punkty kontrolne nie używają sześciennej krzywej Beziera. Generowanie ich jest problemem, a nie rozwiązaniem.

6

Wygląda na to, że tutaj obracasz i skalujesz kwadrat, na którym znasz dwa dolne punkty i musisz obliczyć pozostałe dwa. Dwa znane punkty tworzą dwa trójkąty z pozostałymi dwoma, więc musimy po prostu znaleźć trzeci punkt w trójkącie. Supose koniec punktem jest x1, y1:

PointF c1 = calculateTriangle(x0, y0, x1, y1, true); //find left third point 
PointF c2 = calculateTriangle(x0, y0, x1, y1, false); //find right third point 

cubePath.reset(); 
cubePath.moveTo(x0, y0); 
cubePath.cubicTo(c1.x, c1.y, c2.x, c2.y, x1, y1); 


private PointF calculateTriangle(float x1, float y1, float x2, float y2, boolean left) { 
       PointF result = new PointF(0,0); 
       float dy = y2 - y1; 
       float dx = x2 - x1; 
       float dangle = (float) (Math.atan2(dy, dx) - Math.PI /2f); 
       float sideDist = (float) Math.sqrt(dx * dx + dy * dy); //square 
       if (left){ 
        result.x = (int) (Math.cos(dangle) * sideDist + x1); 
        result.y = (int) (Math.sin(dangle) * sideDist + y1);      
       }else{ 
        result.x = (int) (Math.cos(dangle) * sideDist + x2); 
        result.y = (int) (Math.sin(dangle) * sideDist + y2); 
       } 
       return result; 
      } 

...

Nie ma innego sposobu na to, gdzie to nie ma znaczenia, ile punktów trzeba się pomiędzy pierwszym a ostatnim punktem w zrobić ścieżkę lub zdarzenie jej kształt.

//Find scale 
Float oldDist = (float) Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); 
Float newDist = (float) Math.sqrt((x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0)); 
Float scale = newDist/oldDist; 

//find angle 
Float oldAngle = (float) (Math.atan2(y1 - y0, x1 - x0) - Math.PI /2f); 
Float newAngle = (float) (Math.atan2(y2 - y0, x2 - x0) - Math.PI /2f); 
Float angle = newAngle - oldAngle; 

//set matrix 
Matrix matrix = new Matrix(); 
matrix.postScale(scale, scale, x0, y0); 
matrix.postRotate(angle, x0, y0); 

//transform the path 
cubePath.transform(matrix); 
4

Niewielka odmiana na sugestię przez Lumis

// Find scale 
Float oldDist = (float) Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); 
Float newDist = (float) Math.sqrt((x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0)); 
Float scale = newDist/oldDist; 

// Find angle 
Float oldAngle = (float) (Math.atan2(y1 - y0, x1 - x0)); 
Float newAngle = (float) (Math.atan2(y2 - y0, x2 - x0)); 
Float angle = newAngle - oldAngle; 

Matrix matrix = new Matrix(); 
matrix.postScale(scale, scale); 
matrix.postRotate(angle); 

float[] p = { c1.x, c1.y, c2.x, c2.y }; 
matrix.mapVectors(p); 
PointF newC1 = new PointF(p[0], p[1]); 
PointF newC2 = new PointF(p[2], p[3]); 
+1

Użyłbym macierzy.mapPoints (p). Sam o tym myślałem, ale musi to robić tylko wtedy, gdy chce używać punktów kontrolnych do czegoś innego lub przechowywać je do późniejszego wykorzystania, ponieważ zawsze mogą one pochodzić z p0, p1 i p3. – Lumis

Powiązane problemy