/**
* Copyright mutantleg ( http://wonderfl.net/user/mutantleg )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/4xB4
*/
package {
import flash.events.Event;
import flash.display.Sprite;
public class FlashTest extends Sprite {
public var myTri:Tri = new Tri();
public var myRes:CutResultTri = new CutResultTri();
public function FlashTest() {
myTri.v0.setValue(60, 50, 0);
myTri.v1.setValue(300, 60, 0);
myTri.v2.setValue(140, 200, 0);
stage.addEventListener(Event.ENTER_FRAME, onEnter);
}//ctor
public function onEnter(e:Event):void
{
graphics.clear();
graphics.lineStyle(1, 0);
graphics.moveTo(myTri.v0.cx, myTri.v0.cy);
graphics.lineTo(myTri.v1.cx, myTri.v1.cy);
graphics.lineTo(myTri.v2.cx, myTri.v2.cy);
graphics.lineTo(myTri.v0.cx, myTri.v0.cy);
var bCut:Boolean;
var nx:Number;
var ny:Number;
var px:Number;
var py:Number;
nx = 1;
ny = 0;
px = mouseX;
py = mouseY;
bCut = cutTriByPlane(myTri, px, py, 0, nx, ny, 0, myRes);
graphics.drawCircle(px, py, 4);
graphics.moveTo(px, py);
graphics.lineTo(px+nx*10,py+ny*10);
graphics.moveTo(px-ny*300,py+nx*300);
graphics.lineTo(px+ny*300,py-nx*300);
if (bCut == false) { return; }
var tri:Tri;
tri = myRes.t0;
graphics.beginFill(0xFF0000, 0.5);
graphics.moveTo(tri.v0.cx, tri.v0.cy);
graphics.lineTo(tri.v1.cx, tri.v1.cy);
graphics.lineTo(tri.v2.cx, tri.v2.cy);
graphics.lineTo(tri.v0.cx, tri.v0.cy);
graphics.endFill();
tri = myRes.t1;
graphics.beginFill(0x00FF00, 0.5);
graphics.moveTo(tri.v0.cx, tri.v0.cy);
graphics.lineTo(tri.v1.cx, tri.v1.cy);
graphics.lineTo(tri.v2.cx, tri.v2.cy);
graphics.lineTo(tri.v0.cx, tri.v0.cy);
graphics.endFill();
if (myRes.num > 2)
{
tri = myRes.t2;
graphics.beginFill(0x0000FF, 0.5);
graphics.moveTo(tri.v0.cx, tri.v0.cy);
graphics.lineTo(tri.v1.cx, tri.v1.cy);
graphics.lineTo(tri.v2.cx, tri.v2.cy);
graphics.lineTo(tri.v0.cx, tri.v0.cy);
graphics.endFill();
}
}//onenter
public function flipTri(t:Tri):void
{
var tmp:Vert;
tmp = t.v1;
t.v1 = t.v2;
t.v2 = tmp;
}//fliptri
//arrays used by the function, so we dont make new objects everytime we call it
public var vecUp:Array = [new Vert(), new Vert(), new Vert(), new Vert()];
public var vecDown:Array = [new Vert(), new Vert(), new Vert(), new Vert()];
public function cutTriByPlane(tri:Tri, //triangle to cut
px:Number, py:Number, pz:Number, //plane position
nx:Number, ny:Number, nz:Number, //plane normal
ret:CutResultTri):Boolean
{
if (ret == null) { return false; }
/*
ok, so what's happenning here is that:
- first we determine if the triangle can be cut at all
or if there is no point to it:
(we cannot cut it, if the triangle is exactly on the plane,
or more than one vertex is on the plane,
or if all the vertices are on one side of the plane)
- then we check that which edges are cut by the plane
- we generate new vertices where the plane cuts an edge
- and we build triangles based on these new vertices
(we got an easy job because there will be at most 4 vertices on either side of the plane)
(also we got 2 cases: either we need to make two new triangles, that is if a vertex is on the plane,
or three new if the plane cuts two edges)
- for the newly generated triangles we check if their normal is roughly the
same as the cut triangle's normal, if not we flip it
and thats it, then the hard and messy part is somehow returning
our newly generated triangles
(of course in flash the easiest way would be to return them in an array and call it a day)
*/
var v0:Vert;
var v1:Vert;
var v2:Vert;
var itu:int;
var itd:int;
var dist0:Number;
var dist1:Number;
var dist2:Number;
var nump:int;
var numneg:int;
var ab:int; //01
var ac:int; //02
var bc:int; //12
var abd:Number;
var acd:Number;
var bcd:Number;
var t:Number;
var dot:Number;
itu = 0;
itd = 0;
nump = 0;
numneg = 0;
v0 = tri.v0;
v1 = tri.v1;
v2 = tri.v2;
//distance of the triangle's points to the plane
dist0 = (v0.cx - px)*nx + (v0.cy - py)*ny + (v0.cz - pz)*nz;
dist1 = (v1.cx - px)*nx + (v1.cy - py)*ny + (v1.cz - pz)*nz;
dist2 = (v2.cx - px)*nx + (v2.cy - py)*ny + (v2.cz - pz)*nz;
if (dist0 == 0) { nump += 1; }
if (dist1 == 0) { nump += 1; }
if (dist2 == 0) { nump += 1; }
//an edge or the whole triangle is on the plane: don't cut
if (nump > 1) { return false; }
if (dist0 < 0) { numneg += 1; }
if (dist1 < 0) { numneg += 1; }
if (dist2 < 0) { numneg += 1; }
//if all points are on one side of the plane: nothing to cut
if (numneg == 3 || numneg == 0) { return false; }
//edges length
abd = Math.abs(dist0) + Math.abs(dist1);
acd = Math.abs(dist0) + Math.abs(dist2);
bcd = Math.abs(dist1) + Math.abs(dist2);
//check which edges the plane has cut
ab = ((dist0 < 0 && dist1 > 0) || (dist0 > 0 && dist1 < 0 ) ) ? 1 : 0;
ac = ((dist0 < 0 && dist2 > 0) || (dist0 > 0 && dist2 < 0 ) ) ? 1 : 0;
bc = ((dist1 < 0 && dist2 > 0) || (dist1 > 0 && dist2 < 0 ) ) ? 1 : 0;
//put in already existing vertices to the resulting tris list
//based on their distance to the plane
// (i guess you could put together some ifs and make it more optimised
// but i want it to look clean, so its easier to see what is happening here)
var dv:Vert;
if (dist0 > 0)
{
dv = vecUp[itu];
dv.copyVert(v0);
itu += 1;
}//endif
if (dist1 > 0)
{
dv = vecUp[itu];
dv.copyVert(v1);
itu += 1;
}//endif
if (dist2 > 0)
{
dv = vecUp[itu];
dv.copyVert(v2);
itu += 1;
}//endif
if (dist0 < 0)
{
dv = vecDown[itd];
dv.copyVert(v0);
itd += 1;
}//endif
if (dist1 < 0)
{
dv = vecDown[itd];
dv.copyVert(v1);
itd += 1;
}//endif
if (dist2 < 0)
{
dv = vecDown[itd];
dv.copyVert(v2);
itd += 1;
}//endif
//special case: vertex is on the plane
if (dist0 == 0)
{
dv = vecUp[itu];
dv.copyVert(v0);
itu += 1;
dv = vecDown[itd];
dv.copyVert(v0);
itd += 1;
}//endif
if (dist1 == 0)
{
dv = vecUp[itu];
dv.copyVert(v1);
itu += 1;
dv = vecDown[itd];
dv.copyVert(v1);
itd += 1;
}//endif
if (dist2 == 0)
{
dv = vecUp[itu];
dv.copyVert(v2);
itu += 1;
dv = vecDown[itd];
dv.copyVert(v2);
itd += 1;
}//endif
//generate new vertices at where the plane cuts the edge
if (ab > 0)
{
t = Math.abs(dist0) / abd;
dv = vecUp[itu];
dv.cx = v0.cx + (v1.cx - v0.cx) * t;
dv.cy = v0.cy + (v1.cy - v0.cy) * t;
dv.cz = v0.cz + (v1.cz - v0.cz) * t;
itu += 1;
dv = vecDown[itd];
dv.cx = v0.cx + (v1.cx - v0.cx) * t;
dv.cy = v0.cy + (v1.cy - v0.cy) * t;
dv.cz = v0.cz + (v1.cz - v0.cz) * t;
itd += 1;
}//endif
if (ac > 0)
{
t = Math.abs(dist0) / acd;
dv = vecUp[itu];
dv.cx = v0.cx + (v2.cx - v0.cx) * t;
dv.cy = v0.cy + (v2.cy - v0.cy) * t;
dv.cz = v0.cz + (v2.cz - v0.cz) * t;
itu += 1;
dv = vecDown[itd];
dv.cx = v0.cx + (v2.cx - v0.cx) * t;
dv.cy = v0.cy + (v2.cy - v0.cy) * t;
dv.cz = v0.cz + (v2.cz - v0.cz) * t;
itd += 1;
}//endif
if (bc > 0)
{
t = Math.abs(dist1) / bcd;
dv = vecUp[itu];
dv.cx = v1.cx + (v2.cx - v1.cx) * t;
dv.cy = v1.cy + (v2.cy - v1.cy) * t;
dv.cz = v1.cz + (v2.cz - v1.cz) * t;
itu += 1;
dv = vecDown[itd];
dv.cx = v1.cx + (v2.cx - v1.cx) * t;
dv.cy = v1.cy + (v2.cy - v1.cy) * t;
dv.cz = v1.cz + (v2.cz - v1.cz) * t;
itd += 1;
}//endif
if (nump == 1) //one vertex was on the plane, we made 2 triangles
{
ret.num = 2;
ret.t0.v0.copyVert( vecUp[0] );
ret.t0.v1.copyVert( vecUp[1] );
ret.t0.v2.copyVert( vecUp[2] );
ret.t1.v0.copyVert( vecDown[0] );
ret.t1.v1.copyVert( vecDown[1] );
ret.t1.v2.copyVert( vecDown[2] );
}
else
if (itu == 3) //triangle cut 2 edges, make 3 triangles
{
ret.num = 3;
ret.t0.v0.copyVert( vecUp[0] );
ret.t0.v1.copyVert( vecUp[1] );
ret.t0.v2.copyVert( vecUp[2] );
ret.t1.v0.copyVert( vecDown[0] );
ret.t1.v1.copyVert( vecDown[1] );
ret.t1.v2.copyVert( vecDown[2] );
ret.t2.v0.copyVert( vecDown[1] );
ret.t2.v1.copyVert( vecDown[2] );
ret.t2.v2.copyVert( vecDown[3] );
}
else //two triangles were on the other side of the plane
{
ret.num = 3;
ret.t0.v0.copyVert( vecDown[0] );
ret.t0.v1.copyVert( vecDown[1] );
ret.t0.v2.copyVert( vecDown[2] );
ret.t1.v0.copyVert( vecUp[0] );
ret.t1.v1.copyVert( vecUp[1] );
ret.t1.v2.copyVert( vecUp[2] );
ret.t2.v0.copyVert( vecUp[1] );
ret.t2.v1.copyVert( vecUp[2] );
ret.t2.v2.copyVert( vecUp[3] );
}//endif
tri.calcNormal();
ret.t0.calcNormal();
dot = (tri.norm.cx * ret.t0.norm.cx) +(tri.norm.cy * ret.t0.norm.cy) + (tri.norm.cz * ret.t0.norm.cz);
if (dot < 0) { flipTri(ret.t0); }
ret.t1.calcNormal();
dot = (tri.norm.cx * ret.t1.norm.cx) +(tri.norm.cy * ret.t1.norm.cy) + (tri.norm.cz * ret.t1.norm.cz);
if (dot < 0) { flipTri(ret.t1); }
if (ret.num > 2)
{
ret.t2.calcNormal();
dot = (tri.norm.cx * ret.t2.norm.cx) +(tri.norm.cy * ret.t2.norm.cy) + (tri.norm.cz * ret.t2.norm.cz);
if (dot < 0) { flipTri(ret.t2); }
}//endif
return true;
}//cuttriplane
}//classend
}//package
internal class Vert
{
public var cx:Number = 0;
public var cy:Number = 0;
public var cz:Number = 0;
public function setValue(x:Number, y:Number, z:Number):void
{
cx = x;
cy = y;
cz = z;
}//setvalue
public function copyVert(v:Vert):void
{
cx = v.cx;
cy = v.cy;
cz = v.cz;
}//copyvert
};//Vert
internal class Tri
{
public var v0:Vert = new Vert();
public var v1:Vert = new Vert();
public var v2:Vert = new Vert();
public var norm:Vert = new Vert();
public function calcNormal():void
{
var ax:Number;
var ay:Number;
var az:Number;
var bx:Number;
var by:Number;
var bz:Number;
var mag:Number;
ax = v0.cx - v1.cx;
ay = v0.cy - v1.cy;
az = v0.cz - v1.cz;
bx = v0.cx - v2.cx;
by = v0.cy - v2.cy;
bz = v0.cz - v2.cz;
//cross product
norm.cx = (ay * bz) - (az * by);
norm.cy = (az * bx) - (ax * bz);
norm.cz = (ax * by) - (ay * bx);
mag = Math.sqrt( (norm.cx*norm.cx) + (norm.cy*norm.cy) + (norm.cz*norm.cz) );
if (mag == 0) { return; }
norm.cx /= mag;
norm.cy /= mag;
norm.cz /= mag;
}//calcnormal
};//Tri
internal class CutResultTri
{
public var num:int = 0;
public var t0:Tri = new Tri();
public var t1:Tri = new Tri();
public var t2:Tri = new Tri();
};//triresult