如何检测其3D立方体的一侧被点击? [英] how to detect which side of 3d cube was clicked?
问题描述
我写一个小的3D引擎,在画布上..不是一个真正的引擎,但更多的应用程序的基础上,从的埃里克Pascarello的演示(命中数7几次看到它旋转)
这一切都进行得非常不错,我甚至加按平均z值排序为每架飞机(方) - 这样我就可以使用由多个立方体一个身体,使贴近相机的形状最后绘制
现在,我想检测哪一方被点击,当鼠标clickes画布。 像<一个href="http://www.fi.uu.nl/toepassingen/00208/toepassing_rekenweb.xml?style=rekenweb&language=en&use=game"相对=nofollow>这个
提示:
- 每侧由4(X,Y,Z)的角定义
- 在每个角落都使用的是什么,我相信是有规律的透视投影 吸引到画布
这是我的code - 这是很酷:用鼠标旋转
//编辑:见code。在我的回答如下
完美!想我需要问这个问题,所以我可以回答自己: - )
该解决方案是基于一些惊人的片段,我发现,用于检测多边形点presence。这是2D功能,但嘿...所以是我的画布 - 我傻
这是完整的,跨浏览器:
&LT;!DOCTYPE HTML PUBLIC - // W3C // DTD HTML 4.01过渡// EN&GT;
&LT; HTML&GT;
&LT; HEAD&GT;
&LT; META HTTP-当量=内容类型内容=text / html的;字符集= UTF-8/&GT;
&LT;冠军&GT;קובייה&LT; /标题&GT;
&LT;链接REL =快捷方式图标的href =Rubiks.png/&GT;
&LT;脚本&GT; VAR isIE =假LT; / SCRIPT&GT;
&LT;! - [如果IE] GT;
&LT; SCRIPT LANGUAGE =JavaScript的类型=文/ JavaScript的SRC =../ js_canvas / excanvas_r69.js&GT;&LT; / SCRIPT&GT;
&LT;脚本&GT; isIE =真LT; / SCRIPT&GT;
百分比抑制率ENDIF] - GT!;
&LT;风格&GT;
体 {
填充:2px的;
字体-family:宋体;
}
帆布 {
边界:1px的纯黑色;
背景:白色;
光标:默认值;
}
。移动 {
光标:移动;
}
DIV,TD,输入{
字体大小:15px的;
}
.header {
字体大小:22px;
字体重量:大胆的;
保证金底:10px的;
}
.subHeader {
字体大小:16像素;
}
.tblHeader {
背景:#003f00;
颜色:白色;
字体重量:大胆的;
字体大小:24PX;
边界:0px;
}
。信息 {
背景:浅黄;
边界:1px的纯黑色;
字体大小:15px的;
宽度:350像素;
}
.opac {
/ *
不透明度:0.85;
过滤器:α(不透明= 85);
-ms过滤器:阿尔法(不透明度= 85);
-khtml不透明度:0.85;
-moz-不透明度:0.85;
* /
}
.btn {
颜色:黑色;
显示:inline-block的;
宽度:100像素;
边界:2px的一开始就#ddd;
文字修饰:无;
填充:2px的;
背景:#ddd;
文本对齐:中心;
字体-family:宋体;
字体大小:12px的;
字体重量:大胆的;
}
.btn_hover {
颜色:蓝色;
}
.btn_down {
边界:2px的插图#ddd;
}
&LT; /风格&GT;
&LT; /头&GT;
&LT;脚本类型=文/ JavaScript的&GT;
//立方code埃里克Pascarello共享
VAR sideLength = 50;
VAR WIDTH = 600;
VAR HEIGHT = 450;
VAR中心=新的点(宽度/ 2,高度/ 2)
VAR角度= sideLength * 16;
变种xzRotation = -Math.PI / 2;
变种yzRotation = 0;
变种xyRotation = 0;
VAR isColored = TRUE;
VAR立方体,calcCube,LGTH
VAR帆布,CTX,动画
VAR鼠标=新的点(0,0);
VAR absMouse =新的点(0,0)
VAR clickedMouse =新的点(0,0)
VAR posCanvas
VAR clickRGB = [248,128,23] //橙色=#F88017
VAR clickRGB = [255192203] //粉色
VAR arrPolygons = []
VAR arrSortedIndex
/ **立方的东西** /
功能旋转(bForce){
如果(bForce&安培;!及(this.last_xyRotation == xyRotation和放大器;&安培;
this.last_xzRotation == xzRotation和放大器;&安培;
this.last_yzRotation == yzRotation
|| !dragCube)){
返回
}
VAR drawStyle = getRadioValue(drawStyle)//颜色,体重,或反
如果(drawStyle ==跨){
$(chkWire)。禁用= TRUE
$(chkWire)。检查=真
} 其他 {
$(chkWire)。禁用= FALSE
}
//旋转立方体进入calcCube。还设置颜色。
对于(VAR I = 0; I&LT; LGTH;我++){
VAR方= cube.sides [I]
VAR calcSide = calcCube.sides [I]
VAR avgZ = 0
VAR side_polygon = []
为(变种J = 0; J&4;; J ++){
VAR角落= side.corners [J]。
变种CALC1计算值=(corner.x,corner.y,xyRotation,1);
变种CALC2 =计算值(calc1.p1,corner.z,xzRotation,1);
VAR CALC3 =计算(calc1.p2,calc2.p2,yzRotation,-1);
VAR X =(calc2.p1 *视角)/(视角 - calc3.p2)+ center.x;
变种Y =(calc3.p1 *视角)/(视角 - calc3.p2)+ center.y;
calcSide.corners [J] .X = X
calcSide.corners [J] .Y = Y
side_polygon.push(新的点(x,y))为
avgZ + = calc3.p2
}
calcSide.avgZ = avgZ // / 4
calcSide.polygon = side_polygon
VAR光= side.light;
变种CALC1计算值=(light.x,light.y,xyRotation,1);
变种CALC2 =计算值(calc1.p1,light.z,xzRotation,1);
VAR CALC3 =计算(calc1.p2,calc2.p2,yzRotation,-1);
calcSide.light = calc3.p2;
//决定颜色
VAR亮度= Math.floor(calcSide.light);
亮度= trimVal(亮度,0,255)
VAR colorRGB = []
VAR颜色codeRGB =(side.clickState)? clickRGB:(drawStyle ==颜色side.color:B,B,B)分裂()。
为(变种C = 0; C&LT; 3,C ++){
colorRGB [C] =(彩色codeRGB [C] ==B)?亮度:颜色codeRGB [C]
}
fillRGBA =RGBA(+ colorRGB +,255);
calcSide.fillRGBA = fillRGBA
}
//通过avgZ排序两侧!
arrSortedIndex = []
对于(VAR I = 0; I&LT; LGTH;我++){
arrSortedIndex [我] =我
}
对于(VAR I = 0; I&LT; LGTH-1;我++){
对于(VAR J = I + 1; J&LT; LGTH; J ++){
如果(calcCube.sides [I] .avgZ&GT; calcCube.sides [J] .avgZ){
变种临时= calcCube.sides [I] .avgZ
calcCube.sides [I] .avgZ = calcCube.sides [J] .avgZ
calcCube.sides [J] .avgZ =温度
变种临时= arrSortedIndex [I]
arrSortedIndex [i] = arrSortedIndex [J]。
arrSortedIndex [J] =气温
}
}
}
//借鉴各方面
ctx.clearRect(0,0,宽度,高度);
对于(VAR I = 0; I&LT; LGTH;我++){
VAR calcSide = calcCube.sides [arrSortedIndex [I]];
ctx.fillStyle = calcSide.fillRGBA
VAR角= calcSide.corners;
ctx.beginPath();
ctx.moveTo(角[0] .X,边角[0] .Y);
ctx.lineTo(角[1] .X,角[1] .Y);
ctx.lineTo(角[2] .X,角[2] .Y);
ctx.lineTo(角部[3] .X,角[3] .Y);
ctx.lineTo(角[0] .X,边角[0] .Y);
如果(drawStyle!=跨){
ctx.fill();
}
如果($(chkWire)。选中){
ctx.stroke();
}
}
this.last_xyRotation = xyRotation
this.last_xzRotation = xzRotation
this.last_yzRotation = yzRotation
}
函数计算(P1,P2,陈子昂,PN){
VAR cosAng = Math.cos(ANG);
VAR私囊= Math.sin(ANG);
VAR R1 = cosAng * P1 - PN *私囊* P2;
VAR R2 = cosAng * P2 + PN *私囊* P1;
返回{P1:R1,P2:R2};
}
功能getCube(sideLength){
VAR RET = {
双方:
{ //面前
角落:[{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}],
光:{X:0,Y:0,Z:255},
色:0,B,0
},
{ //背部
角落:[{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}],
光:{X:0,Y:0,Z:-255},
颜色:0,0,B
},
{ //对
角落:[{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}],
光:{X:255,Y:0,Z:0},
颜色:B,0,0
},
{ //剩下
角落:[{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}],
光:{X:-255,Y:0,Z:0},
色:0,B,B
},
{ //最佳
角落:[{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}],
光:{X:0,Y:-255,Z:0},
色:B,B,0
},
{ //底部
角落:[{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}],
光:{X:0,Y:255,Z:0},
颜色:B,0,B
},
// anoter立方体后面,上面 - 我除了
{ //面前
角落:[{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}],
光:{X:0,Y:0,Z:255},
色:0,B,0
},
{ //背部
角落:[{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}],
光:{X:0,Y:0,Z:-255},
颜色:0,0,B
},
{ //对
角落:[{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}],
光:{X:255,Y:0,Z:0},
颜色:B,0,0
},
{ //剩下
角落:[{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}],
光:{X:-255,Y:0,Z:0},
色:0,B,B
},
{ //最佳
角落:[{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}],
光:{X:0,Y:-255,Z:0},
色:B,B,0
},
{ //底部
角落:[{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}],
光:{X:0,Y:255,Z:0},
颜色:B,0,B
}
]
}
LGTH = ret.sides.length;
calcCube = {方面:[]}
对于(VAR I = 0; I&LT; LGTH;我++){
calcCube.sides [i] = {角落:[{},{},{},{}],光:0,avgZ:0}
ret.sides [0] .clickState = FALSE
}
返回RET
}
/ ** 2D的东西** /
函数点(X,Y){
this.x = X
this.y = Y
}
传播多边形(){
VAR RET = []
对于(VAR I = 0; I&LT;与arguments.length;我++){
ret.push(参数[我])
}
返回RET
}
功能isPointInPoly(聚,PT){
从http://snippets.dzone.com/posts/show/5295 // - 哇!
对于(VAR C =假,I = -1,L = poly.length,J = L - 1 ++ I&LT; L,J =)
((聚[I] .Y&LT; = pt.y和放大器;&安培; pt.y&LT;聚[J] .Y)||(聚[J] .Y&LT; = pt.y和放大器;&安培; PT .Y&LT;聚[I] .Y))
&功放;&安培; (pt.x≤(聚[j]的.X - 聚[I] .X)*(pt.y - 聚[I] .Y)/(聚[j]的.Y - 聚[I] .Y) +聚[I] .X)
&功放;&安培; (C = C!);
返回℃;
}
/ **事件处理** /
VAR dragCube = NULL
功能的mouseMove(五){
变种POSX = 0;
变种POSY = 0;
Ë= E || window.event;
如果(isIE&安培;&安培; FALSE){
POSX = e.offsetX
束花= e.offsetY
} 其他 {
如果(e.pageX || e.pageY){
POSX = e.pageX;
束花= e.pageY;
}否则,如果(e.clientX || e.clientY){
POSX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
束花= e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
absMouse =(新点(POSX,铭文))
}
//$("debug").innerHTML = myStringify(absMouse)
如果(dragCube){
VAR差异=新的点(POSX - dragCube.anchor.x,铭文 - dragCube.anchor.y)
如果(!dragCube.moved){
dragCube.moved =真
addClass($(CV),动)
}
dragCube.anchor =新的点(POSX,花束)
xzRotation - = diff.x / 100
yzRotation + = diff.y / 100
//$("debug").innerHTML = myStringify(差异)
}
}
功能MOUSEDOWN(五){
Ë= E || window.event;
dragCube = {
主播:新点(absMouse.x,absMouse.y)
感动:假的,
方:-1
}
VAR内部=新的点(absMouse.x - posCanvas.x,absMouse.y - posCanvas.y)
//为(VAR I = 0; I&LT; LGTH;我++){
对于(VAR I = LGTH-1; I&GT; = 0;我 - ){
如果(isPointInPoly(calcCube.sides [arrSortedIndex [I],多边形,内)){
dragCube.side = arrSortedIndex [I]
打破;
}
}
/ *
ctx.beginPath()
ctx.arc(inside.x,inside.y,1,0,Math.PI * 2,假);
ctx.stroke();
* /
}
功能mouseUp事件(E){
如果(dragCube&安培;&安培;!dragCube.moved){
VAR指数= dragCube.side
如果(索引&gt; = 0){
cube.sides [指数] .clickState =!cube.sides [指数] .clickState
旋转(真)
}
}
dragCube = NULL
removeClass移除($(CV),动)
}
/ **按钮** /
功能addClass(objElement,strClass){
如果回报(objElement!);
如果(objElement.className){
removeClass移除(objElement,strClass);
objElement.className + =''+ strClass;
} 其他 {
objElement.className = strClass;
}
}
功能removeClass移除(objElement,strClass){
如果回报(objElement!);
如果(objElement.className){
变种arrList = objElement.className.split('');
变种strClassUpper = strClass.toUpperCase();
对于(VAR I = 0; I&LT; arrList.length;我++){
如果(arrList [I] .toUpperCase()== strClassUpper){
arrList.splice(ⅰ,1);
一世 - ;
}
}
objElement.className = arrList.join('');
}
}
/ **杂项和UTIL ** /
功能$(ID){
返回的document.getElementById(ID);
}
功能findPos(OBJ){
//http://www.quirksmode.org/js/findpos.html
变种curleft = curtop = 0;
如果(OBJ和放大器;&安培; obj.offsetParent){
做 {
curleft + = obj.offsetLeft;
curtop + = obj.offsetTop;
}而(OBJ = obj.offsetParent);
}
返回新的点(curleft,curtop);
}
函数DEC2HEX(D,填充){
VAR十六进制数=(D)的ToString(16);
填充=填充|| 2
而(hex.length&LT;填充){
十六进制=0+六角;
}
返回十六进制;
}
功能trimVal(VAL,最小值,最大值){
返回Math.max(Math.min(VAL,最大值),最小值)
}
功能getRadioValue(名字){
对于(i = 0; I&LT; document.forms [FRM] [名] .length;我++){
如果(document.forms [FRM] [名] [我] .checked){
返回document.forms [FRM] [名] [我]。价值;
}
}
返回NULL
}
/** 在里面 **/
功能的init(){
帆布= $(CV);
canvas.style.width =宽度;
canvas.style.height =高度;
canvas.setAttribute(宽度,宽)
canvas.setAttribute(高度,高度)
CTX = canvas.getContext('二维');
posCanvas = findPos(画布)
立方= getCube(sideLength)
旋转(真)
动画= window.setInterval(旋转(),50);
}
&LT; / SCRIPT&GT;
&LT;身体的onload =的init()
的OnMouseMove =的mouseMove(事件)
onmouseup =mouseUp事件(事件)&GT;
&LT; DIV DIR = RTL级=头与GT;קובייה&LT; / DIV&GT;
&LT; DIV DIR = RTL级=子报&GT;גיררואתהקוביהעםהעכברעלמנתלסובבאותה&LT; / DIV&GT;
&LT; DIV DIR = RTL级=子报&GT;ליחצועלפאהעלמנתלסמןאותה&LT; / DIV&GT;
&LT; BR&GT;
&LT;! - 主画布 - &GT;
&LT;中心&GT;
&LT; DIV ID =包装DIR =升&GT;
&LT;帆布onmousedown事件=鼠标按下(事件)ID =CVWIDTH =100HEIGHT =100&GT;&LT; /帆布&GT;
&LT; / DIV&GT;
&LT; /中心&GT;
&LT;! - 控制面板 - &GT;
&LT; DIV DIR =RTL级=OPAC的风格=的位置是:绝对的;背景:浅黄;边框:1px的纯黑色;右:10px的;顶部:100px的;字体大小:12px的;填充:4PX;宽度: 120px&GT;
&LT; DIV DIR = RTL&GT;
&LT;表格名称= FRM&GT;
&LT;输入名称=drawStyle类型=无线电值=色的onclick =旋转(真)的onchange =旋转(真)签&GT;צבעוני&LT; BR&GT;
&LT;输入名称=drawStyle类型=无线电值=BW的onclick =旋转(真)的onchange =旋转(真)&GT;שחורלבן&LT; BR&GT;
&LT;输入名称=drawStyle类型=无线电值=跨的onclick =旋转(真)的onchange =旋转(真)&GT;שקוף&LT; BR&GT;
&LT;输入ID =chkWire类型=复选框的onclick =旋转(真)的onchange =旋转(真)&GT;מסגרת&LT; BR&GT;
&LT; /形式GT;
&LT; / DIV&GT;
&LT; / DIV&GT;
&LT; DIV ID =调试&GT;&LT; / DIV&GT;
&LT; /身体GT;
&LT; / HTML&GT;
呵呵......我不介意多的文档类型,但评论感谢。
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:
- each side is defined by 4 (x,y,z) corners
- 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
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.
这篇关于如何检测其3D立方体的一侧被点击?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!