diff --git a/.travis.yml b/.travis.yml index 89faa20..3b62242 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,21 +7,12 @@ matrix: - env: PYTHON="2.7" CONDA_PY=27 os: linux dist: trusty - - env: PYTHON="2.7" CONDA_PY=27 - os: osx - osx_image: xcode8.3 - env: PYTHON="3.5" CONDA_PY=35 os: linux dist: trusty - - env: PYTHON="3.5" CONDA_PY=35 - os: osx - osx_image: xcode8.3 - env: PYTHON="3.6" CONDA_PY=36 os: linux dist: trusty - - env: PYTHON="3.6" CONDA_PY=36 - os: osx - osx_image: xcode8.3 install: - if [ ${PYTHON:0:1} == "2" ]; then @@ -52,16 +43,9 @@ install: conda create -n pythonocc-utils-test; source activate pythonocc-utils-test; fi; - - conda install -c dlr-sc -c conda-forge -c oce -c pythonocc pythonocc-core=0.18.1 + - conda install -c conda-forge scipy # solve radius example + - conda install -c dlr-sc -c conda-forge -c oce -c pythonocc pythonocc-core=0.18.2 -script: - # osx needs a dedicate environment - - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - source activate pythonocc-utils-test; - fi; - - python setup.py build install - - cd test - - python occutils_test.py branches: only: diff --git a/OCCUtils/Common.py b/OCCUtils/Common.py index 62597a6..0b9875f 100644 --- a/OCCUtils/Common.py +++ b/OCCUtils/Common.py @@ -19,30 +19,29 @@ import random -from OCC.Core.Bnd import Bnd_Box -from OCC.Core.BRepBndLib import brepbndlib_Add -from OCC.Core.TColgp import (TColgp_HArray1OfPnt, - TColgp_Array1OfPnt, - TColgp_Array1OfPnt2d, - TColgp_Array1OfVec) -from OCC.Core.TColStd import TColStd_HArray1OfBoolean +from OCC.Core import Graphic3d from OCC.Core.BRepAdaptor import (BRepAdaptor_Curve, BRepAdaptor_HCurve, BRepAdaptor_CompCurve, BRepAdaptor_HCompCurve) -from OCC.Core.GeomAPI import (GeomAPI_Interpolate, GeomAPI_PointsToBSpline, - GeomAPI_ProjectPointOnCurve) -from OCC.Core.gp import gp_Pnt, gp_Vec, gp_Trsf -from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_Transform -from OCC.Core.TopoDS import TopoDS_Edge, TopoDS_Shape, TopoDS_Wire, TopoDS_Vertex -from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_RGB -from OCC.Core.GProp import GProp_GProps -from OCC.Core.GeomAbs import GeomAbs_C1, GeomAbs_C2, GeomAbs_C3 +from OCC.Core.BRepBndLib import brepbndlib_Add from OCC.Core.BRepGProp import (brepgprop_LinearProperties, brepgprop_SurfaceProperties, brepgprop_VolumeProperties) -from OCC.Core.GeomAdaptor import GeomAdaptor_Curve +from OCC.Core.Bnd import Bnd_Box +from OCC.Core.GProp import GProp_GProps from OCC.Core.Geom import Geom_Curve +from OCC.Core.GeomAPI import (GeomAPI_Interpolate, GeomAPI_PointsToBSpline, + GeomAPI_ProjectPointOnCurve) +from OCC.Core.GeomAbs import GeomAbs_C2 +from OCC.Core.GeomAdaptor import GeomAdaptor_Curve +from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_RGB +from OCC.Core.TColStd import TColStd_HArray1OfBoolean +from OCC.Core.TColgp import (TColgp_HArray1OfPnt, + TColgp_Array1OfPnt, + TColgp_Array1OfPnt2d, + TColgp_Array1OfVec) +from OCC.Core.TopoDS import TopoDS_Edge, TopoDS_Shape, TopoDS_Wire, TopoDS_Vertex +from OCC.Core.gp import gp_Pnt, gp_Vec -from OCC.Core import Graphic3d #=========================================================================== # No PythonOCC dependencies... @@ -438,7 +437,7 @@ def resample_curve_with_uniform_deflection(curve, deflection=0.5, degreeMin=3, d defl = GCPnts_UniformDeflection(crv, deflection) with assert_isdone(defl, 'failed to compute UniformDeflection'): print("Number of points:", defl.NbPoints()) - sampled_pnts = [defl.Value(i) for i in xrange(1, defl.NbPoints())] + sampled_pnts = [defl.Value(i) for i in range(1, defl.NbPoints())] resampled_curve = GeomAPI_PointsToBSpline(point_list_to_TColgp_Array1OfPnt(sampled_pnts), degreeMin, degreeMax, continuity, tolerance) return resampled_curve.Curve().GetObject() diff --git a/OCCUtils/Construct.py b/OCCUtils/Construct.py index b3e0ba3..79f2b3c 100644 --- a/OCCUtils/Construct.py +++ b/OCCUtils/Construct.py @@ -22,46 +22,37 @@ ''' from __future__ import with_statement -from functools import wraps -import warnings -import operator + import math +import operator +import warnings +from functools import wraps -from OCC.Core.BRep import BRep_Tool +from OCC.Core.BRep import BRep_Tool, BRep_Builder from OCC.Core.BRepAdaptor import BRepAdaptor_HCurve +from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeFace, BRepBuilderAPI_Transform, BRepBuilderAPI_Sewing, \ + BRepBuilderAPI_MakePolygon, BRepBuilderAPI_MakeWire, BRepBuilderAPI_MakeSolid, BRepBuilderAPI_MakeShell, \ + BRepBuilderAPI_MakeEdge2d, BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeVertex, BRepBuilderAPI_FindPlane from OCC.Core.BRepOffset import BRepOffset_Skin -from OCC.Geom import Geom_TrimmedCurve -from OCC.GeomConvert import GeomConvert_ApproxCurve -from OCC.GeomLProp import GeomLProp_SLProps -from OCC.Core.BRepBuilderAPI import (BRepBuilderAPI_MakeFace, - BRepBuilderAPI_Transform, - BRepBuilderAPI_Sewing, - BRepBuilderAPI_MakePolygon, - BRepBuilderAPI_MakeWire, - BRepBuilderAPI_MakeSolid, - BRepBuilderAPI_MakeShell, - BRepBuilderAPI_MakeEdge2d, - BRepBuilderAPI_MakeEdge, - BRepBuilderAPI_MakeVertex, - BRepBuilderAPI_FindPlane) -from OCC.Core.BRepPrimAPI import (BRepPrimAPI_MakeBox, BRepPrimAPI_MakePrism) from OCC.Core.BRepOffsetAPI import BRepOffsetAPI_MakeEvolved -from OCC.GeomAbs import (GeomAbs_Arc, GeomAbs_C2, GeomAbs_C0, GeomAbs_Tangent, - GeomAbs_Intersection, GeomAbs_G1, GeomAbs_G2, - GeomAbs_C1) +from OCC.Core.BRepPrimAPI import (BRepPrimAPI_MakeBox, BRepPrimAPI_MakePrism) +from OCC.Core.Geom import Geom_TrimmedCurve +from OCC.Core.GeomAbs import (GeomAbs_Arc, GeomAbs_C2, GeomAbs_C0, GeomAbs_Tangent, + GeomAbs_Intersection, GeomAbs_C1) +from OCC.Core.GeomConvert import GeomConvert_ApproxCurve +from OCC.Core.GeomLProp import GeomLProp_SLProps +from OCC.Core.TColgp import TColgp_SequenceOfVec, TColgp_HArray1OfPnt from OCC.Core.TopAbs import TopAbs_REVERSED -from OCC.Core.TopoDS import (TopoDS_Wire, TopoDS_Solid, TopoDS_Vertex, TopoDS_Shape, - TopoDS_Builder, TopoDS_Compound) -from OCC.TColgp import TColgp_SequenceOfVec, TColgp_HArray1OfPnt -from OCC.gp import (gp_Vec, gp_Pnt, gp_Dir, gp_Trsf, gp_Ax1, gp_Quaternion, - gp_Circ, gp_Pln) +from OCC.Core.TopoDS import (TopoDS_Wire, TopoDS_Vertex, TopoDS_Shape, + TopoDS_Builder, TopoDS_Compound) +from OCC.Core.gp import (gp_Vec, gp_Pnt, gp_Dir, gp_Trsf, gp_Ax1, gp_Quaternion, + gp_Circ, gp_Pln) from OCCUtils.Common import (TOLERANCE, assert_isdone, to_tcol_, to_adaptor_3d, vertex2pnt, smooth_pnts, points_to_bspline, project_point_on_curve) -from OCCUtils.types_lut import ShapeToTopology from OCCUtils.Topology import Topo - +from OCCUtils.types_lut import ShapeToTopology EPSILON = TOLERANCE = 1e-6 ST = ShapeToTopology() @@ -307,6 +298,20 @@ def make_closed_polygon(*args): result = poly.Wire() return result +def make_compound(topology_to_compound): + # type: (Iterable) -> TopoDS_Compound + """ + + :type topology_to_compound: iterable + """ + compound = TopoDS_Compound() + builder = BRep_Builder() + builder.MakeCompound(compound) + for shp in topology_to_compound: + builder.Add(compound, shp) + + return compound + #=========================================================================== # PRIMITIVES #=========================================================================== diff --git a/OCCUtils/Iteration.py b/OCCUtils/Iteration.py index bd94952..9d4486a 100644 --- a/OCCUtils/Iteration.py +++ b/OCCUtils/Iteration.py @@ -18,7 +18,7 @@ ''' This module helps looping through topology ''' -from OCC.BRep import BRep_Tool +from OCC.Core.BRep import BRep_Tool from OCCUtils.Topology import WireExplorer, Topo from OCCUtils.edge import Edge diff --git a/OCCUtils/base.py b/OCCUtils/base.py index 896dc6a..0382b66 100644 --- a/OCCUtils/base.py +++ b/OCCUtils/base.py @@ -37,13 +37,14 @@ import functools +from OCC.Core import TopAbs from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_Copy from OCC.Core.BRepGProp import (brepgprop_VolumeProperties, brepgprop_LinearProperties, brepgprop_SurfaceProperties) from OCC.Core.BRepCheck import (BRepCheck_Vertex, BRepCheck_Edge, BRepCheck_Wire, BRepCheck_Face, BRepCheck_Shell, BRepCheck_Analyzer) -from OCC.GProp import GProp_GProps +from OCC.Core.GProp import GProp_GProps from OCC.Display.SimpleGui import init_display from OCCUtils.Common import get_boundingbox @@ -81,6 +82,47 @@ def __call__(self, *args, **kwargs): #============ + +def dumps_class_name(klass): + """ Improve string output for any oce object. + By default, __repr__ method returns something like: + > + This is too much verbose. + We prefer : + class<'gp_Pnt'> + or + class<'TopoDS_Shape'; Type:Solid; Id:59391729> + """ + # klass_name = str(klass.__class__).split(".")[3].split("'")[0] + klass_name = str(klass.__class__).split(".")[-1].split("\'")[0] + repr_string = "class<'" + klass_name + "'" +# for TopoDS_Shape, we also look for the base type + if klass_name == "TopoDS_Shape": + if klass.IsNull(): + repr_string += " : Null>" + return repr_string + st = klass.ShapeType() + types = {TopAbs.TopAbs_VERTEX: "Vertex", + TopAbs.TopAbs_SOLID: "Solid", + TopAbs.TopAbs_EDGE: "Edge", + TopAbs.TopAbs_FACE: "Face", + TopAbs.TopAbs_SHELL: "Shell", + TopAbs.TopAbs_WIRE: "Wire", + TopAbs.TopAbs_COMPOUND: "Compound", + TopAbs.TopAbs_COMPSOLID: "Compsolid"} + repr_string += "; Type:%s" % types[st] +# for each class that has an HashCode method define, +# print the id + if hasattr(klass, "HashCode"): + klass_id = hash(klass) + repr_string += "; id:%s" % klass_id + if hasattr(klass, "IsNull"): + if klass.IsNull(): + repr_string += "; Null" + repr_string += ">" + return repr_string + + class BaseObject(object): """base class for all objects""" def __init__(self, name=None, tolerance=TOLERANCE): @@ -90,6 +132,7 @@ def __init__(self, name=None, tolerance=TOLERANCE): self.tolerance = tolerance self.display_set = False + @property def is_dirty(self): '''when an object is dirty, its topology will be @@ -182,6 +225,12 @@ def __eq__(self, other): def __ne__(self, other): return not self.__eq__(other) + def __repr__(self): + return dumps_class_name(self) + + def __str__(self): + return dumps_class_name(self) + class GlobalProperties(object): ''' diff --git a/OCCUtils/boolean.py b/OCCUtils/boolean.py new file mode 100644 index 0000000..548d6a1 --- /dev/null +++ b/OCCUtils/boolean.py @@ -0,0 +1,23 @@ + +import logging +from typing import Iterable + +from OCC.Core.BOPAlgo import BOPAlgo_MakerVolume +from OCC.Core.TopTools import TopTools_ListOfShape +from OCC.Core.TopoDS import TopoDS_Shape + +log = logging.getLogger("OCCUtils") + + +def common_volume(shapes): + # type: (Iterable) -> TopoDS_Shape + mv = BOPAlgo_MakerVolume() + ls = TopTools_ListOfShape() + for i in shapes: + ls.Append(i) + mv.SetArguments(ls) + mv.Perform() + + log.debug("error status: {}".format(mv.ErrorStatus())) + + return mv.Shape() diff --git a/OCCUtils/edge.py b/OCCUtils/edge.py index 13f3c3b..a1221ec 100644 --- a/OCCUtils/edge.py +++ b/OCCUtils/edge.py @@ -15,26 +15,24 @@ ##You should have received a copy of the GNU Lesser General Public License ##along with pythonOCC. If not, see +from OCC.Core.BRep import BRep_Tool, BRep_Tool_Continuity from OCC.Core.BRepAdaptor import BRepAdaptor_Curve, BRepAdaptor_HCurve -from OCC.GCPnts import GCPnts_UniformAbscissa -from OCC.Geom import Geom_OffsetCurve, Geom_TrimmedCurve +from OCC.Core.BRepIntCurveSurface import BRepIntCurveSurface_Inter +from OCC.Core.BRepLProp import BRepLProp_CLProps +from OCC.Core.GCPnts import GCPnts_AbscissaPoint, GCPnts_QuasiUniformDeflection, GCPnts_UniformAbscissa +from OCC.Core.Geom import Geom_OffsetCurve, Geom_TrimmedCurve +from OCC.Core.GeomAPI import GeomAPI_ProjectPointOnCurve +from OCC.Core.GeomLProp import GeomLProp_CurveTool +from OCC.Core.GeomLib import geomlib +from OCC.Core.ShapeAnalysis import ShapeAnalysis_Edge from OCC.Core.TopExp import topexp from OCC.Core.TopoDS import TopoDS_Edge, TopoDS_Vertex, TopoDS_Face -from OCC.gp import gp_Vec, gp_Dir, gp_Pnt -from OCC.GeomLProp import GeomLProp_CurveTool -from OCC.Core.BRepLProp import BRepLProp_CLProps -from OCC.GeomLib import geomlib -from OCC.GCPnts import GCPnts_AbscissaPoint -from OCC.GeomAPI import GeomAPI_ProjectPointOnCurve -from OCC.ShapeAnalysis import ShapeAnalysis_Edge -from OCC.Core.BRep import BRep_Tool, BRep_Tool_Continuity -from OCC.Core.BRepIntCurveSurface import BRepIntCurveSurface_Inter - +from OCC.Core.gp import gp_Vec, gp_Dir, gp_Pnt # high-level from OCCUtils.Common import vertex2pnt, minimum_distance, assert_isdone, fix_continuity from OCCUtils.Construct import make_edge -from OCCUtils.types_lut import geom_lut from OCCUtils.base import BaseObject +from OCCUtils.types_lut import geom_lut class IntersectCurve(object): @@ -123,9 +121,10 @@ def derivative(self, u, n): def points_from_tangential_deflection(self): pass -#=========================================================================== + +# =========================================================================== # Curve.Construct -#=========================================================================== +# =========================================================================== class ConstructFromCurve(): @@ -264,9 +263,9 @@ def domain(self): '''returns the u,v domain of the curve''' return self.adaptor.FirstParameter(), self.adaptor.LastParameter() -#=========================================================================== -# Curve.GlobalProperties -#=========================================================================== + # =========================================================================== + # Curve.GlobalProperties + # =========================================================================== def length(self, lbound=None, ubound=None, tolerance=1e-5): '''returns the curve length @@ -275,17 +274,19 @@ def length(self, lbound=None, ubound=None, tolerance=1e-5): ''' _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())) + 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())) + 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 -#=========================================================================== + # =========================================================================== + # Curve.modify + # =========================================================================== def trim(self, lbound, ubound): ''' @@ -309,9 +310,9 @@ def extend_by_point(self, pnt, degree=3, beginning=True): raise ValueError('to extend you self.curve should be <= 3, is %s' % (self.degree)) return geomlib.ExtendCurveToPoint(self.curve, pnt, degree, beginning) -#=========================================================================== -# Curve. -#=========================================================================== + # =========================================================================== + # Curve. + # =========================================================================== def closest(self, other): return minimum_distance(self, other) @@ -324,6 +325,17 @@ def project_vertex(self, pnt_or_vertex): poc = GeomAPI_ProjectPointOnCurve(pnt_or_vertex, self.curve_handle) return poc.LowerDistanceParameter(), poc.NearestPoint() + def discretize(self, deflection=0.01): + _verts = [] + discrete = GCPnts_QuasiUniformDeflection(self.adaptor, deflection) + + for i in range(1, discrete.NbPoints() + 1): + param = discrete.Parameter(i) + pnt: gp_Pnt = self.adaptor.Value(param) + _verts.append((pnt.X(), pnt.Y(), pnt.Z())) + + return _verts + 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 @@ -339,7 +351,7 @@ def mid_point(self): its corresponding gp_Pnt """ _min, _max = self.domain() - _mid = (_min+_max) / 2. + _mid = (_min + _max) / 2. return _mid, self.adaptor.Value(_mid) def divide_by_number_of_points(self, n_pts, lbound=None, ubound=None): @@ -358,17 +370,17 @@ def divide_by_number_of_points(self, n_pts, lbound=None, ubound=None): try: npts = GCPnts_UniformAbscissa(self.adaptor, n_pts, _lbound, _ubound) + except: 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 + 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 def __eq__(self, other): if hasattr(other, 'topo'): @@ -399,9 +411,9 @@ def as_vec(self): else: raise ValueError("edge is not a line, hence no meaningful vector can be returned") -#=========================================================================== -# Curve. -#=========================================================================== + # =========================================================================== + # Curve. + # =========================================================================== def parameter_to_point(self, u): '''returns the coordinate at parameter u @@ -418,9 +430,9 @@ def fix_continuity(self, continuity): def continuity_from_faces(self, f1, f2): return BRep_Tool_Continuity(self, f1, f2) -#=========================================================================== -# Curve. -#=========================================================================== + # =========================================================================== + # Curve. + # =========================================================================== def is_line(self): '''checks if the curve is planar @@ -443,9 +455,9 @@ def is_edge_on_face(self, face): ''' return ShapeAnalysis_Edge().HasPCurve(self, face) -#=========================================================================== -# Curve.graphic -#=========================================================================== + # =========================================================================== + # Curve.graphic + # =========================================================================== def show(self): ''' poles, knots, should render all slightly different. @@ -459,6 +471,7 @@ def show(self): if __name__ == '__main__': from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox from OCCUtils.Topology import Topo + b = BRepPrimAPI_MakeBox(10, 20, 30).Shape() t = Topo(b) ed = next(t.edges()) diff --git a/OCCUtils/face.py b/OCCUtils/face.py index 9c8571f..5de744e 100644 --- a/OCCUtils/face.py +++ b/OCCUtils/face.py @@ -14,22 +14,23 @@ ## ##You should have received a copy of the GNU Lesser General Public License ##along with pythonOCC. If not, see +from typing import Tuple from OCC.Core.BRep import BRep_Tool_Surface, BRep_Tool from OCC.Core.BRepTopAdaptor import BRepTopAdaptor_FClass2d -from OCC.Geom import Geom_Curve -from OCC.GeomAPI import GeomAPI_ProjectPointOnSurf -from OCC.GeomLib import GeomLib_IsPlanarSurface +from OCC.Core.Geom import Geom_Curve +from OCC.Core.GeomAPI import GeomAPI_ProjectPointOnSurf +from OCC.Core.GeomLib import GeomLib_IsPlanarSurface from OCC.Core.TopAbs import TopAbs_IN from OCC.Core.TopExp import topexp from OCC.Core.TopoDS import TopoDS_Vertex, TopoDS_Face, TopoDS_Edge -from OCC.GeomLProp import GeomLProp_SLProps +from OCC.Core.GeomLProp import GeomLProp_SLProps from OCC.Core.BRepTools import breptools_UVBounds from OCC.Core.BRepAdaptor import BRepAdaptor_Surface, BRepAdaptor_HSurface -from OCC.ShapeAnalysis import ShapeAnalysis_Surface -from OCC.GeomProjLib import geomprojlib -from OCC.Adaptor3d import Adaptor3d_IsoCurve -from OCC.gp import gp_Pnt2d, gp_Dir +from OCC.Core.ShapeAnalysis import ShapeAnalysis_Surface +from OCC.Core.GeomProjLib import geomprojlib +from OCC.Core.Adaptor3d import Adaptor3d_IsoCurve +from OCC.Core.gp import gp_Pnt2d, gp_Dir, gp_Pnt from OCCUtils.base import BaseObject from OCCUtils.edge import Edge @@ -94,11 +95,11 @@ def mean_curvature(self, u, v): def max_curvature(self, u, v): return self.curvature(u, v).MaxCurvature() - def normal(self, u, v): + def normal(self, u, v) -> Tuple[gp_Dir, gp_Pnt]: # TODO: should make this return a gp_Vec curv = self.curvature(u, v) if curv.IsNormalDefined(): - return curv.Normal() + return (curv.Normal(), curv.Value()) else: raise ValueError('normal is not defined at u,v: {0}, {1}'.format(u, v)) @@ -372,11 +373,6 @@ def iso_curve(self, u_or_v, param): def edges(self): return [Edge(i) for i in WireExplorer(next(self.topo.wires())).ordered_edges()] - def __repr__(self): - return self.name - - def __str__(self): - return self.__repr__() if __name__ == "__main__": from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere diff --git a/OCCUtils/shell.py b/OCCUtils/shell.py index 99434c3..7b73f15 100644 --- a/OCCUtils/shell.py +++ b/OCCUtils/shell.py @@ -16,11 +16,14 @@ ##along with pythonOCC. If not, see from OCC.Core.TopoDS import TopoDS_Shell -from OCC.ShapeAnalysis import ShapeAnalysis_Shell +from OCC.Core.ShapeAnalysis import ShapeAnalysis_Shell from OCCUtils.Topology import Topo from OCCUtils.base import BaseObject, GlobalProperties - +from OCCUtils.edge import Edge +from OCCUtils.wire import Wire +from OCCUtils.face import Face +from typing import List class Shell(TopoDS_Shell, BaseObject): _n = 0 @@ -42,27 +45,19 @@ def __init__(self, shell): self._n += 1 def analyse(self): - """ - - :return: - """ ss = ShapeAnalysis_Shell(self) if ss.HasFreeEdges(): bad_edges = [e for e in Topo(ss.BadEdges()).edges()] return bad_edges def Faces(self): - """ - - :return: - """ - return Topo(self, True).faces() + # type: () -> List[Face] + return map(Face, Topo(self).faces()) def Wires(self): - """ - :return: - """ - return Topo(self, True).wires() + # type: () -> List[Wire] + return map(Wire, Topo(self).wires()) def Edges(self): - return Topo(self, True).edges() + # type: () -> List['Edge'] + return map(Edge, Topo(self).edges()) diff --git a/OCCUtils/types_lut.py b/OCCUtils/types_lut.py index 4541bd3..b74aa18 100644 --- a/OCCUtils/types_lut.py +++ b/OCCUtils/types_lut.py @@ -16,11 +16,11 @@ ##along with pythonOCC. If not, see from OCC.Core.BRepCheck import * -from OCC.GeomAbs import * +from OCC.Core.GeomAbs import * from OCC.Core.TopoDS import topods, TopoDS_Shape from OCC.Core.BRep import BRep_Tool_Surface from OCC.Core.TopAbs import * -from OCC.Geom import Handle_Geom_CylindricalSurface, Handle_Geom_Plane +from OCC.Core.Geom import Handle_Geom_CylindricalSurface, Handle_Geom_Plane class ShapeToTopology(object): diff --git a/OCCUtils/vertex.py b/OCCUtils/vertex.py index edfd053..2ccac59 100644 --- a/OCCUtils/vertex.py +++ b/OCCUtils/vertex.py @@ -15,9 +15,9 @@ ##You should have received a copy of the GNU Lesser General Public License ##along with pythonOCC. If not, see -from OCC.gp import gp_Pnt, gp_Vec, gp_Dir, gp_XYZ, gp_Pnt2d +from OCC.Core.gp import gp_Pnt, gp_Vec, gp_Dir, gp_XYZ, gp_Pnt2d from OCC.Core.TopoDS import TopoDS_Vertex -from OCC.ShapeBuild import ShapeBuild_ReShape +from OCC.Core.ShapeBuild import ShapeBuild_ReShape from OCCUtils.base import BaseObject from OCCUtils.Construct import make_vertex @@ -89,9 +89,6 @@ def xyz(self, *val): self._pnt.SetXYZ(*val) self._update() - def __repr__(self): - return self.name - @property def as_vec(self): '''returns a gp_Vec version of self''' diff --git a/OCCUtils/wire.py b/OCCUtils/wire.py index 20ed5ca..6804c82 100644 --- a/OCCUtils/wire.py +++ b/OCCUtils/wire.py @@ -20,6 +20,8 @@ from OCC.Core.TopoDS import TopoDS_Wire from OCCUtils.base import BaseObject +from OCCUtils.edge import Edge +from OCCUtils.Topology import Topo class Wire(TopoDS_Wire, BaseObject): @@ -37,3 +39,6 @@ def __init__(self, wire): self.Location(wire.Location()) self.Orientation(wire.Orientation()) assert not self.IsNull() + + def Edges(self): + return [Edge(e) for e in Topo(self).edges()] diff --git a/examples/occutils_boolean.py b/examples/occutils_boolean.py new file mode 100644 index 0000000..0f59a56 --- /dev/null +++ b/examples/occutils_boolean.py @@ -0,0 +1,31 @@ +from OCC.Core.gp import gp_Pln +from OCC.Core.gp import gp_Pnt, gp_Vec +from OCC.Display.SimpleGui import init_display +from OCCUtils import Topo +from OCCUtils.Construct import make_box, make_face +from OCCUtils.Construct import vec_to_dir +from OCCUtils.boolean import common_volume + + +def test_common_volume(): + display, start_display, add_menu, add_function_to_menu = init_display() + + box = make_box(100, 100, 100) + + p, v = gp_Pnt(50, 50, 50), gp_Vec(0, 0, -1) + pln = gp_Pln(p, vec_to_dir(v)) + fc = make_face(pln, -1000, 1000, -1000, 1000) # limited, not infinite plane + + display.DisplayShape(fc, transparency=0.5) + common_shape = common_volume([box, fc]) + + solids = Topo(common_shape).solids() + display.DisplayShape(next(solids), transparency=0.5) + display.DisplayColoredShape(next(solids), "BLACK") + + display.FitAll() + start_display() + + +if __name__ == "__main__": + test_common_volume() \ No newline at end of file diff --git a/examples/occutils_geomplate.py b/examples/occutils_geomplate.py index 220b2f4..262e607 100644 --- a/examples/occutils_geomplate.py +++ b/examples/occutils_geomplate.py @@ -24,26 +24,21 @@ from __future__ import print_function import os -import types import sys import time -from OCC.gp import gp_Pnt -from OCC.Core.BRepAdaptor import BRepAdaptor_HCurve +from OCC.Core.BRep import BRep_Builder from OCC.Core.BRep import BRep_Tool -from OCC.ShapeAnalysis import ShapeAnalysis_Surface -from OCC.GeomLProp import GeomLProp_SLProps +from OCC.Core.BRepAdaptor import BRepAdaptor_HCurve from OCC.Core.BRepFill import BRepFill_CurveConstraint -from OCC.GeomPlate import (GeomPlate_MakeApprox, - GeomPlate_BuildPlateSurface, - GeomPlate_PointConstraint) -from OCC.IGESControl import IGESControl_Reader -from OCC.IFSelect import (IFSelect_RetDone, - IFSelect_ItemsByEntity) -from OCC.Display.SimpleGui import init_display +from OCC.Core.GeomLProp import GeomLProp_SLProps +from OCC.Core.GeomPlate import GeomPlate_MakeApprox, GeomPlate_BuildPlateSurface, GeomPlate_PointConstraint +from OCC.Core.IFSelect import IFSelect_RetDone, IFSelect_ItemsByEntity +from OCC.Core.IGESControl import IGESControl_Reader +from OCC.Core.ShapeAnalysis import ShapeAnalysis_Surface from OCC.Core.TopoDS import TopoDS_Compound -from OCC.Core.BRep import BRep_Builder -display, start_display, add_menu, add_function_to_menu = init_display() +from OCC.Core.gp import gp_Pnt +from OCC.Display.SimpleGui import init_display from OCCUtils.Construct import (make_closed_polygon, make_n_sided, @@ -53,11 +48,19 @@ try: from scipy import arange from scipy.optimize import fsolve + HAVE_SCIPY = True except ImportError: print('scipy not installed, will not be able to run the geomplate example') HAVE_SCIPY = False +HAS_TRAVIS = False +if "TRAVIS" in os.environ: + HAS_TRAVIS = True + display = None +else: + display, start_display, add_menu, add_function_to_menu = init_display() + class IGESImporter(object): def __init__(self, filename=None): @@ -85,7 +88,7 @@ def read_file(self): nbr = aReader.NbRootsForTransfer() aReader.PrintCheckTransfer(failsonly, IFSelect_ItemsByEntity) # ok = aReader.TransferRoots() - for n in range(1, nbr+1): + for n in range(1, nbr + 1): self.nbs = aReader.NbShapes() if self.nbs == 0: print("At least one shape in IGES cannot be transfered") @@ -95,7 +98,7 @@ def read_file(self): print("At least one shape in IGES cannot be transferred") self._shapes.append(aResShape) else: - for i in range(1, self.nbs+1): + for i in range(1, self.nbs + 1): aShape = aReader.Shape(i) if aShape.IsNull(): print("At least one shape in STEP cannot be transferred") @@ -124,7 +127,8 @@ def get_shapes(self): def geom_plate(event=None): - display.EraseAll() + if not HAS_TRAVIS: + display.EraseAll() p1 = gp_Pnt(0, 0, 0) p2 = gp_Pnt(0, 10, 0) p3 = gp_Pnt(0, 10, 10) @@ -133,13 +137,15 @@ def geom_plate(event=None): poly = make_closed_polygon([p1, p2, p3, p4]) edges = [i for i in Topo(poly).edges()] face = make_n_sided(edges, [p5]) - display.DisplayShape(edges) - display.DisplayShape(make_vertex(p5)) - display.DisplayShape(face, update=True) + if not HAS_TRAVIS: + display.DisplayShape(edges) + display.DisplayShape(make_vertex(p5)) + display.DisplayShape(face, update=True) -#============================================================================ + +# ============================================================================ # Find a surface such that the radius at the vertex is n -#============================================================================ +# ============================================================================ def build_plate(polygon, points): @@ -166,7 +172,7 @@ def build_plate(polygon, points): maxSeg, maxDeg, critOrder = 9, 8, 0 tol = 1e-4 - dmax = max([tol, 10*bpSrf.G0Error()]) + dmax = max([tol, 10 * bpSrf.G0Error()]) srf = bpSrf.Surface() plate = GeomPlate_MakeApprox(srf, tol, maxSeg, maxDeg, dmax, critOrder) @@ -185,15 +191,15 @@ def radius_at_uv(face, u, v): # uv_domain = GeomLProp_SurfaceTool().Bounds(h_srf) curvature = GeomLProp_SLProps(h_srf, u, v, 1, 1e-6) try: - _crv_min = 1./curvature.MinCurvature() + _crv_min = 1. / curvature.MinCurvature() except ZeroDivisionError: _crv_min = 0. try: - _crv_max = 1./curvature.MaxCurvature() + _crv_max = 1. / curvature.MaxCurvature() except ZeroDivisionError: _crv_max = 0. - return abs((_crv_min+_crv_max)/2.) + return abs((_crv_min + _crv_max) / 2.) def uv_from_projected_point_on_face(face, pt): @@ -211,6 +217,7 @@ class RadiusConstrainedSurface(object): ''' returns a surface that has `radius` at `pt` ''' + def __init__(self, display, poly, pnt, targetRadius): self.display = display self.targetRadius = targetRadius @@ -223,27 +230,27 @@ def build_surface(self): builds and renders the plate ''' self.plate = build_plate([self.poly], [self.pnt]) - self.display.EraseAll() - self.display.DisplayShape(self.plate) vert = make_vertex(self.pnt) - self.display.DisplayShape(vert, update=True) + if not HAS_TRAVIS: + self.display.EraseAll() + self.display.DisplayShape(self.plate) + self.display.DisplayShape(vert, update=True) def radius(self, z): ''' sets the height of the point constraining the plate, returns the radius at this point ''' - if isinstance(z, types.FloatType): + if isinstance(z, float): self.pnt.SetX(z) else: self.pnt.SetX(float(z[0])) self.build_surface() - uv = uv_from_projected_point_on_face(self.plate, self.pnt) - print(uv) - radius = radius_at_uv(self.plate, uv.X(), uv.Y()) + u, v = uv_from_projected_point_on_face(self.plate, self.pnt) + radius = radius_at_uv(self.plate, u, v) print('z: %f radius: %f ' % (z, radius)) self.curr_radius = radius - return self.targetRadius-abs(radius) + return self.targetRadius - abs(radius) def solve(self): fsolve(self.radius, 1, maxfev=1000) @@ -260,13 +267,15 @@ def solve_radius(event=None): poly = make_closed_polygon([p1, p2, p3, p4]) for i in arange(0.1, 3., 0.2).tolist(): rcs = RadiusConstrainedSurface(display, poly, p5, i) - # face = rcs.solve() + face = rcs.solve() print('Goal: %s radius: %s' % (i, rcs.curr_radius)) - time.sleep(0.5) + if not HAS_TRAVIS: + time.sleep(0.5) def build_geom_plate(edges): bpSrf = GeomPlate_BuildPlateSurface(3, 9, 12) + face = None # add curve constraints for edg in edges: @@ -281,15 +290,17 @@ def build_geom_plate(edges): bpSrf.Perform() except RuntimeError: print('Failed to build the geom plate surface') + else: - maxSeg, maxDeg, critOrder = 9, 8, 0 + maxSeg, maxDeg, critOrder = 9, 8, 0 - srf = bpSrf.Surface() - plate = GeomPlate_MakeApprox(srf, 1e-04, 100, 9, 1e-03, 0) + srf = bpSrf.Surface() + plate = GeomPlate_MakeApprox(srf, 1e-04, 100, 9, 1e-03, 0) - uMin, uMax, vMin, vMax = srf.GetObject().Bounds() - face = make_face(plate.Surface(), uMin, uMax, vMin, vMax, 1e-6) - return face + uMin, uMax, vMin, vMax = srf.GetObject().Bounds() + face = make_face(plate.Surface(), uMin, uMax, vMin, vMax, 1e-6) + finally: + return face def build_curve_network(event=None): @@ -297,7 +308,8 @@ def build_curve_network(event=None): mimic the curve network surfacing command from rhino ''' print('Importing IGES file...', end='') - iges = IGESImporter('./curve_geom_plate.igs') + filename = os.path.join(os.path.dirname(__file__), 'curve_geom_plate.igs') + iges = IGESImporter(filename) iges.read_file() iges_cpd = iges.get_compound() print('done.') @@ -307,10 +319,12 @@ def build_curve_network(event=None): edges_list = list(topo.edges()) face = build_geom_plate(edges_list) print('done.') - display.EraseAll() - display.DisplayShape(edges_list) - display.DisplayShape(face) - display.FitAll() + + if not HAS_TRAVIS and face: + display.EraseAll() + display.DisplayShape(edges_list) + display.DisplayShape(face) + display.FitAll() print('Cutting out of edges...') # Make a wire from outer edges # _edges = [edges_list[2], edges_list[3], edges_list[4], edges_list[5]] @@ -320,6 +334,7 @@ def build_curve_network(event=None): def exit(event=None): sys.exit() + if __name__ == "__main__": add_menu('geom plate') add_function_to_menu('geom plate', geom_plate) diff --git a/test/occutils_test.py b/test/occutils_test.py index 3750116..5ed6ea6 100644 --- a/test/occutils_test.py +++ b/test/occutils_test.py @@ -21,9 +21,10 @@ import sys sys.path.append('../OCCUtils') +sys.path.append('../examples') -from OCC.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeSphere -from OCC.TopoDS import TopoDS_Face, TopoDS_Edge +from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeSphere +from OCC.Core.TopoDS import TopoDS_Face, TopoDS_Edge from Topology import Topo, WireExplorer from edge import Edge @@ -218,6 +219,36 @@ def test_creat_solid(self): my_solid = Solid(BRepPrimAPI_MakeBox(10, 20, 30).Solid()) assert my_solid.tolerance == 1e-06 +class TestImportOCCUtils(unittest.TestCase): + """make sure all pythonocc-utils can be imported + + """ + def test_import(self): + import base as base_ + import Common as Common_ + import Construct as Construct_ + import edge as edge_ + import face as face_ + import Image as Image_ + import Iteration as Iteration_ + import shell as shell_ + import solid as solid_ + import Topology as Topology_ + import types_lut as types_lut_ + import vertex as vertex_ + import wire as wire_ + +class TestExamples(unittest.TestCase): + def test_geom_plate(self): + from examples import occutils_geomplate + occutils_geomplate.build_curve_network() + occutils_geomplate.solve_radius() + occutils_geomplate.geom_plate() + + def test_surfaces(self): + from examples import occutils_surfaces + occutils_surfaces.n_sided_patch() + def suite(): test_suite = unittest.TestSuite()