| import numpy as np
|
| from collections import OrderedDict
|
| from .util import create_point, create_unit_vec, get_transform, create_sketch_plane
|
|
|
|
|
| from OCC.Core.BRepCheck import BRepCheck_Analyzer
|
| from OCC.Core.GC import GC_MakeArcOfCircle
|
| from OCC.Core.BRepBuilderAPI import (
|
| BRepBuilderAPI_MakeFace,
|
| BRepBuilderAPI_MakeWire,
|
| BRepBuilderAPI_MakeEdge,
|
| )
|
| from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse, BRepAlgoAPI_Cut, BRepAlgoAPI_Common
|
| from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakePrism
|
| from OCC.Core.BRepAdaptor import BRepAdaptor_Surface
|
| from OCC.Core.BRepGProp import brepgprop_VolumeProperties, brepgprop_SurfaceProperties
|
| from OCC.Core.GProp import GProp_GProps
|
| from OCC.Core.ShapeFix import ShapeFix_Face, ShapeFix_Wire
|
| from OCC.Core.gp import gp_Vec, gp_Ax2, gp_Dir, gp_Circ
|
| from OCC.Extend.DataExchange import write_stl_file
|
|
|
|
|
| class OBJReconverter:
|
| """OBJ Data Reconverter"""
|
|
|
| def __init__(self):
|
| self.vertex_dict = OrderedDict()
|
| self.PRECISION = 1e-5
|
| self.eps = 1e-7
|
| self.x_axis = gp_Dir(1.0, 0.0, 0.0)
|
|
|
| def convert_curve(self, curve):
|
| """
|
| convert to json dict format
|
| """
|
| json_curve = {}
|
|
|
| if curve.type == "circle":
|
| json_curve["type"] = "Circle3D"
|
| json_curve["center_point"] = {
|
| "x": curve.center[0],
|
| "y": curve.center[1],
|
| "z": 0,
|
| }
|
| json_curve["radius"] = curve.radius
|
|
|
| if curve.type == "line":
|
| json_curve["type"] = "Line3D"
|
| json_curve["start_point"] = {
|
| "x": curve.start[0],
|
| "y": curve.start[1],
|
| "z": 0,
|
| }
|
| json_curve["end_point"] = {"x": curve.end[0], "y": curve.end[1], "z": 0}
|
|
|
| if curve.type == "arc":
|
| json_curve["type"] = "Arc3D"
|
| json_curve["start_point"] = {
|
| "x": curve.start[0],
|
| "y": curve.start[1],
|
| "z": 0,
|
| }
|
| json_curve["end_point"] = {"x": curve.end[0], "y": curve.end[1], "z": 0}
|
| json_curve["mid_point"] = {"x": curve.mid[0], "y": curve.mid[1], "z": 0}
|
| json_curve["center_point"] = {
|
| "x": curve.center[0],
|
| "y": curve.center[1],
|
| "z": 0,
|
| }
|
|
|
| json_curve["is_outer"] = curve.is_outer
|
| return json_curve
|
|
|
| def convert_vertices(self):
|
| """Convert all the vertices to .obj format"""
|
| vertex_strings = ""
|
| for pt in self.vertex_dict.values():
|
|
|
| vertex_string = f"v {pt[0]} {pt[1]}\n"
|
| vertex_strings += vertex_string
|
| return vertex_strings
|
|
|
| def parse_obj(self, faces, meta_info):
|
| """
|
| reconstruct brep from obj file
|
| """
|
|
|
| for face in faces:
|
| for loop in face:
|
| if len(loop) > 1:
|
| for idx, curve in enumerate(loop[:-1]):
|
| next_curve = np.vstack([loop[idx + 1].start, loop[idx + 1].end])
|
| diff1 = np.sum(np.abs(curve.start - next_curve), 1)
|
| diff2 = np.sum(np.abs(curve.end - next_curve), 1)
|
|
|
| if min(diff2) == 0 or min(diff1) == 0:
|
| continue
|
|
|
| assert (
|
| min(diff1) < 1e-3 or min(diff2) < 1e-3
|
| )
|
|
|
| if min(diff1) > min(diff2):
|
| min_idx = np.argmin(diff2)
|
| if min_idx == 0:
|
| loop[idx + 1].start_idx = curve.end_idx
|
| loop[idx + 1].start = curve.end
|
| else:
|
| loop[idx + 1].end_idx = curve.end_idx
|
| loop[idx + 1].end = curve.end
|
| else:
|
| min_idx = np.argmin(diff1)
|
| if min_idx == 0:
|
| loop[idx + 1].start_idx = curve.start_idx
|
| loop[idx + 1].start = curve.start
|
| else:
|
| loop[idx + 1].end_idx = curve.start_idx
|
| loop[idx + 1].end = curve.start
|
|
|
|
|
| shared_idx = list(
|
| set([loop[-2].start_idx, loop[-2].end_idx]).intersection(
|
| set([loop[-1].start_idx, loop[-1].end_idx])
|
| )
|
| )
|
|
|
| assert len(shared_idx) >= 1
|
|
|
| if len(shared_idx) == 2:
|
| assert len(loop) == 2
|
| else:
|
| if shared_idx[0] == loop[-1].start_idx:
|
| do_start = False
|
| else:
|
| do_start = True
|
| start_curve = np.vstack([loop[0].start, loop[0].end])
|
|
|
| if do_start:
|
| diff = np.sum(np.abs(loop[-1].start - start_curve), 1)
|
| else:
|
| diff = np.sum(np.abs(loop[-1].end - start_curve), 1)
|
| assert min(diff) < 1e-3
|
|
|
| min_idx = np.argmin(diff)
|
| if min_idx == 0:
|
| if do_start:
|
| loop[-1].start_idx = loop[0].start_idx
|
| loop[-1].start = loop[0].start
|
| else:
|
| loop[-1].end_idx = loop[0].start_idx
|
| loop[-1].end = loop[0].start
|
| else:
|
| if do_start:
|
| loop[-1].start_idx = loop[0].end_idx
|
| loop[-1].start = loop[0].end
|
| else:
|
| loop[-1].end_idx = loop[0].end_idx
|
| loop[-1].end = loop[0].end
|
|
|
|
|
| extrusion = {}
|
| extrusion["profiles"] = []
|
| for face in faces:
|
| profile = {}
|
| profile["loops"] = []
|
| for loop in face:
|
| pl = {}
|
| pl["profile_curves"] = []
|
| for curve in loop:
|
|
|
| pl["profile_curves"].append(self.convert_curve(curve))
|
| profile["loops"].append(pl)
|
| extrusion["profiles"].append(profile)
|
|
|
|
|
| sketch = {}
|
| transform = {}
|
| transform["origin"] = {
|
| "x": meta_info["t_orig"][0],
|
| "y": meta_info["t_orig"][1],
|
| "z": meta_info["t_orig"][2],
|
| }
|
| transform["x_axis"] = {
|
| "x": meta_info["t_x"][0],
|
| "y": meta_info["t_x"][1],
|
| "z": meta_info["t_x"][2],
|
| }
|
| transform["y_axis"] = {
|
| "x": meta_info["t_y"][0],
|
| "y": meta_info["t_y"][1],
|
| "z": meta_info["t_y"][2],
|
| }
|
| transform["z_axis"] = {
|
| "x": meta_info["t_z"][0],
|
| "y": meta_info["t_z"][1],
|
| "z": meta_info["t_z"][2],
|
| }
|
| sketch["transform"] = transform
|
|
|
|
|
| extrude_params = {}
|
| extrude_params["extrude_type"] = meta_info["set_op"]
|
| extrude_params["extrude_values"] = meta_info["extrude_value"]
|
|
|
|
|
| all_faces = []
|
| curve_strings = ""
|
| curve_count = 0
|
| for profile in extrusion["profiles"]:
|
| ref_face, face, curve_string, c_count = self.parse_sketch(sketch, profile)
|
| curve_strings += curve_string
|
| curve_count += c_count
|
| all_faces.append(face)
|
|
|
|
|
| plane_face = all_faces[0]
|
| for face in all_faces[1:]:
|
| plane_face = self.my_op(plane_face, face, "fuse")
|
| solid = self.extrude_face(ref_face, plane_face, extrude_params)
|
| return solid, curve_strings, curve_count
|
|
|
| def my_op(self, big, small, op_name):
|
| if op_name == "cut":
|
| op = BRepAlgoAPI_Cut(big, small)
|
| elif op_name == "fuse":
|
| op = BRepAlgoAPI_Fuse(big, small)
|
| elif op_name == "common":
|
| op = BRepAlgoAPI_Common(big, small)
|
| op.SetFuzzyValue(self.PRECISION)
|
| op.Build()
|
| return op.Shape()
|
|
|
| def build_body(self, face, normal, value):
|
| extrusion_vec = gp_Vec(normal).Multiplied(value)
|
| make_prism = BRepPrimAPI_MakePrism(face, extrusion_vec)
|
| make_prism.Build()
|
| prism = make_prism.Prism()
|
| return prism.Shape()
|
|
|
| def extrudeBasedOnType(self, face, normal, distance):
|
|
|
| if not (distance[0] < distance[1]):
|
| raise Exception("incorrect distance")
|
| large_value = distance[1]
|
| small_value = distance[0]
|
|
|
| if large_value == 0:
|
| return self.build_body(face, -normal, -small_value)
|
| elif small_value == 0:
|
| return self.build_body(face, normal, large_value)
|
| elif np.sign(large_value) == np.sign(small_value):
|
| if large_value < 0:
|
| body1 = self.build_body(face, -normal, -small_value)
|
| body2 = self.build_body(face, -normal, -large_value)
|
| return self.my_op(body1, body2, "cut")
|
| else:
|
| assert large_value > 0
|
| body1 = self.build_body(face, normal, small_value)
|
| body2 = self.build_body(face, normal, large_value)
|
| return self.my_op(body2, body1, "cut")
|
| else:
|
| assert np.sign(large_value) != np.sign(small_value)
|
| body1 = self.build_body(face, normal, large_value)
|
| body2 = self.build_body(face, -normal, -small_value)
|
| return self.my_op(body1, body2, "fuse")
|
|
|
| def extrude_face(self, ref_face, face, extrude_params):
|
| distance = extrude_params["extrude_values"]
|
| surf = BRepAdaptor_Surface(ref_face).Plane()
|
| normal = surf.Axis().Direction()
|
| extruded_shape = self.extrudeBasedOnType(face, normal, distance)
|
| return extruded_shape
|
|
|
| def parse_sketch(self, sketch, profile):
|
| """
|
| Sketch in one closed loop (one out, multiple ins)
|
| """
|
|
|
| transform = get_transform(sketch["transform"])
|
|
|
|
|
| outer_facelist = []
|
| inner_facelist = []
|
| curve_count = 0
|
| outer_string = []
|
| inner_string = []
|
| plane = create_sketch_plane(sketch["transform"])
|
|
|
| for idx, pl in enumerate(profile["loops"]):
|
|
|
| loop, curve_string, num_curve = self.parse_loop(
|
| pl["profile_curves"], transform
|
| )
|
|
|
| face_builder = BRepBuilderAPI_MakeFace(plane, loop)
|
| if not face_builder.IsDone():
|
| raise Exception("face builder not done")
|
| face = face_builder.Face()
|
|
|
| fixer = ShapeFix_Face(face)
|
| fixer.SetPrecision(self.PRECISION)
|
| fixer.FixOrientation()
|
|
|
| analyzer = BRepCheck_Analyzer(fixer.Face())
|
| if not analyzer.IsValid():
|
| raise Exception("face check failed")
|
|
|
| curve_count += num_curve
|
|
|
| if pl["profile_curves"][0]["is_outer"]:
|
| outer_facelist.append(fixer.Face())
|
| outer_string.append(curve_string)
|
| else:
|
| inner_facelist.append(fixer.Face())
|
| inner_string.append(curve_string)
|
|
|
|
|
| assert len(outer_facelist) > 0
|
| final_face = outer_facelist[0]
|
| for face in outer_facelist[1:]:
|
| final_face = self.my_op(final_face, face, "fuse")
|
| for face in inner_facelist:
|
| final_face = self.my_op(final_face, face, "cut")
|
|
|
|
|
| assert len(outer_string) == 1
|
| out_str = ""
|
| in_str = ""
|
| for c_str in outer_string:
|
| out_str += "out\n" + c_str + "\n"
|
| for c_str in inner_string:
|
| in_str += "in\n" + c_str + "\n"
|
| final_str = "face\n" + out_str + in_str
|
|
|
| return outer_facelist[0], final_face, final_str, curve_count
|
|
|
| def parse_loop(self, profile_loop, transform):
|
| """Create face in one closed loop"""
|
| topo_wire = BRepBuilderAPI_MakeWire()
|
| curve_strings = ""
|
| curve_count = 0
|
|
|
|
|
| for profile_curve in profile_loop:
|
| curve_edge, curve_string = self.parse_curve(profile_curve, transform)
|
| topo_wire.Add(curve_edge)
|
| if not topo_wire.IsDone():
|
| raise Exception("wire builder not done")
|
|
|
| curve_string += "\n"
|
| curve_count += 1
|
| curve_strings += curve_string
|
|
|
| fixer = ShapeFix_Wire()
|
| fixer.Load(topo_wire.Wire())
|
| fixer.SetPrecision(self.PRECISION)
|
| fixer.FixClosed()
|
| fixer.Perform()
|
| return fixer.Wire(), curve_strings, curve_count
|
|
|
| def parse_curve(self, curve, transform):
|
| if curve["type"] == "Line3D":
|
| return self.create_line(curve, transform)
|
| elif curve["type"] == "Circle3D":
|
| return self.create_circle(curve, transform)
|
| elif curve["type"] == "Arc3D":
|
| return self.create_arc(curve, transform)
|
| else:
|
| raise Exception("unknown curve type")
|
|
|
| def create_line(self, line, transform):
|
| start = create_point(line["start_point"], transform)
|
| end = create_point(line["end_point"], transform)
|
| if start.Distance(end) == 0:
|
| raise Exception("start/end point same location")
|
| topo_edge = BRepBuilderAPI_MakeEdge(start, end)
|
|
|
|
|
| star_idx = self.save_vertex(
|
| line["start_point"]["x"] + 0.0, line["start_point"]["y"] + 0.0, "p"
|
| )
|
| end_idx = self.save_vertex(
|
| line["end_point"]["x"] + 0.0, line["end_point"]["y"] + 0.0, "p"
|
| )
|
| curve_string = f"l {star_idx} {end_idx}"
|
| return topo_edge.Edge(), curve_string
|
|
|
| def create_arc(self, arc, transform):
|
| start = create_point(arc["start_point"], transform)
|
| mid = create_point(arc["mid_point"], transform)
|
| end = create_point(arc["end_point"], transform)
|
| arc_occ = GC_MakeArcOfCircle(start, mid, end).Value()
|
| topo_edge = BRepBuilderAPI_MakeEdge(arc_occ)
|
|
|
|
|
| start_idx = self.save_vertex(
|
| arc["start_point"]["x"] + 0.0, arc["start_point"]["y"] + 0.0, "p"
|
| )
|
| end_idx = self.save_vertex(
|
| arc["end_point"]["x"] + 0.0, arc["end_point"]["y"] + 0.0, "p"
|
| )
|
| center_idx = self.save_vertex(
|
| arc["center_point"]["x"] + 0.0, arc["center_point"]["y"] + 0.0, "p"
|
| )
|
| mid_idx = self.save_vertex(
|
| arc["mid_point"]["x"] + 0.0, arc["mid_point"]["y"] + 0.0, "p"
|
| )
|
| curve_string = f"a {start_idx} {mid_idx} {center_idx} {end_idx}"
|
| return topo_edge.Edge(), curve_string
|
|
|
| def create_circle(self, circle, transform):
|
| center = create_point(circle["center_point"], transform)
|
| radius = circle["radius"]
|
| normal = create_unit_vec({"x": 0.0, "y": 0.0, "z": 1.0}, transform)
|
| ref_vector3d = self.x_axis.Transformed(transform)
|
| axis = gp_Ax2(center, normal, ref_vector3d)
|
| gp_circle = gp_Circ(axis, abs(float(radius)))
|
| topo_edge = BRepBuilderAPI_MakeEdge(gp_circle)
|
|
|
| center_idx = self.save_vertex(
|
| circle["center_point"]["x"] + 0.0, circle["center_point"]["y"] + 0.0, "p"
|
| )
|
| radius_idx = self.save_vertex(abs(float(radius)) + 0.0, 0, "r")
|
| curve_string = f"c {center_idx} {radius_idx}"
|
| return topo_edge.Edge(), curve_string
|
|
|
| def save_vertex(self, h_x, h_y, text):
|
| unique_key = f"{text}:x{h_x}y{h_y}"
|
| index = 0
|
| for key in self.vertex_dict.keys():
|
|
|
| if unique_key == key:
|
| return index
|
| index += 1
|
|
|
| self.vertex_dict[unique_key] = [h_x, h_y]
|
| return index
|
|
|