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)" >

P1P2P3P4

Rysowanie krzywej sterowanej

SkokOdchył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); 
}