constructivesolidgeometry有很多实际用途,它用于需要简单几何对象的情况,或者数学精度很重要的情况,几乎所有的工程CAD包都使用CSG(可以用来表示工具切割,以及零件必须装配在一起的特征)。CSG是ConstructiveSolidGeometry建模技术的缩写。通过切减,合并并交运算,组合复杂的模型效果。HT封装了ht.CSGNode、ht.CSGShape等原始类型,支持CSG的组合功能。常用于墙面镂空门窗的应用场景。CSG对象可以用二叉树表示,其中叶子代表原语,节点代表操作。在此图中,节点标记为∩表示交集,∪表示并集,-表示差异。CSG提供的模型或表面看起来很复杂,但实际上只不过是巧妙地组合或分解对象。ht.CSGNode继承自ht.Node。当style的shape3d属性为空时,显示六面体效果。如果通过setHost将CSGNode吸附到宿主CSGNode或CSGShape上,则宿主CSGNode或CSGShape可以与吸附的CSGNode图元结合创建CSG模具。详情请参考《HTforWebModelingManual》CSGNode章节。这里我用CSG的概念写了一个例子,方便大家更好的理解这个概念。本例的演示地址:http://hightopo.com/guide/guide/plugin/modeling/examples/example_bookshelf.html首先看效果图:从上面的效果图可以看出,我们将界面分为三部分,这三个部分首先在右侧进行上下分割,然后将整个界面进行左右分割。HT使用封装后的ht.widget.SplitView来划分界面,然后在底层div中添加拆分组件:dm=newht.DataModel();//DatamodeltreeView=newht.widget.TreeView(dm);//树组件gv1=newht.graph3d.Graph3dView(dm);//3D组件gv2=newht.graph3d.Graph3dView(dm);splitView\=newht.widget.SplitView(gv1,gv2,'v',0.6);//拆分组件mainSplit=newht.widget.SplitView(treeView,splitView,'h',0.27);查看\=mainSplit.getView();view.className\='main';document.body.appendChild(view);window.addEventListener('resize',function(e){mainSplit.invalidate();},false);上面的代码是一种很常见的在HTML中添加HT组件的方法。详见HTforWeb入门手册组件章节。在这种方式添加HT组件的时候有一个需要注意的地方,因为HT一般使用设置position为absolute的绝对定位方式,必须设置left,right,top,bottom等基本的css样式,比如这个:.main{保证金:0px;填充:0px;位置:绝对;顶部:0px;底部:0px;左:0px;right:0px;}所以为了方便加载最外层组件填满窗口,HT的所有组件都有addToDOM函数,其思想逻辑如下,其中iv是invalidate的缩写:addToDOM=function(){varself=this,view\=self.getView(),style\=view.style;document.body.appendChild(视图);样式.left\='0';style.right\='0';style.top\='0';style.bottom\='0';window.addEventListener('resize',function(){self.iv();},false);以后我们可以直接在代码中调用addToDOM函数,不用写很多代码。上面的代码换成addToDOM如下,不需要绘制css样式:dm=newht.DataModel();//datamodeltreeView=newht.widget.TreeView(dm);//树组件gv1=newht.graph3d.Graph3dView(dm);//3D组件gv2=newht.graph3d.Graph3dView(dm);splitView\=newht.widget.SplitView(gv1,gv2,'v',0.6);//拆分组件mainSplit=newht.widget.SplitView(treeView,splitView,'h',0.27);mainSplit.addToDOM();接口分配好之后,我们就往里面添加内容。界面左边部分是HT封装的树组件。我在上一篇文章中写到,树组件是一个非常方便的绘制树状关系的组件。开发者可以方便的从数据模型DataModel中获取数据和节点间的关系,并将其放到树上,只需要在树组件声明的过程中,将对应的数据模型DataModel放入树组件的参数中即可。当然我们也扩展了很多树组件相关的功能,非常方便实用。这里我们只使用expandAll函数展开所有对象:treeView=newht.widget.TreeView(dm);//树组件treeView.expandAll();右边上下两部分,都是3D场景,只是设置显示有点不同,其他完全一样。上面的3D场景重载了getVisibleFunc函数,如果元素的showMe属性为true,则可见;如果节点是ht.CSGNode类型,节点的getHost函数参数为空,则不可见;否则可见:gv1.setVisibleFunc(function(data){if(data.showMe){returntrue;}if(datainstanceofht.CSGNode&&data.getHost()){returnfalse;}returntrue;});我们先在3D场景中添加元素对象,我们先解释一下中间的Bookshelves,补充两边缺失的Bookshelves首先,我们添加一个ht.CSGNode节点shelf,作为书架的主节点,其他节点依附于这个节点,为这个节点设置六个边的位置、大小、名称和颜色,然后添加到数据模型DataModel:varshelf=newht.CSGNode();shelf.s3(500,400,120);shelf.p3(0,200,0);shelf.setName('shelf1');shelf.s({'all.color':'#E5BB77'});dm.add(货架);然后在这个书架上添加10个节点,做成书架的网格效果,并设置依附关系和父子关系添加到数据模型中:for(vari=0;i<2;i++){for(varj=0;j<5;j++){varclipNode=newht.CSGNode();clipNode.setHost(架子);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(架子);dm.add(剪辑节点);}}为了让书架更美观,我们在书架的上下左右分别添加了ht.CSGNode,最后为了更具体,我们还添加了一本书,实现方式类似,非常简单:varbook=newht.Node();book.setName('CSS3:TheMissingManual');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=newht.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显示正面内容'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=newht.Node();earth.setName('earth');earth。s3(70,70,70);earth.p3(0,50,0);earth.s({'shape3d':'sphere','shape3d.image':'earth'});earth.setHost(架子);earth.setParent(架子);dm.add(地球);右边的书架也有一个主节点,其他节点依附在它上面,但是我们看到这里换成了一个新的节点类型ht.DoorWindow,ht.DoorWindow继承自ht.CSGNode。整体旋转、展开、关闭操作常作为门窗业务对象,吸附到CSGNode或CSGShape的宿主作为墙体元素。此节点类型是ht.CSGNode的扩展。相对来说,增加不同的样式参数来区分实际应用。更多的属性请去HTforWebModelingManual的DoorWindow章节添加到节点中玩:photos=newht.DoorWindow();photos.setName('DoorWindow-Photos');photos.setIcon('ben12');photos.s3(110,100,130);photos.p3(5,180,0);照片.setHost(货架);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(照片);最后,我们将左边的地球旋转到右边的照片photo:varangle=0;setInterval(function(){angle+=Math.PI/40;earth.r3(0,angle,0);photos.s('dw.angle',角度);},50);我们可以看到,HT虽然封装了很多不同的CSG节点类型,但是实际的应用都是大同小异的,内容也没有太大区别。区别在于样式参数,但确实在实际开发中,这种区别会大大加快开发速度。毕竟名字一目了然,知道使用哪些样式属性。
