Разделение тел: split и slice
Операции split и slice разрезают тело на отдельные объёмные части.
split — разделить тело заданной геометрией
split(body, tools) разрезает тело body с помощью одного или нескольких геометрических инструментов. Инструмент задаёт границу разреза: это может быть плоская грань, например infplane(), или другое тело.
Результат — SplitResult, коллекция отдельных тел Solid. Части можно перебирать, получать по индексу и обрабатывать независимо. Их количество зависит от расположения инструментов.
Две плоскости — три слоя
Разрежем параллелепипед высотой 18 двумя горизонтальными плоскостями на отметках 6 и 12. Получатся три слоя толщиной 6. Для рисунка переместим весь набор вправо и добавим зазоры между слоями.
from zencad import *
body = box(30, 20, 18)
planes = [infplane().up(6), infplane().up(12)]
parts = split(body, planes)
# Расположим три слоя снизу вверх.
layers = sorted(parts, key=lambda part: float(part.center().z))
assert len(layers) == 3
# Слева исходное тело, справа — разнесённые слои.
display(body, color=Color(0.7, 0.7, 0.7))
for index, part in enumerate(layers):
display(part.translate(45, 0, index * 7),
color=[Color(0.2, 0.6, 0.8), Color(1, 0.65, 0.2),
Color(0.4, 0.75, 0.4)][index])
show()

Разделение цилиндром
Здесь инструментом служит цилиндр, проходящий насквозь через куб. Получаются две части: куб с цилиндрическим отверстием и цилиндрическая сердцевина. При вычитании body - cutter осталась бы только первая часть; split сохраняет обе.
from zencad import *
body = box(24)
cutter = cylinder(r=6, h=32).translate(12, 12, -4)
parts = split(body, cutter)
# При этих размерах цилиндрическая часть имеет меньший объём.
core, remainder = sorted(parts, key=lambda part: float(part.mass()))
assert abs(float(core.mass() + remainder.mass() - body.mass())) < 1e-6
# Исходное тело, тело с отверстием и извлечённая сердцевина.
display(body, color=Color(0.7, 0.7, 0.7))
display(remainder.right(36), color=Color(0.2, 0.6, 0.8))
display(core.right(64), color=Color(1, 0.65, 0.2))
show()

Пустой набор инструментов вызывает ValueError; входная форма должна содержать объёмные тела.
slice — разделить тело одной плоскостью
slice разделяет тело одной плоскостью. Для горизонтального разреза достаточно указать высоту: parts = slice(body, z=8). Части упорядочены по проекции их центров на нормаль плоскости.
Результат SliceResult — коллекция тел. Если плоскость не разрезает тело или только касается его, в коллекции остаётся одно тело. Например, len(slice(box(2), z=3)) == 1.
При двух частях можно написать lower, upper = parts. Свойства .lower и .upper — синонимы parts[0] и parts[1]; при одной части обращение к .upper вызывает IndexError при вычислении.
Наклонный разрез
Произвольная плоскость задаётся парой plane=(точка, нормаль). В примере она проходит через (0, 0, 8) и имеет нормаль (0, -0.3, 1): высота разреза увеличивается от 8 до 14 при движении по Y от 0 до 20.
Для наклонной плоскости порядок частей определяется её нормалью: первая лежит с отрицательной стороны, вторая — с положительной.
from zencad import *
body = box(30, 20, 20)
plane = ((0, 0, 8), (0, -0.3, 1)) # Точка на плоскости и её нормаль.
lower, upper = slice(body, plane=plane)
assert abs(float(lower.mass() + upper.mass() - body.mass())) < 1e-6
# Переносы нужны только для наглядного отображения частей.
display(body, color=Color(0.7, 0.7, 0.7))
display(lower.right(45), color=Color(0.2, 0.6, 0.8))
display(upper.translate(45, 0, 12), color=Color(1, 0.65, 0.2))
show()
