Хотя расположение объектов было схожим, выходное значение и форма полностью отличались от оригинала.
Это мои коды для преобразования файла .stp в .scad
Analyzer.py< /em>
Код: Выделить всё
import logging
import OCC.Core.TopAbs as TopAbs
from OCC.Core.Bnd import Bnd_Box
from OCC.Core.BRepAdaptor import BRepAdaptor_Surface
from OCC.Core.BRepBndLib import brepbndlib
from OCC.Core.BRepGProp import brepgprop
from OCC.Core.GeomAbs import (
GeomAbs_BezierSurface,
GeomAbs_BSplineSurface,
GeomAbs_Cone,
GeomAbs_Cylinder,
GeomAbs_OffsetSurface,
GeomAbs_OtherSurface,
GeomAbs_Plane,
GeomAbs_Sphere,
GeomAbs_SurfaceOfExtrusion,
GeomAbs_SurfaceOfRevolution,
GeomAbs_Torus,
)
from OCC.Core.GProp import GProp_GProps
from OCC.Core.TopExp import TopExp_Explorer
from OCC.Core.TopoDS import TopoDS_Shape, topods
import lib.types as DataTypes
from lib import FileReader, GeometryValidator, ShapeRecognizer
class Analyzer:
def __init__(self, file_path: str):
self.shapes: TopoDS_Shape
self.logger = logging.getLogger("Analyzer")
self._read_step_file(file_path)
def _read_step_file(self, file_path: str) -> bool:
"""Read STEP file and store shapes
Returns:
bool: Success
"""
reader = FileReader(file_path)
self.shapes = reader.read()
def identify_surface_type(self, face) -> str:
"""Identify the type of surface. Extends `BRepAdaptor_Surface -> GetType()`
Returns:
str: The type of surface in strings"""
try:
surf = BRepAdaptor_Surface(face)
surf_type = surf.GetType()
surface_types = {
# Elementary surfaces
GeomAbs_Plane: "Plane",
GeomAbs_Cylinder: "Cylinder",
GeomAbs_Cone: "Cone",
GeomAbs_Sphere: "Sphere",
GeomAbs_Torus: "Torus",
# Bounded surfaces
GeomAbs_BezierSurface: "BezierSurface",
GeomAbs_BSplineSurface: "BSplineSurface",
# Swept surfaces
GeomAbs_SurfaceOfRevolution: "SurfaceOfRevolution",
GeomAbs_SurfaceOfExtrusion: "SurfaceOfExtrusion",
# Misc
GeomAbs_OffsetSurface: "OffsetSurface",
GeomAbs_OtherSurface: "OtherSurface",
}
return surface_types.get(surf_type, f"Unknown({surf_type})")
except Exception as e:
self.logger.warning(f"Error identifying surface type: {str(e)}")
return "Unknown"
def extract_geometric_data(self) -> list[DataTypes.Geometry]:
"""Extract geometric data from shapes"""
if not self.shapes:
self.logger.error("No shapes available for extraction")
return None
geometric_data: list[DataTypes.Geometry] = []
shape_explorer = TopExp_Explorer(self.shapes, TopAbs.TopAbs_SOLID)
while shape_explorer.More():
current_shape = shape_explorer.Current()
payload: DataTypes.Geometry = {}
# Get overall dimensions from bounding box
bbox = Bnd_Box()
brepbndlib.Add(current_shape, bbox)
xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get()
payload["dimensions"] = {
"width": xmax - xmin,
"length": ymax - ymin,
"height": zmax - zmin,
}
# Extract faces
payload["faces"] = self.extract_face_data(current_shape)
# Add analysis results
props = GProp_GProps()
brepgprop.VolumeProperties(self.shapes, props)
center = props.CentreOfMass()
payload["analysis"] = {
"volume": props.Mass(),
"center": (center.X(), center.Y(), center.Z()),
}
# Go next geometry
geometric_data.append(payload)
shape_explorer.Next()
return geometric_data
def extract_face_data(self, geometric_data: TopoDS_Shape) -> list[DataTypes.Face]:
"""Extracts face data from geometric data"""
face_data: list[DataTypes.Face] = []
face_explorer = TopExp_Explorer(geometric_data, TopAbs.TopAbs_FACE)
while face_explorer.More():
face = topods.Face(face_explorer.Current())
_face_data = self._extract_face_data(face)
# face_data["boundaries"] = self._extract_face_boundaries(face)
face_data.append(_face_data)
# geometric_data["topology"]["faces_n"] += 1
face_explorer.Next()
self.logger.info(f"Extracted {len(face_data)} faces")
return face_data
# def _extract_face_boundaries(self, face):
# """Extract boundary information for a face"""
# boundaries = {
# "outer_wire": None,
# "inner_wires": [],
# "edges": []
# }
# # Get the wires (loops) of the face
# wire_explorer = TopExp_Explorer(face, TopAbs.TopAbs_WIRE)
# is_first = True
# while wire_explorer.More():
# wire = topods.Wire(wire_explorer.Current())
# wire_data = WireDataExtractor().extract(wire)
# if is_first:
# boundaries["outer_wire"] = wire_data
# is_first = False
# else:
# boundaries["inner_wires"].append(wire_data)
# wire_explorer.Next()
# return boundaries
def _extract_face_data(self, face):
"""Extract properties from a face"""
surface_type = self.identify_surface_type(face)
face_data = {
"surfaceType": surface_type,
"parameters": self._get_surface_parameters(face, surface_type),
}
return face_data
def _get_surface_parameters(self, face, surface_type):
"""Extract specific parameters based on surface type"""
try:
surf = BRepAdaptor_Surface(face)
params = {}
if surface_type == "Cylinder":
cylinder = surf.Cylinder()
params["radius"] = cylinder.Radius()
location = cylinder.Location()
params["location"] = (location.X(), location.Y(), location.Z())
elif surface_type == "Plane":
plane = surf.Plane()
location = plane.Location()
params["location"] = (location.X(), location.Y(), location.Z())
elif surface_type == "Cone":
cone = surf.Cone()
location = cone.Location()
params["radius"] = cone.RefRadius()
params["angle"] = cone.SemiAngle()
params["location"] = (location.X(), location.Y(), location.Z())
elif surface_type == "Sphere":
sphere = surf.Sphere()
location = sphere.Location()
params["radius"] = sphere.Radius()
params["location"] = (location.X(), location.Y(), location.Z())
elif surface_type == "Torus":
torus = surf.Torus()
location = torus.Location()
params["MajorRadius"] = torus.MajorRadius()
params["MinorRadius"] = torus.MinorRadius()
params["location"] = (location.X(), location.Y(), location.Z())
params["Volume"] = torus.Volume()
elif surface_type == "BSplineSurface":
b_spline_surface = surf.BSpline()
params["D0"] = b_spline_surface.D0()
else:
self.logger.warning(
f"Unknown surface type {surface_type} when extracting surface parameters"
)
return params
except Exception as e:
self.logger.warning(
f"Error extracting parameters for {surface_type}: {str(e)}"
)
return {}
def analyze_and_validate(self):
"""Analyze and validate the loaded shapes"""
results = []
shape_info = ShapeRecognizer.analyze(self.shapes)
validation = GeometryValidator.validate(self.shapes)
results.append(
{
"shape_type": shape_info["type"],
"properties": shape_info,
"validation": validation,
}
)
# Log results
self.logger.info(f"Shape Type: {shape_info['type']}")
self.logger.info(f"Volume: {shape_info['volume']:.2f}")
self.logger.info(f"Center: {shape_info['center']}")
if not validation["is_valid"]:
self.logger.warning("Validation issues found:")
for issue in validation["issues"]:
self.logger.warning("- %s", issue)
return results
Код: Выделить всё
import logging
from OCC.Core.TopoDS import TopoDS_Shape
from converter import Analyzer
DEFAULT_SMOOTHNESS = 50
class Converter:
def __init__(self, file_path: str):
self.file_path = file_path
self.shapes: TopoDS_Shape
self.geometric_data = []
self.logger = logging.getLogger("Converter")
self._analyze()
def _analyze(self):
"""Validates and stores the geometric data in a variable"""
analyzer = Analyzer(self.file_path)
analyzer.analyze_and_validate()
self.geometric_data = analyzer.extract_geometric_data()
# ! Currently very limited (hardcoded values).
# TODO: Allow complex models procedurally
def generate_openscad_code(self):
"""Generate OpenSCAD code based on analyzed geometry"""
code = [
"// OpenSCAD code generated from STEP file",
"// Generated by StepToCADConverter\n",
"$fn = 50; // Set smoothness for curved surfaces\n",
]
# Extract dimensions from the analysis
dims = self.geometric_data[0].get("dimensions", {})
width = float(dims.get("width"))
height = float(dims.get("height"))
cylinder_radius = float(dims.get("cylinderRadius", 5))
# Generate the actual geometry code
# TODO: remove comment
# for i in geometric_data["faces"]:
# print(i["surface-type"])
# print(i["parameters"])
code.extend([
"difference() { // Main shape",
" union() { // Combine positive shapes",
""
])
for i in self.geometric_data:
for a in i['faces']:
try:
surf_type = a['surfaceType']
location = a['parameters']['location']
dimensions = a['parameters'].get('dimensions', [1, 1, 1]) # Default dimensions if not specified
radius = a['parameters'].get('radius', 1) # Default radius if not specified
if surf_type == 'Plane':
code.extend([
" // Plane/Rectangle",
f" translate([{location[0]:.2f}, {location[1]:.2f}, {location[2]:.2f}])",
f" cube([{dimensions[0]:.2f}, {dimensions[1]:.2f}, 0.01]);" # Very thin cube to represent a plane
])
elif surf_type == 'Sphere':
code.extend([
" // Sphere",
f" translate([{location[0]:.2f}, {location[1]:.2f}, {location[2]:.2f}])",
f" sphere(r = {radius:.2f});"
])
elif surf_type == 'Cylinder':
height = a['parameters'].get('height', 1)
code.extend([
" // Cylinder",
f" translate([{location[0]:.2f}, {location[1]:.2f}, {location[2]:.2f}])",
f" cylinder(h = {height:.2f}, r = {radius:.2f});"
])
elif surf_type == 'Cone':
height = a['parameters'].get('height', 1)
radius2 = a['parameters'].get('radius2', 0) # Top radius
code.extend([
" // Cone",
f" translate([{location[0]:.2f}, {location[1]:.2f}, {location[2]:.2f}])",
f" cylinder(h = {height:.2f}, r1 = {radius:.2f}, r2 = {radius2:.2f});"
])
elif surf_type == 'Torus':
outer_radius = a['parameters'].get('outerRadius', 2)
code.extend([
" // Torus",
f" translate([{location[0]:.2f}, {location[1]:.2f}, {location[2]:.2f}])",
f" rotate_extrude()",
f" translate([{outer_radius:.2f}, 0, 0])",
f" circle(r = {radius:.2f});"
])
except Exception as e:
print(a)
print(e)
code.extend(
[
" } // End union",
"} // End difference",
]
)
# Add analysis info as comments
analysis = self.geometric_data[0].get("analysis", {})
code.extend(
[
"\n// Shape Information:",
f"// - Total Width: {width:.2f}",
f"// - Total Height: {height:.2f}",
f"// - Cylinder Radius: {cylinder_radius:.2f}",
f"// - Volume: {analysis.get('volume', 0):.2f}",
f"// - Center: {analysis.get('center', (0,0,0))}",
f"// - Number of Faces: {len(self.geometric_data[0]['faces'])}",
]
)
self.logger.info("Convert complete")
return "\n".join(code)
Я думаю, что размер и наклон не применялись. А может это другая проблема.
Или есть более простой способ?
Подробнее здесь: https://stackoverflow.com/questions/791 ... ired-resul