使用SVG实现3D图形显示,移动和旋转

来源:互联网 发布:ubuntu无法更新软件源 编辑:程序博客网 时间:2024/05/19 06:39
 
本例可以作为上文的一个补充,在这个例子中主要有
 
1,在2D中建立3D坐标
       原点 屏幕的中心点
X轴 向右
Y轴 向上
Z轴 与XY成135度角
 
2,建立3D点到2D的映射关系(多对一,即可能有多个3D点映射到一个2D点上)
 
x'=x-z*sin(theta);
              y'=y-z*cos(theta); 
             
              x"=Cx+x'=Cx+x-z*sin(theta);
              y"=Cy-y'=Cy-y+z*cos(theta);      
 
在本例中theta = PI/180 * 135
 
3, 建立三种3D对象:Point Line和Cube
 
4,实现三种对象的显示,移动和旋转,其中Line和Cube的移动和旋转都是基于其顶点坐标的改变。显示和移动比较简单,旋转比较复杂,可以参见http://local.wasp.uwa.edu.au/~pbourke/geometry/rotate/,注意其中有个小错误。
 
本例中没有实现透视,显示顺序,颜色,纹理等其它变化。
 
<?xml version="1.0" encoding="utf-8"?>
<svg id="doc" viewbox="0 0 200 200" zoomAndPan="disable" onload="init()" onresize="sizeChange()">
    
<desc>SVG test</desc>
    
<script type="text/javascript"><![CDATA[
    
    
/*
        Setup coordinate
        X axis: 
            direction: right
            origin: horizontal center of client area(Cx)
        Y axis:
            direction: up
            origin: vertical center of client area(Cy)
        Z axis:
            direction: left down, angle with X axis is 90+theta (usually 45 degree)
            
        Set 3D point(x,y,z), its display point position(x",y")
        
        x'=x-z*sin(theta);
        y'=y-z*cos(theta);    
        
        x"=Cx+x'=Cx+x-z*sin(theta);
        y"=Cy-y'=Cy-y+z*cos(theta);    
        
    
*/

    
    
    
var svgns = "http://www.w3.org/2000/svg";
    
var xlinkNS = "http://www.w3.org/1999/xlink";
    
var myNS = "http://blog.csdn.net/firefight/others";
    
    
var sin_value = 0.7071;
    
var cos_value = 0.7071;
    
    
//Transform string    
    var t1;
    
var t2;
    
    
//Draw range and center point
    var maxX,maxY,centerX,centerY;
        
    
//Speed and accelerate
    var maxSpeed = 100;
    
var maxAccelerate = 5;
    
    
//Refresh interval
    var interval = 100;
    
    
//Object number
    var count = 3;
    
var objList = new Array();
    
    
var rotateAxis = null;
    
    
///
    ///  Point class
    ///
    function Point(x, y, z)
    
{
        
this.x = x;
        
this.y = y;
        
this.z = z;
        
        
this.move = movePoint;
        
this.rotate = rotatePoint;
        
this.rotateArbitrary = rotatePointArbitrary;
    }

    
    
function movePoint(x, y, z)
    
{
        
this.x = this.x + x;
        
this.y = this.y + y;
        
this.z = this.z + z;
    }

    
    
/*
    
     z axis rotate matrix 
        cos(a) -sin(a) 0
        sin(a) cos(a) 0
        0      0      1
    
    
*/

    
function rotatePoint(angle)
    
{    
        
var cos = Math.cos(angle);
        
var sin = Math.sin(angle);
        
        
var newX, newY;
        newX 
= cos*this.x - sin*this.y;
        newY 
= sin*this.x + cos*this.y;
        
        
this.x = newX;
        
this.y = newY;
        
//this.z = this.z;
    }

    
    
/*
        Compare to rotate around z axis, rotate around an arbitrary vector is a litte complicate,
        
        x'                            x
        y'   = T^(-1) * Rx^(-1) * Ry^(-1) * Rz * Ry * Rx * T  * y   
        z'                                                      z
        1                                                       1
        
        Please refer to http://local.wasp.uwa.edu.au/~pbourke/geometry/rotate/
        
        Note: 
            the Rz matrix in artical have a little mistake, it should be
            
            cos(a) -sin(a) 0  0
            sin(a) cos(a)  0  0
            0      0       1  0
            0      0       0  1
            
    
*/

    
function rotatePointArbitrary(angle, x1, y1, z1, x2, y2, z2)
    
{
        
var ux,uy,uz;      //Unit vector through origin
        var qx1,qy1,qz1;
        
var qx2,qy2,qz2;
        
var d;        //Project length of unit vector on yz space
        
        
/* Step 1  T * Q */
        qx1 
= this.x - x1;
        qy1 
= this.y - y1;
        qz1 
= this.z - z1;
        
        
//Set line through origin point
        ux = x2 - x1;
        uy 
= y2 - y1;
        uz 
= z2 - z1;
        
        
//Normalize to unit vector
        //Normalise(&u);
        
        
var l = Math.sqrt(ux*ux + uy*uy + uz*uz);
        ux 
= ux / l;
        uy 
= uy / l;
        uz 
= uz / l;
        
        
//Get project length of unit vector on yz space
        d = Math.sqrt(uy*uy + uz*uz);
        
        
/* Step 2  Rx * Q */
        
if (d != 0
        
{
            qx2 
= qx1;
            qy2 
= qy1 * uz / d - qz1 * uy / d;
            qz2 
= qy1 * uy / d + qz1 * uz / d;
        }
 
        
else 
        
{
            qx2 
= qx1;
            qy2 
= qy1; 
            qz2 
= qz1;
        }
          
              
        
/* Step 3 Ry * Q */
        qx1 
= qx2 * d - qz2 * ux;
        qy1 
= qy2;
        qz1 
= qx2 * ux + qz2 * d;
        
        
/* Step 4 Rz * Q */
        qx2 
= qx1 * Math.cos(angle) - qy1 * Math.sin(angle);
        qy2 
= qx1 * Math.sin(angle) + qy1 * Math.cos(angle);
        qz2 
= qz1;
        
        
/* Inverse of step 3 Ry^(-1) * Q */
        qx1 
=   qx2 * d + qz2 * ux;
        qy1 
=   qy2;
        qz1 
= - qx2 * ux + qz2 * d;
        
        
/* Inverse of step 2 Rx^(-1) * Q */
        
if (d != 0
        
{
            qx2 
=   qx1;
            qy2 
=   qy1 * uz / d + qz1 * uy / d;
            qz2 
= - qy1 * uy / d + qz1 * uz / d;
        }
 
        
else 
        
{
            qx2 
= qx1;
            qy2 
= qy1; 
            qz2 
= qz1;
        }

        
        
/* Inverse of step 1 T^(-1) * Q */
        qx1 
= qx2 + x1;
        qy1 
= qy2 + y1;
        qz1 
= qz2 + z1;
        
        
this.x = qx1;
        
this.y = qy1;
        
this.z = qz1;
    }


    
///
    ///  Line class
    ///
    function Line(groupId, id, p1, p2, stroke)
    
{
        
this.groupId = groupId;
        
this.id = id;
        
this.p1 = p1;
        
this.p2 = p2;
        
this.stroke = stroke;
        
        
this.move = moveLine;
        
this.rotate = rotateLine;
        
this.rotateArbitrary = rotateLineArbitrary;
        
        
this.draw = drawLine;
        
this.erase = eraseLine;
    }

    
    
function moveLine(x, y, z)
    
{
        
this.p1.move(x, y, z);
        
this.p2.move(x, y, z);
    }

    
    
function rotateLine(angle)
    
{
        
this.p1.rotate(angle);
        
this.p2.rotate(angle);
    }

    
    
function rotateLineArbitrary(angle, x1, y1, z1, x2, y2, z2)
    
{
        
this.p1.rotateArbitrary(angle, x1, y1, z1, x2, y2, z2);
        
this.p2.rotateArbitrary(angle, x1, y1, z1, x2, y2, z2);
    }

    
    
function drawLine()
    
{
        
var displayX1 = centerX + this.p1.x - this.p1.z*sin_value;
        
var displayY1 = centerY - this.p1.y + this.p1.z*cos_value;
        
var displayX2 = centerX + this.p2.x - this.p2.z*sin_value;
        
var displayY2 = centerY - this.p2.y + this.p2.z*cos_value;
        
        
var group = svgDocument.getElementById( this.groupId );
        
        
if(group == null)
        
{
            group 
= svgDocument.createElementNS(svgns, "g" )
            group.setAttributeNS(
null,"id",this.groupId);
            svgDocument.documentElement.appendChild(group);
        }

        
        
//Erase line
        this.erase();
        
        
//Draw line
        var elem = svgDocument.createElementNS(svgns, "line" );
        elem.setAttributeNS(
null"id"this.id);
        elem.setAttributeNS(
null"x1", displayX1);
        elem.setAttributeNS(
null"y1", displayY1);
        elem.setAttributeNS(
null"x2", displayX2);
        elem.setAttributeNS(
null"y2", displayY2);
        elem.setAttributeNS(
null,"stroke",this.stroke);
        group.appendChild(elem);
    }

    
    
function eraseLine()
    
{
        
var group = svgDocument.getElementById( this.groupId );
        
        
if(group != null)
        
{
            
var elem = svgDocument.getElementById( this.id );
            
if(elem != null)
                group.removeChild(elem);
        }

    }

    
///
    ///  Line class finish
    ///
    
    
///
    ///  Cube class
    ///
    function Cube(groupId, id, length, width, height, stroke)
    
{
        
this.groupId = groupId;
        
this.id = id;
        
this.stroke = stroke;
        
        
//
        //Set cube points, length:z; width:x; height:y;
        //    
        this.a1 = new Point(0, height, length);
        
this.a2 = new Point(00, length);
        
this.b1 = new Point(width, height, length);
        
this.b2 = new Point(width, 0, length);
        
this.c1 = new Point(width, height, 0);
        
this.c2 = new Point(width, 00);
        
this.d1 = new Point(0, height, 0);
        
this.d2 = new Point(000);
                        
        
this.draw = drawCube;
        
this.move = moveCube;
        
this.rotate = rotateCube;
        
this.rotateArbitrary = rotateCubeArbitrary;
    }

    
    
function moveCube(x, y, z)
    
{
        
this.a1.move(x, y, z); 
        
this.a2.move(x, y, z); 
        
this.b1.move(x, y, z); 
        
this.b2.move(x, y, z); 
        
this.c1.move(x, y, z); 
        
this.c2.move(x, y, z); 
        
this.d1.move(x, y, z); 
        
this.d2.move(x, y, z); 
    }

    
    
function rotateCube(angle)
    
{
        
this.a1.rotate(angle); 
        
this.a2.rotate(angle); 
        
this.b1.rotate(angle); 
        
this.b2.rotate(angle); 
        
this.c1.rotate(angle); 
        
this.c2.rotate(angle); 
        
this.d1.rotate(angle); 
        
this.d2.rotate(angle); 
    }

    
    
function rotateCubeArbitrary(angle, x1, y1, z1, x2, y2, z2)
    
{
        
this.a1.rotateArbitrary(angle, x1, y1, z1, x2, y2, z2);
        
this.a2.rotateArbitrary(angle, x1, y1, z1, x2, y2, z2); 
        
this.b1.rotateArbitrary(angle, x1, y1, z1, x2, y2, z2);
        
this.b2.rotateArbitrary(angle, x1, y1, z1, x2, y2, z2);
        
this.c1.rotateArbitrary(angle, x1, y1, z1, x2, y2, z2);
        
this.c2.rotateArbitrary(angle, x1, y1, z1, x2, y2, z2);
        
this.d1.rotateArbitrary(angle, x1, y1, z1, x2, y2, z2);
        
this.d2.rotateArbitrary(angle, x1, y1, z1, x2, y2, z2);
    }

    
    
function drawCube()
    
{
        
var objList = new Array();
        
        
var line = new Line(this.groupId, this.id + "a1a2"this.a1, this.a2, this.stroke);
        objList[objList.length] 
= line;
        
var line = new Line(this.groupId, this.id + "b1b2"this.b1, this.b2, this.stroke);
        objList[objList.length] 
= line;
        
var line = new Line(this.groupId, this.id + "c1c2"this.c1, this.c2, this.stroke);
        objList[objList.length] 
= line;
        
var line = new Line(this.groupId, this.id + "d1d2"this.d1, this.d2, this.stroke);
        objList[objList.length] 
= line;
        
        
        
var line = new Line(this.groupId, this.id + "a1d1"this.a1, this.d1, this.stroke);
        objList[objList.length] 
= line;
        
var line = new Line(this.groupId, this.id + "a1b1"this.a1, this.b1, this.stroke);
        objList[objList.length] 
= line;
        
var line = new Line(this.groupId, this.id + "b1c1"this.b1, this.c1, this.stroke);
        objList[objList.length] 
= line;
        
var line = new Line(this.groupId, this.id + "c1d1"this.c1, this.d1, this.stroke);
        objList[objList.length] 
= line;    
        
        
var line = new Line(this.groupId, this.id + "a2d2"this.a2, this.d2, this.stroke);
        objList[objList.length] 
= line;
        
var line = new Line(this.groupId, this.id + "a2b2"this.a2, this.b2, this.stroke);
        objList[objList.length] 
= line;
        
var line = new Line(this.groupId, this.id + "b2c2"this.b2, this.c2, this.stroke);
        objList[objList.length] 
= line;
        
var line = new Line(this.groupId, this.id + "c2d2"this.c2, this.d2, this.stroke);
        objList[objList.length] 
= line;    
        
        
for (i in objList)
        
{
            objList[i].draw();
        }

    }

    
    
function setRange()
    
{
        maxX 
= window.innerWidth;
        maxY 
= window.innerHeight;
        centerX 
= maxX/2;
        centerY 
= maxY/2;
    }

    
    
function sizeChange()
    
{
        setRange();
        drawAxis();
        drawRotateAxis();
        refresh();
    }

    
    
function refresh()
    
{
        
var angle = Math.PI/180;
            
        
for (i in objList)
        
{    
            objList[i].rotateArbitrary(angle, 
                        rotateAxis.p1.x, rotateAxis.p1.y, rotateAxis.p1.z, 
                        rotateAxis.p2.x, rotateAxis.p2.y, rotateAxis.p2.z);
            objList[i].draw();
        }

        
        
//var group = document.getElementById( "g1" );
        //alert(printNode(group));
    }

    
    
function drawAxis()
    
{
        
//Draw axis
        var groupId = "axis";
        
var stroke = "red";
        
        
var axisLen = 300;
        
var p1 = new Point(000);
        
var p2 = new Point(axisLen, 00);
        
var p3 = new Point(0, axisLen, 0);
        
var p4 = new Point(00, axisLen);
        
        
var line = new Line(groupId, "x", p1, p2, stroke);
        line.draw();
        line 
= new Line(groupId, "y", p1, p3, stroke);
        line.draw();
        line 
= new Line(groupId, "z", p1, p4, stroke);
        line.draw();
    }

    
    
function drawRotateAxis()
    
{
        
var groupId = "axis";
        
var stroke = "blue";
        
        
var p1 = new Point(000);
        
var p2 = new Point(100100100);
        rotateAxis 
= new Line(groupId, "r", p1, p2, stroke);
        rotateAxis.draw();
    }

    
    
function init()
    
{
        
//Get range
        setRange();
        drawAxis();
        drawRotateAxis();
        
        
//Draw cube
        var cube1 = new Cube("g1""cube1"505050"yellow");
        cube1.move(
50,50,50);
        objList[objList.length] 
= cube1;
        
        
var cube2 = new Cube("g1""cube2"155020"green");
        cube2.move(
100,100,50);
        objList[objList.length] 
= cube2;
        
        
var p1 = new Point(00100);
        
var p2 = new Point(1000100);
        
var line = new Line("g1""line1", p1, p2, "purple");
        objList[objList.length] 
= line;
        
        refresh();
        setInterval(
"refresh()",interval);
    }



    ]]
></script>
    
    
<defs>
    
</defs>
    
    
<rect id="back" x="0" y="0" width="100%" height="100%" fill="black" fill-opacity="1"/>
    
    
<g id="axis">
    
</g>
    
    
<g id="g1">
    
</g>
</svg>