Geometrie/Algoritmus de Casteljau

Z testwiki
Verze z 31. 3. 2023, 12:13, kterou vytvořil imported>JAnDbot (robot: kosmetické úpravy)
(rozdíl) ← Starší verze | zobrazit aktuální verzi (rozdíl) | Novější verze → (rozdíl)
Skočit na navigaci Skočit na vyhledávání

Popis

Pro řadu algoritmů s [[../Bézierova křivka|Bézierovou křivkou]] (výpočet bodu na křivce, zvýšení stupně Bézierovy křivky, rozdělení Bézierovy křivky atd.) bude základem algoritmus de Casteljau. Ukážeme si základní principy tohoto algoritmu.

Vyjádření

Výchozím algoritmem je rekurentní výpočet Bernsteinových polynomů.

Bin(u)=(1u)Bin1+uBi1n1

Vlastní algoritmus de Casteljau vypadá takto:

Pik=(1u)Pik1+uPi+1k1 kde k = 1,...,n; i = 0,...,n-k.

Výpočet bodu na Bézierově křivce

Pro výpočet bodu na Bézierově křivce můžeme použít algoritmus de Casteljau.

Když si výpočet graficky znázorníme, zjistíme, že se ve skutečnosti nejedná o nic jiného, než o postupné dělení úseček řídícího polygonu v zadaném poměru. Počet nově vzniklých bodů se v každém kroku zmenšuje o 1 a ve chvíli, kdy zůstane bod jediný, dostaneme hledaný bod křivky. Bod na Bézierově křivce můžeme rovněž vypočítat přímo pomocí vektorové rovnice Bézierovy křivky, kdy použijeme algoritmus pro výpočet Bernsteinových polynomů

Rozdělení Bézierovy křivky

Pro rozdělení Bézierovy křivky v daném bodě na dvě křivky použijeme modifikovaného algoritmu de Casteljau. Pokud chceme rozdělit Bézierovu kubiku, bude dělícím bodem bod P33 a nově vytvořené kubiky budou určeny řídícími polygony:

P1(u):P00,P11,P22,P33,P44,P55

P2(u):P55,P54,P53,P52,P51,P50

Skládání Bézierových křivek

Podmínkou hladkého spojení Bézierových křivek je zajištění požadované spojitosti (parametrické nebo geometrické) ve společném bodě těchto křivek. Mějme dánu Bézierovu křivku P=P(u), u ∈ <0,1> řídícím polygonem Pi (i=0,..,n) a Bézierovu křivku Q=Q(v), v ∈ <0,1> řídícím polygonem Qj(j=0,...,n). Předpokládejme, že platí:

P(1)=Q(0) - počáteční bod křivky Q(v) je koncovým bodem křivky P(u).

Pro tečné vektory P(1),Q(0)platí:

P(1)=m(PmPm1) Q(1)=n(Q1Q0)


Pokud chceme pro Bézierovy křivky ve společném bodě zajistit C1 spojitost (totožné tečné vektory), musí platit:

n(Q1Q0)=m(PmPm1)

Pro Pm=Q0 dostaneme z této rovnice vztah pro výpočet bodu Q1:

Q1=m/n(PmPm1)+Pm

Pokud nemají obě křivky stejnou parametrizaci, je výpočet vrcholů navazujícího polygonu (který má být C1 nebo C2 spojitý) závislý na poměru parametrizace: Δ u0 : Δ u1. Stupeň spojitosti určuje počet definovaných vrcholů nově vytvořeného řídícího polygonu. Pro skládání křivek můžeme rovněž použít upraveného algoritmu de Casteljau.

Zvýšení stupně Bézierovy křivky

Častým technickým požadavkem při modelování křivek bývá přidání dalšího bodu do řídícího polygonu tak, aby se výsledný tvar křivky nezměnil (hovoříme o zvýšení stupně aproximační křivky). Pro vyřešení tohoto problému můžeme použít modifikovaný algoritmus de Casteljau.

Pokud si vrcholy původního řídícího polygonu označíme jako P00,...,Pn0, pak pro vrcholy nového řídícího polygonu platí:

Pi1=uiPi10+(1ui)Pi0

kde

ui=i/(n+1),i=0,...,n+1

Při bližším pohledu na uvedený postup zjistíme, že neděláme nic jiného, než že původní parametr u jakoby „rozsekáme“ na n.(n+1) částí, kdy původní body jsou umístěny vždy v násobcích (n+1) a nové body umístíme do násobků n. Graficky vzato, každou úsečku původního řídícího polygonu rozdělíme na (n+1) stejných částí a pak po každých n dílcích umístíme bod nový.

Algoritmizace

Vrací bod na Bézierově křivce v obecném parametru s

public override Vector GetPoint(double s)
{
	int n = ControlPoly.Count;
	double t = (s - StartParam)/(EndParam-StartParam);
	Vector[] points = new Vector[n];
	for (int i=0;i<n;i++)
	{
		points[i] = ((RichPoint)(ctrlPoly[i])).Locate;
	}
	for (int j=0;j<n-1;j++)
		for (int i=0;i<n-1;i++)
			points[i]=points[i].lerp(t,points[i+1]);
	return points[0];
}

Rozdělí Bézierovu křivku na dvě v bodě určeném prametrem s

// s - parametr, ve kterém se má bod rozdělit
// c1 - první nová křivka
// c2 - druhá nová křivka

public override void Split(double s, out Curve c1, out Curve c2)
{
	int n = ControlPoly.Count;
	if (n>=2)
	{
		double t = (s- StartParam)/(EndParam-StartParam);
		Vector[] pointsc1 = new Vector[n];
		Vector[] pointsc2 = new Vector[n];
		Vector[] points= new Vector[n];
		for (int i=0;i<n;i++)
		{
			points[i] = ((RichPoint)(ctrlPoly[i])).Locate;
		}
		for (int j=0;j<n-1;j++)
		{
			pointsc1[j]= points[0];
			pointsc2[n-1-j]= points[n-1-j];
			for (int i=0;i<n-1;i++)
			{
				points[i]=points[i].lerp(t,points[i+1]);
			}
		}
		pointsc1[n-1]= points[0];
		pointsc2[0]= (Vector)points[0].Clone();
		c1 = new BezierSynteticly(pointsc1);
		c2 = new BezierSynteticly(pointsc2);
	}
	else if (n==1)
	{
		c1 = new BezierSynteticly(ctrlPoly);
		Vector v = (Vector)((RichPoint)ControlPoly[0]).Locate.Clone();
		c2 = new BezierSynteticly(new Vector[]{v});
	}
	else
	{
		c1= new BezierSynteticly(new Vector[]{});
		c2= new BezierSynteticly(new Vector[]{});
	}
}

Vrátí Bezierovu křivku o stupeň vyšší

public void IncreaseDegreeByOne()
{
	double ti=0;
	int n = ControlPoly.Count;
	Vector[] points= new Vector[n];
	for (int i=0;i<n;i++)
	{
		points[i] = ((RichPoint)(ctrlPoly[i])).Locate;
	}
	for (int i=1;i<n;i++)
	{
		ti = ((double)i)/n;
		RichPoint rp = (RichPoint)this.ControlPoly[i];
		rp.Locate = points[i].lerp(ti,points[i-1]);
	}
	this.ControlPoly.Add(new RichPoint(points[n-1]));
}

public override RichPoint CreateRichPoint()
{
	return new RichPoint(new Vector(0,0));
}

Autoři

Tento text vypracovali studenti Univerzity Palackého v Olomouci katedry Matematické informatiky jako zápočtový úkol do předmětu Počítačová geometrie.