Jak dochodzimy do rozwiązania
Na jednej z prelekcji Jonathana Rasmussona, autora książki "The Agile Samurai", oglądanej przeze mnie na YouTube, zobaczyłem poniższy rysunek. Jego intuicyjny przekaz jest tak czytelny, że nie muszę tu niczego tłumaczyć. Jednak tam rysunek był wykonany jakby odręcznie. No to pomyślałem jak napisać program, który będzie sam rysował podobne dla dowolnego kształtu sterującego. Poniżej całe rozwiązanie.
Linia kształtu czyli zmienna sterująca
Przyjmijmy, że naszą linię kształtu będzie opisywała krzywa Beziera trzeciego stopnia, którą w formacie svg deklarujemy tak:
<path d="MP1.x,P1.y CP2.x,P2.y P3.x,P3.y P4.x,P4.y" />
Znajdowanie punktu na krzywej Beziera
A = (1-t)3; B = 3*(1-t)2*t; C = 3*(1-t)*t2; D = t3; x = A * P1.x + B * P2.x + C * P3.x + D * P4.x; y = A * P1.y + B * P2.y + C * P3.y + D * P4.y;
Rysowanie krzywej sterującej
Zmiana wartości koordynat x i y punktów kontrolnych krzywej Beziera spowoduje odrysowanie jej nowego kształtu. Zwróćmy uwagę, że układ współrzędnych grafiki svg został przetransformowany do zrozumiałego przez człowieka układu osi kartezjańskich.
<g transform="translate(10,210) scale(1,-1)" >
P1 | P2 | P3 | P4 |
---|---|---|---|
Rysowanie krzywej sterowanej
Skok | Odchyłka |
---|---|
Pełny kod aplikacji
function $(id) { return document.getElementById(id); } function drawSterBezier() { var text = "M"; text += parseInt($("p1x").value)+","+parseInt($("p1y").value) + " C"; text += parseInt($("p2x").value)+","+parseInt($("p2y").value) + " "; text += parseInt($("p3x").value)+","+parseInt($("p3y").value) + " "; text += parseInt($("p4x").value)+","+parseInt($("p4y").value); $("sterBezier").setAttribute("d", text); } function drawMainBezier() { var cB = {p1x: parseInt($("p1x").value), p1y: parseInt($("p1y").value), p2x: parseInt($("p2x").value), p2y: parseInt($("p2y").value), p3x: parseInt($("p3x").value), p3y: parseInt($("p3y").value), p4x: parseInt($("p4x").value), p4y: parseInt($("p4y").value)}; var jump = parseInt($("jumpX").value); var limes = parseInt($("limesY").value); var text = "M0,0 "; var pt = {}, kt = 1; for (var i = 0; i < 100; i += jump) { pt = getPointOnCurve(i+0.33*jump,cB); pt.y = (pt.y - limes > 0) ? pt.y - limes : 0; text += "C" + pt.x + "," + pt.y * kt; pt = getPointOnCurve(i+0.67*jump,cB); pt.y = (pt.y - limes > 0) ? pt.y - limes : 0; text += " " + pt.x + "," + pt.y * kt; kt *= -1; pt = getPointOnCurve(i+jump,cB); text += " " + pt.x + ",0"; } $("mainCurve").setAttribute("d", text); } function getPointOnCurve(t, cB) { t = t/100; t = (t<0)?0:(t>1)?1:t; var x1 = calculate_bezier(t, cB.p1x, cB.p2x, cB.p3x, cB.p4x); var y1 = calculate_bezier(t, cB.p1y, cB.p2y, cB.p3y, cB.p4y); return {x:parseInt(Math.round(x1*10))/10,y:parseInt(Math.round(y1*10))/10}; } function calculate_bezier(t, p0, p1, p2, p3) { var mt = (1-t); return (mt*mt*mt*p0) + (3*mt*mt*t*p1) + (3*mt*t*t*p2) + (t*t*t*p3); }