def poles_from_bsplinecurve_edge(occedge): """ This function fetches the poles of a bspline OCCedge. Parameters ---------- occedge : OCCedge The OCCedge to be examined. The OCCedge needs to contain a bspline curve Returns ------- List of poles : pyptlist List of poles of the bspline curve """ adaptor = BRepAdaptor_Curve(occedge) adaptor_handle = BRepAdaptor_HCurve(adaptor) handle_bspline = Handle_Geom_BSplineCurve() bspline = handle_bspline.DownCast(adaptor.Curve().Curve()).GetObject() npoles = bspline.NbPoles() polelist = [] for np in range(npoles): pole = bspline.Pole(np+1) pypole = (pole.X(), pole.Y(), pole.Z()) polelist.append(pypole) if topods_Edge(occedge).Orientation() == TopAbs_REVERSED: polelist.reverse() return polelist
def trimedge(lbound, ubound, occedge): """ This function trims the OCCedge according to the specified lower and upper bound. Parameters ---------- lbound : float The lower bound of the OCCedge. ubound : float The upper bound of the OCCedge. occedge : OCCedge The edge to be trimmed. Returns ------- trimmed edge : OCCedge The trimmed OCCedge. """ adaptor = BRepAdaptor_Curve(occedge) tr = Geom_TrimmedCurve(adaptor.Curve().Curve(), lbound, ubound) tr.SetTrim(lbound, ubound) bspline_handle = geomconvert_CurveToBSplineCurve(tr.GetHandle()) tr_edge = BRepBuilderAPI_MakeEdge(bspline_handle) return tr_edge.Edge()
def project_point_on_curve(crv: TopoDS_Edge, pnt: gp_Pnt) -> Tuple[float, gp_Pnt]: """ :param crv: TopoDS_Edge :param pnt: :return: """ if isinstance(crv, TopoDS_Shape): # get the curve handle... adaptor = BRepAdaptor_Curve(crv) crv = adaptor.Curve().Curve() rrr = GeomAPI_ProjectPointOnCurve(pnt, crv) return rrr.LowerDistanceParameter(), rrr.NearestPoint() else: raise NotImplementedError('expected a TopoDS_Edge...')
def poles_from_bsplinecurve_edge(occedge): ''' occedge: the edge to be measured type: occedge ''' #occutil_edge = edge.Edge(occedge) adaptor = BRepAdaptor_Curve(occedge) adaptor_handle = BRepAdaptor_HCurve(adaptor) handle_bspline = Handle_Geom_BSplineCurve() bspline = handle_bspline.DownCast(adaptor.Curve().Curve()).GetObject() npoles = bspline.NbPoles() polelist = [] for np in range(npoles): pole = bspline.Pole(np + 1) pypole = (pole.X(), pole.Y(), pole.Z()) polelist.append(pypole) if topods_Edge(occedge).Orientation() == TopAbs_REVERSED: polelist.reverse() return polelist
def trimedge(lbound, ubound, occedge): ''' lbound: lower bound of the parameterise edge type: float ubound: upper bound of the parameterise edge type: float occedge: the edge to be trimmed type: occedge ''' adaptor = BRepAdaptor_Curve(occedge) #print adaptor.Trim(adaptor.FirstParameter(), adaptor.LastParameter(), adaptor.Tolerance()).GetObject().NbPoles().Curve() tr = Geom_TrimmedCurve(adaptor.Curve().Curve(), lbound, ubound) tr.SetTrim(lbound, ubound) bspline_handle = geomconvert_CurveToBSplineCurve(tr.GetHandle()) #print tr.GetHandle() tr_edge = BRepBuilderAPI_MakeEdge(bspline_handle) #occutil_edge = edge.Edge(occedge) #trimedge = occutil_edge.trim(lbound, ubound) return tr_edge.Edge()
class Edge(KbeObject, TopoDS_Edge): ''' ''' def __init__(self, edge): ''' ''' assert isinstance( edge, TopoDS_Edge), 'need a TopoDS_Edge, got a %s' % edge.__class__ KbeObject.__init__(self, 'edge') TopoDS_Edge.__init__(self, edge) # tracking state self._local_properties_init = False self._curvature_init = False self._geometry_lookup_init = False self._curve_handle = None self._curve = None self._adaptor = None self._adaptor_handle = None # instantiating cooperative classes # cooperative classes are distinct through CamelCaps from normal method -> pep8 self.DiffGeom = DiffGeomCurve(self) self.Intersect = IntersectCurve(self) self.Construct = ConstructFromCurve(self) #self.graphic = GraphicCurve(self) # GeomLProp object self._curvature = None def is_closed(self): return self.adaptor.IsClosed() def is_periodic(self): return self.adaptor.IsPeriodic() def is_rational(self): return self.adaptor.IsRational() def continuity(self): return self.adaptor.Continuity def degree(self): if 'line' in self.type: return 1 elif 'curve' in self.type: return self.adaptor.Degree() else: # hyperbola, parabola, circle return 2 def nb_knots(self): return self.adaptor.NbKnots() def nb_poles(self): return self.adaptor.NbPoles() @property def curve(self): if self._curve is not None and not self.is_dirty: pass else: self._curve_handle = BRep_Tool().Curve(self)[0] self._curve = self._curve_handle.GetObject() return self._curve @property def curve_handle(self): if self._curve_handle is not None and not self.is_dirty: pass else: self.curve return self._curve_handle @property def adaptor(self): if self._adaptor is not None and not self.is_dirty: pass else: self._adaptor = BRepAdaptor_Curve(self) self._adaptor_handle = BRepAdaptor_HCurve(self._adaptor) return self._adaptor @property def adaptor_handle(self): if self._adaptor_handle is not None and not self.is_dirty: pass else: self.adaptor return self._adaptor_handle @property def geom_curve_handle(self): """ :return: Handle_Geom_Curve adapted from `self` """ if self._adaptor_handle is not None and not self.is_dirty: pass else: self.adaptor return self._adaptor.Curve().Curve() @property def type(self): return geom_lut[self.adaptor.Curve().GetType()] def pcurve(self, face): """ computes the 2d parametric spline that lies on the surface of the face :return: Geom2d_Curve, u, v """ crv, u, v = BRep_Tool().CurveOnSurface(self, face) return crv.GetObject(), u, v def _local_properties(self): self._lprops_curve_tool = GeomLProp_CurveTool() self._local_properties_init = True def domain(self): '''returns the u,v domain of the curve''' return self.adaptor.FirstParameter(), self.adaptor.LastParameter() def project(self, other): '''projects self with a point, curve, edge, face, solid method wraps dealing with the various topologies ''' raise NotImplementedError #=============================================================================== # Curve.GlobalProperties #=============================================================================== def length(self, lbound=None, ubound=None, tolerance=1e-5): '''returns the curve length if either lbound | ubound | both are given, than the lenght of the curve will be measured over that interval ''' _min, _max = self.domain() if _min < self.adaptor.FirstParameter(): raise ValueError( 'the lbound argument is lower than the first parameter of the curve: %s ' % (self.adaptor.FirstParameter())) if _max > self.adaptor.LastParameter(): raise ValueError( 'the ubound argument is greater than the last parameter of the curve: %s ' % (self.adaptor.LastParameter())) lbound = _min if lbound is None else lbound ubound = _max if ubound is None else ubound return GCPnts_AbscissaPoint().Length(self.adaptor, lbound, ubound, tolerance) #=============================================================================== # Curve.modify #=============================================================================== def trim(self, lbound, ubound, periodic=False): ''' trim the curve @param lbound: @param ubound: ''' a, b = sorted([lbound, ubound]) tr = Geom_TrimmedCurve(self.adaptor.Curve().Curve(), a, b).GetHandle() return Edge(make_edge(tr)) def extend_by_point(self, pnt, degree=3, beginning=True): '''extends the curve to point does not extend if the degree of self.curve > 3 @param pnt: @param degree: @param beginning: ''' if self.degree > 3: raise ValueError, 'to extend you self.curve should be <= 3, is %s ' % ( self.degree) return GeomLib().ExtendCurveToPoint(self.curve, pnt, degree, beginning) #=============================================================================== # Curve. #=============================================================================== def closest(self, other): return minimum_distance(self.brep, other) def project_vertex(self, pnt_or_vertex): ''' returns the closest orthogonal project on `pnt` on edge ''' if isinstance(pnt_or_vertex, TopoDS_Vertex): pnt_or_vertex = vertex2pnt(pnt_or_vertex) poc = GeomAPI_ProjectPointOnCurve(pnt_or_vertex, self.curve_handle) return poc.LowerDistanceParameter(), poc.NearestPoint() def distance_on_curve(self, distance, close_parameter, estimate_parameter, check_seam=True): '''returns the parameter if there is a parameter on the curve with a distance length from u raises OutOfBoundary if no such parameter exists ''' ccc = GCPnts_AbscissaPoint(self.adaptor, distance, close_parameter, estimate_parameter, 1e-5) with assert_isdone(ccc, 'couldnt compute distance on curve'): return ccc.Parameter() def mid_point(self): """ :return: the parameter at the mid point of the curve, and its corresponding gp_Pnt """ _min, _max = self.domain() _mid = (_min + _max) / 2. return _mid, self.adaptor.Value(_mid) def divide_by_number_of_points(self, n_pts, lbound=None, ubound=None): '''returns a nested list of parameters and points on the edge at the requested interval [(param, gp_Pnt),...] ''' _lbound, _ubound = self.domain() if lbound: _lbound = lbound elif ubound: _ubound = ubound # minimally two points or a Standard_ConstructionError is raised if n_pts <= 1: n_pts = 2 try: npts = GCPnts_UniformAbscissa(self.adaptor, n_pts, _lbound, _ubound) except: #import ipdb; ipdb.set_trace() print "Warning : GCPnts_UniformAbscissa failed" if npts.IsDone(): tmp = [] for i in xrange(1, npts.NbPoints() + 1): param = npts.Parameter(i) pnt = self.adaptor.Value(param) tmp.append((param, pnt)) return tmp else: return None @property def color(self, indx=None): '''sets or gets the color of self, possible also on a specified controlPoint index ''' raise NotImplementedError @property def weight(self, indx): '''descriptor sets or gets the weight of a control point at the index ''' #TODO self.curve has to be generalized to a bspline for this... raise NotImplementedError def control_pt_coord(self, indx): #TODO confused; vertices != control points '''descriptor setting or getting the coordinate of a control point at indx''' raise NotImplementedError def greville_points(self): #TODO confused; vertices != greville points '''descriptor setting or getting the coordinate of a control point at indx''' raise NotImplementedError def control_point(self, indx, pt=None): '''gets or sets the coordinate of the control point ''' raise NotImplementedError def __eq__(self, other): if hasattr(other, 'topo'): return self.IsEqual(other) else: return self.IsEqual(other) def __ne__(self, other): return not (self.__eq__(other)) def first_vertex(self): # TODO: should return Vertex, not TopoDS_Vertex return TopExp.FirstVertex(self) def last_vertex(self): return TopExp.LastVertex(self) def common_vertex(self, edge): vert = TopoDS_Vertex() if TopExp.CommonVertex(self, edge, vert): return vert else: return False def as_vec(self): if self.is_line(): first, last = map( vertex2pnt, [self.first_vertex(), self.last_vertex()]) return gp_Vec(first, last) else: raise ValueError( "edge is not a line, hence no meaningful vector can be returned" ) #=============================================================================== # Curve. #=============================================================================== def parameter_to_point(self, u): '''returns the coordinate at parameter u ''' return self.adaptor.Value(u) def point_to_parameter(self, coord): '''returns the parameters / pnt on edge at world coordinate `coord` ''' return self.project_pnt_on_edge(coord) def transform(self, transform): '''affine transform ''' raise NotImplementedError def fix_continuity(self, continuity): """ splits an edge to achieve a level of continuity :param continuity: GeomAbs_C* """ return fix_continuity(self, continuity) def continuity_to_another_curve(self, other): '''returns continuity between self and another curve ''' return self._lprops_curve_tool(self.curve) def continuity_from_faces(self, f1, f2): return BRep_Tool_Continuity(self, f1, f2) #=============================================================================== # Curve.loop #=============================================================================== def iter_control_points(self): '''iterator over the control points ''' raise NotImplementedError def iter_weights(self): '''iterator over the weights ''' raise NotImplementedError #=============================================================================== # Curve. #=============================================================================== def is_trimmed(self): '''checks if curve is trimmed check if the underlying geom type is trimmed ''' raise NotImplementedError def is_line(self, tolerance=None): '''checks if the curve is planar within a tolerance ''' if self.nb_knots() == 2 and self.nb_poles() == 2: return True else: return False def is_seam(self, face): """ :return: True if the edge has two pcurves on one surface ( in the case of a sphere for example... ) """ sae = ShapeAnalysis_Edge() return sae.IsSeam(self, face) def is_edge_on_face(self, face): '''checks whether curve lies on a surface or a face ''' return ShapeAnalysis_Edge().HasPCurve(self, face) def on_edge(self, edge): '''checks if the curve lies on an edge or a border ''' raise NotImplementedError #=============================================================================== # Curve.graphic #=============================================================================== def show(self, poles=False, vertices=False, knots=False): ''' poles, knots, should render all slightly different. here's how... http://www.opencascade.org/org/forum/thread_1125/ ''' show = super(Edge, self).show() def update(self, context): '''updates the graphic presentation when called ''' raise NotImplementedError @property def color(self, *rgb): '''color descriptor for the curve ''' raise NotImplementedError
class Edge(TopoDS_Edge, BaseObject): def __init__(self, edge, data=None): assert isinstance( edge, TopoDS_Edge), 'need a TopoDS_Edge, got a %s' % edge.__class__ assert not edge.IsNull() super(Edge, self).__init__() BaseObject.__init__(self, 'edge', data=data) # we need to copy the base shape using the following three # lines assert self.IsNull() self.TShape(edge.TShape()) self.Location(edge.Location()) self.Orientation(edge.Orientation()) assert not self.IsNull() # tracking state self._local_properties_init = False self._curvature_init = False self._geometry_lookup_init = False self._curve_handle = None self._curve = None self._adaptor = None self._adaptor_handle = None # instantiating cooperative classes # cooperative classes are distinct through CamelCaps from # normal method -> pep8 self.DiffGeom = DiffGeomCurve(self) self.Intersect = IntersectCurve(self) self.Construct = ConstructFromCurve(self) # GeomLProp object self._curvature = None def Shape(self): return list(Topo(self).edges()).pop() def is_closed(self): return self.adaptor.IsClosed() def is_periodic(self): return self.adaptor.IsPeriodic() def is_rational(self): return self.adaptor.IsRational() def continuity(self): return self.adaptor.Continuity def degree(self): if 'line' in self.type: return 1 elif 'curve' in self.type: return self.adaptor.Degree() else: # hyperbola, parabola, circle return 2 def nb_knots(self): return self.adaptor.NbKnots() def nb_poles(self): return self.adaptor.NbPoles() @property def curve(self): if self._curve is not None and not self.is_dirty: pass else: self._curve_handle = BRep_Tool().Curve(self)[0] self._curve = self._curve_handle.GetObject() return self._curve @property def curve_handle(self): if self._curve_handle is not None and not self.is_dirty: return self._curve_handle else: return None @property def adaptor(self): if self._adaptor is not None and not self.is_dirty: pass else: self._adaptor = BRepAdaptor_Curve(self) self._adaptor_handle = BRepAdaptor_HCurve(self._adaptor) return self._adaptor @property def adaptor_handle(self): if self._adaptor_handle is not None and not self.is_dirty: pass else: self.adaptor return self._adaptor_handle @property def geom_curve_handle(self): """ :return: Handle_Geom_Curve adapted from `self` """ if self._adaptor_handle is not None and not self.is_dirty: return self._adaptor.Curve().Curve() else: return None @property def type(self): return geom_lut[self.adaptor.Curve().GetType()] def pcurve(self, face): """ computes the 2d parametric spline that lies on the surface of the face :return: Geom2d_Curve, u, v """ crv, u, v = BRep_Tool().CurveOnSurface(self, face) return crv.GetObject(), u, v def _local_properties(self): self._lprops_curve_tool = GeomLProp_CurveTool() self._local_properties_init = True def domain(self): """returns the u,v domain of the curve""" return self.adaptor.FirstParameter(), self.adaptor.LastParameter() # =========================================================================== # Curve.GlobalProperties def length(self, lbound=None, ubound=None, tolerance=1e-5): """returns the curve length if either lbound | ubound | both are given, than the length of the curve will be measured over that interval """ _min, _max = self.domain() if _min < self.adaptor.FirstParameter(): raise ValueError( 'the lbound argument is lower than the first parameter of the curve: %s ' % (self.adaptor.FirstParameter())) if _max > self.adaptor.LastParameter(): raise ValueError( 'the ubound argument is greater than the last parameter of the curve: %s ' % (self.adaptor.LastParameter())) lbound = _min if lbound is None else lbound ubound = _max if ubound is None else ubound return GCPnts_AbscissaPoint().Length(self.adaptor, lbound, ubound, tolerance) # =========================================================================== # Curve.modify def trim(self, lbound, ubound): """ trim the curve @param lbound: @param ubound: """ a, b = sorted([lbound, ubound]) tr = Geom_TrimmedCurve(self.adaptor.Curve().Curve(), a, b).GetHandle() return Edge(make_edge(tr)) def extend_by_point(self, pnt, degree=3, beginning=True): """extends the curve to point does not extend if the degree of self.curve > 3 @param pnt: @param degree: @param beginning: """ if self.degree > 3: raise ValueError('to extend you self.curve should be <= 3, is %s' % (self.degree)) return geomlib.ExtendCurveToPoint(self.curve, pnt, degree, beginning) # =========================================================================== # Curve. def closest(self, other): return minimum_distance(self, other) def project_vertex(self, pnt_or_vertex): """ returns the closest orthogonal project on `pnt` on edge """ if isinstance(pnt_or_vertex, TopoDS_Vertex): pnt_or_vertex = vertex2pnt(pnt_or_vertex) poc = GeomAPI_ProjectPointOnCurve(pnt_or_vertex, self.curve_handle) return poc.LowerDistanceParameter(), poc.NearestPoint() def distance_on_curve(self, distance, close_parameter, estimate_parameter): """returns the parameter if there is a parameter on the curve with a distance length from u raises OutOfBoundary if no such parameter exists """ gcpa = GCPnts_AbscissaPoint(self.adaptor, distance, close_parameter, estimate_parameter, 1e-5) with assert_isdone(gcpa, 'couldnt compute distance on curve'): return gcpa.Parameter() def mid_point(self): """ :return: the parameter at the mid point of the curve, and its corresponding gp_Pnt """ _min, _max = self.domain() _mid = (_min + _max) / 2. return _mid, self.adaptor.Value(_mid) def divide_by_number_of_points(self, n_pts, lbound=None, ubound=None): """returns a nested list of parameters and points on the edge at the requested interval [(param, gp_Pnt),...] """ _lbound, _ubound = self.domain() if lbound: _lbound = lbound elif ubound: _ubound = ubound # minimally two points or a Standard_ConstructionError is raised if n_pts <= 1: n_pts = 2 try: npts = GCPnts_UniformAbscissa(self.adaptor, n_pts, _lbound, _ubound) except: print("Warning : GCPnts_UniformAbscissa failed") if npts.IsDone(): tmp = [] for i in range(1, npts.NbPoints() + 1): param = npts.Parameter(i) pnt = self.adaptor.Value(param) tmp.append((param, pnt)) return tmp else: return None def __eq__(self, other): if hasattr(other, 'topo'): return self.IsEqual(other) else: return self.IsEqual(other) def __ne__(self, other): return not self.__eq__(other) def first_vertex(self) -> TopoDS_Vertex: return topexp.FirstVertex(self) def last_vertex(self) -> TopoDS_Vertex: return topexp.LastVertex(self) def get_common_vertex(self, edge: TopoDS_Edge) -> Union[TopoDS_Vertex, None]: return self.common_vertex(self, edge) @classmethod def common_vertex(cls, edge1, edge2): vert = TopoDS_Vertex() if topexp.CommonVertex(edge1, edge2, vert): return vert else: return None def as_vec(self) -> gp_Vec: if self.is_line(): first, last = map( vertex2pnt, [self.first_vertex(), self.last_vertex()]) return gp_Vec(first, last) else: raise ValueError( "edge is not a line, hence no meaningful vector can be returned" ) @classmethod def create(cls, *args): """ https://www.opencascade.com/doc/occt-7.3.0/refman/html/class_b_rep_builder_a_p_i___make_edge.html :type V1: TopoDS_Vertex & :type V2: TopoDS_Vertex & :type P1: gp_Pnt :type P2: gp_Pnt :type L: gp_Lin :type p1: (Optional) float or gp_Pnt or TopoDS_Vertex :type p2: (Optional) float or gp_Pnt or TopoDS_Vertex :type L: gp_Circ :type p1: (Optional) float or gp_Pnt or TopoDS_Vertex :type p2: (Optional) float or gp_Pnt or TopoDS_Vertex :type L: gp_Elips :type p1: (Optional) float or gp_Pnt or TopoDS_Vertex :type p2: (Optional) float or gp_Pnt or TopoDS_Vertex :type L: gp_Hypr :type p1: (Optional) float or gp_Pnt or TopoDS_Vertex :type p2: (Optional) float or gp_Pnt or TopoDS_Vertex :type L: gp_Parab :type p1: (Optional) float or gp_Pnt or TopoDS_Vertex :type p2: (Optional) float or gp_Pnt or TopoDS_Vertex :type L: Handle_Geom_Curve & :type p1: (Optional) float or gp_Pnt or TopoDS_Vertex :type p2: (Optional) float or gp_Pnt or TopoDS_Vertex :type L: Handle_Geom_Curve & :type p1: (Optional) gp_Pnt or TopoDS_Vertex :type p2: (Optional) gp_Pnt or TopoDS_Vertex :type p1: (Optional) float :type p2: (Optional) float :type L: Handle_Geom2d_Curve & :type S: Handle_Geom_Surface & :type p1: (Optional) float or gp_Pnt or TopoDS_Vertex :type p2: (Optional) float or gp_Pnt or TopoDS_Vertex :type L: Handle_Geom2d_Curve :type S: Handle_Geom_Surface & :type P1: (Optional) gp_Pnt or TopoDS_Vertex :type P2: (Optional) gp_Pnt or TopoDS_Vertex :type p1: (Optional) float :type p2: (Optional) float * The general method to directly create an edge is to give - a 3D curve C as the support (geometric domain) of the edge, - two vertices V1 and V2 to limit the curve (definition of the restriction of the edge), and - two real values p1 and p2 which are the parameters for the vertices V1 and V2 on the curve. The curve may be defined as a 2d curve in the parametric space of a surface: a pcurve. The surface on which the edge is built is then kept at the level of the edge. The default tolerance will be associated with this edge. """ edge = BRepBuilderAPI_MakeEdge(*args) with assert_isdone(edge, 'failed to produce edge'): result = edge.Edge() edge.Delete() return result # =========================================================================== # Curve. def parameter_to_point(self, u): """returns the coordinate at parameter u """ return self.adaptor.Value(u) def fix_continuity(self, continuity): """ splits an edge to achieve a level of continuity :param continuity: GeomAbs_C* """ return fix_continuity(self, continuity) def continuity_from_faces(self, f1, f2): return BRep_Tool_Continuity(self, f1, f2) def is_line(self): """checks if the curve is planar """ if self.nb_knots() == 2 and self.nb_poles() == 2: return True else: return False def is_seam(self, face): """ :return: True if the edge has two pcurves on one surface ( in the case of a sphere for example... ) """ sae = ShapeAnalysis_Edge() return sae.IsSeam(self, face) def is_edge_on_face(self, face): """checks whether 'self' lies on a surface or a face """ shp = ShapeAnalysis_Edge() return shp.HasPCurve(self, face) # =========================================================================== # Curve.graphic def show(self): """ poles, knots, should render all slightly different. here's how... http://www.opencascade.org/org/forum/thread_1125/ """ super(Edge, self).show()