如何检测 3d 立方体的哪一侧被点击?

发布于 2024-10-12 04:36:55 字数 607 浏览 2 评论 0原文

我正在画布中编写一个小型 3D“引擎”..并不是真正的引擎,而是更多基于 Eric Pascarello 的演示(点击数字“7”几次可以看到它旋转)

一切都非常好,我什至添加了按每个平面(“侧面”)的平均 z 值排序 - 所以我可以使用由多个立方体组成的主体,以便最后绘制接近相机的形状。

现在,我想检测当鼠标单击画布时单击了哪一侧。 类似这个

提示:

  1. 每条边由 4 个(x,y,z)角定义
  2. 每个角都使用我认为常规的“透视投影”绘制到画布上

这是我的代码 - 很酷:使用鼠标旋转

//edit: see the code in my answer below

I am writing a small 3d "engine" in canvas.. not really an engine, but more of an application based on code from Eric Pascarello's demo (hit number "7" a few times to see it rotating)

it's all going very nice, I even added sorting by average z-value for each plane ("side") - so that I can use a body made of several cubes so that close to camera shapes are drawn last.

now, I want to detect which side was clicked, when a mouse clickes the canvas.
something like this

hints:

  1. each side is defined by 4 (x,y,z) corners
  2. each corner is drawn to canvas using what I believe is regular "perspective projection"

here's my code - it's cool: use the mouse to rotate

//edit: see the code in my answer below

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

煮茶煮酒煮时光 2024-10-19 04:36:55

完美的!我想我需要问这个问题,这样我才能回答自己:-)

该解决方案基于我发现的一些令人惊奇的片段,用于检测多边形中点的存在。这是二维函数,但是嘿......我的画布也是如此 - 愚蠢的我。

这是完整的、跨浏览器的:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
        <title>קובייה</title>

        <link rel="shortcut icon" href="Rubiks.png" />  
        <script>var isIE = false</script>
        <!--[if IE]>
            <script language="javascript" type="text/javascript" src="../js_canvas/excanvas_r69.js"></script>
            <script>isIE = true</script>
        <![endif]-->        
        <style>
            body {
                padding: 2px;
                font-family: arial;
            }
            canvas {
                border:1px solid black;
                background:white;
                cursor:default;
            }
            .move {
                cursor:move;
            }
            div, td, input {
                font-size:15px;
            }
            .header {
                font-size:22px;
                font-weight:bold;
                margin-bottom:10px;
            }
            .subHeader {
                font-size:16px;
            }
            .tblHeader {
                background:#003f00; 
                color:white; 
                font-weight:bold;
                font-size:24px;
                border:0px;
            }
            .info {
                background:lightyellow; 
                border:1px solid black;
                font-size:15px;
                width:350px;
            }
            .opac {
                /*
                opacity: .85;
                filter: alpha(opacity=85);
                -ms-filter: "alpha(opacity=85)";
                -khtml-opacity: .85;
                -moz-opacity: .85;
                */
            }           
            .btn {
                color: black;
                display: inline-block;
                width:100px;
                border: 2px outset #ddd;
                text-decoration:none;
                padding:2px;
                background: #ddd;
                text-align:center;
                font-family: arial;
                font-size:12px;
                font-weight:bold;
            }
            .btn_hover {
                color: blue;
            }
            .btn_down {
                border: 2px inset #ddd;
            }

        </style>

    </head>

<script type="text/javascript">
//  cube code shared by Eric Pascarello 
var sideLength = 50;
var width = 600;
var height = 450;
var center = new Point(width/2, height/2)
var perspective = sideLength * 16;  
var xzRotation = -Math.PI/2;
var yzRotation = 0;
var xyRotation = 0;
var isColored = true;
var cube, calcCube, lgth 
var canvas, ctx, animation
var mouse = new Point(0,0);
var absMouse = new Point(0,0)
var clickedMouse = new Point(0,0)
var posCanvas
var clickRGB = [248, 128, 23]       // orange = #F88017
var clickRGB = [255,192,203]        // pink
var arrPolygons = []
var arrSortedIndex


/** cube stuff **/
function rotate(bForce) {
    if (!bForce && (this.last_xyRotation == xyRotation && 
        this.last_xzRotation == xzRotation && 
        this.last_yzRotation == yzRotation
        || !dragCube) ) {
        return
    }

    var drawStyle = getRadioValue("drawStyle")      // color, bw, or trans
    if (drawStyle=="trans") {
        $("chkWire").disabled = true
        $("chkWire").checked = true
    } else {
        $("chkWire").disabled = false
    }

    // rotate cube into calcCube. also set colors.
    for (var i=0; i<lgth; i++) {
        var side = cube.sides[i];
        var calcSide = calcCube.sides[i];
        var avgZ = 0
        var side_polygon = []
        for (var j=0 ; j<4; j++){
            var corner = side.corners[j];
            var calc1 = calc(corner.x, corner.y, xyRotation, 1);
            var calc2 = calc(calc1.p1, corner.z, xzRotation, 1);
            var calc3 = calc(calc1.p2, calc2.p2, yzRotation, -1);
            var x = (calc2.p1 * perspective) / (perspective - calc3.p2) + center.x;
            var y = (calc3.p1 * perspective) / (perspective - calc3.p2) + center.y;
            calcSide.corners[j].x = x
            calcSide.corners[j].y = y
            side_polygon.push (new Point(x, y))
            avgZ += calc3.p2
        }
        calcSide.avgZ = avgZ // /4
        calcSide.polygon = side_polygon

        var light = side.light;
        var calc1 = calc(light.x, light.y, xyRotation, 1);
        var calc2 = calc(calc1.p1, light.z, xzRotation, 1);
        var calc3 = calc(calc1.p2, calc2.p2, yzRotation, -1);
        calcSide.light = calc3.p2;

        // decide color
        var brightness = Math.floor(calcSide.light);
        brightness = trimVal(brightness, 0, 255)
        var colorRGB = []
        var colorCodeRGB = (side.clickState) ?  clickRGB : (drawStyle=="color" ? side.color : "b,b,b").split(",")
        for (var c=0; c<3; c++) {   
            colorRGB[c] = (colorCodeRGB[c]=="b") ? brightness : colorCodeRGB[c]
        }
        fillRGBA = "rgba(" + colorRGB + ",255)";
        calcSide.fillRGBA = fillRGBA
    }

    // sort sides by avgZ !!    
    arrSortedIndex = []
    for (var i=0; i<lgth; i++) {
        arrSortedIndex[i] = i
    }
    for (var i=0; i<lgth-1; i++) {
        for (var j=i+1; j<lgth; j++) {
            if (calcCube.sides[i].avgZ > calcCube.sides[j].avgZ) {
                var temp = calcCube.sides[i].avgZ
                calcCube.sides[i].avgZ = calcCube.sides[j].avgZ
                calcCube.sides[j].avgZ = temp

                var temp = arrSortedIndex[i]
                arrSortedIndex[i] = arrSortedIndex[j]
                arrSortedIndex[j] = temp
            }
        }
    }


    // draw all sides
    ctx.clearRect (0,0, width, height);    
    for (var i=0; i<lgth; i++) {
        var calcSide = calcCube.sides[arrSortedIndex[i]];
        ctx.fillStyle = calcSide.fillRGBA
        var corners = calcSide.corners;
        ctx.beginPath();
        ctx.moveTo (corners[0].x, corners[0].y);
        ctx.lineTo (corners[1].x, corners[1].y);
        ctx.lineTo (corners[2].x, corners[2].y);
        ctx.lineTo (corners[3].x, corners[3].y);
        ctx.lineTo (corners[0].x, corners[0].y);
        if (drawStyle!="trans") {
            ctx.fill();
        }   
        if ($("chkWire").checked) {
            ctx.stroke();
        }   
    }   

    this.last_xyRotation = xyRotation
    this.last_xzRotation = xzRotation
    this.last_yzRotation = yzRotation
}
function calc(p1,p2,ang,pn){

    var cosAng = Math.cos(ang);
    var sinAng = Math.sin(ang);

     var r1 = cosAng * p1 - pn * sinAng * p2;
     var r2 = cosAng * p2 + pn * sinAng * p1;

    return { "p1": r1,"p2":r2};
}
function getCube(sideLength) {

    var ret =  {
        sides : [
            { //FRONT
            corners : [ {x:-1*sideLength, y: 1*sideLength, z: 1*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z: 1*sideLength},
                        {x: 1*sideLength, y:-1*sideLength, z: 1*sideLength},
                        {x:-1*sideLength, y:-1*sideLength, z: 1*sideLength} ],
            light   : {x: 0, y: 0, z: 255 },
            color   : "0,b,0"
            },

            { //BACK
            corners : [ {x:-1*sideLength, y: 1*sideLength, z:-1*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z:-1*sideLength},
                        {x: 1*sideLength, y:-1*sideLength, z:-1*sideLength},
                        {x:-1*sideLength, y:-1*sideLength, z:-1*sideLength} ],
            light  :  {x: 0, y: 0, z: -255 },
            color   : "0,0,b"
            },

            {  //RIGHT
            corners : [ {x: 1*sideLength, y: 1*sideLength, z: 1*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z:-1*sideLength},
                        {x: 1*sideLength, y:-1*sideLength, z:-1*sideLength},
                        {x: 1*sideLength, y:-1*sideLength, z: 1*sideLength} ],
            light  :  {x: 255, y: 0, z: 0 },
            color   : "b,0,0"
            },

            { //LEFT
            corners : [ {x:-1*sideLength, y: 1*sideLength, z: 1*sideLength},
                        {x:-1*sideLength, y: 1*sideLength, z:-1*sideLength},
                        {x:-1*sideLength, y:-1*sideLength, z:-1*sideLength},
                        {x:-1*sideLength, y:-1*sideLength, z: 1*sideLength} ],
            light  :  {x: -255, y: 0, z: 0},
            color   : "0,b,b"
            },

            {  //top
            corners : [ {x:-1*sideLength, y:-1*sideLength, z: 1*sideLength},    
                        {x: 1*sideLength, y:-1*sideLength, z: 1*sideLength},
                        {x: 1*sideLength, y:-1*sideLength, z:-1*sideLength},
                        {x:-1*sideLength, y:-1*sideLength, z:-1*sideLength} ],
            light  :  {x: 0, y:-255 , z: 0},
            color   : "b,b,0"
            },

            {  //bottom
            corners : [ {x:-1*sideLength, y: 1*sideLength, z: 1*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z: 1*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z:-1*sideLength},
                        {x:-1*sideLength, y: 1*sideLength, z:-1*sideLength} ],
            light  :  {x: 0, y: 255, z: 0},
            color   : "b,0,b"
            },

            // anoter cube behind and above - my addition

            { //FRONT
            corners : [ {x:-1*sideLength, y: 3*sideLength, z: -1*sideLength},
                        {x: 1*sideLength, y: 3*sideLength, z: -1*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z: -1*sideLength},
                        {x:-1*sideLength, y: 1*sideLength, z: -1*sideLength} ],
            light   : {x: 0, y: 0, z: 255 },
            color   : "0,b,0"
            },

            { //BACK
            corners : [ {x:-1*sideLength, y: 3*sideLength, z:-3*sideLength},
                        {x: 1*sideLength, y: 3*sideLength, z:-3*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z:-3*sideLength},
                        {x:-1*sideLength, y: 1*sideLength, z:-3*sideLength} ],
            light  :  {x: 0, y: 0, z: -255 },
            color   : "0,0,b"
            },

            {  //RIGHT
            corners : [ {x: 1*sideLength, y: 3*sideLength, z: -1*sideLength},
                        {x: 1*sideLength, y: 3*sideLength, z:-3*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z:-3*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z: -1*sideLength} ],
            light  :  {x: 255, y: 0, z: 0 },
            color   : "b,0,0"
            },

            { //LEFT
            corners : [ {x:-1*sideLength, y: 3*sideLength, z: -1*sideLength},
                        {x:-1*sideLength, y: 3*sideLength, z:-3*sideLength},
                        {x:-1*sideLength, y: 1*sideLength, z:-3*sideLength},
                        {x:-1*sideLength, y: 1*sideLength, z: -1*sideLength} ],
            light  :  {x: -255, y: 0, z: 0},
            color   : "0,b,b"
            },

            {  //top
            corners : [ {x:-1*sideLength, y: 1*sideLength, z: -1*sideLength},   
                        {x: 1*sideLength, y: 1*sideLength, z: -1*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z:-3*sideLength},
                        {x:-1*sideLength, y: 1*sideLength, z:-3*sideLength} ],
            light  :  {x: 0, y:-255 , z: 0},
            color   : "b,b,0"
            },

            {  //bottom
            corners : [ {x:-1*sideLength, y: 3*sideLength, z: -1*sideLength},
                        {x: 1*sideLength, y: 3*sideLength, z: -1*sideLength},
                        {x: 1*sideLength, y: 3*sideLength, z:-3*sideLength},
                        {x:-1*sideLength, y: 3*sideLength, z:-3*sideLength} ],
            light  :  {x: 0, y: 255, z: 0},
            color   : "b,0,b"
            }           

        ]
    }
    lgth = ret.sides.length; 

    calcCube = {sides:[]}
    for (var i=0; i<lgth; i++) {
        calcCube.sides[i] =  {corners : [{},{},{},{}], light:0, avgZ:0} 
        ret.sides[0].clickState=false
    }
    return ret
}

/** 2d stuff **/
function Point(x,y) {
    this.x = x
    this.y = y
}
function Polygon() {
    var ret = []
    for (var i=0; i<arguments.length; i++) {
        ret.push (arguments[i])
    }
    return ret
}
function isPointInPoly(poly, pt) {
    // from http://snippets.dzone.com/posts/show/5295 - wow!
    for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
        ((poly[i].y <= pt.y && pt.y < poly[j].y) || (poly[j].y <= pt.y && pt.y < poly[i].y))
        && (pt.x < (poly[j].x - poly[i].x) * (pt.y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x)
        && (c = !c);
    return c;
}

/** event handlers **/
var dragCube = null
function mouseMove(e) {
    var posx = 0;
    var posy = 0;
    e = e || window.event;
    if (isIE&&false) {
        posx = e.offsetX
        posy = e.offsetY
    } else {
        if (e.pageX || e.pageY) {
            posx = e.pageX;
            posy = e.pageY;
        } else if (e.clientX || e.clientY) {
            posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
            posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
        }
        absMouse = (new Point(posx, posy))
    }
    //$("debug").innerHTML = myStringify(absMouse)
    if (dragCube) {
        var diff = new Point (posx - dragCube.anchor.x, posy - dragCube.anchor.y)
        if (!dragCube.moved) {
            dragCube.moved = true
            addClass ($("cv"), "move")
        } 
        dragCube.anchor = new Point (posx, posy)
        xzRotation -= diff.x/100
        yzRotation += diff.y/100
        //$("debug").innerHTML = myStringify(diff)
    }
}
function mouseDown(e) {
    e = e || window.event;
    dragCube = {
        anchor : new Point( absMouse.x, absMouse.y),
        moved : false,
        side: -1
    }
    var inside = new Point (absMouse.x - posCanvas.x, absMouse.y - posCanvas.y )
    //for (var i=0; i<lgth; i++) {
    for (var i=lgth-1; i>=0; i--) {
        if (isPointInPoly(calcCube.sides[arrSortedIndex[i]].polygon, inside)) {
            dragCube.side = arrSortedIndex[i]
            break;
        }
    }
    /*
    ctx.beginPath()
    ctx.arc(inside.x, inside.y, 1, 0, Math.PI*2, false);    
    ctx.stroke();   
    */
}
function mouseUp(e) {
    if (dragCube && !dragCube.moved) {
        var index = dragCube.side
        if (index>=0) {
            cube.sides[index].clickState = !cube.sides[index].clickState
            rotate(true)
        }       
    }
    dragCube = null
    removeClass ($("cv"), "move")
}

/** buttons **/
function addClass(objElement, strClass) {
    if (!objElement) return;
    if (objElement.className) {
        removeClass(objElement, strClass);
        objElement.className += ' '+strClass;
    } else {
        objElement.className = strClass;
    }
}
function removeClass(objElement, strClass) {
    if (!objElement) return;
    if (objElement.className) {
        var arrList = objElement.className.split(' ');
        var strClassUpper = strClass.toUpperCase();
        for (var i = 0; i < arrList.length; i++) {
            if (arrList[i].toUpperCase() == strClassUpper) {
                arrList.splice(i, 1);
                i--;
            }
        }
        objElement.className = arrList.join(' ');
    }
}

/** misc and util **/
function $(id) {
    return document.getElementById(id);
}
function findPos(obj) {
    //http://www.quirksmode.org/js/findpos.html
    var curleft = curtop = 0;
    if (obj && obj.offsetParent) {
        do {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        } while (obj = obj.offsetParent);
    }
    return new Point(curleft,curtop);
}
function dec2hex(d, padding) {
    var hex = Number(d).toString(16);
    padding = padding || 2
    while (hex.length < padding) {
        hex = "0" + hex;
    }
    return hex;
}
function trimVal(val, min, max) {
    return Math.max( Math.min(val, max), min)
}
function getRadioValue (name) {
    for (i=0;i<document.forms["frm"][name].length;i++) {
        if (document.forms["frm"][name][i].checked) {
            return document.forms["frm"][name][i].value;
        }
    }
    return null
}

/** init **/
function init() {
    canvas = $("cv");
    canvas.style.width = width;
    canvas.style.height = height;
    canvas.setAttribute("width", width)
    canvas.setAttribute("height", height)   
    ctx = canvas.getContext('2d');
    posCanvas = findPos(canvas)

    cube = getCube(sideLength)

    rotate(true)
    animation = window.setInterval("rotate()", 50);
}


</script>

<body onload="init()"
    onmousemove="mouseMove(event)"
    onmouseup="mouseUp(event)">
    <div dir=rtl class="header">קובייה</div>
    <div dir=rtl class="subHeader">גיררו את הקוביה עם העכבר על מנת לסובב אותה.</div>
    <div dir=rtl class="subHeader">ליחצו על פאה על מנת לסמן אותה.</div>

    <BR>
    <!-- main canvas -->
    <center>
        <div id="wrapper" dir="ltr">
            <canvas onmousedown="mouseDown(event)" id="cv" width="100" height="100"></canvas>   
        </div>
    </center>   

    <!-- control panel -->
    <div dir="rtl" class="opac" style="position:absolute; background:lightyellow; border:1px solid black; right:10px; top:100px; font-size:12px; padding:4px; width:120px">
        <div dir=rtl>
            <form name=frm>
                <input name="drawStyle" type="radio" value="color" onclick="rotate(true)" onchange="rotate(true)" checked>צבעוני<br>
                <input name="drawStyle" type="radio" value="bw" onclick="rotate(true)" onchange="rotate(true)">שחור לבן<br>
                <input name="drawStyle" type="radio" value="trans" onclick="rotate(true)" onchange="rotate(true)">שקוף<br>
                <input id="chkWire" type="checkbox" onclick="rotate(true)" onchange="rotate(true)">מסגרת<BR>
            </form> 
        </div>
    </div>  

    <div id="debug"></div>
</body>
</html>

哦...我不太介意文档类型,但感谢您的评论。

perfect! guess I needed to ask this question so I could answer myself :-)

the solution is based on some amazing snippet i found for detecting presence of point in polygon. this is 2d function, but hey... so is my canvas - silly me.

here it is, complete and cross-browser:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
        <title>קובייה</title>

        <link rel="shortcut icon" href="Rubiks.png" />  
        <script>var isIE = false</script>
        <!--[if IE]>
            <script language="javascript" type="text/javascript" src="../js_canvas/excanvas_r69.js"></script>
            <script>isIE = true</script>
        <![endif]-->        
        <style>
            body {
                padding: 2px;
                font-family: arial;
            }
            canvas {
                border:1px solid black;
                background:white;
                cursor:default;
            }
            .move {
                cursor:move;
            }
            div, td, input {
                font-size:15px;
            }
            .header {
                font-size:22px;
                font-weight:bold;
                margin-bottom:10px;
            }
            .subHeader {
                font-size:16px;
            }
            .tblHeader {
                background:#003f00; 
                color:white; 
                font-weight:bold;
                font-size:24px;
                border:0px;
            }
            .info {
                background:lightyellow; 
                border:1px solid black;
                font-size:15px;
                width:350px;
            }
            .opac {
                /*
                opacity: .85;
                filter: alpha(opacity=85);
                -ms-filter: "alpha(opacity=85)";
                -khtml-opacity: .85;
                -moz-opacity: .85;
                */
            }           
            .btn {
                color: black;
                display: inline-block;
                width:100px;
                border: 2px outset #ddd;
                text-decoration:none;
                padding:2px;
                background: #ddd;
                text-align:center;
                font-family: arial;
                font-size:12px;
                font-weight:bold;
            }
            .btn_hover {
                color: blue;
            }
            .btn_down {
                border: 2px inset #ddd;
            }

        </style>

    </head>

<script type="text/javascript">
//  cube code shared by Eric Pascarello 
var sideLength = 50;
var width = 600;
var height = 450;
var center = new Point(width/2, height/2)
var perspective = sideLength * 16;  
var xzRotation = -Math.PI/2;
var yzRotation = 0;
var xyRotation = 0;
var isColored = true;
var cube, calcCube, lgth 
var canvas, ctx, animation
var mouse = new Point(0,0);
var absMouse = new Point(0,0)
var clickedMouse = new Point(0,0)
var posCanvas
var clickRGB = [248, 128, 23]       // orange = #F88017
var clickRGB = [255,192,203]        // pink
var arrPolygons = []
var arrSortedIndex


/** cube stuff **/
function rotate(bForce) {
    if (!bForce && (this.last_xyRotation == xyRotation && 
        this.last_xzRotation == xzRotation && 
        this.last_yzRotation == yzRotation
        || !dragCube) ) {
        return
    }

    var drawStyle = getRadioValue("drawStyle")      // color, bw, or trans
    if (drawStyle=="trans") {
        $("chkWire").disabled = true
        $("chkWire").checked = true
    } else {
        $("chkWire").disabled = false
    }

    // rotate cube into calcCube. also set colors.
    for (var i=0; i<lgth; i++) {
        var side = cube.sides[i];
        var calcSide = calcCube.sides[i];
        var avgZ = 0
        var side_polygon = []
        for (var j=0 ; j<4; j++){
            var corner = side.corners[j];
            var calc1 = calc(corner.x, corner.y, xyRotation, 1);
            var calc2 = calc(calc1.p1, corner.z, xzRotation, 1);
            var calc3 = calc(calc1.p2, calc2.p2, yzRotation, -1);
            var x = (calc2.p1 * perspective) / (perspective - calc3.p2) + center.x;
            var y = (calc3.p1 * perspective) / (perspective - calc3.p2) + center.y;
            calcSide.corners[j].x = x
            calcSide.corners[j].y = y
            side_polygon.push (new Point(x, y))
            avgZ += calc3.p2
        }
        calcSide.avgZ = avgZ // /4
        calcSide.polygon = side_polygon

        var light = side.light;
        var calc1 = calc(light.x, light.y, xyRotation, 1);
        var calc2 = calc(calc1.p1, light.z, xzRotation, 1);
        var calc3 = calc(calc1.p2, calc2.p2, yzRotation, -1);
        calcSide.light = calc3.p2;

        // decide color
        var brightness = Math.floor(calcSide.light);
        brightness = trimVal(brightness, 0, 255)
        var colorRGB = []
        var colorCodeRGB = (side.clickState) ?  clickRGB : (drawStyle=="color" ? side.color : "b,b,b").split(",")
        for (var c=0; c<3; c++) {   
            colorRGB[c] = (colorCodeRGB[c]=="b") ? brightness : colorCodeRGB[c]
        }
        fillRGBA = "rgba(" + colorRGB + ",255)";
        calcSide.fillRGBA = fillRGBA
    }

    // sort sides by avgZ !!    
    arrSortedIndex = []
    for (var i=0; i<lgth; i++) {
        arrSortedIndex[i] = i
    }
    for (var i=0; i<lgth-1; i++) {
        for (var j=i+1; j<lgth; j++) {
            if (calcCube.sides[i].avgZ > calcCube.sides[j].avgZ) {
                var temp = calcCube.sides[i].avgZ
                calcCube.sides[i].avgZ = calcCube.sides[j].avgZ
                calcCube.sides[j].avgZ = temp

                var temp = arrSortedIndex[i]
                arrSortedIndex[i] = arrSortedIndex[j]
                arrSortedIndex[j] = temp
            }
        }
    }


    // draw all sides
    ctx.clearRect (0,0, width, height);    
    for (var i=0; i<lgth; i++) {
        var calcSide = calcCube.sides[arrSortedIndex[i]];
        ctx.fillStyle = calcSide.fillRGBA
        var corners = calcSide.corners;
        ctx.beginPath();
        ctx.moveTo (corners[0].x, corners[0].y);
        ctx.lineTo (corners[1].x, corners[1].y);
        ctx.lineTo (corners[2].x, corners[2].y);
        ctx.lineTo (corners[3].x, corners[3].y);
        ctx.lineTo (corners[0].x, corners[0].y);
        if (drawStyle!="trans") {
            ctx.fill();
        }   
        if ($("chkWire").checked) {
            ctx.stroke();
        }   
    }   

    this.last_xyRotation = xyRotation
    this.last_xzRotation = xzRotation
    this.last_yzRotation = yzRotation
}
function calc(p1,p2,ang,pn){

    var cosAng = Math.cos(ang);
    var sinAng = Math.sin(ang);

     var r1 = cosAng * p1 - pn * sinAng * p2;
     var r2 = cosAng * p2 + pn * sinAng * p1;

    return { "p1": r1,"p2":r2};
}
function getCube(sideLength) {

    var ret =  {
        sides : [
            { //FRONT
            corners : [ {x:-1*sideLength, y: 1*sideLength, z: 1*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z: 1*sideLength},
                        {x: 1*sideLength, y:-1*sideLength, z: 1*sideLength},
                        {x:-1*sideLength, y:-1*sideLength, z: 1*sideLength} ],
            light   : {x: 0, y: 0, z: 255 },
            color   : "0,b,0"
            },

            { //BACK
            corners : [ {x:-1*sideLength, y: 1*sideLength, z:-1*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z:-1*sideLength},
                        {x: 1*sideLength, y:-1*sideLength, z:-1*sideLength},
                        {x:-1*sideLength, y:-1*sideLength, z:-1*sideLength} ],
            light  :  {x: 0, y: 0, z: -255 },
            color   : "0,0,b"
            },

            {  //RIGHT
            corners : [ {x: 1*sideLength, y: 1*sideLength, z: 1*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z:-1*sideLength},
                        {x: 1*sideLength, y:-1*sideLength, z:-1*sideLength},
                        {x: 1*sideLength, y:-1*sideLength, z: 1*sideLength} ],
            light  :  {x: 255, y: 0, z: 0 },
            color   : "b,0,0"
            },

            { //LEFT
            corners : [ {x:-1*sideLength, y: 1*sideLength, z: 1*sideLength},
                        {x:-1*sideLength, y: 1*sideLength, z:-1*sideLength},
                        {x:-1*sideLength, y:-1*sideLength, z:-1*sideLength},
                        {x:-1*sideLength, y:-1*sideLength, z: 1*sideLength} ],
            light  :  {x: -255, y: 0, z: 0},
            color   : "0,b,b"
            },

            {  //top
            corners : [ {x:-1*sideLength, y:-1*sideLength, z: 1*sideLength},    
                        {x: 1*sideLength, y:-1*sideLength, z: 1*sideLength},
                        {x: 1*sideLength, y:-1*sideLength, z:-1*sideLength},
                        {x:-1*sideLength, y:-1*sideLength, z:-1*sideLength} ],
            light  :  {x: 0, y:-255 , z: 0},
            color   : "b,b,0"
            },

            {  //bottom
            corners : [ {x:-1*sideLength, y: 1*sideLength, z: 1*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z: 1*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z:-1*sideLength},
                        {x:-1*sideLength, y: 1*sideLength, z:-1*sideLength} ],
            light  :  {x: 0, y: 255, z: 0},
            color   : "b,0,b"
            },

            // anoter cube behind and above - my addition

            { //FRONT
            corners : [ {x:-1*sideLength, y: 3*sideLength, z: -1*sideLength},
                        {x: 1*sideLength, y: 3*sideLength, z: -1*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z: -1*sideLength},
                        {x:-1*sideLength, y: 1*sideLength, z: -1*sideLength} ],
            light   : {x: 0, y: 0, z: 255 },
            color   : "0,b,0"
            },

            { //BACK
            corners : [ {x:-1*sideLength, y: 3*sideLength, z:-3*sideLength},
                        {x: 1*sideLength, y: 3*sideLength, z:-3*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z:-3*sideLength},
                        {x:-1*sideLength, y: 1*sideLength, z:-3*sideLength} ],
            light  :  {x: 0, y: 0, z: -255 },
            color   : "0,0,b"
            },

            {  //RIGHT
            corners : [ {x: 1*sideLength, y: 3*sideLength, z: -1*sideLength},
                        {x: 1*sideLength, y: 3*sideLength, z:-3*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z:-3*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z: -1*sideLength} ],
            light  :  {x: 255, y: 0, z: 0 },
            color   : "b,0,0"
            },

            { //LEFT
            corners : [ {x:-1*sideLength, y: 3*sideLength, z: -1*sideLength},
                        {x:-1*sideLength, y: 3*sideLength, z:-3*sideLength},
                        {x:-1*sideLength, y: 1*sideLength, z:-3*sideLength},
                        {x:-1*sideLength, y: 1*sideLength, z: -1*sideLength} ],
            light  :  {x: -255, y: 0, z: 0},
            color   : "0,b,b"
            },

            {  //top
            corners : [ {x:-1*sideLength, y: 1*sideLength, z: -1*sideLength},   
                        {x: 1*sideLength, y: 1*sideLength, z: -1*sideLength},
                        {x: 1*sideLength, y: 1*sideLength, z:-3*sideLength},
                        {x:-1*sideLength, y: 1*sideLength, z:-3*sideLength} ],
            light  :  {x: 0, y:-255 , z: 0},
            color   : "b,b,0"
            },

            {  //bottom
            corners : [ {x:-1*sideLength, y: 3*sideLength, z: -1*sideLength},
                        {x: 1*sideLength, y: 3*sideLength, z: -1*sideLength},
                        {x: 1*sideLength, y: 3*sideLength, z:-3*sideLength},
                        {x:-1*sideLength, y: 3*sideLength, z:-3*sideLength} ],
            light  :  {x: 0, y: 255, z: 0},
            color   : "b,0,b"
            }           

        ]
    }
    lgth = ret.sides.length; 

    calcCube = {sides:[]}
    for (var i=0; i<lgth; i++) {
        calcCube.sides[i] =  {corners : [{},{},{},{}], light:0, avgZ:0} 
        ret.sides[0].clickState=false
    }
    return ret
}

/** 2d stuff **/
function Point(x,y) {
    this.x = x
    this.y = y
}
function Polygon() {
    var ret = []
    for (var i=0; i<arguments.length; i++) {
        ret.push (arguments[i])
    }
    return ret
}
function isPointInPoly(poly, pt) {
    // from http://snippets.dzone.com/posts/show/5295 - wow!
    for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
        ((poly[i].y <= pt.y && pt.y < poly[j].y) || (poly[j].y <= pt.y && pt.y < poly[i].y))
        && (pt.x < (poly[j].x - poly[i].x) * (pt.y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x)
        && (c = !c);
    return c;
}

/** event handlers **/
var dragCube = null
function mouseMove(e) {
    var posx = 0;
    var posy = 0;
    e = e || window.event;
    if (isIE&&false) {
        posx = e.offsetX
        posy = e.offsetY
    } else {
        if (e.pageX || e.pageY) {
            posx = e.pageX;
            posy = e.pageY;
        } else if (e.clientX || e.clientY) {
            posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
            posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
        }
        absMouse = (new Point(posx, posy))
    }
    //$("debug").innerHTML = myStringify(absMouse)
    if (dragCube) {
        var diff = new Point (posx - dragCube.anchor.x, posy - dragCube.anchor.y)
        if (!dragCube.moved) {
            dragCube.moved = true
            addClass ($("cv"), "move")
        } 
        dragCube.anchor = new Point (posx, posy)
        xzRotation -= diff.x/100
        yzRotation += diff.y/100
        //$("debug").innerHTML = myStringify(diff)
    }
}
function mouseDown(e) {
    e = e || window.event;
    dragCube = {
        anchor : new Point( absMouse.x, absMouse.y),
        moved : false,
        side: -1
    }
    var inside = new Point (absMouse.x - posCanvas.x, absMouse.y - posCanvas.y )
    //for (var i=0; i<lgth; i++) {
    for (var i=lgth-1; i>=0; i--) {
        if (isPointInPoly(calcCube.sides[arrSortedIndex[i]].polygon, inside)) {
            dragCube.side = arrSortedIndex[i]
            break;
        }
    }
    /*
    ctx.beginPath()
    ctx.arc(inside.x, inside.y, 1, 0, Math.PI*2, false);    
    ctx.stroke();   
    */
}
function mouseUp(e) {
    if (dragCube && !dragCube.moved) {
        var index = dragCube.side
        if (index>=0) {
            cube.sides[index].clickState = !cube.sides[index].clickState
            rotate(true)
        }       
    }
    dragCube = null
    removeClass ($("cv"), "move")
}

/** buttons **/
function addClass(objElement, strClass) {
    if (!objElement) return;
    if (objElement.className) {
        removeClass(objElement, strClass);
        objElement.className += ' '+strClass;
    } else {
        objElement.className = strClass;
    }
}
function removeClass(objElement, strClass) {
    if (!objElement) return;
    if (objElement.className) {
        var arrList = objElement.className.split(' ');
        var strClassUpper = strClass.toUpperCase();
        for (var i = 0; i < arrList.length; i++) {
            if (arrList[i].toUpperCase() == strClassUpper) {
                arrList.splice(i, 1);
                i--;
            }
        }
        objElement.className = arrList.join(' ');
    }
}

/** misc and util **/
function $(id) {
    return document.getElementById(id);
}
function findPos(obj) {
    //http://www.quirksmode.org/js/findpos.html
    var curleft = curtop = 0;
    if (obj && obj.offsetParent) {
        do {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        } while (obj = obj.offsetParent);
    }
    return new Point(curleft,curtop);
}
function dec2hex(d, padding) {
    var hex = Number(d).toString(16);
    padding = padding || 2
    while (hex.length < padding) {
        hex = "0" + hex;
    }
    return hex;
}
function trimVal(val, min, max) {
    return Math.max( Math.min(val, max), min)
}
function getRadioValue (name) {
    for (i=0;i<document.forms["frm"][name].length;i++) {
        if (document.forms["frm"][name][i].checked) {
            return document.forms["frm"][name][i].value;
        }
    }
    return null
}

/** init **/
function init() {
    canvas = $("cv");
    canvas.style.width = width;
    canvas.style.height = height;
    canvas.setAttribute("width", width)
    canvas.setAttribute("height", height)   
    ctx = canvas.getContext('2d');
    posCanvas = findPos(canvas)

    cube = getCube(sideLength)

    rotate(true)
    animation = window.setInterval("rotate()", 50);
}


</script>

<body onload="init()"
    onmousemove="mouseMove(event)"
    onmouseup="mouseUp(event)">
    <div dir=rtl class="header">קובייה</div>
    <div dir=rtl class="subHeader">גיררו את הקוביה עם העכבר על מנת לסובב אותה.</div>
    <div dir=rtl class="subHeader">ליחצו על פאה על מנת לסמן אותה.</div>

    <BR>
    <!-- main canvas -->
    <center>
        <div id="wrapper" dir="ltr">
            <canvas onmousedown="mouseDown(event)" id="cv" width="100" height="100"></canvas>   
        </div>
    </center>   

    <!-- control panel -->
    <div dir="rtl" class="opac" style="position:absolute; background:lightyellow; border:1px solid black; right:10px; top:100px; font-size:12px; padding:4px; width:120px">
        <div dir=rtl>
            <form name=frm>
                <input name="drawStyle" type="radio" value="color" onclick="rotate(true)" onchange="rotate(true)" checked>צבעוני<br>
                <input name="drawStyle" type="radio" value="bw" onclick="rotate(true)" onchange="rotate(true)">שחור לבן<br>
                <input name="drawStyle" type="radio" value="trans" onclick="rotate(true)" onchange="rotate(true)">שקוף<br>
                <input id="chkWire" type="checkbox" onclick="rotate(true)" onchange="rotate(true)">מסגרת<BR>
            </form> 
        </div>
    </div>  

    <div id="debug"></div>
</body>
</html>

oh... and I don't mind much for the doctype, but thanks for commenting.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文