保利保利裁剪

Poly in poly clipping

从这里编写原始代码:https://www.khanacademy.org/cs/i/6630051796746240

我有一个程序,其中盒子的面递归地从原始盒子中取出并剪裁到最后的面尺寸。虽然,当玩家位于盒子的中心时,多边形裁剪功能 inters 不会产生正确的形状,空白空间水平方向失败。

实习生代码

var inters=function(p0, p1){
    var p2=[], i,j,k, n0=p0.length,n1=p1.length,n2, cx,cy, dx,dy, x0,y0, x1,y1, m,m0,m1;
    for(k=0; k < n1; k+=2){
        cx=p1[k]; cy=p1[k + 1];
        dy=p1[(k + 2)%n1] - cx;
        dx=cy - p1[(k + 3)%n1];
        p2=[]; n2=0;
        for(i=0; i<n0 + 2; i+=2){
            j=i*(i - n0 >>> 31);
            x0=x1; y0=y1; m0=m1;
            x1=p0[j]; y1=p0[j + 1];
            m1=(x1 - cx)*dx + (y1 - cy)*dy;
            if(!i){continue;}
            if(m0*m1 < 0){
                m=m0/(m0 - m1);
                p2[n2    ]=x0 + (x1 - x0)*m;
                p2[n2 + 1]=y0 + (y1 - y0)*m;
                n2+=2;
            }
            if(m1 >= 0){
                p2[n2]=x1; p2[n2 + 1]=y1;
                n2+=2;
            }
        }
        if(n2 < 6){return [];}
        p0=p2; n0=n2;
    }
    return p0;
};

我不确定这是为什么,两个程序(我的和原来的)创建相同的视觉分析。

我的程序代码:

//This will work perfectly xD
//frameRate(0);
noiseSeed(0);
var keys = [];
var p = {
    x: 8.5,
    y: 8.5,
    z: 0.5,
    vx: 0,
    vy: 0,
    vz: 0,
    pit: 0,
    yaw: 0
};
p.setOrthogonal = function(){
    var sy=Math.sin(this.yaw), cy=Math.cos(this.yaw),
        sp=Math.sin(this.pit), cp=Math.cos(this.pit);
    this.v0x=sy*cp;
    this.v0y=cy*cp;
    this.v0z=-sp;
    this.v1x=cy;
    this.v1y=-sy;
    this.v1z=0;
    this.v2x=sy*sp;
    this.v2y=cy*sp;
    this.v2z=cp;
};
p.setOrthogonal();
//https://www.khanacademy.org/cs/-/4700070684278784
var clip=function(p0){
    var p1=[], i,j,k,n0=p0.length,n1, x0,y0,z0, x1,y1,z1, m,m0,m1;
    for(k=0; k < 4; k++){
        p1=[]; n1=0;
        for(i=0; i<n0 + 3; i+=3){
            j=i*(i - n0 >>> 31);
            x0=x1; y0=y1; z0=z1; m0=m1;
            x1=p0[j]; y1=p0[j + 1]; z1=p0[j + 2];
            m1=z1 - ((k & 2) - 1)*((k & 1)*x1 + (~k & 1)*y1);
            if(!i){continue;}
            if(m0*m1 < 0){
                m=m0/(m0 - m1);
                p1[n1    ]=x0 + (x1 - x0)*m;
                p1[n1 + 1]=y0 + (y1 - y0)*m;
                p1[n1 + 2]=z0 + (z1 - z0)*m;
                n1+=3;
            }
            if(m1 >= 0){
                p1[n1]=x1; p1[n1 + 1]=y1; p1[n1 + 2]=z1;
                n1+=3;
            }
        }
        if(n1 < 9){return [];}
        p0=p1; n0=n1;
    }
    return p0;
};
//https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm
var inters=function(p0, p1){
    var p2=[], i,j,k, n0=p0.length,n1=p1.length,n2, cx,cy, dx,dy, x0,y0, x1,y1, m,m0,m1;
    for(k=0; k < n1; k+=2){
        cx=p1[k]; cy=p1[k + 1];
        dy=p1[(k + 2)%n1] - cx;
        dx=cy - p1[(k + 3)%n1];
        p2=[]; n2=0;
        for(i=0; i<n0 + 2; i+=2){
            j=i*(i - n0 >>> 31);
            x0=x1; y0=y1; m0=m1;
            x1=p0[j]; y1=p0[j + 1];
            //cx+dyt = x0 + (x1-x0)t
            //cy-dxt = y0 + (y1-y0)t
            //x| t = (x0-cx)/(dy-x1+x0)
            //y| t = (y0-cy)/(y0-y1-dx)
            //(x0-cx)/(dy-x1+x0) = (y0-cy)/(y0-y1-dx)
            //(y0-y1-dx)(x0-cx) = (dy-x1+x0)(y0-cy)
            //(y0-y1-dx)(x0-cx) - (dy-x1+x0)(y0-cy) = 0
            //(x0+(x1-x0)t-cx)*dx + (y0+(y1-y0)t-cy)*dy = 0
            
            m1=(x1 - cx)*dx + (y1 - cy)*dy;
            if(!i){continue;}
            if(m0*m1 < 0){
                m=m0/(m0 - m1);
                p2[n2    ]=x0 + (x1 - x0)*m;
                p2[n2 + 1]=y0 + (y1 - y0)*m;
                n2+=2;
            }
            if(m1 >= 0){
                p2[n2]=x1; p2[n2 + 1]=y1;
                n2+=2;
            }
        }
        if(n2 < 6){return [];}
        p0=p2; n0=n2;
    }
    return p0;
};
var World = (function(){
    //some stuff that is local to World
    var shift = 4;//binary shift
    var value = 1 << shift;//value of shift (16)
    var minus = value - 1;//can't think of cool name
    var depth = shift << 1;//z shift (32)
    var area = value*value*value;//area of chunck (4096)
    var noiseScale = 0.15;
    //create world object
    var World = function(){
        this.chunks = [];
        this.rd = [];
        this.boxFaces = [];
    };
    World.prototype.createChunk = function(ox, oy, oz){
        for(var y = 0; y < value; y ++){
            for(var x = 0; x < value; x ++){
                var cap = value*noise(x*noiseScale,y*noiseScale)|0;
                for(var z = 0; z < value; z ++){
                    this.chunks[z<<depth|y<<shift|x] = z>cap ? 1 : 0;
                }
            }
        }
    };
    World.prototype.recursiveFetching = function(cx, cy, cz, poly, step){
        if(step>10){return;}
        var dx,dy,dz,xp,yp,zp,x,y,z,i,j,j,k,m,nx,ny,nz,v;
        dx = cx - p.x;
        dy = cy - p.y;
        dz = cz - p.z;
        for(i = 0; i < 5; i ++){
            //cull backface
            if([dx, dy, dz][i >> 1]*(1 - (i*2 & 2)) + (~i & 1) < 0){continue;}
            
            //create face
            var poly3=[];
            for(j=0; j < 4; j++){
                k = i*4 + j;
                xp=dx+(0x96960f>>k&1);
                yp=dy+(0x330f69>>k&1);
                zp=dz+(0x0fcccc>>k&1);
                x=xp*p.v1x+yp*p.v1y+zp*p.v1z;
                y=xp*p.v2x+yp*p.v2y+zp*p.v2z;
                z=xp*p.v0x+yp*p.v0y+zp*p.v0z;
                poly3[j*3    ]=x;
                poly3[j*3 + 1]=y;
                poly3[j*3 + 2]=z;
            }
            
            //check if behind camera
            if((dx+0.5)*p.v0x + (dy+0.5)*p.v0y + (dz+0.5)*p.v0z < 2){
                poly3=clip(poly3);
                if(!poly3 || poly3.length < 9){continue;}
            }
            
            //convert to clip space
            var poly2 = [];
            for(j=k=0; j<poly3.length; j+=3, k+=2){
                m = 400/poly3[j + 2];
                poly2[k] = 200 + m*poly3[j];
                poly2[k + 1] = 200 + m*poly3[j + 1];
            }
            
            //constrain to poly
            poly2=inters(poly2, poly);
            if(!poly2 || poly2.length < 6){continue;}
            
            //area of polygon
            for(m=j=0, k=poly2.length; j < k; j+=2){
                m+=(poly2[j] + poly2[(j + 2)%k])*(poly2[(j + 3)%k] - poly2[j + 1]);
            }
            if(m <= 1e-4){continue;}
            
            //check value of border
            j = i << 1;
            nx = cx+(0x552>>j&3)-1;
            ny = cy+(0x525>>j&3)-1;
            nz = cz+(0x255>>j&3)-1;
            v = this.chunks[nz<<depth|ny<<shift|nx];
            if(((nx|ny|nz)>>shift)||v){
                if((nx|ny|nz)>>shift){
                    //this.rd.push([poly2.slice(),1,6]);
                }else{
                    this.rd.push([poly2.slice(),v,i]);
                }
                continue;
            }
            this.recursiveFetching(nx,ny,nz,poly2.slice(),step+1);
        }
    };
    World.prototype.raster = function(){
        this.rd = [];
        this.boxFaces = [];
        this.recursiveFetching(p.x|0,p.y|0,p.z|0,[0,0,400,0,400,400,0,400],0);
        for(var i = 0; i < this.rd.length; i ++){
            var data = this.rd[i];
            var poly = data[0];
            var ef = 255*data[2]/6;
            stroke(0,0,0);
            switch(data[1]){
                case 1:
                fill(ef, 0, 0);
                //stroke(ef,0,0);
                break;
                case 2:
                fill(0, 0, ef);
                //stroke(0,0,ef);
                break;
            }
            beginShape();
            for(var j = 0; j < poly.length; j +=2){
                vertex(poly[j],poly[j+1]);
            }
            endShape(CLOSE);
        }
    };
    World.prototype.getValue = function(x, y, z){
        return this.chunks[z<<depth|y<<shift|x];
    };
    //return world object
    return World;
})();
var world = new World();
world.createChunk();
draw = function() {
    background(0,150,255);
    world.raster();
    var newx = p.x, newy = p.y, newz = p.z;
    newz += p.vz;
    var spd = 0.1;
    if(keys[UP]||keys.w){
        newx += p.v0x*spd;
        newy += p.v0y*spd;
    }
    if(keys[DOWN]||keys.s){
        newx -= p.v0x*spd;
        newy -= p.v0y*spd;
    }
    if(keys[LEFT]||keys.a){
        newx -= p.v0y*spd;
        newy += p.v0x*spd;
    }
    if(keys[RIGHT]||keys.d){
        newx += p.v0y*spd;
        newy -= p.v0x*spd;
    }
    newx = constrain(newx, 0.1, 15.9);
 newy = constrain(newy, 0.1, 15.9);
 newz = constrain(newz, 0.1, 15.9);
    var nx = newx|0, ny = newy|0, nz = newz|0;
    var cx = p.x|0, cy = p.y|0, cz = p.z|0;
    if(world.getValue(cx,cy,nz+1)===0){
        p.z = newz;
        p.vz += 0.01;
        cz = nz;
    }else{
        if(keys[" "]){
            p.vz = -0.15;
        }
    }
    if(world.getValue(nx,cy,cz)===0&&world.getValue(nx,cy,cz+1)===0){
        p.x = newx;
        cx = nx;
    }
    if(world.getValue(cx,ny,cz)===0&&world.getValue(nx,ny,cz+1)===0){
        p.y = newy;
        cy = ny;
    }
    fill(255);
    noStroke();
    textSize(20);
    rect(0,0,40,25);
    fill(0,0,0);
    textAlign(LEFT,TOP);
    text(this.__frameRate|0,10,0);
};
keyPressed = function(){
    keys[keyCode] = keys[(key.toString()).toLowerCase()] = 1;
};
keyReleased = function(){
    keys[keyCode] = keys[(key.toString()).toLowerCase()]=0;
};
mouseDragged = function(){
    p.yaw += (mouseX - pmouseX) / 20;
 p.pit += (pmouseY - mouseY) / 20;
 p.setOrthogonal();
};
<script src="https://cdn.jsdelivr.net/processing.js/1.4.8/processing.min.js"></script> 

我确定问题出在多边形剪裁中,任何不产生相同异常的新算法都将被排除在答案之外

精度错误,我四舍五入屏幕坐标来解决问题。