📄 convexhull2d.as
字号:
/**
* project3D Engine
* @author John Sword
* @version 2 - AS3
*/
package engine.geom
{
import flash.display.Graphics;
public class convexHull2D
{
public var screenZ:Number;
public var H:Array = [];
public var k:int = 10; // the approximation accuracy (large k = more accurate)
private static var cP:Vector; // the current point being considered
/*
public var minX:int;
public var maxX:int;
public var minY:int;
public var maxY:int;
public var minZ:int;
public var maxZ:int;
public var boundlines:Array = [];
*/
public function convexHull2D ()
public function build ( P:Array ) : int
{
var n:int = P.length;
if ( n < 3 ) return 0;
// the output array H[] will be used as the stack
var bot:int=0, top:int=(-1), // indices for bottom and top of the stack
i:int; // array scan index
// Get the indices of points with min x-coord and min|max y-coord
var minmin:int = 0, minmax:int, xmin:int = P[0].x;
for (i=1; i<n; i++) {
if (P[i].x != xmin) break;
minmax = i-1;
if (minmax == n-1) { // degenerate case: all x-coords == xmin
H[++top] = P[minmin];
if (P[minmax].y != P[minmin].y) // a nontrivial segment
H[++top] = P[minmax];
H[++top] = P[minmin]; // add polygon endpoint
return top+1;
}
}
// Get the indices of points with max x-coord and min|max y-coord
var maxmin:int, maxmax:int = n-1, xmax:int = P[n-1].x;
for (i=n-2; i>=0; i--) {
if (P[i].x != xmax) break;
maxmin = i+1;
}
// Compute the lower hull on the stack H
H[++top] = P[minmin]; // push minmin point onto stack
i = minmax;
while (++i <= maxmin)
{
// the lower line joins P[minmin] with P[maxmin]
if (isLeft( P[minmin], P[maxmin], P[i]) >= 0 && i < maxmin)
continue; // ignore P[i] above or on the lower line
while (top > 0) // there are at least 2 points on the stack
{
// test if P[i] is left of the line at the stack top
if (isLeft( H[top-1], H[top], P[i]) > 0)
break; // P[i] is a new hull vertex
else
top--; // pop top point off stack
}
H[++top] = P[i]; // push P[i] onto stack
}
// Next, compute the upper hull on the stack H above the bottom hull
if (maxmax != maxmin) // if distinct xmax points
H[++top] = P[maxmax]; // push maxmax point onto stack
bot = top; // the bottom point of the upper hull stack
i = maxmin;
while (--i >= minmax)
{
// the upper line joins P[maxmax] with P[minmax]
if (isLeft( P[maxmax], P[minmax], P[i]) >= 0 && i > minmax)
continue; // ignore P[i] below or on the upper line
while (top > bot) // at least 2 points on the upper stack
{
// test if P[i] is left of the line at the stack top
if (isLeft( H[top-1], H[top], P[i]) > 0)
break; // P[i] is a new hull vertex
else
top--; // pop top point off stack
}
H[++top] = P[i]; // push P[i] onto stack
}
if (minmax != minmin)
H[++top] = P[minmin]; // push joining endpoint onto stack
return top+1;
}
public function buildApproximate ( P:Array ) : int
{
var n:int = P.length, i:int, b:int,
minmin:int=0, minmax:int=0,
maxmin:int=0, maxmax:int=0,
xmin:int = P[0].screen.x, xmax:int = P[0].screen.x,
bot:int=0, top:int=(-1); // indices for bottom and top of the stack
//var cP:Vector; // the current point being considered
if ( n < 3 ) return 0;
// Get the points with (1) min-max x-coord, and (2) min-max y-coord
for (i=1; i<n; i++) {
cP = P[i].screen;
if (cP.x <= xmin) {
if (cP.x < xmin) { // new xmin
xmin = cP.x;
minmin = minmax = i;
}
else { // another xmin
if (cP.y < P[minmin].screen.y)
minmin = i;
else if (cP.y > P[minmax].screen.y)
minmax = i;
}
}
if (cP.x >= xmax) {
if (cP.x > xmax) { // new xmax
xmax = cP.x;
maxmin = maxmax = i;
}
else { // another xmax
if (cP.y < P[maxmin].screen.y)
maxmin = i;
else if (cP.y > P[maxmax].screen.y)
maxmax = i;
}
}
}
if (xmin == xmax) { // degenerate case: all x-coords == xmin
H[++top] = P[minmin].screen; // a point, or
if (minmax != minmin) // a nontrivial segment
H[++top] = P[minmax].screen;
return top+1; // one or two points
}
// Next, get the max and min points in the k range bins
var B:Array = new Array(); // first allocate the bins
B[0] = new Object();
B[0].min = minmin; B[0].max = minmax; // set bin 0
B[k+1] = new Object();
B[k+1].min = maxmin; B[k+1].max = maxmax; // set bin k+1
for (b=1; b<=k; b++) { // initially nothing is in the other bins
B[b] = new Object();
B[b].min = B[b].max = null;
}
for (b, i=0; i<n; i++) {
cP = P[i].screen;
if (cP.x == xmin || cP.x == xmax) // already have bins 0 and k+1
continue;
// check if a lower or upper point
if (isLeft( P[minmin].screen, P[maxmin].screen, cP) < 0) { // below lower line
b = int(( k * (cP.x - xmin) / (xmax - xmin) ) + 1); // bin #
if (B[b].min == null) // no min point in this range
B[b].min = i; // first min
else if (cP.y < P[B[b].min].screen.y)
B[b].min = i; // new min
continue;
}
if (isLeft( P[minmax].screen, P[maxmax].screen, cP) > 0) { // above upper line
b = int((k * (cP.x - xmin) / (xmax - xmin) ) + 1); // bin #
if (B[b].max == null) // no max point in this range
B[b].max = i; // first max
else if (cP.y > P[B[b].max].screen.y)
B[b].max = i; // new max
continue;
}
}
// Now, use the chain algorithm to get the lower and upper hulls
// the output array H[] will be used as the stack
// First, compute the lower hull on the stack H
for (i=0; i <= k+1; ++i)
{
if (B[i].min == null) // no min point in this range
continue;
cP = P[ B[i].min ].screen; // select the current min point
while (top > 0) // there are at least 2 points on the stack
{
// test if current point is left of the line at the stack top
if (isLeft( H[top-1], H[top], cP) > 0)
break; // cP is a new hull vertex
else
top--; // pop top point off stack
}
H[++top] = cP; // push current point onto stack
}
// Next, compute the upper hull on the stack H above the bottom hull
if (maxmax != maxmin) // if distinct xmax points
H[++top] = P[maxmax].screen; // push maxmax point onto stack
bot = top; // the bottom point of the upper hull stack
for (i=k; i >= 0; --i)
{
if (B[i].max == null) // no max point in this range
continue;
cP = P[ B[i].max ].screen; // select the current max point
while (top > bot) // at least 2 points on the upper stack
{
// test if current point is left of the line at the stack top
if (isLeft( H[top-1], H[top], cP) > 0)
break; // current point is a new hull vertex
else
top--; // pop top point off stack
}
H[++top] = cP; // push current point onto stack
}
if (minmax != minmin)
H[++top] = P[minmin].screen; // push joining endpoint onto stack
return top+1; // # of points on the stack
}
public function contains ( x:int, y:int ) : Boolean
{
var hlen:int = H.length;
if (hlen < 3) return false;
var p3:Vector = new Vector(x,y,0);
var i:int,t:int;
for (i = 0; i < hlen; i++)
{
t = isLeft( H[i],H[(i+1)%hlen],p3 );
if ( t < 0 ) return false;
}
return true;
}
public function render ( g:Graphics ) : void
{
var hlen:int = H.length;
if (hlen < 3) return;
var v:Vector = H[0];
g.lineStyle( 5, 0xFF0000, 1);
g.moveTo( v.x,v.y );
for (var i:int = 1; i < hlen; i++)
{
v = H[i];
g.lineTo(v.x, v.y);
}
g.lineStyle();
}
/*
public function fromScreenCoords ( P:Array ) : Array
{
var Q:Array = [];
var i:Vertex;
var v:Vector,b:Vector;
for each ( i in P )
{
if ( i.visible )
{
v = i.screen;
Q.push( v );
}
}
/*
for each ( i in P )
{
if ( i.visible )
{
v = i.screen;
Q.push( v );
if (!b)
b = v
else
if (b.y < v.y)
b = v;
else
if (b.y == v.y)
if (b.x < v.x)
b = v;
}
}
var xx:int,yy:int;
for each (v in Q)
{
xx = (v.x - b.x);
yy = (v.y - b.y);
if ( xx && yy ) v.num = xx / yy;
else v.num = 0;
}
Q.sortOn("num", Array.NUMERIC);
return Q;
}
*/
private function isLeft ( P0:Vector, P1:Vector, P2:Vector ) : Number
{
return (P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y - P0.y);
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -