first commit
This commit is contained in:
@@ -0,0 +1,545 @@
|
||||
# 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]
|
||||
Reference in New Issue
Block a user