魔方吧·中文魔方俱乐部

标题: Fenz 的在线魔方程序【更新变形魔方】 [打印本页]

作者: Fenz    时间: 2014-5-5 01:01:47     标题: Fenz 的在线魔方程序【更新变形魔方】

本帖最后由 Fenz 于 2014-5-21 21:40 编辑

本程序基于 SVG+JS 技术目前在 Chrome、FireFox、Safari、Opero、IE11 等浏览器下测试通过,国产的那些二次包装的浏览器就不一一测了。至于低版本的IE(9以下)不支持SVG,没有办法了,建议还是换更优秀的浏览器用吧。
代码是开源的,欢迎参考借鉴,更欢迎高手指点。

网址:http://uukoo.xjisu.cn/cube/

操作和胡老师的程序差不多,转视角操作略有不同,是用中键。
shift+中键拖拽是缩放,Alt+中键拖拽是调焦距,Ctrl+中键拖动是在屏幕面上旋转。
没有中键的朋友可以用其它键在魔方以外的地方拖拽,代替中键。Opera浏览器的中键行为比较怪异,请在魔方以外用左右键拖动。

5月9日更新了视角转动算法,将原来的二维转动改为三维转动,由于使用的是微分算法,和鼠标拖动事件的离散性,导致拖动过快时会有跳动的BUG,所以刻意减慢了转动速度比率。不知道其他程序是怎么处理三维视角转动,希望有人能指点一下。

5月10日更新视角控制规则,学习借鉴了胡波老师的算法,视角的三维旋转这块算是完善了。另外发现利用这个坐标变换矩阵,还能做许多有趣的事情。

魔方的转动具体是
二阶:直接点击转单层,Shift点击转整体;
三阶:直接点击转单层,Shift点击转中层,Ctrl点击转两层,Shift+Ctrl点击转整体

目前做了5个魔方。

这次更新除了修正五个魔方的链接都已经开放出来了,之前
有链接的魔方有三个,另外有两个隐藏魔方(都是市面上从未出现过的魔方,即偏门又奇怪),可以通过地址栏输入正确的地址访问到,给大家一点提示,看看有没有人能试出来。提示二:每个魔方的地址都是有规律的,前半段都一样。

其实

另外,隐藏的两个魔方也体现了我的程序的最大特点,也是和许多其他魔方程序的不同之处。

对了,上个截图才能在首页左上角显示。这是一个非常空的空心魔方。

2014-05-05_010554.png



附件: 2014-05-05_010554.png (2014-5-5 01:06:22, 45.23 KB) / 下载次数 60
http://www.mf8-china.com/forum.php?mod=attachment&aid=MjM3Mjc2fDkwMjkzYzgwfDE3MTc0MDE1MzN8MHww
作者: honglei    时间: 2014-5-5 06:46:20

没试出来,看不到的可以下载uc浏览器.

作者: hubo5563    时间: 2014-5-5 08:58:32

本帖最后由 hubo5563 于 2014-5-5 09:39 编辑

我看了3×3×3的数据文件3x3x3.js,确实不少。我没用过SVG+JS 技术,提不出什么建议。
如果魔方块多时,最好用程序生成那些块的坐标数据。
我也没有猜到其他两个的名字。

作者: Fenz    时间: 2014-5-5 09:39:44

honglei 发表于 2014-5-5 06:46
没试出来,看不到的可以下载uc浏览器.

浏览器兼容会尽快完善的,但是旧版本的IE本身不支持SVG,没办法了
作者: Fenz    时间: 2014-5-5 10:54:59

hubo5563 发表于 2014-5-5 08:58
我看了3×3×3的数据文件3x3x3.js,确实不少。我没用过SVG+JS 技术,提不出什么建议。
如果魔方块多时,最 ...

数据结构是为包容各种奇形怪状和变形的魔方设计的,所以对于这些简单魔方,数据有很大冗余。
对于复杂魔方,怎么建立这些数据还真是令人头疼的问题。
坐标数据可利用对称性用程序生成,转动操作群的数据我却毫无思路,目前都是手工完成的。不知胡老师在这方面是怎么处理的。

SVG+JS只是决定展示的实现方式,程序原理本身应该还是一样的
作者: hubo5563    时间: 2014-5-5 11:16:33

[ 本帖最后由 hubo5563 于 2014-5-5 11:53 编辑 ]

转动操作的块轮换我也是手工输入的,魔方复杂时数据很多,例如百慕大五角24面体,24个转动面,每个面有11个块,我用一个数组表示,这些数据也是手工输入的:
private static final int[][] PiceLunHuan=
{      //24个面转动360/11度角的各个块的位置轮换
      {37,92, 36, 93,38, 94,39, 95,25, 80,24},     //0
      {24,80, 25, 81,26, 82,27, 83,28, 84,29},     //1
      {29,84, 28, 85,30, 86,31, 87,32, 88,33},     //2
      {33,88, 32, 89,34, 90,35, 91,36, 92,37},     //3
      {54,113,53,117,56,116,57,115,58,114,59},     //4
      {59,114,58,118,38, 93,36, 91,35,109,60},     //5
      {60,109,35, 90,34,108,50,107,49,110,55},     //6
      {55,110,49,106,48,111,52,112,53,113,54},     //7
      {65,123,64,127,71,128,72,129,73,131,74},     //8      
      {74,131,73,130,26, 81,25, 95,39,125,67},     //9      
      {67,125,39, 94,38,118,58,115,57,124,66},     //10      
      {66,124,57,116,56,121,63,122,64,123,65},     //11      
      {76,139,75,134,44, 99,41, 97,40,137,79},     //12      
      {79,137,40, 96,30, 85,28, 83,27,138,78},     //13      
      {78,138,27, 82,26,130,73,129,72,132,77},     //14      
      {77,132,72,128,71,126,70,133,75,139,76},     //15      
      {47,102,46,105,48,106,49,107,50,103,51},     //16      
      {51,103,50,108,34, 89,32, 87,31,104,43},     //17      
      {43,104,31, 86,30, 96,40, 97,41, 98,42},     //18      
      {42, 98,41, 99,44,100,45,101,46,102,47},     //19
      {69,135,70,126,71,127,64,122,63,120,62},     //20
      {62,120,63,121,56,117,53,112,52,119,61},     //21
      {61,119,52,111,48,105,46,101,45,136,68},     //22      
      {68,136,45,100,44,134,75,133,70,135,69}      //23
        
};
里面都是块的顺序号。
处理转动群的程序简单:

void lunhuancz(int n)
{   //正向转动一层后的块轮换操作
   int i,k;
   k=CubeX[PiceLunHuan[n][0]];
   
   for(i=1;i<11;i++)
   {
      CubeX[PiceLunHuan[n][i-1]]=CubeX[PiceLunHuan[n][i] ];
   };
   CubeX[PiceLunHuan[n][10]]=k;
   CubeTurn[n]=(CubeTurn[n]+1) % 11;
};

void lunhuanczf(int n)
{   //反向转动一层后的块轮换操作
   int i,k;
   
   
   k=CubeX[PiceLunHuan[n][10]];
   
   for(i=10;i>0;i--)
   {
      CubeX[PiceLunHuan[n][i]]=CubeX[PiceLunHuan[n][i-1] ];
   };
   CubeX[PiceLunHuan[n][0]]=k;
   CubeTurn[n]=(CubeTurn[n]+10) % 11;   
};

CubeTurn[]是一个记录每个面的转动角度数组,用来判断相邻面是否可以转动的,初始都是根据面编码程序自动生成的。
CubeX是个数组,记录转动后魔方各个位置的块序号,初始是顺序的。
n是转动的面号,每次转动根据转动方向执行上面对应的轮换程序。
至于块的方向,都反映在坐标里了,不需要记录。

每次转动时只转动转面牵涉到的块:

      for(i=0;i<11;i++)
        {
        zhuandong(Cube[CubeX[PiceLunHuan[n][i]]]);     //面上的棱块和角块
        };
        zhuandong(Cube[n]);                         //中心块

zhuandong()
就是对块里的每个3D点坐标变换。Cube[]是Piece 数组。

那些数据是根据下面的图输入的,图中粉色数是中心块号,蓝色数是棱块和角块号,需要把图中数加上24:因为块数组前24个是中心块。

轮换数据图.jpg





附件: 轮换数据图.jpg (2014-5-5 11:47:55, 811.08 KB) / 下载次数 49
http://www.mf8-china.com/forum.php?mod=attachment&aid=MjM3MjgzfDA5YWY1MTM3fDE3MTc0MDE1MzN8MHww
作者: otischeng    时间: 2014-5-5 12:52:01

本身想著隱藏的兩款魔方應該是Fenz最近討論的百慕大幾何體, 不過試了幾個可能的詞彙也沒有中. 看來是猜錯了.
作者: 高低手007    时间: 2014-5-5 14:05:43

不知道什么问题,打不开
作者: dongjiangfeng    时间: 2014-5-5 16:39:25

Chrome中测试通过!支持楼主的辛勤劳动
作者: redcarrot    时间: 2014-5-5 17:30:35

个人觉得空心点角块才能转的操作方式有点奇怪。。。还是想去点中间。。。
求公布剩下两个,试不出来!!!
作者: Fenz    时间: 2014-5-5 18:39:55

redcarrot 发表于 2014-5-5 17:30
个人觉得空心点角块才能转的操作方式有点奇怪。。。还是想去点中间。。。
求公布剩下两个,试不出来!!!

棱块预留给中层转动了,中间又没有块可以点。。不过倒是可以考虑加一些透明块在中间
作者: Fenz    时间: 2014-5-5 18:45:33

otischeng 发表于 2014-5-5 12:52
本身想著隱藏的兩款魔方應該是Fenz最近討論的百慕大幾何體, 不過試了幾個可能的詞彙也沒有中. 看來是猜錯了 ...

百慕大系列的数据比较复杂,还没着手呢。
那两个是做完了二阶,改三阶的过程中无意间弄出来的。看来光靠猜命中的概率太小了,今晚就把链接放出来。
作者: Fenz    时间: 2014-5-5 18:56:27

hubo5563 发表于 2014-5-5 11:16
[ 本帖最后由 hubo5563 于 2014-5-5 11:53 编辑 ]

转动操作的块轮换我也是手工输入的,魔方复杂时数据很多 ...

这数据好多。。。
现在才体会到胡老师做一个魔方软件背后的辛苦。

胡老师用轮换处理,在位置变化这点上,数据量比我的节省了3/4。值得借鉴一下。
作者: Fenz    时间: 2014-5-5 22:11:36

高低手007 发表于 2014-5-5 14:05
不知道什么问题,打不开

你用的是什么浏览器?
这个程序已经兼容Chrome、FireFox、Safari、Opera。
IE 10和11我的电脑系统不够新装不了,没有条件测,9以下是不行的,不支持SVG没有办法。
作者: honglei    时间: 2014-5-6 20:11:15

那两个隐藏的魔方看起来就是普通的二阶.
作者: 海上晴天    时间: 2014-5-6 22:09:45

支持楼主 SVG倒是想研究研究
作者: thindy    时间: 2014-5-6 22:27:51

隐藏的两个魔方其实都是一般的2x2x2而已,只不过是用电脑程序做成奇怪的样子,目前的可以估计造不出来,不过以后就难说了~
作者: Fenz    时间: 2014-5-6 23:07:32

thindy 发表于 2014-5-6 22:27
隐藏的两个魔方其实都是一般的2x2x2而已,只不过是用电脑程序做成奇怪的样子,目前的可以估计造不出来,不过 ...

是的,二阶改三阶的过程中搞出来的怪东西。真要造的话可以用像鱼竿那样的伸缩装置。不过估计没人有兴趣去去搞吧。
作者: Fenz    时间: 2014-5-6 23:19:06

honglei 发表于 2014-5-6 20:11
那两个隐藏的魔方看起来就是普通的二阶.

是的,变形的二阶。这个程序对变形的处理主要是为了让像非精准百慕大这样的魔方外观完整,那种小程度的变形不容易被感觉到。
这两个二阶是副产品中的简单例子,还可以弄出好些有趣的东西,比如那个分两半的二阶加上面、棱块,变成三阶,而面、棱块很多时候又会挤扁了看不见,转一转才能出现,玩起来别有味道。
作者: Fenz    时间: 2014-5-6 23:33:41

海上晴天 发表于 2014-5-6 22:09
支持楼主 SVG倒是想研究研究

SVG非常有意思,DOM结构的矢量图,玩起来就像在拼一些可变形的积木。而canvas 则像是一帧一帧地画画,图也是一个像素一个像素的位图。所以我更喜欢玩SVG。
作者: Fenz    时间: 2014-5-6 23:35:41

redcarrot 发表于 2014-5-5 17:30
个人觉得空心点角块才能转的操作方式有点奇怪。。。还是想去点中间。。。
求公布剩下两个,试不出来!!!

现在空心魔方的中间可以点了。
作者: 乌木    时间: 2014-5-7 10:35:59

本帖最后由 乌木 于 2014-5-7 11:37 编辑

初一看,以为那个“2x2x2 偏移”是如下这种捆绑魔方;再转动转动它们,两者大不同!那个“偏移二阶”有如通过一种严重而有规则的畸变透镜看二阶,一个块的形状会随着转动而变形(有如一个个变形虫,或者某种气态物质,所以无法做成实物),以致有一种魔幻效果,真有意思。

不知能否对三阶魔方也来个“块畸变三阶”?

[KBMFjava=450,400]
[param=MFlength]6[/param]
[param=MFwidth]6[/param]
[param=MFheight]6[/param]
[param=Speed]10[/param]
[param=bind]U:1155;6115;1651;F:1151;1255;6215;L:1151;1255;6215;B:2151;1215;2255;R:2151;1215;2255;D:1151;1255;6215;[/param]
[param=butbgcolor]99d658[/param]
[param=bgcolor]f3a0e2[/param]
[/KBMFjava]

作者: Fenz    时间: 2014-5-7 22:05:34

乌木 发表于 2014-5-7 10:35
初一看,以为那个“2x2x2 偏移”是如下这种捆绑魔方;再转动转动它们,两者大不同!那个“偏移二阶”有如通 ...

额,这个捆绑能用吗。。。
那个要做实物我想也是可以通过像鱼竿那样的伸缩原理做的。具体实现结构没有想过。
三阶也可以做畸变的,只要改一些坐标位置(虽然说是“只要”,目前的条件下也是个累人的差事)就能做到。
我这个程序本质就是把块的所有点按一定路径运动到目标点来实现块的运动,所以很容易实现变形。
作者: Fenz    时间: 2014-5-9 00:59:11

hubo5563 发表于 2014-5-5 11:16
[ 本帖最后由 hubo5563 于 2014-5-5 11:53 编辑 ]

转动操作的块轮换我也是手工输入的,魔方复杂时数据很多 ...

胡老师,请教一下视角转动的问题?
我的程序之前视角只有二维转动,现在写成三维转动,具体算法是这样的:
这里Dy和Dx是鼠标移动的“速度”(两个事件间单位时间移动的距离);
camera.lat是摄像机的纬度;
camera.lon是经度;
camera.rot是摄像机绕垂直于屏幕轴转的角度。
那几个三角函数是刚算好的。
                        camera.lat+=Dy*cosRot-Dx*sinRot;
                        var Dlon=(Dx*cosRot+Dy*sinRot)/cosLat;
                        camera.lon+=Dlon;
                        camera.rot+=Dlon*sinLat;

问题在于鼠标移动的事件是一步一步的,做不到真正的微分,所以会有少量的偏差,在一些地方偏差比较大,如果拖动得太快,偏差会大得无法忍受。
我看您的魔方是没有这个问题的,请问应该如何避免这个问题呢?
作者: hubo5563    时间: 2014-5-9 11:23:01

Fenz 发表于 2014-5-9 00:59
胡老师,请教一下视角转动的问题?
我的程序之前视角只有二维转动,现在写成三维转动,具体算法是这样的 ...

我处理的方法和你不同,鼠标拖动不改变模型的3D坐标。
我用一个矩阵记录转动积累,每次鼠标拖动计算x和y方向的转动角度,然后变换那个矩阵。
具体显示时,用当前矩阵变换模型中的点,再显示。
模型中的坐标只有转动魔方的某层时才变化,平常鼠标拖动是不变的。

作者: 至尊达哥    时间: 2014-5-9 15:42:45

奇怪的东东···········果然很奇怪
作者: Fenz    时间: 2014-5-9 19:32:43

hubo5563 发表于 2014-5-9 11:23
我处理的方法和你不同,鼠标拖动不改变模型的3D坐标。
我用一个矩阵记录转动积累,每次鼠标拖动计算x和y ...

其实我的也不会改变魔方的坐标,改变的是摄像机的坐标。然后再根据摄像机坐标来投影绘图。原理是一样的,区别在于算法。您用矩阵,我用的是坐标变换的微分关系。
我的算法是有问题的,所以想学习一下更好的算法。可否详细描述一下您的算法?
作者: hubo5563    时间: 2014-5-9 22:10:10

本帖最后由 hubo5563 于 2014-5-9 22:20 编辑
Fenz 发表于 2014-5-9 19:32
其实我的也不会改变魔方的坐标,改变的是摄像机的坐标。然后再根据摄像机坐标来投影绘图。原理是一样的, ...


程序:
//////////////////////////////////////////////
//鼠标拖动事件处理
public void mouseDragged(MouseEvent e){
    int x = e.getX();
    int y = e.getY();
    if (pushed)
    return;
    if (dragging) {
    if(prevy>top && prevy<height+top)
    {
    if (painted) {
      painted = false;
      double xtheta = (y-prevy) * (360.0d / getSize().width);     //计算按x轴转动的角度
      double ytheta = (prevx-x) * (360.0d / getSize().height);    //计算按y轴转动的角度
      SpinX(xtheta);                //变换矩阵
      SpinY(ytheta);                //变换矩阵
      Graphics gg=getGraphics();
      xianshimq(gg);             //显示魔方
      drawButtons(gg);           //画底下的按钮
      painted = true;
       };   
     };  
     };
    prevx = x;
    prevy = y;
    e.consume();
};



        初始化矩阵
       //将坐标变换矩阵设为单位矩阵
        da11=1;        da12=0;        da13=0;
        da21=0;        da22=1;        da23=0;
        da31=0;        da32=0;        da33=1;


void  SpinX(double jiao)
{
double a11,a12,a13,a21,a22,a23,a31,a32,a33;
double sina,cosa;
sina=Math.sin( jiao*Math.PI/180);
cosa=Math.cos( jiao*Math.PI/180);
a11=da11;
a21=cosa*da21+sina*da31;
a31=-sina*da21+cosa*da31;
a12=da12;
a22=cosa*da22+sina*da32;
a32=-sina*da22+cosa*da32;
a13=da13;
a23=cosa*da23+sina*da33;
a33=-sina*da23+cosa*da33;
//给全局变量赋值
da11=a11;da12=a12;da13=a13;
da21=a21;da22=a22;da23=a23;
da31=a31;da32=a32;da33=a33;
};
//以Y轴为轴魔方整体顺时针旋转一个角度
void  SpinY(double jiao)
{

        double a11,a12,a13,a21,a22,a23,a31,a32,a33;
double sina,cosa;
sina=Math.sin( jiao*Math.PI/180);
cosa=Math.cos( jiao*Math.PI/180);
a11=cosa*da11+sina*da31;
a21=da21;
a31=-sina*da11+cosa*da31;
a12=cosa*da12+sina*da32;
a22=da22;
a32=-sina*da12+cosa*da32;
a13=cosa*da13+sina*da33;
a23=da23;
a33=-sina*da13+cosa*da33;
//给全局变量赋值
da11=a11;da12=a12;da13=a13;
da21=a21;da22=a22;da23=a23;
da31=a31;da32=a32;da33=a33;
};

//以当前旋转矩阵变换三维点,整体旋转时采用
void  dbh3(Point3d p,Point3d pp)
{
        p.x=(pp.x)*da11+(pp.y)*da12+(pp.z)*da13;
        p.y=(pp.x)*da21+(pp.y)*da22+(pp.z)*da23;
        p.z=(pp.x)*da31+(pp.y)*da32+(pp.z)*da33;
};

//将三维坐标转换为2维透视坐标,转换x坐标
int  dbh5x(Point3d p)
{
double xx,zz;int ii;
xx=p.x+xx1;
zz=p.z+zz1;
xx=xx*zz0/(zz0+zz);
xx=xx0+factor_x*xx;
ii=(int)xx;
return ii;
};
//将三维坐标转换为2维透视坐标,转换y坐标
int  dbh5y(Point3d p)
{
double yy,zz;int ii;
yy=p.y+yy1;
zz=p.z+zz1;
yy=yy*zz0/(zz0+zz);
yy=yy0-factor_y*yy;
ii=(int)yy;
return ii;
};

经过变换就是屏幕的实际坐标。
作者: Fenz    时间: 2014-5-9 23:50:26

本帖最后由 Fenz 于 2014-5-10 00:07 编辑
hubo5563 发表于 2014-5-9 22:10
程序:
//////////////////////////////////////////////
//鼠标拖动事件处理


多谢胡老师。我慢慢学习。
今晚我又写了另一个基于坐标变换的算法,看看是否正确可用,如果还行,就要借用您的算法了。

--------------
原来如此,我用的是通过三个表示摄像机状态的角来完成从世界坐标系到屏幕坐标的变换,相当于每次通过三角函数的计算得到变换矩阵,而您的程序则是直接储存这个变换矩阵来表示摄像机状态。
作者: Fenz    时间: 2014-5-10 16:16:26

hubo5563 发表于 2014-5-9 22:10
程序:
//////////////////////////////////////////////
//鼠标拖动事件处理

最后还是改用了您的方法,用矩阵表示摄像机位置和方向,这样比我原来的算法少了许多三角函数和反三角函数(以及带来的正负判断)等计算。矩阵这块看来我需要补一补。
作者: hubo5563    时间: 2014-5-11 08:06:57

Fenz 发表于 2014-5-10 16:16
最后还是改用了您的方法,用矩阵表示摄像机位置和方向,这样比我原来的算法少了许多三角函数和反三角函数 ...


作者: schuma    时间: 2014-5-21 11:41:34

不错。我还没用过SVG来做过魔方。干得好!
作者: Fenz    时间: 2014-5-21 13:04:56

schuma 发表于 2014-5-21 11:41
不错。我还没用过SVG来做过魔方。干得好!

svg比较面向对象,事件绑定比较方便。记得你的魔方是用canvas做的吧。
原来也想把你的反射魔方也加进去,但是还有一点法线问题。
要改进的地方蛮多,不打算改了,直接写2.0的节奏。
作者: schuma    时间: 2014-5-21 13:20:55

Fenz 发表于 2014-5-20 21:04
svg比较面向对象,事件绑定比较方便。记得你的魔方是用canvas做的吧。
原来也想把你的反射魔方也加进去, ...

不错。我回头也试试SVG
作者: zhang2345    时间: 2019-10-6 09:42:22

看得出来:1#、2#、3#、7#、10#都是大神呀,向大神致敬。6#那些数据,那个大图,不要说其中关系,就是照着画画都费老劲。




欢迎光临 魔方吧·中文魔方俱乐部 (http://www.mf8-china.com/) Powered by Discuz! X2