跳转至

阵列

约 183 个字 348 行代码 3 张图片 预计阅读时间 5 分钟

occ没有内置的阵列方法,用户可以在几何变换(平移、旋转)的基础上实现阵列操作,本质上计算出物体的位置,然后进行位置变换,复制出一个新的物体的过程。

线性阵列

def linear_array(
    shape: TopoDS_Shape,
    count: int,
    delta: Tuple[float, float, float],
    include_original: bool = True,
) -> Tuple[List[TopoDS_Shape], TopoDS_Compound]:
    """
    线性阵列:沿 (dx, dy, dz) 方向等距平移复制。

    :param shape: 基准 shape
    :param count: 实例个数(>=1)
    :param delta: 相邻两实例的位移 (dx, dy, dz)
    :param include_original: 是否包含原件
    :return: (instances, compound)
    """
    if count < 1:
        raise ValueError("count must be >= 1")

    instances: List[TopoDS_Shape] = []
    start_idx = 0

    if include_original:
        instances.append(shape)
        start_idx = 1

    # 逐个生成副本,循环+平移
    for i in range(start_idx, count):
        vec = (delta[0] * i, delta[1] * i, delta[2] * i)
        inst = transform_shape(shape, "translate", {"vec": vec})
        instances.append(inst)

    comp = _make_compound(instances)
    print(f"[LinearArray] Generated {len(instances)} instances.")
    return instances, comp

网格阵列

def grid_array(
    shape: TopoDS_Shape,
    rows: int,
    cols: int,
    dx: float,
    dy: float,
    include_original: bool = True,
) -> Tuple[List[TopoDS_Shape], TopoDS_Compound]:
    """
    网格阵列(Grid Array):在 XY 平面内排布 rows × cols 个实例

    :param shape: 基准 shape
    :param rows: 行数 (>=1)
    :param cols: 列数 (>=1)
    :param dx: X 方向间距
    :param dy: Y 方向间距
    :param include_original: 是否包含原件 (默认 True)
    :return: (instances, compound)
    """
    if rows < 1 or cols < 1:
        raise ValueError("rows and cols must be >= 1")

    instances: List[TopoDS_Shape] = []
    # 按行、列双重循环+平移
    for i in range(rows):
        for j in range(cols):
            if i == 0 and j == 0 and include_original:
                instances.append(shape)
                continue
            vec = (dx * j, dy * i, 0.0)
            inst = transform_shape(shape, "translate", {"vec": vec})
            instances.append(inst)

    comp = _make_compound(instances)
    print(f"[GridArray] Generated {len(instances)} instances "
          f"({rows} rows × {cols} cols).")
    return instances, comp

环形阵列

def circular_array(
    shape: TopoDS_Shape,
    count: int,
    axis_point: Tuple[float, float, float],
    axis_dir: Tuple[float, float, float],
    total_angle_deg: float = 360.0,
    include_original: bool = True,
    start_angle_deg: float = 0.0,
) -> Tuple[List[TopoDS_Shape], TopoDS_Compound]:
    """
    圆形阵列:绕指定轴等角度旋转复制。

    :param shape: 基准 shape
    :param count: 实例个数(>=1)
    :param axis_point: 旋转轴上一点 (x, y, z)
    :param axis_dir: 旋转轴方向 (dx, dy, dz),将会单位化
    :param total_angle_deg: 总旋转角度,默认 360°
    :param include_original: 是否包含原件
    :param start_angle_deg: 起始角(度),默认 0°
    :return: (instances, compound)
    """
    if count < 1:
        raise ValueError("count must be >= 1")

    instances: List[TopoDS_Shape] = []
    step = total_angle_deg / (count if include_original else max(count, 1))

    if include_original:
        # 起始角度的一个实例(可选)
        if abs(start_angle_deg) > 1e-12:
            inst0 = transform_shape(
                shape,
                "rotate",
                {
                    "axis_point": axis_point,
                    "axis_dir": axis_dir,
                    "angle": start_angle_deg,
                },
            )
            instances.append(inst0)
        else:
            instances.append(shape)

        start_index = 1
    else:
        start_index = 0

    # 生成其余实例(循环+旋转)
    for i in range(start_index, count):
        ang = start_angle_deg + step * i
        inst = transform_shape(
            shape,
            "rotate",
            {
                "axis_point": axis_point,
                "axis_dir": axis_dir,
                "angle": ang,
            },
        )
        instances.append(inst)

    comp = _make_compound(instances)
    print(f"[CircularArray] Generated {len(instances)} instances, step={step:.6f} deg.")
    return instances, comp

完整代码

需要结合之前的几何变换代码from transform_shape import transform_shape

arrays.py

"""
阵列工具:线性阵列、圆形阵列、矩形阵列
"""
from typing import Iterable, List, Tuple
from OCC.Core.TopoDS import TopoDS_Shape
from OCC.Core.BRep import BRep_Builder
from OCC.Core.TopoDS import TopoDS_Compound

from transform_shape import transform_shape


def _make_compound(shapes: Iterable[TopoDS_Shape]) -> TopoDS_Compound:
    """把一组形体打包成复合体,便于一次性显示或导出。"""
    # 装配
    comp = TopoDS_Compound()
    builder = BRep_Builder()
    builder.MakeCompound(comp)

    for s in shapes:
        builder.Add(comp, s)
    return comp


def linear_array(
    shape: TopoDS_Shape,
    count: int,
    delta: Tuple[float, float, float],
    include_original: bool = True,
) -> Tuple[List[TopoDS_Shape], TopoDS_Compound]:
    """
    线性阵列:沿 (dx, dy, dz) 方向等距平移复制。

    :param shape: 基准 shape
    :param count: 实例个数(>=1)
    :param delta: 相邻两实例的位移 (dx, dy, dz)
    :param include_original: 是否包含原件
    :return: (instances, compound)
    """
    if count < 1:
        raise ValueError("count must be >= 1")

    instances: List[TopoDS_Shape] = []
    start_idx = 0

    if include_original:
        instances.append(shape)
        start_idx = 1

    # 逐个生成副本
    for i in range(start_idx, count):
        vec = (delta[0] * i, delta[1] * i, delta[2] * i)
        inst = transform_shape(shape, "translate", {"vec": vec})
        instances.append(inst)

    comp = _make_compound(instances)
    print(f"[LinearArray] Generated {len(instances)} instances.")
    return instances, comp


def circular_array(
    shape: TopoDS_Shape,
    count: int,
    axis_point: Tuple[float, float, float],
    axis_dir: Tuple[float, float, float],
    total_angle_deg: float = 360.0,
    include_original: bool = True,
    start_angle_deg: float = 0.0,
) -> Tuple[List[TopoDS_Shape], TopoDS_Compound]:
    """
    圆形阵列:绕指定轴等角度旋转复制。

    :param shape: 基准 shape
    :param count: 实例个数(>=1)
    :param axis_point: 旋转轴上一点 (x, y, z)
    :param axis_dir: 旋转轴方向 (dx, dy, dz),将会单位化
    :param total_angle_deg: 总旋转角度,默认 360°
    :param include_original: 是否包含原件
    :param start_angle_deg: 起始角(度),默认 0°
    :return: (instances, compound)
    """
    if count < 1:
        raise ValueError("count must be >= 1")

    instances: List[TopoDS_Shape] = []
    step = total_angle_deg / (count if include_original else max(count, 1))

    if include_original:
        # 起始角度的一个实例(可选)
        if abs(start_angle_deg) > 1e-12:
            inst0 = transform_shape(
                shape,
                "rotate",
                {
                    "axis_point": axis_point,
                    "axis_dir": axis_dir,
                    "angle": start_angle_deg,
                },
            )
            instances.append(inst0)
        else:
            instances.append(shape)

        start_index = 1
    else:
        start_index = 0

    # 生成其余实例
    for i in range(start_index, count):
        ang = start_angle_deg + step * i
        inst = transform_shape(
            shape,
            "rotate",
            {
                "axis_point": axis_point,
                "axis_dir": axis_dir,
                "angle": ang,
            },
        )
        instances.append(inst)

    comp = _make_compound(instances)
    print(f"[CircularArray] Generated {len(instances)} instances, step={step:.6f} deg.")
    return instances, comp

def grid_array(
    shape: TopoDS_Shape,
    rows: int,
    cols: int,
    dx: float,
    dy: float,
    include_original: bool = True,
) -> Tuple[List[TopoDS_Shape], TopoDS_Compound]:
    """
    网格阵列(Grid Array):在 XY 平面内排布 rows × cols 个实例

    :param shape: 基准 shape
    :param rows: 行数 (>=1)
    :param cols: 列数 (>=1)
    :param dx: X 方向间距
    :param dy: Y 方向间距
    :param include_original: 是否包含原件 (默认 True)
    :return: (instances, compound)
    """
    if rows < 1 or cols < 1:
        raise ValueError("rows and cols must be >= 1")

    instances: List[TopoDS_Shape] = []
    for i in range(rows):
        for j in range(cols):
            if i == 0 and j == 0 and include_original:
                instances.append(shape)
                continue
            vec = (dx * j, dy * i, 0.0)
            inst = transform_shape(shape, "translate", {"vec": vec})
            instances.append(inst)

    comp = _make_compound(instances)
    print(f"[GridArray] Generated {len(instances)} instances "
          f"({rows} rows × {cols} cols).")
    return instances, comp

主程序调用:

from OCC.Display.SimpleGui import init_display
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCylinder
from OCC.Core.gp import gp_Ax2, gp_Pnt, gp_Dir
import OCC.Core.Quantity as Q

from arrays import linear_array, circular_array, grid_array

box = BRepPrimAPI_MakeBox(6, 4, 3).Shape()

ax2 = gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))
cyl = BRepPrimAPI_MakeCylinder(ax2, 2.0, 6.0).Shape()

# ---------- 线性阵列----------
box_list, box_comp = linear_array(
    box,                           # 阵列对象
    count=6,                       # 数量
    delta=(8.0, 0.0, 0.0),         # 移动方向和增量
    include_original=True          # 包含原对象
)

# ---------- 圆形阵列 ----------
cyl_list, cyl_comp = circular_array(
    cyl,                           # 阵列对象
    count=8,                       # 数量
    axis_point=(30.0, 0.0, 0.0),   # 旋转轴过此点
    axis_dir=(0.0, 0.0, 1.0),      # 朝向 Z+
    total_angle_deg=360.0,         # 旋转角度
    include_original=True,         # 包含原对象
    start_angle_deg=0.0            # 起始相位
)

# ---------- 网格阵列 ----------
_, grid_comp = grid_array(
    box,                           # 阵列对象
    rows=3,                        # 行数
    cols=4,                        # 列数
    dx=10,                         # 水平间距
    dy=8                           # 垂直间距
)

display, start_display, *_ = init_display()

# 线性阵列
display.DisplayShape(box_comp, color=Q.Quantity_NOC_BLUE3, transparency=0.35, update=False)

# 圆形阵列
display.DisplayShape(cyl_comp, color=Q.Quantity_NOC_CORAL, transparency=0.45, update=False)

# 网格阵列
display.DisplayShape(grid_comp, color=Q.Quantity_NOC_RED, transparency=0.55, update=True)

display.FitAll()
start_display()

其中返回的instances, comp,分别是不同层次的结果:

  • instancesList[TopoDS_Shape]阵列中每一个独立的 shape 副本(单个零件/几何体),可以后续单独对阵列中某个零件进行操作
  • comp:把阵列里的所有副本组合成一个复合体