union/subtract/intersect(布尔运算)

语法

  • union { operationsA | operationsB }
  • subtract { operationsA | operationsB }
  • intersect { operationsA | operationsB }

参数

  1. operationsA
    应用于运算对象 A 的当前形状的一系列操作。
  2. operationsB
    应用于运算对象 B 的当前形状的一系列操作。

描述

unionintersectsubtract 操作将对内联 operationsA 的结果(运算对象 A)和 operationsB 内联的结果(运算对象 B)执行布尔运算。 构造运算对象类似于使用 inline(unify),即首先使用布尔 union 操作合并由 operationsA(或 operationsB)推导得到的形状。

第一个运算对象 A 将确定生成的形状和规则属性,并指定用于叠加几何的材料。 如果运算对象具有冲突的材料属性,则首先将其材料写入几何,由此确保保留所有材料属性。

组件标签

操作自动将语义组件标签应用于生成的面组件:

union、subract 和 intersect 组件标签

"bool.A"

"bool.B"

"bool.cut"

来自运算对象 A 的面(蓝色)

来自运算对象 B 的面(绿色)

相交边(橙色)

Left   --> union { Cube | Sphere }
           comp(f) { isTagged("bool.A")   : Blue 
                   | isTagged("bool.B")   : Green  }
           comp(e) { isTagged("bool.cut") : Orange }

Cube   --> primitiveCube
Sphere --> t(0.3,0.3,0.3) primitiveSphere

Middle --> subtract  { Cube | Sphere } ...
Right  --> intersect { Cube | Sphere } ...

注:
对于重叠面(2D 相交),同时应用 "bool.A""bool.B"

有关使用组件标签的详细信息,请参阅:自动标签标签传播

开放和闭合运算对象

布尔运算在 2D(平面)和 3D 闭合网格中都有明确的定义。 在 3D 中,只要边界不相交,开放运算对象(具有孔洞的平面和网格)均被视为闭合。 请参阅以下参考表示例

在后续 comp 操作中,通过使用“bool”自动标签功能,可以轻松实现“Plane subtract Volume”和“Plane intersect Volume”这两种常见用例。 以下示例对此进行了说明:

交叉点类结果
subtract { Blue | Green }
intersect { Blue | Green }
交叉点类标签
comp(f) { isTagged("bool.A") : Blue }

参考表

两个运算对象均为闭合状态

开放和闭合运算对象

平面法线朝上

开放和闭合运算对象

平面法线朝下

开放和闭合运算对象

仅部分相交

输入运算对象

输入运算对象
union { Blue 
      | Green }
union (blue,green)
subtract { Blue 
         | Green }
subtract (blue,green)
subtract { Green 
         | Blue }
subtract (green,blue)
intersect { Blue 
          | Green }
intersect (blue,green)
inline(unify) {
    Green 
    Blue 
}
inline unify (green,blue)

相关内容

示例

颜色

这些规则适用于在以下示例中使用的颜色。

Blue   --> color("#0399F5")
Green  --> color("#09DE1F")
Yellow --> color("#FADB19")
Purple --> color("#8D09DE")
Red    --> color("#FF360A")
Orange --> color("#FA9100")

运算对象的顺序

Left     --> subtract { Cube     | Cylinder }
Right    --> subtract { Cylinder | Cube     }

Cube     --> primitiveCube 
             Blue

Cylinder --> t(3,3,3)
             primitiveCylinder
             Green

从立方体中减去圆柱体,反之亦然。

减去圆柱体
Left   --> union { Quad   | Circle }
Right  --> union { Circle | Quad }
 
Quad   --> primitiveQuad 
           Blue

Circle --> t(3,0,3)
           primitiveDisk
           Green

第一个运算对象 A 将确定叠加面的材料。

运算对象 A 将确定叠加面的材料。
attr Height = 0

Left   --> intersect { Quad   | Circle }
           extrude(Height) // Height == 10
    
Right  --> intersect { Circle | Quad   }
           extrude(Height) // Height == 1

Quad   --> set(Height, 10)
           primitiveQuad

Circle --> set(Height, 1)
           t(3,0,3)
           primitiveDisk

第一个运算对象 A 将确定生成形状的属性。

运算对象 A 将确定生成形状的属性

运算对象内联

Left  --> primitiveCube
          union { Blue
                | t(1,1,1) Green }
    
Right --> primitiveCube
          union { Blue
                | t(1,1,1) Green
                  t(1,1,1) Green
                  t(1,1,1) Green }

一个运算对象可以产生单个形状 (Left) 或多个形状(Right 中的运算对象 B)。 如果存在多个形状,则首先将其合并,类似于使用 inline(unify)

运算对象产生单个形状和多个形状
Init --> primitiveCube
         union { Blue
               | t(1,1,1+10) Green
                 t(1,1,1)    Green
                 t(1,1,1)    Green }

如果运算对象产生多个形状,即使它们未与运算对象 A 相交,它们也是统一的。

运算对象产生多个形状

体量建模

Init -->
    intersect { Envelope | Sphere }

Envelope --> 
    extrude(40)

Sphere --> 
    s('1, 40, '2)
    center(z)
    t(0, '-0.2, 0)
    primitiveSphere

步骤 1:已拉伸的初始形状(当前形状,作为运算对象 A)与插入的球体(规则 Sphere 的结果,作为运算对象 B)相交。

布尔半球相交
Init -->
    intersect { Envelope | Sphere }
    inline(unify) split(y) { ~1: LowerHalf
                           | 20: UpperHalf }

LowerHalf --> X.
UpperHalf --> t(10, 0, 0) 
              r(scopeCenter, 0, 7.2, 0)

步骤 2:将当前形状分割成两部分。 如果使用 inline(unify),则分割操作的生成形状 LowerHalfUpperHalf 统一(注意消失的内表面)。

布尔半球统一
Init -->
    intersect { Envelope | Sphere }
    inline(unify) split(y) { ~1: LowerHalf
                           | 20: UpperHalf }
    subtract { CurrentMass. | Hole }
    
Hole -->
    s(16, 16, '1.2)
    center(z)
    t(30, 5, 0)
    rotateScope(-90, 0, 0)
    primitiveCylinder

步骤 3:我们将通过减去一个圆柱体(规则 Hole 的结果,用作运算对象 B)来继续细化体量(步骤 1 和 2 后的当前形状,作为运算对象 A)。

布尔半球减去
Init -->
    intersect { Envelope | Sphere }
    inline(unify) split(y) { ~1: LowerHalf
                           | 20: UpperHalf }
    subtract { CurrentMass. | Hole }
    comp(f) { isTagged("Envelope") && side   : Blue
            | isTagged("Lower")    && top    : Red
            | isTagged("Upper")    && bottom : Orange
            | isTagged("Lower")              : Green
            | isTagged("Upper")              : Yellow
            | isTagged("Hole")               : Purple }

Envelope -->
    extrude(40)
    tag("envelope")

Sphere --> 
    s('1, 40, '2)
    center(z)
    t(0, '-0.2, 0)
    primitiveSphere

LowerHalf --> tag("Lower")
UpperHalf --> 
    t(10, 0, 0) 
    r(scopeCenter, 0, 7.2, 0)
    tag("Upper")
              
Hole --> 
    s(16, 16, '1.2)
    center(z)
    t(30, 5, 0)
    rotateScope(-90, 0, 0)
    primitiveCylinder
    tag("Hole")

添加标签:使用 3D 布尔操作,可以轻松创建具有复杂表面的体量模型。 在本示例中,在每个步骤中添加了标签,由此可在最后为不同的表面分配正确的规则(此处使用不同的颜色表示)。

布尔半球 3 个标签

3D 布尔和自动标签

Init --> 
    extrude(40)
    subtract { Mass.
             | t(20, -15, -15)
               rotate(rel, scope, 15, 12, 25) }
    SurfaceSplitter

SurfaceSplitter -->
    comp(f) { isTagged("extrude.top") 
                  && isTagged("bool.A"): Purple
            | isTagged("extrude.top") 
                  && isTagged("bool.B"): Green
            | isTagged("extrude.side")
                  && isTagged("bool.A"): Blue
            | isTagged("extrude.side") 
                  && isTagged("bool.B"): Yellow }

在本示例中,首先拉伸初始形状,由此可向所有面添加“extrude自动标签。 然后,减去形状的平移和旋转副本,由此可向生成的面额外添加“bool”自动标签。 最后,这些标签用于提取不同的表面并对其进行相应着色。

3D 布尔和自动标签

布局建模

Init -->
    primitiveDisk(5)
    union { HouseFootprint. | FrontyardFootprint }
    ShowBoolAutotags
    
FrontyardFootprint -->
    s(10, 0, 10)
    center(x)
    t(0, 0, '-0.33)
    
ShowBoolAutotags -->
    comp(f) { isTagged("bool.A")
                  && isTagged("bool.B"): Yellow
            | isTagged("bool.A")       : Blue
            | isTagged("bool.B")       : Green }

步骤 1:插入五边形 (primitiveDisk(5)),然后用缩小的副本执行 union 操作。 union 操作将保留所有原始边,这意味着生成的几何包含 3 个面,如颜色所示。

布局建模 0
Init -->
    primitiveDisk(5)
    deleteUV(0)
    union { HouseFootprint. | FrontyardFootprint }
    inline comp(f) { isTagged("bool.B")= B
                   | isTagged("bool.A")= A }
    cleanupGeometry(vertices, 0) // reconnect faces
    ShowTags
   
A --> tag("eHouse", edges)
B --> deleteTags("bool.A")
      deleteTags("bool.cut")
      cleanupGeometry(edges, 0) // merge faces
      tag("eFrontyard", edges)
      
ShowTags -->
    ShowBoolAutotags
    comp(e) { isTagged("eFrontyard") 
                  && isTagged("eHouse"): YellowEdge
            | isTagged("eHouse")       : BlueEdge
            | isTagged("eFrontyard")   : GreenEdge }

步骤 2:目标是合并黄色和绿色的面并标记边,为步骤 3 做好准备。 comp 用于修改相应的面,inliningcleanupGeometry 用于将修改后的部分重新合并在一起。

注:
为了使用 cleanupGeometry 合并规则 B 中的面,需要使用 deleteUV 移除已插入资产上的 UV 坐标,然后需要使用 deleteTags 移除“bool.A”和“bool.cut”,否则会阻止该过程。

布局建模 1
Init -->
    primitiveDisk(5)
    deleteUV(0)
    union { HouseFootprint. | FrontyardFootprint }
    inline comp(f) { isTagged("bool.B")= B
                   | isTagged("bool.A")= A }
    cleanupGeometry(vertices, 0) // reconnect faces
    comp(f) { isTagged("bool.A"): House
            | isTagged("bool.B"): Frontyard }
            
House -->
    extrude(3)
    comp(f) { isTagged("eFrontyard"): Yellow
            | all                   : Blue }

Frontyard -->
    offset(-2)
    comp(f) { isTagged("eHouse"): Yellow
            | all               : Green }

步骤 3:对房屋覆盖区进行拉伸,并对前院覆盖区进行偏移。 步骤 2 中的边标签将自动传播到新面,由此可区分面向前院的立面以及与房屋接触的偏移边界。

此信息可用于在后续步骤中对入口区域进行建模。

布局建模 2

开放运算对象的行为

Left  --> primitiveCube
          union { Blue
                | t(2,2,2) 
                  comp(f) { front: NIL | all= Green } }
    
Right --> primitiveCube
          union { Blue
                | t(2,2,2) 
                  comp(f) { left: NIL | all= Green } }

运算对象 A 为封闭立方体,而运算对象 B 为开放立方体。 左:B 的开放边界不是相交的运算对象 A。 右:B 的开放边界是相交的运算对象 A。

布尔封闭立方体和开放立方体
Left  --> primitiveCube
          union { Blue
                | primitiveQuad
                  s(12,0,12)
                  center(xyz)
                  Green }
    
Right --> primitiveCube
          union { Blue
                | primitiveQuad
                  s(12,0,10)
                  t(-1,5,2)
                  Green }

运算对象 A 为封闭立方体,而运算对象 B 为 2D 四边形。 左:四边形完全切割立方体。 平面以下的所有部分均视为“内部”,因此将会消失。 右:四边形部分切割立方体。

布尔封闭立方体和开放立方体