546 lines
15 KiB
Markdown
546 lines
15 KiB
Markdown
# 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
|
|
|
|
```python
|
|
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:**
|
|
```python
|
|
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:**
|
|
```python
|
|
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:**
|
|
```python
|
|
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:**
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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]
|