Files
2026-07-22 13:48:46 +08:00

15 KiB

Face

Overview

Face is the face class in the SimpleCAD API, representing 2D surface geometry. A face is bounded by one or more wires, including an outer boundary and possibly inner boundaries (holes). It wraps the OCP Face object and adds tagging functionality.

Class Definition

class Face(TaggedMixin):
    """面类,包装OCP的Face,添加标签功能"""

Inheritance

  • Inherits from TaggedMixin, providing tag and metadata functionality

Usage

  • Represent 2D surface areas
  • Form the boundary of solids (Solid)
  • Define cross-sections for sweep, extrude, and other operations
  • Calculate geometric properties such as area and normal vectors

Constructor

__init__(wrapped)

Initializes a face object.

Parameters:

  • wrapped (OCP TopoDS_Face): A OCP face object

Raises:

  • ValueError: When the input face object is invalid

Example:

from simplecadapi import (
    make_rectangle_rface,
    make_circle_rface,
    make_face_from_wire_rface,
    make_rectangle_rwire
)

# 通过 SimpleCAD 函数创建面
rectangle = make_rectangle_rface(width=5, height=3)
circle = make_circle_rface(center=(0, 0, 0), radius=2.0)

# 从线创建面
wire = make_rectangle_rwire(width=4, height=4)
face_from_wire = make_face_from_wire_rface(wire)

Main Properties

  • wrapped: The underlying OCP face object
  • _tags: Tag set (inherited from TaggedMixin)
  • _metadata: Metadata dictionary (inherited from TaggedMixin)

Common Methods

get_area()

Get the area of the face.

Returns:

  • float: The area of the face

Raises:

  • ValueError: When area retrieval fails

Example:

from simplecadapi import make_rectangle_rface, make_circle_rface
import math

# 矩形面
rectangle = make_rectangle_rface(width=5, height=3)
rect_area = rectangle.get_area()
print(f"矩形面积: {rect_area}")  # 15.0

# 圆形面
circle = make_circle_rface(center=(0, 0, 0), radius=2.0)
circle_area = circle.get_area()
expected_area = math.pi * 2.0 * 2.0
print(f"圆形面积: {circle_area:.3f}, 期望: {expected_area:.3f}")

get_normal_at(u, v)

Get the normal vector of the face at the specified parameter position.

Parameters:

  • u (float, optional): U parameter, default 0.5
  • v (float, optional): V parameter, default 0.5

Returns:

  • simplecadapi.core.Vec3: Normal vector

Raises:

  • ValueError: When normal vector retrieval fails

Example:

from simplecadapi import make_rectangle_rface

rectangle = make_rectangle_rface(width=5, height=3)
normal = rectangle.get_normal_at()
print(f"法向量: ({normal.x:.3f}, {normal.y:.3f}, {normal.z:.3f})")

get_outer_wire()

Get the outer boundary wire of the face.

Returns:

  • Wire: Outer boundary wire object

Raises:

  • ValueError: When outer boundary wire retrieval fails

Example:

from simplecadapi import make_rectangle_rface

rectangle = make_rectangle_rface(width=5, height=3)
outer_wire = rectangle.get_outer_wire()
edges = outer_wire.get_edges()
print(f"外边界由 {len(edges)} 条边组成")

Tagging and Metadata

Use the functional public API apply_tag(shape, tag) and list_tags(shape) for tags. Use set_metadata(key, value) and get_metadata(key, default=None) for structured metadata.

Usage Examples

Creating Different Types of Faces

from simplecadapi import (
    make_rectangle_rface,
    make_circle_rface,
    make_face_from_wire_rface,
    make_polyline_rwire
)

# 矩形面
rectangle = make_rectangle_rface(width=10, height=6)
apply_tag(rectangle, "rectangle")
apply_tag(rectangle, "quadrilateral")

# 圆形面
circle = make_circle_rface(center=(0, 0, 0), radius=3.0)
apply_tag(circle, "circle")
apply_tag(circle, "curved")

# 复杂多边形面
points = [
    (0, 0, 0), (4, 0, 0), (4, 3, 0), (2, 5, 0), (0, 3, 0), (0, 0, 0)
]
polygon_wire = make_polyline_rwire(points=points)
polygon = make_face_from_wire_rface(polygon_wire)
apply_tag(polygon, "polygon")
apply_tag(polygon, "complex")

# 分析面的属性
faces = [rectangle, circle, polygon]
for face in faces:
    area = face.get_area()
    normal = face.get_normal_at()
    outer_wire = face.get_outer_wire()
    edges = outer_wire.get_edges()
    tags = list_tags(face)
    
    print(f"面类型: {tags}")
    print(f"  面积: {area:.3f}")
    print(f"  法向量: ({normal.x:.3f}, {normal.y:.3f}, {normal.z:.3f})")
    print(f"  边数: {len(edges)}")
    print()

Geometric Analysis of Faces

from simplecadapi import make_rectangle_rface, make_circle_rface
import math

def analyze_face_geometry():
    """分析面的几何属性"""
    
    # 创建不同尺寸的矩形
    rectangles = [
        make_rectangle_rface(width=2, height=3),
        make_rectangle_rface(width=4, height=4),
        make_rectangle_rface(width=6, height=2)
    ]
    
    # 创建不同半径的圆
    circles = [
        make_circle_rface(center=(0, 0, 0), radius=1.0),
        make_circle_rface(center=(0, 0, 0), radius=2.0),
        make_circle_rface(center=(0, 0, 0), radius=3.0)
    ]
    
    # 分析矩形
    for i, rect in enumerate(rectangles):
        area = rect.get_area()
        outer_wire = rect.get_outer_wire()
        edges = outer_wire.get_edges()
        
        # 计算周长
        perimeter = sum(edge.get_length() for edge in edges)
        
        # 计算长宽比
        lengths = [edge.get_length() for edge in edges]
        lengths.sort()
        aspect_ratio = lengths[1] / lengths[0] if lengths[0] > 0 else 1.0
        
        apply_tag(rect, f"rectangle_{i}")
        rect.set_metadata("area", area)
        rect.set_metadata("perimeter", perimeter)
        rect.set_metadata("aspect_ratio", aspect_ratio)
        
        if aspect_ratio == 1.0:
            apply_tag(rect, "square")
        elif aspect_ratio > 2.0:
            apply_tag(rect, "elongated")
        
        print(f"矩形 {i}: 面积={area:.3f}, 周长={perimeter:.3f}, 长宽比={aspect_ratio:.3f}")
    
    # 分析圆形
    for i, circle in enumerate(circles):
        area = circle.get_area()
        outer_wire = circle.get_outer_wire()
        edges = outer_wire.get_edges()
        
        # 计算周长(圆周长)
        perimeter = sum(edge.get_length() for edge in edges)
        
        # 从面积计算半径
        radius_from_area = math.sqrt(area / math.pi)
        
        # 从周长计算半径
        radius_from_perimeter = perimeter / (2 * math.pi)
        
        apply_tag(circle, f"circle_{i}")
        circle.set_metadata("area", area)
        circle.set_metadata("perimeter", perimeter)
        circle.set_metadata("radius_from_area", radius_from_area)
        circle.set_metadata("radius_from_perimeter", radius_from_perimeter)
        
        if radius_from_area < 1.5:
            apply_tag(circle, "small")
        elif radius_from_area > 2.5:
            apply_tag(circle, "large")
        else:
            apply_tag(circle, "medium")
        
        print(f"圆形 {i}: 面积={area:.3f}, 周长={perimeter:.3f}, 半径={radius_from_area:.3f}")

analyze_face_geometry()

Faces with Holes

from simplecadapi import (
    make_rectangle_rface,
    make_circle_rface,
    make_face_from_wire_rface,
    make_rectangle_rwire,
    make_circle_rwire
)

def create_face_with_holes():
    """创建带孔的面(概念示例)"""
    
    # 创建外边界
    outer_boundary = make_rectangle_rwire(width=10, height=8)
    
    # 创建内边界(孔)
    hole1 = make_circle_rwire(center=(3, 2, 0), radius=1.0)
    hole2 = make_circle_rwire(center=(7, 6, 0), radius=1.5)
    
    # 注意:SimpleCAD 当前版本可能不直接支持多边界面
    # 这里展示概念和标签使用
    
    # 主面
    main_face = make_rectangle_rface(width=10, height=8)
    apply_tag(main_face, "main_surface")
    apply_tag(main_face, "with_holes")
    
    # 孔面(用于布尔运算)
    hole_face1 = make_circle_rface(center=(3, 2, 0), radius=1.0)
    apply_tag(hole_face1, "hole")
    apply_tag(hole_face1, "circular")
    hole_face1.set_metadata("hole_id", 1)
    hole_face1.set_metadata("center", (3, 2, 0))
    hole_face1.set_metadata("radius", 1.0)
    
    hole_face2 = make_circle_rface(center=(7, 6, 0), radius=1.5)
    apply_tag(hole_face2, "hole")
    apply_tag(hole_face2, "circular")
    hole_face2.set_metadata("hole_id", 2)
    hole_face2.set_metadata("center", (7, 6, 0))
    hole_face2.set_metadata("radius", 1.5)
    
    # 计算有效面积
    main_area = main_face.get_area()
    hole1_area = hole_face1.get_area()
    hole2_area = hole_face2.get_area()
    effective_area = main_area - hole1_area - hole2_area
    
    main_face.set_metadata("total_area", main_area)
    main_face.set_metadata("hole_area", hole1_area + hole2_area)
    main_face.set_metadata("effective_area", effective_area)
    
    print(f"主面面积: {main_area:.3f}")
    print(f"孔面积总和: {hole1_area + hole2_area:.3f}")
    print(f"有效面积: {effective_area:.3f}")
    
    return main_face, [hole_face1, hole_face2]

main_face, holes = create_face_with_holes()

Face Transformation and Operations

from simplecadapi import (
    make_rectangle_rface,
    translate_shape,
    rotate_shape
)

def transform_faces():
    """变换面的操作"""
    
    # 创建基础面
    base_face = make_rectangle_rface(width=4, height=3)
    apply_tag(base_face, "base")
    apply_tag(base_face, "original")
    
    # 应用变换
    translated_face = translate_shape(base_face, offset=(6, 0, 0))
    apply_tag(translated_face, "translated")
    
    rotated_face = rotate_shape(base_face, axis=(0, 0, 1), angle=45)
    apply_tag(rotated_face, "rotated")
    
    elevated_face = translate_shape(base_face, offset=(0, 0, 2))
    apply_tag(elevated_face, "elevated")
    
    # 收集所有面
    all_faces = [base_face, translated_face, rotated_face, elevated_face]
    
    # 分析变换结果
    for face in all_faces:
        area = face.get_area()
        normal = face.get_normal_at()
        outer_wire = face.get_outer_wire()
        edges = outer_wire.get_edges()
        
        # 计算边界框
        all_coords = []
        for edge in edges:
            start_coords = edge.get_start_vertex().get_coordinates()
            end_coords = edge.get_end_vertex().get_coordinates()
            all_coords.extend([start_coords, end_coords])
        
        if all_coords:
            min_x = min(coord[0] for coord in all_coords)
            max_x = max(coord[0] for coord in all_coords)
            min_y = min(coord[1] for coord in all_coords)
            max_y = max(coord[1] for coord in all_coords)
            min_z = min(coord[2] for coord in all_coords)
            max_z = max(coord[2] for coord in all_coords)
            
            face.set_metadata("bbox_min", (min_x, min_y, min_z))
            face.set_metadata("bbox_max", (max_x, max_y, max_z))
        
        face.set_metadata("area", area)
        face.set_metadata("normal", (normal.x, normal.y, normal.z))
        
        print(f"面标签: {list_tags(face)}")
        print(f"  面积: {area:.3f}")
        print(f"  法向量: ({normal.x:.3f}, {normal.y:.3f}, {normal.z:.3f})")
        if face.get_metadata("bbox_min"):
            print(f"  边界框: {face.get_metadata('bbox_min')}{face.get_metadata('bbox_max')}")
        print()

transform_faces()

Face Classification and Filtering

from simplecadapi import make_rectangle_rface, make_circle_rface

def classify_faces():
    """分类和筛选面"""
    
    # 创建不同类型的面
    faces = []
    
    # 小矩形
    small_rects = [
        make_rectangle_rface(width=1, height=1),
        make_rectangle_rface(width=2, height=1),
        make_rectangle_rface(width=1, height=2)
    ]
    
    # 大矩形
    large_rects = [
        make_rectangle_rface(width=5, height=4),
        make_rectangle_rface(width=6, height=3),
        make_rectangle_rface(width=4, height=6)
    ]
    
    # 圆形
    circles = [
        make_circle_rface(center=(0, 0, 0), radius=1.0),
        make_circle_rface(center=(0, 0, 0), radius=2.0),
        make_circle_rface(center=(0, 0, 0), radius=3.0)
    ]
    
    # 标记面
    for i, face in enumerate(small_rects):
        apply_tag(face, "rectangle")
        apply_tag(face, "small")
        face.set_metadata("size_category", "small")
        face.set_metadata("shape_type", "rectangle")
        faces.append(face)
    
    for i, face in enumerate(large_rects):
        apply_tag(face, "rectangle")
        apply_tag(face, "large")
        face.set_metadata("size_category", "large")
        face.set_metadata("shape_type", "rectangle")
        faces.append(face)
    
    for i, face in enumerate(circles):
        apply_tag(face, "circle")
        area = face.get_area()
        if area < 10:
            apply_tag(face, "small")
            face.set_metadata("size_category", "small")
        elif area > 20:
            apply_tag(face, "large")
            face.set_metadata("size_category", "large")
        else:
            apply_tag(face, "medium")
            face.set_metadata("size_category", "medium")
        face.set_metadata("shape_type", "circle")
        faces.append(face)
    
    # 分类统计
    rectangles = [f for f in faces if "rectangle" in list_tags(f)]
    circles = [f for f in faces if "circle" in list_tags(f)]
    small_faces = [f for f in faces if "small" in list_tags(f)]
    large_faces = [f for f in faces if "large" in list_tags(f)]
    
    print(f"总面数: {len(faces)}")
    print(f"矩形面: {len(rectangles)}")
    print(f"圆形面: {len(circles)}")
    print(f"小面: {len(small_faces)}")
    print(f"大面: {len(large_faces)}")
    
    # 计算统计信息
    total_area = sum(f.get_area() for f in faces)
    avg_area = total_area / len(faces)
    
    print(f"总面积: {total_area:.3f}")
    print(f"平均面积: {avg_area:.3f}")
    
    return faces

classified_faces = classify_faces()

String Representation

from simplecadapi import make_rectangle_rface

face = make_rectangle_rface(width=5, height=3)
apply_tag(face, "example_face")
face.set_metadata("material", "steel")

print(face)

Output:

Face:
  area: 15.000
  normal: [0.000, 0.000, 1.000]
  outer_wire:
    Wire:
      edge_count: 4
      closed: True
      edges:
        edge_0:
          length: 5.000
          vertices:
            start: (0.0, 0.0, 0.0)
            end: (5.0, 0.0, 0.0)
        edge_1:
          length: 3.000
          vertices:
            start: (5.0, 0.0, 0.0)
            end: (5.0, 3.0, 0.0)
        edge_2:
          length: 5.000
          vertices:
            start: (5.0, 3.0, 0.0)
            end: (0.0, 3.0, 0.0)
        edge_3:
          length: 3.000
          vertices:
            start: (0.0, 3.0, 0.0)
            end: (0.0, 0.0, 0.0)
  tags: [example_face]
  metadata:
    material: steel

Relationships with Other Geometry

  • Wire (Wire): Boundary of the face
  • Edge (Edge): Indirectly associated through wires
  • Solid (Solid): Faces form the surfaces of a solid
  • Shell (Shell): A collection of surfaces composed of multiple faces

Notes

  • Faces must be closed, bounded by closed wires
  • The face normal direction follows the right-hand rule
  • Area calculation includes all regions bounded by the boundary
  • Faces with holes require special treatment (outer boundary + inner boundary)
  • Face orientation affects subsequent solid operations
  • Complex faces may have self-intersection or degenerate cases
  • The u, v parameter range is typically [0, 1]