def _calcTransforms(self): """Computes transformation matrices to convert between coordinates Computes transformation matrices to convert between local and global coordinates. """ # r is the forward transformation matrix from world to local coordinates # ok i will be really honest, i cannot understand exactly why this works # something bout the order of the translation and the rotation. # the double-inverting is strange, and I don't understand it. forward = Matrix() inverse = Matrix() forwardT = gp_Trsf() inverseT = gp_Trsf() global_coord_system = gp_Ax3() local_coord_system = gp_Ax3( gp_Pnt(*self.origin.toTuple()), gp_Dir(*self.zDir.toTuple()), gp_Dir(*self.xDir.toTuple()), ) forwardT.SetTransformation(global_coord_system, local_coord_system) forward.wrapped = gp_GTrsf(forwardT) inverseT.SetTransformation(local_coord_system, global_coord_system) inverse.wrapped = gp_GTrsf(inverseT) self.lcs = local_coord_system self.rG = inverse self.fG = forward
def tessellate(self): self.vertices = [] self.triangles = [] self.normals = [] # global buffers p_buf = gp_Pnt() n_buf = gp_Vec() loc_buf = TopLoc_Location() offset = -1 # every line below is selected for performance. Do not introduce functions to "beautify" the code for face in get_faces(self.shape): if face.Orientation() == TopAbs_Orientation.TopAbs_REVERSED: i1, i2 = 2, 1 else: i1, i2 = 1, 2 internal = face.Orientation() == TopAbs_Orientation.TopAbs_INTERNAL poly = BRep_Tool.Triangulation_s(face, loc_buf) if poly is not None: Trsf = loc_buf.Transformation() # add vertices flat = [] for i in range(1, poly.NbNodes() + 1): flat.extend(poly.Node(i).Transformed(Trsf).Coord()) self.vertices.extend(flat) # add triangles flat = [] for i in range(1, poly.NbTriangles() + 1): coord = poly.Triangle(i).Get() flat.extend((coord[0] + offset, coord[i1] + offset, coord[i2] + offset)) self.triangles.extend(flat) # add normals if poly.HasUVNodes(): prop = BRepGProp_Face(face) flat = [] for i in range(1, poly.NbNodes() + 1): u, v = poly.UVNode(i).Coord() prop.Normal(u, v, p_buf, n_buf) if n_buf.SquareMagnitude() > 0: n_buf.Normalize() flat.extend(n_buf.Reverse().Coord( ) if internal else n_buf.Coord()) self.normals.extend(flat) offset += poly.NbNodes()
def rotated(self, rotate=(0, 0, 0)): """Returns a copy of this plane, rotated about the specified axes Since the z axis is always normal the plane, rotating around Z will always produce a plane that is parallel to this one. The origin of the workplane is unaffected by the rotation. Rotations are done in order x, y, z. If you need a different order, manually chain together multiple rotate() commands. :param rotate: Vector [xDegrees, yDegrees, zDegrees] :return: a copy of this plane rotated as requested. """ # NB: this is not a geometric Vector rotate = Vector(rotate) # Convert to radians. rotate = rotate.multiply(math.pi / 180.0) # Compute rotation matrix. T1 = gp_Trsf() T1.SetRotation( gp_Ax1(gp_Pnt(*(0, 0, 0)), gp_Dir(*self.xDir.toTuple())), rotate.x) T2 = gp_Trsf() T2.SetRotation( gp_Ax1(gp_Pnt(*(0, 0, 0)), gp_Dir(*self.yDir.toTuple())), rotate.y) T3 = gp_Trsf() T3.SetRotation( gp_Ax1(gp_Pnt(*(0, 0, 0)), gp_Dir(*self.zDir.toTuple())), rotate.z) T = Matrix(gp_GTrsf(T1 * T2 * T3)) # Compute the new plane. newXdir = self.xDir.transform(T) newZdir = self.zDir.transform(T) return Plane(self.origin, newXdir, newZdir)
def _dxf_spline(el): try: degree = el.dxf.degree periodic = el.closed rational = False knots_unique = OrderedDict() for k in el.knots: if k in knots_unique: knots_unique[k] += 1 else: knots_unique[k] = 1 # assmble knots knots = TColStd_Array1OfReal(1, len(knots_unique)) multiplicities = TColStd_Array1OfInteger(1, len(knots_unique)) for i, (k, m) in enumerate(knots_unique.items()): knots.SetValue(i + 1, k) multiplicities.SetValue(i + 1, m) # assemble weights if present: if el.weights: rational = True weights = TColStd_Array1OfReal(1, len(el.weights)) for i, w in enumerate(el.weights): weights.SetValue(i + 1, w) # assemble control points pts = TColgp_Array1OfPnt(1, len(el.control_points)) for i, p in enumerate(el.control_points): pts.SetValue(i + 1, gp_Pnt(*p)) if rational: spline = Geom_BSplineCurve(pts, weights, knots, multiplicities, degree, periodic) else: spline = Geom_BSplineCurve(pts, knots, multiplicities, degree, periodic) return (Edge(BRepBuilderAPI_MakeEdge(spline).Edge()), ) except Exception: return ()
def addLines(self): origin = (0, 0, 0) ais_list = [] for name, color, direction in zip(('X', 'Y', 'Z'), ('red', 'lawngreen', 'blue'), ((1, 0, 0), (0, 1, 0), (0, 0, 1))): line_placement = Geom_Line( gp_Ax1(gp_Pnt(*origin), gp_Dir(*direction))) line = AIS_Line(line_placement) line.SetColor(to_occ_color(color)) self.Helpers.addChild(ObjectTreeItem(name, ais=line)) ais_list.append(line) self.sigObjectsAdded.emit(ais_list)
def testVertices(self): e = Shape.cast( BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(1, 1, 0)).Edge()) self.assertEqual(2, len(e.Vertices()))
def _make_ellipse(self): ellipse = gp_Elips(gp_Ax2(gp_Pnt(1, 2, 3), gp.DZ_s()), 4.0, 2.0) return Shape.cast(BRepBuilderAPI_MakeEdge(ellipse).Edge())
def _make_circle(self): circle = gp_Circ(gp_Ax2(gp_Pnt(1, 2, 3), gp.DZ_s()), 2.0) return Shape.cast(BRepBuilderAPI_MakeEdge(circle).Edge())
def toPnt(self) -> gp_Pnt: return gp_Pnt(self.wrapped.XYZ())
def tessellate(self): self.vertices = array("f") self.triangles = array("f") self.normals = array("f") # global buffers p_buf = gp_Pnt() n_buf = gp_Vec() loc_buf = TopLoc_Location() offset = -1 # every line below is selected for performance. Do not introduce functions to "beautify" the code for face in get_faces(self.shape): if face.Orientation() == TopAbs_Orientation.TopAbs_REVERSED: i1, i2 = 2, 1 else: i1, i2 = 1, 2 internal = face.Orientation() == TopAbs_Orientation.TopAbs_INTERNAL poly = BRep_Tool.Triangulation_s(face, loc_buf) if poly is not None: Trsf = loc_buf.Transformation() # add vertices # [node.Transformed(Trsf).Coord() for node in poly.Nodes()] is 5-8 times slower! items = poly.Nodes() coords = [items.Value(i).Transformed(Trsf).Coord() for i in range(items.Lower(), items.Upper() + 1)] flat = [] for coord in coords: flat += coord self.vertices.extend(flat) # add triangles items = poly.Triangles() coords = [items.Value(i).Get() for i in range(items.Lower(), items.Upper() + 1)] flat = [] for coord in coords: flat += (coord[0] + offset, coord[i1] + offset, coord[i2] + offset) self.triangles.extend(flat) # add normals if poly.HasUVNodes(): def extract(uv0, uv1): prop.Normal(uv0, uv1, p_buf, n_buf) if n_buf.SquareMagnitude() > 0: n_buf.Normalize() return n_buf.Reverse().Coord() if internal else n_buf.Coord() prop = BRepGProp_Face(face) items = poly.UVNodes() uvs = [items.Value(i).Coord() for i in range(items.Lower(), items.Upper() + 1)] flat = [] for uv1, uv2 in uvs: flat += extract(uv1, uv2) self.normals.extend(flat) offset += poly.NbNodes()
def show_axis(self,origin = (0,0,0), direction=(0,0,1)): ax_placement = Geom_Axis1Placement(gp_Ax1(gp_Pnt(*origin), gp_Dir(*direction))) ax = AIS_Axis(ax_placement) self._display_ais(ax)
def tessellate(shape, tolerance: float, angularTolerance: float = 0.1): # Remove previous mesh data BRepTools.Clean_s(shape) triangulated = BRepTools.Triangulation_s(shape, tolerance) if not triangulated: # this will add mesh data to the shape and prevent calculating an exact bounding box after this call BRepMesh_IncrementalMesh(shape, tolerance, True, angularTolerance) vertices = [] triangles = [] normals = [] offset = 0 for face in get_faces(shape): loc = TopLoc_Location() poly = BRep_Tool.Triangulation_s(face, loc) Trsf = loc.Transformation() reverse = face.Orientation() == TopAbs_Orientation.TopAbs_REVERSED internal = face.Orientation() == TopAbs_Orientation.TopAbs_INTERNAL # add vertices if poly is not None: vertices += [(v.X(), v.Y(), v.Z()) for v in (v.Transformed(Trsf) for v in poly.Nodes())] # add triangles triangles += [( t.Value(1) + offset - 1, t.Value(3 if reverse else 2) + offset - 1, t.Value(2 if reverse else 3) + offset - 1, ) for t in poly.Triangles()] # add normals if poly.HasUVNodes(): prop = BRepGProp_Face(face) uvnodes = poly.UVNodes() for uvnode in uvnodes: p = gp_Pnt() n = gp_Vec() prop.Normal(uvnode.X(), uvnode.Y(), p, n) if n.SquareMagnitude() > 0: n.Normalize() if internal: n.Reverse() normals.append((n.X(), n.Y(), n.Z())) offset += poly.NbNodes() if not triangulated: # Remove the mesh data again BRepTools.Clean_s(shape) return ( np.asarray(vertices, dtype=np.float32), np.asarray(triangles, dtype=np.uint32), np.asarray(normals, dtype=np.float32), )
def getSVG(shape, opts=None): """ Export a shape to SVG """ d = {"width": 800, "height": 240, "marginLeft": 200, "marginTop": 20} if opts: d.update(opts) # need to guess the scale and the coordinate center uom = guessUnitOfMeasure(shape) width = float(d["width"]) height = float(d["height"]) marginLeft = float(d["marginLeft"]) marginTop = float(d["marginTop"]) hlr = HLRBRep_Algo() hlr.Add(shape.wrapped) projector = HLRAlgo_Projector(gp_Ax2(gp_Pnt(), DEFAULT_DIR)) hlr.Projector(projector) hlr.Update() hlr.Hide() hlr_shapes = HLRBRep_HLRToShape(hlr) visible = [] visible_sharp_edges = hlr_shapes.VCompound() if not visible_sharp_edges.IsNull(): visible.append(visible_sharp_edges) visible_smooth_edges = hlr_shapes.Rg1LineVCompound() if not visible_smooth_edges.IsNull(): visible.append(visible_smooth_edges) visible_contour_edges = hlr_shapes.OutLineVCompound() if not visible_contour_edges.IsNull(): visible.append(visible_contour_edges) hidden = [] hidden_sharp_edges = hlr_shapes.HCompound() if not hidden_sharp_edges.IsNull(): hidden.append(hidden_sharp_edges) hidden_contour_edges = hlr_shapes.OutLineHCompound() if not hidden_contour_edges.IsNull(): hidden.append(hidden_contour_edges) # Fix the underlying geometry - otherwise we will get segfaults for el in visible: BRepLib.BuildCurves3d_s(el, TOLERANCE) for el in hidden: BRepLib.BuildCurves3d_s(el, TOLERANCE) # convert to native CQ objects visible = list(map(Shape, visible)) hidden = list(map(Shape, hidden)) (hiddenPaths, visiblePaths) = getPaths(visible, hidden) # get bounding box -- these are all in 2-d space bb = Compound.makeCompound(hidden + visible).BoundingBox() # width pixels for x, height pixesl for y unitScale = min(width / bb.xlen * 0.75, height / bb.ylen * 0.75) # compute amount to translate-- move the top left into view (xTranslate, yTranslate) = ( (0 - bb.xmin) + marginLeft / unitScale, (0 - bb.ymax) - marginTop / unitScale, ) # compute paths ( again -- had to strip out freecad crap ) hiddenContent = "" for p in hiddenPaths: hiddenContent += PATHTEMPLATE % p visibleContent = "" for p in visiblePaths: visibleContent += PATHTEMPLATE % p svg = SVG_TEMPLATE % ({ "unitScale": str(unitScale), "strokeWidth": str(1.0 / unitScale), "hiddenContent": hiddenContent, "visibleContent": visibleContent, "xTranslate": str(xTranslate), "yTranslate": str(yTranslate), "width": str(width), "height": str(height), "textboxY": str(height - 30), "uom": str(uom), }) # svg = SVG_TEMPLATE % ( # {"content": projectedContent} # ) return svg
def show_line(self,origin = (0,0,0), direction=(0,0,1)): line_placement = Geom_Line(gp_Ax1(gp_Pnt(*origin), gp_Dir(*direction))) line = AIS_Line(line_placement) self._display_ais(line)
def getSVG(shape, opts=None): """ Export a shape to SVG text. :param shape: A CadQuery shape object to convert to an SVG string. :type Shape: Vertex, Edge, Wire, Face, Shell, Solid, or Compound. :param opts: An options dictionary that influences the SVG that is output. :type opts: Dictionary, keys are as follows: width: Document width of the resulting image. height: Document height of the resulting image. marginLeft: Inset margin from the left side of the document. marginTop: Inset margin from the top side of the document. projectionDir: Direction the camera will view the shape from. showAxes: Whether or not to show the axes indicator, which will only be visible when the projectionDir is also at the default. strokeWidth: Width of the line that visible edges are drawn with. strokeColor: Color of the line that visible edges are drawn with. hiddenColor: Color of the line that hidden edges are drawn with. showHidden: Whether or not to show hidden lines. """ # Available options and their defaults d = { "width": 800, "height": 240, "marginLeft": 200, "marginTop": 20, "projectionDir": (-1.75, 1.1, 5), "showAxes": True, "strokeWidth": -1.0, # -1 = calculated based on unitScale "strokeColor": (0, 0, 0), # RGB 0-255 "hiddenColor": (160, 160, 160), # RGB 0-255 "showHidden": True, } if opts: d.update(opts) # need to guess the scale and the coordinate center uom = guessUnitOfMeasure(shape) width = float(d["width"]) height = float(d["height"]) marginLeft = float(d["marginLeft"]) marginTop = float(d["marginTop"]) projectionDir = tuple(d["projectionDir"]) showAxes = bool(d["showAxes"]) strokeWidth = float(d["strokeWidth"]) strokeColor = tuple(d["strokeColor"]) hiddenColor = tuple(d["hiddenColor"]) showHidden = bool(d["showHidden"]) hlr = HLRBRep_Algo() hlr.Add(shape.wrapped) projector = HLRAlgo_Projector(gp_Ax2(gp_Pnt(), gp_Dir(*projectionDir))) hlr.Projector(projector) hlr.Update() hlr.Hide() hlr_shapes = HLRBRep_HLRToShape(hlr) visible = [] visible_sharp_edges = hlr_shapes.VCompound() if not visible_sharp_edges.IsNull(): visible.append(visible_sharp_edges) visible_smooth_edges = hlr_shapes.Rg1LineVCompound() if not visible_smooth_edges.IsNull(): visible.append(visible_smooth_edges) visible_contour_edges = hlr_shapes.OutLineVCompound() if not visible_contour_edges.IsNull(): visible.append(visible_contour_edges) hidden = [] hidden_sharp_edges = hlr_shapes.HCompound() if not hidden_sharp_edges.IsNull(): hidden.append(hidden_sharp_edges) hidden_contour_edges = hlr_shapes.OutLineHCompound() if not hidden_contour_edges.IsNull(): hidden.append(hidden_contour_edges) # Fix the underlying geometry - otherwise we will get segfaults for el in visible: BRepLib.BuildCurves3d_s(el, TOLERANCE) for el in hidden: BRepLib.BuildCurves3d_s(el, TOLERANCE) # convert to native CQ objects visible = list(map(Shape, visible)) hidden = list(map(Shape, hidden)) (hiddenPaths, visiblePaths) = getPaths(visible, hidden) # get bounding box -- these are all in 2D space bb = Compound.makeCompound(hidden + visible).BoundingBox() # width pixels for x, height pixels for y unitScale = min(width / bb.xlen * 0.75, height / bb.ylen * 0.75) # compute amount to translate-- move the top left into view (xTranslate, yTranslate) = ( (0 - bb.xmin) + marginLeft / unitScale, (0 - bb.ymax) - marginTop / unitScale, ) # If the user did not specify a stroke width, calculate it based on the unit scale if strokeWidth == -1.0: strokeWidth = 1.0 / unitScale # compute paths hiddenContent = "" # Prevent hidden paths from being added if the user disabled them if showHidden: for p in hiddenPaths: hiddenContent += PATHTEMPLATE % p visibleContent = "" for p in visiblePaths: visibleContent += PATHTEMPLATE % p # If the caller wants the axes indicator and is using the default direction, add in the indicator if showAxes and projectionDir == (-1.75, 1.1, 5): axesIndicator = AXES_TEMPLATE % ({ "unitScale": str(unitScale), "textboxY": str(height - 30), "uom": str(uom) }) else: axesIndicator = "" svg = SVG_TEMPLATE % ( { "unitScale": str(unitScale), "strokeWidth": str(strokeWidth), "strokeColor": ",".join([str(x) for x in strokeColor]), "hiddenColor": ",".join([str(x) for x in hiddenColor]), "hiddenContent": hiddenContent, "visibleContent": visibleContent, "xTranslate": str(xTranslate), "yTranslate": str(yTranslate), "width": str(width), "height": str(height), "textboxY": str(height - 30), "uom": str(uom), "axesIndicator": axesIndicator, }) return svg