CSG 构造实体几何这个概念在工业水利水电施工上、游戏上已经有很多人使用了,最简单的实体表示叫作体元,通常是形状简单的物体,如立方体、圆柱体、棱柱、棱锥、球体、圆锥等。根据每个软件包的不同这些体元也有所不同,在一些软件包中可以使用弯曲的物体进行 CSG 处理,在另外一些软件包中则不支持这些功能。构造物体就是将体元根据集合论的布尔逻辑组合在一起,这些运算包括:并集、交集以及补集。我们一般可以用 CSG 来将简单的模型合在一起生成复杂的模型,这样在构造模型的时候会省很多力。

HT 中的 ht.CSGNode 图元类型就是参考 CSG 封装的一个函数,ht.CSGNode 继承于 ht.Node,当 style 的 shape3d 属性为空时显示为六面体效果,CSGNode如果通过 setHost 吸附到 宿主 CSGNode 或 CSGShape 后,宿主 CSGNode 或 CSGShape 可与吸附的CSGNode图元进行CSG的组合建模。详情请参考 HT for Web 建模手册CSGNode 章节。这里我用 CSG 的概念写了一个例子,让大家能更好地理解这个概念。

本例 Demo 地址: http://hightopo.com/guide/guide/plugin/modeling/examples/example_bookshelf.html

先来看下效果图:

从上面效果图可以看到,我们将界面分为三个部分,这三个部分先是右边部分上下分割,然后将整个界面左右分割,HT 用封装好的 ht.widget.SplitView 进行界面的分割,然后将分割组件添加进底层 div 中:

 dm = new ht.DataModel();// 数据模型
 treeView = new ht.widget.TreeView(dm); //树组件
 gv1 = new ht.graph3d.Graph3dView(dm); //3D 组件
 gv2 = new ht.graph3d.Graph3dView(dm);
 splitView = new ht.widget.SplitView(gv1, gv2, 'v', 0.6);//分割组件
 mainSplit = new ht.widget.SplitView(treeView, splitView, 'h', 0.27);   

 view = mainSplit.getView();
 view.className = 'main';
 document.body.appendChild(view);
 window.addEventListener('resize', function (e) {
     mainSplit.invalidate();
 }, false);       

上面代码是一种非常常见的在 HTML 中添加 HT 组件的方法,详情可参考 HT for Web 入门手册组件章节。这种方法进行添加 HT 组件有一个需要注意的点,因为 HT 一般都以设置 position 为 absolute 的绝对定位方式,必须设置 left、right、top、bottom 等等基础 css 样式,像这样:

 .main {
      margin: 0px;
      padding: 0px;
      position: absolute;
      top: 0px;
      bottom: 0px;
      left: 0px;
      right: 0px;
 }

所以为了最外层组件加载填充满窗口的方便性,HT 的所有组件都有 addToDOM 函数,其思想逻辑如下,其中 iv 是 invalidate 的缩写:

 addToDOM = function(){
     var self = this,
     view = self.getView(),
     style = view.style;
     document.body.appendChild(view);
     style.left = '0';
     style.right = '0';
     style.top = '0';
     style.bottom = '0';
     window.addEventListener('resize', function () { self.iv(); }, false);
 }  

以后我们在代码中就可以直接调用 addToDOM 函数,而不用写一大堆代码了,上面代码用 addToDOM 取代之后的代码如下,而且不用描绘 css 样式:

 dm = new ht.DataModel();// 数据模型
 treeView = new ht.widget.TreeView(dm); //树组件
 gv1 = new ht.graph3d.Graph3dView(dm); //3D 组件
 gv2 = new ht.graph3d.Graph3dView(dm);
 splitView = new ht.widget.SplitView(gv1, gv2, 'v', 0.6);//分割组件
 mainSplit = new ht.widget.SplitView(treeView, splitView, 'h', 0.27);
 mainSplit.addToDOM();

界面分配好之后我们就要对其添加内容了,界面的左边部分是 HT 封装的树组件,我在之前的文章写到过,树组件是一个非常方便的绘制树形关系的组件,开发人员能够轻松地从数据模型 DataModel 中获取数据和节点之间的关系放到树上,只需要在树组件声明的过程中,将对应的数据模型 DataModel 放进树组件的参数即可,当然我们还扩展了很多跟树组件有关的函数,非常方便实用,这里我们只用了 expandAll 函数,将所有对象展开:

 treeView = new ht.widget.TreeView(dm); //树组件
 treeView.expandAll();

右边部分上下分为两部分,都是 3D 场景,就是设置显示有点不同,其他完全相同,上面的 3D 场景重载了 getVisibleFunc 函数,如果元素的 showMe 属性为 true,则可视;如果节点为 ht.CSGNode 类型并且节点的 getHost 函数的参数为空,则不可视;其他情况均可视:

 gv1.setVisibleFunc(function(data){
     if(data.showMe){
         return true;
     }
     if(data instanceof ht.CSGNode && data.getHost()){
         return false;
     }
     return true;
 });

我们先向 3D 场景中添加元素对象,我们先解释中间的书架,对两边的书架有缺的再进行补充。首先我们添加了一个 ht.CSGNode 节点 shelf,作为书架的主节点,其他的节点都是依附于这个节点的,对这个节点设置了位置、大小、名称以及六个面的颜色,然后添加进数据模型 DataModel:

 var shelf = new ht.CSGNode();
 shelf.s3(500, 400, 120);
 shelf.p3(0, 200, 0);
 shelf.setName('shelf1');
 shelf.s({
     'all.color': '#E5BB77'
 });
 dm.add(shelf);

接着向这个 shelf 中添加 10 个节点,做书架的格子效果,并设置依附关系和父子关系添加进数据模型中:

 for(var i=0; i<2; i++){
     for(var j=0; j<5; j++){
         var clipNode = new ht.CSGNode();
         clipNode.setHost(shelf);
         clipNode.s3(80, 100, 120);
         clipNode.p3(-200+j*100, 340-i*120, 20);
         clipNode.setName('substract-'+i+'-'+j);
         clipNode.s('batch', 'tt');
         clipNode.setParent(shelf);
         dm.add(clipNode);
     }
 }

为了让书架变得更美观一点,我们在书架的上下左右都加上了 ht.CSGNode,最后为了更加具象化,我们还添加了一本书,实现方式也差不多,都非常简单:

 var book = new ht.Node();
 book.setName('CSS3: The Missing Manual');
 book.s3(60, 80, 8);
 book.p3(-100, 210, 20);
 book.r3(-Math.PI/6, Math.PI/5, 0);
 book.setIcon('book');
 book.s({
     'front.image': 'book',
     'back.color': 'white',
     'left.color': 'white',
     'all.color': 'gray'
 });
 book.setHost(shelf);
 book.setParent(shelf);
 dm.add(book);   

接着左边的书架也是类似的构建方法,有一点不同的是,这边有一个 ht.CSGBox 类型,继承于 ht.CSGNode,其除具备父类 CSGNode 的挖空等功能外,还可对六个面进行旋转展开关闭的操作,这里我们的节点只设置了前面的能够旋转展开,并且设置了一系列的样式:

 clipNode = new ht.CSGBox();
 clipNode.setName('CSGBox-Expand-Left');
 clipNode.s3(100, 100, 120);
 clipNode.p3(0, 65, 0.1);
 clipNode.setHost(shelf);
 clipNode.showMe = true;
 clipNode.s({
     'all.visible': false,//6面均不可见
     'front.visible': true,//前面可见
     'front.toggleable': true,//允许前面双击展开
     'front.reverse.flip': true,//前面的反面显示正面的内容
     'front.transparent': true,//前面透明
     'front.end': Math.PI * 0.7,//前面展开状态的结束旋转弧度
     'front.color': 'rgba(0, 50, 50, 0.7)'//前面颜色
 });

可能你们还想知道下面的地球是怎么做到的?还记得之前的文章写到过 HT 中设置了 shape3d 属性,设置这个属性实际上就是在操作 setShape3dModel(name, model) 和 getShape3dModel(name),可以通过这个属性设置为 box|sphere|cylinder|cone|torus|star|rect|roundRect|triangle|rightTriangle|parallelogram|trapezoid 等等模型,这些模型也都是 HT 封装好的,要使用时直接设置 shape3d 为其中的一个值即可,如这个例子中用到 “shape3d: sphere” 就是设置为球体。我们简单地用一张地图图片包裹在这个球体的外侧,当然,这张地图图片是先通过 ht.Default.setImage 注册过的,然后通过 shape3d.image 将图片附到这个节点上:

 earth = new ht.Node();
 earth.setName('earth');
 earth.s3(70, 70, 70);
 earth.p3(0, 50, 0);
 earth.s({
     'shape3d': 'sphere',
     'shape3d.image': 'earth'
 });
 earth.setHost(shelf);
 earth.setParent(shelf);
 dm.add(earth);

右边的书架,同样也是有一个主节点,其他节点依附于它,但是我们看到这边换了一个新的节点类型 ht.DoorWindow,ht.DoorWindow继承于 ht.CSGNode,其除具备父类 CSGNode 的挖空等功能外,还可进行整体的旋转展开关闭的操作, 常用于作为门或窗的业务对象,吸附于 CSGNode 或 CSGShape 的 host 作为墙面的图元。这个节点类型就是 ht.CSGNode 的延展,相对来说就是区分了实际应用而添加了不同的 style 参数,更多的属性请到 HT for Web 建模手册 DoorWindow 章节 查看然后添加到节点中玩玩:

 photos = new ht.DoorWindow();
 photos.setName('DoorWindow-Photos');
 photos.setIcon('ben12');
 photos.s3(110, 100, 130);
 photos.p3(5, 180, 0);
 photos.setHost(shelf);
 photos.showMe = true;
 photos.s({
     'bottom.uv': [1,1, 1,0, 0,0, 0,1],
     'bottom.uv.scale': [1, 1],
     'left.uv.scale': [3, 3],
     'top.uv.scale': [2, 2],
     'dw.s3': [0.8, 0.9, 0.05],
     'dw.t3': [0, -5, 0],
     'dw.axis': 'v',
     'dw.toggleable': false,
     'front.image': 'ben1',
     'back.image': 'ben2',
     'all.color': '#F8CE8B'
 });
 photos.setParent(shelf);
 dm.add(photos);

最后,我们将左侧的地球 earth 和右侧的照片 photo 旋转起来:

 var angle = 0;
 setInterval(function(){
     angle += Math.PI/40;
     earth.r3(0, angle, 0);
     photos.s('dw.angle', angle);
 }, 50);

我们看到,其实虽然 HT 封装了很多不同的 CSG 节点类型,但是实际应用都差不多,而且内容也没有差特别多,差别都是在 style 参数上,但是真的在实际开发中,这种区分就会很大程度上加快开发速度,毕竟名称一目了然,就知道要运用哪些 style 属性了。

基于HTML5 Canvas的CSG构造实体几何书架的更多相关文章

  1. 基于HTML5 Canvas的网页画板实现教程

    HTML5的功能非常强大,尤其是Canvas的应用更加广泛,Canvas画布上面不仅可以绘制任意的图形,而且可以实现多种多样的动画,甚至是一些交互式的应用,比如网页网版.这次我们要来看的就是一款基于H ...

  2. 基于html5 Canvas图表库 : ECharts

    ECharts开源来自百度商业前端数据可视化团队,基于html5 Canvas,是一个纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表.创新的拖拽重计算.数据视图.值 ...

  3. 基于HTML5 Canvas实现的图片马赛克模糊特效

    效果请点击下面网址: http://hovertree.com/texiao/html5/1.htm 一.开门见山受美国肖像画家Chuck Close的启发,此脚本通过使用HTML5 canvas元素 ...

  4. 基于html5 canvas和js实现的水果忍者网页版

    今天爱编程小编给大家分享一款基于html5 canvas和js实现的水果忍者网页版. <水果忍者>是一款非常受喜欢的手机游戏,刚看到新闻说<水果忍者>四周年新版要上线了.网页版 ...

  5. 基于HTML5 Canvas的线性区域图表教程

    之前我们看到过很多用jQuery实现的网页图表,有些还是比较实用的.今天我们来介绍一款基于HTML5 Canvas的线性区域图表应用,这个图表应用允许你使用多组数据来同时展示,并且将数据结果以线性图的 ...

  6. 基于HTML5 Canvas实现用户交互

    很多人都有这样的疑问,基于HTML5 Canvas实现的元素怎么和用户进行交互?在这里我们用到HT for Web(http://www.hightopo.com/guide/guide/core/b ...

  7. 基于html5 canvas 的客户端异步上传图片的插件,支持客户端压缩图片尺寸

    /** * Created by xx on 15-05-28. * 基于html5 canvas 的客户端异步上传画片的插件 * 在实际应用中,常常要用于上传图片的功能.在现在越来越多的手机weba ...

  8. 基于 HTML5 Canvas 的智能安防 SCADA 巡逻模块

    基于 HTML5 Canvas 的智能安防 SCADA 巡逻模块 前言 最近学习了 HT for Web flow 插件,除了正常的 flow 效果,其中还有两个十分好用的两个接口 getPercen ...

  9. 基于HTML5 Canvas粒子效果文字动画特效

    之前我们分享过很多超酷的文字特效,其中也有利用HTML5和CSS3的.今天我们要来分享一款基于HTML5 Canvas的文字特效,输入框中输入想要展示的文字,回车后即可在canvas上绘制出粒子效果的 ...

随机推荐

  1. 终极解决方案 at org.apache.jsp.index_jsp._jspInit(index_jsp.java:22) 报空指针

    java.lang.NullPointerException  at org.apache.jsp.index_jsp._jspInit(index_jsp.java:22) 出现这种问题,可能有多方 ...

  2. JavaScript案例开发之扑克游戏

    随着时代的发展,知识也在日益更新,但是基础知识永远不会过时,它是新时代的基石,更是我们进一步学习的保障,下面带着大家用JavaScript开发一款真正的扑克游戏,和大家一起分享,希望你们能够喜欢:闲话 ...

  3. HIT 1917 Peaceful Commission

    这道题题意就是给你n对人,一对中编号为x,x+1,给你m对矛盾,表示这两个人不能同时选. 然后就是Two-Sat的模板题了,就是根据对称性,连边,加缩点,最后拓扑排序,求出一组可行解就可以了. #in ...

  4. webpack2使用ch4-向根目录index.html文件传参并使用参数 使用线上资源 压缩html

    1 webpack.config.js const webpack = require('webpack'), htmlWebpackPlugin = require('html-webpack-pl ...

  5. linux下rename用法--批量重命名

    Linux的rename 命令有两个版本,一个是C语言版本的,一个是Perl语言版本的,早期的Linux发行版基本上使用的是C语言版本的,现在已经很难见到C语言版本的了, 由于历史原因,在Perl语言 ...

  6. HTML的有序列表

    针对HTML的有序列表,由于平常使用的不是很多,刚开始使用的时候也是有遇到一些坑,有几个小问题: 1.li的宽度不能设置为100%,这样的话就没办法看到前面的序号 2.如果设置li的颜色字体大小,前面 ...

  7. (转载)RESTful架构风格下的4大常见安全问题

    转载自<RESTful架构风格下的4大常见安全问题>,作者:马伟 伴随着RESTful架构风格的大量应用微服务架构的流行,一些本来难以察觉到的安全问题也逐渐开始显现出来.在我经历过的各种采 ...

  8. 广州图书馆借阅抓取——httpClient的使用

    欢迎访问我的个人网站,要是能在GitHub上对网站源码给个star就更好了. 搭建自己的网站的时候,想把自己读过借过的书都想记录一下,大学也做过自己学校的借书记录的爬取,但是数据库删掉了==,只保留一 ...

  9. ios获取内核数目

    #include <mach/mach_host.h> unsigned int countCores() { host_basic_info_data_t hostInfo; mach_ ...

  10. 史上最难的一道Java面试题 (分析篇)

    博客园 匠心零度 转载请注明原创出处,谢谢! 无意中了解到如下题目,觉得蛮好. 题目如下: public class TestSync2 implements Runnable { int b = 1 ...