Hajsza az idővel – vektormetszéspont számoló algoritmusok


Vektorok metszéspontjait nagyon sokféleképpen lehet számolni, mi a két leglogikusabb változattal foglalkozunk. Az egyik módszer a normálos-vektoriális szorzatos ( per product ), a másik pedig az egyenes metszéspontos lesz. Kíváncsi voltam, hogy melyik hatákonyabb, hiszen olyan programnál, ahol minden pillanatban vektorok százait kell vizsgálni egyáltalán nem mindegy a sebesség. Meg amúgy sem.

A vektoriális-szorzatos módszer a következő: van két vektorunk v1 és v2. Létrehozunk egy harmadik vektort a két vektor kezdőpontjai között, és vesszük a vektorok normáljainak és a második vektor ( amire vizsgáljuk a metszést ) vektoriáis szorzatát. Ekkor két skalárt kapunk, melyek hányadosa az az arányszám, amivel többszörözve az első vektort megkapjuk a metszéspontot. Bonyolultan hangzik, és az is, már csak ezért nem tetszett már az elején sem ez a módszer :)

v1 ( bx1 , by1 );
v2 ( bx2 , by2 );
v3 ( bx3 , by3 );

n1 ( -by1 , bx1 );
n3 ( -by3 , bx3 );

dp1 = n3.v2 = -by3*bx2 + bx3*by2;
dp2 = n1.v2 = -by1*bx2 + bx1*by2;

rat = dp1/dp2;
crossv = v1*rat;

Ezzel megkapjuk a metszéspontot, de még nem tudjuk, hogy a pontot tartalmazza-e mindkét vektor.

A másik megoldás, amikor a két vizsgált vektorból egyenest csinálunk, és az ő metszéspontjaikat keressük meg az egyenletükből.

v1 ( bx1 , by1 );
v2 ( bx2 , by2 );

l1: y1 = m1*x1 + b1;
l2: y2 = m2*x2 + b2;

Ahol metszi egymást a két egyenes:
x1 = x2;
y1 = y2;

tehát

y = m1*x + b1;
y = m2*x + b2;

egyenlővé téve őket

m1*x + b1 = m2*x + b2;
x*( m1 – m2 ) = b2 – b1;

x = ( b2 – b1 ) / ( m1 – m2 );
y = m1 * x + b1;

És meg is kaptuk a metszéspontot. Sajnos itt sem tudjuk még eldönteni, hogy tartalmazza-e mindkét vektor a pontot. Ezt legegyszerűbben úgy tudjuk meg, hogy a vektorok pontjai és a metszéspont között új vektorokat hozunk létre, és ha ezek hosszabbak az eredeti vektoroknál, akkor a pont nincs bennük. De mivel mindkét fenti esetben ugyanez az eljárás, ezért bele sem veszem a sebességvizsgálatomba.

Nézzük akkor a kódot. Kell először is egy vektor osztály.

//begin
//
//
package com.milgra.geometry
{
	//
	//
	//
	public class Vector
	{
		//
		//
		//
		public var bx:Number;
		public var by:Number;
		public var sx:Number;
		public var sy:Number;
		public var r:Number;
		//
		//
		//
		public function Vector ( stx:Number , sty:Number , enx:Number , eny:Number )
		{
			//
			sx = stx;
			sy = sty;
			bx = enx - stx;
			by = eny - sty;
			//
			r = Math.sqrt( bx*bx + by*by );
			//	
		}
		//
		//
		//
	}
	//
	//
	//
}
//
//
//end

Nem pusztán irányvektorokként kezelem a vektorokat, jegyzem a kezdőpontot is, és kiszámolom a hosszt is, mivel igen gyakran szükség van rá.

Szükségünk lesz egy osztályra, ami elvégzi a vizsgálatokat a vektorokon, ő lesz a VectorOperator.

//begin
//
//
package com.milgra.geometry
{
	//
	//
	//
	import flash.utils.*;
	import flash.geom.Point;
	import com.milgra.geometry.Line;
	//
	//
	//
	public class VectorOperator
	{
		//
		//
		//
		public function VectorOperator ( )
		{
			//
			addLog( "new VectorOperation" );
			//
		}
		//
		//
		//
		public function getPerIntersection ( v1:Vector , v2:Vector ):Point
		{
			//
			addLog( "VectorOperator.getPerIntersection" );
			//
			var v3bx:Number = v2.sx - v1.sx;
			var v3by:Number = v2.sy - v1.sy;
			var perP1:Number = v3bx*v2.by - v3by*v2.bx;
			var perP2:Number = v1.bx*v2.by - v1.by*v2.bx;
			var t:Number = perP1/perP2;
			//
			var cx:Number = v1.sx + v1.bx*t;
			var cy:Number = v1.sy + v1.by*t;
			//
			return new Point( cx , cy );
			//
		}

Itt nincsen semmi ördöngösség, a bevezetőben tárgyalt egyenleteket simán lepötyögtem.

		//
		//
		//
		public function getLineIntersection ( v1:Vector , v2:Vector ):Point
		{
			//
			addLog( "VectorOperator.getLineIntersection" );
			//
			var m1:Number = v1.by / v1.bx;
			var b1:Number = v1.sy - m1*v1.sx;
			var m2:Number = v2.by / v2.bx;
			var b2:Number = v2.sy - m2*v2.sx;
			//
			var cx:Number = ( b2 - b1 )/( m1 - m2 );
			var cy:Number = m1*cx + b1;
			//
			return new Point( cx , cy );
			//
		}
		//
		//
		//
		public function addLog( logText:String ):void
		{
			//
			//trace( logText );
			//
		}
		//
		//
		//
	}
	//
	//
	//
}
//
//
//end

Ezzel meg is volnánk, és mostmár csak egy keretprogram kell, ami létrehoz random vektorokat, és rájuk ereszti a két algoritmust. Töltsétek le a forrást, raktam bele még egy vektor-kirajzoló osztályt is, további érdekesség, hogy csináltam hozzá egy skint a Flash 9 preview-val, hogy szebb legyen a dolog.

Ezután futtatnunk kell, és nyomogatni a start gombot. Ha van kedvünk, belőhetjük a vektor és a metszéspont kirajzolást is true-ra állítva, de vigyázzunk, kb 5000 ciklus fölött már lefagyasztja a böngészőt. A következőket következtethetjük :)

- minden féle gépen és platformon tesztelve a vonalas algoritmus 15-20 százalékkal gyorsabb, néhány kivételtől eltekintve.
– érdemes próbálkozni a típusok elhagyásával mindkét függvényben, ez is jelentős sebességeltérést okozhat, érdekességképpen

A teszt megtekinthető itt

Forrás itt.

MilGra

Kapcsolódó bejegyzések:

A cikket beküldte: MilGra (http://milgra.hu)

1 hozzászólás

  1. butcher says:

    Erre csak most akadtam rá, és remek cikk.

Szólj hozzá
a Hajsza az idővel – vektormetszéspont számoló algoritmusok c. bejegyzéshez

- Engedélyezett HTML elemek: <a> <em> <strong> <ul> <ol> <li>
- Forráskód beküldéséhez tedd a kódot ezek közé: <pre lang="php" line="1">Kódrészlet helye itt</pre>