diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a2db317 --- /dev/null +++ b/.gitignore @@ -0,0 +1,63 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Python stuff +*.pyc +__pycache__ + +# swap file +*~ +.swp + +*.ppm + +*.png +!images/*.png +*.bak +*.mp4 +*.mp3 +*.mpg + +.idea +*.pfd + +*.nsi + +*.stl + +__pycache__ diff --git a/README.md b/README.md index 02de741..b2069ea 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,16 @@ The easiest way to setup PythonOCC and OpenCASCADE is to do it in Ubuntu server To install OpenCASCADE just do `apt-get install libopencascade-dev`. You can get PythonOCC from [here](http://www.pythonocc.org/download/). If you choose to build it from source [these instructions](http://code.google.com/p/pythonocc/source/browse/trunk/INSTALL) are helpful. +Under Windwos it is advised to use the Anaconda Python installation. PythonOCC and the utils can be installed using + + conda install -c https://conda.anaconda.org/dlr-sc pythonocc-core + + pip install git+https://github.com/tpaviot/pythonocc-utils.git#egg=pythonocc-utils + +Note that cadmium can also be directly installed using pip as + + pip install git+https://github.com/eelcovv/cadmium#egg=cadmium + Running example code --------------------- diff --git a/examples/example-001.py b/examples/example-001.py index c49b39b..b51bc4c 100644 --- a/examples/example-001.py +++ b/examples/example-001.py @@ -2,19 +2,23 @@ import sys import math -sys.path.append('./src') from cadmium import * -stlfname = sys.argv[1] +try: + stlfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + + stlfname = splitext(basename(__file__))[0] + ".stl" c0 = Cylinder(radius=1, height=8, center=True) c1 = Cylinder(radius=1, height=8, center=True).rotate(X_axis, 45) c2 = Cylinder(radius=1, height=8, center=True).rotate(X_axis, -45) c3 = Cylinder(radius=1, height=8, center=True).rotate(X_axis, 90) -b = Box(x=8,y=1,z=1,center=True) -s0 = Sphere(r=1).translate(1.5,0,0) -s1 = Sphere(r=1).translate(-1.5,0,0) +b = Box(x=8, y=1, z=1, center=True) +s0 = Sphere(r=1).translate(1.5, 0, 0) +s1 = Sphere(r=1).translate(-1.5, 0, 0) u = ((c0 + c1 + c2 + c3) - b) - (s0 + s1) diff --git a/examples/example-002.py b/examples/example-002.py index 8b53080..12c92b8 100644 --- a/examples/example-002.py +++ b/examples/example-002.py @@ -6,7 +6,11 @@ from cadmium import * -stlfname = sys.argv[1] +try: + stlfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + stlfname = splitext(basename(__file__))[0] + ".stl" b0 = Box(x=4, y=4, z=4, center=True) b1 = Box(x=4, y=4, z=4, center=True).rotate(X_axis, 45) diff --git a/examples/example-003.py b/examples/example-003.py index 9b3e886..6222a1e 100644 --- a/examples/example-003.py +++ b/examples/example-003.py @@ -6,7 +6,11 @@ from cadmium import * -stlfname = sys.argv[1] +try: + stlfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + stlfname = splitext(basename(__file__))[0] + ".stl" b0 = Box(x=4,y=4,z=4, center=True) s0 = Sphere(radius=2.5) diff --git a/examples/example-004.py b/examples/example-004.py index aefe34f..321f457 100644 --- a/examples/example-004.py +++ b/examples/example-004.py @@ -6,7 +6,11 @@ from cadmium import * -stlfname = sys.argv[1] +try: + stlfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + stlfname = splitext(basename(__file__))[0] + ".stl" ring = ( Cylinder(radius=6, height=2, center=True) - \ diff --git a/examples/example-005.py b/examples/example-005.py index 6457bb3..7085dd7 100644 --- a/examples/example-005.py +++ b/examples/example-005.py @@ -54,5 +54,10 @@ final = star_wheel - x -stlfname = sys.argv[1] +try: + stlfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + stlfname = splitext(basename(__file__))[0] + ".stl" + final.toSTL(stlfname) diff --git a/examples/example-006.py b/examples/example-006.py index 09c9480..1e1c0aa 100644 --- a/examples/example-006.py +++ b/examples/example-006.py @@ -69,5 +69,10 @@ def generate_verbose(height, radius, hole_r, border, recess, num_support): bushing = generate_compact(16, 75.0/2, 4.5, 1.56,5,3) -stlfname = sys.argv[1] +try: + stlfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + stlfname = splitext(basename(__file__))[0] + ".stl" + bushing.toSTL(stlfname) diff --git a/examples/example-007.py b/examples/example-007.py index 105b43b..c98b8ef 100644 --- a/examples/example-007.py +++ b/examples/example-007.py @@ -8,7 +8,11 @@ from cadmium import * -stlfname = sys.argv[1] +try: + stlfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + stlfname = splitext(basename(__file__))[0] + ".stl" u = Cylinder(radius=1, height=8, center=True).translate(x=4) + \ Cylinder(radius=1, height=8, center=True).translate(x=-4)\ diff --git a/examples/example-008.py b/examples/example-008.py index 93ce701..017e054 100644 --- a/examples/example-008.py +++ b/examples/example-008.py @@ -6,9 +6,14 @@ from cadmium import * -stlfname = sys.argv[1] +try: + stlfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + stlfname = splitext(basename(__file__))[0] + ".stl" def dragon(k,s): + print("k = {} ".format(k)) if k < 1: return Cylinder(r=2*s, h=1).scale(0.1).translate(x=s) + \ Box(x=s, y=2*s/5.0, z=1).translate(y=-s/5.0) + \ @@ -19,6 +24,7 @@ def dragon(k,s): dragon(k-1,s).rotate(Z_axis, 90).translate(x=c.real,y=c.imag) print 'This is going to take a while...' -u = dragon(8,1) +u = dragon(2,1) +print('Writing result to {}'.format(stlfname) u.toSTL(stlfname) diff --git a/examples/example-009.py b/examples/example-009.py index 2a1b518..84bce3e 100644 --- a/examples/example-009.py +++ b/examples/example-009.py @@ -5,7 +5,12 @@ sys.path.append('./src') from cadmium import * -stlfname = sys.argv[1] + +try: + stlfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + stlfname = splitext(basename(__file__))[0] + ".stl" LIMIT = 9 diff --git a/examples/example-010.py b/examples/example-010.py index 8892977..6979441 100644 --- a/examples/example-010.py +++ b/examples/example-010.py @@ -8,9 +8,13 @@ import sys import math sys.path.append('./src') - from cadmium import * -stlfname = sys.argv[1] + +try: + stlfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + stlfname = splitext(basename(__file__))[0] + ".stl" class Spoke(Solid): def __init__(self, maxr, smallr, bigr, height, ratio, center=True): diff --git a/examples/example-011.py b/examples/example-011.py index b3410a1..f872f30 100644 --- a/examples/example-011.py +++ b/examples/example-011.py @@ -1,4 +1,5 @@ #!/usr/bin/python +# this example does not work import os import sys @@ -6,7 +7,12 @@ sys.path.append('./src') from cadmium import * -jsonfname = sys.argv[1] + +try: + jsonfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + jsonfname = splitext(basename(__file__))[0] + ".json" t = Torus(r1=10,r2=2) @@ -20,7 +26,7 @@ precision += 0.01 t = Torus(r1=10,r2=2) - t.toJSON(jsonfname, precision=precision) + t.toJSON(jsonfname) size = os.stat(jsonfname).st_size print size,precision diff --git a/examples/example-012.py b/examples/example-012.py index 1d674c8..3b4355c 100644 --- a/examples/example-012.py +++ b/examples/example-012.py @@ -2,11 +2,13 @@ import sys import math -sys.path.append('./src') - from cadmium import * -stlfname = sys.argv[1] +try: + stlfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + stlfname = splitext(basename(__file__))[0] + ".stl" s = Sphere(r=3) s.scale(scaleY=2, scaleZ=2) diff --git a/examples/example-013.py b/examples/example-013.py index 6ffcbcc..2130698 100644 --- a/examples/example-013.py +++ b/examples/example-013.py @@ -4,6 +4,12 @@ from cadmium import Revolution from cadmium import CadmiumException +try: + stlfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + stlfname = splitext(basename(__file__))[0] + ".stl" + s = Revolution( knotVector=[0,0,0,0,1,5,6,8,8,8,8], controlPoints=[ @@ -18,7 +24,7 @@ degree=3, axis=1) -s.toSTL(sys.argv[1]) +s.toSTL(stlfname) try: s = Revolution( diff --git a/examples/example-014.py b/examples/example-014.py index 738851d..8c537d0 100644 --- a/examples/example-014.py +++ b/examples/example-014.py @@ -4,6 +4,12 @@ from cadmium import Extrusion from cadmium import CadmiumException +try: + stlfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + stlfname = splitext(basename(__file__))[0] + ".stl" + s = Extrusion( knotVector=[0,0,0,0,1,5,6,8,8,8,8], controlPoints=[ @@ -17,7 +23,7 @@ ], degree=3) -s.toSTL(sys.argv[1]) +s.toSTL(stlfname) try: s = Extrusion( diff --git a/examples/text-example.py b/examples/text-example.py index bd366f5..e376103 100644 --- a/examples/text-example.py +++ b/examples/text-example.py @@ -7,7 +7,11 @@ from cadmium import * -stlfname = sys.argv[1] +try: + stlfname = sys.argv[1] +except IndexError: + from os.path import basename, splitext + stlfname = splitext(basename(__file__))[0] + ".stl" s = Text('Cadmium', fontpath='DejaVuSerif.ttf', # Give full path on your system diff --git a/src/cadmium/__init__.py b/src/cadmium/__init__.py index 800c203..d1475ac 100644 --- a/src/cadmium/__init__.py +++ b/src/cadmium/__init__.py @@ -1,24 +1,27 @@ ''' Python library for Solid Modelling ''' +from __future__ import absolute_import +from builtins import str import os -from OCC.gp import * -X_axis = gp_Ax1(gp_Pnt(0,0,0),gp_Dir(1,0,0)) -Y_axis = gp_Ax1(gp_Pnt(0,0,0),gp_Dir(0,1,0)) -Z_axis = gp_Ax1(gp_Pnt(0,0,0),gp_Dir(0,0,1)) - -import solid -import primitives.cylinder -import primitives.sphere -import primitives.box -import primitives.cone -import primitives.wedge -import primitives.torus -import primitives.text -import primitives.revolution -import primitives.extrusion +from OCC.gp import (gp_Ax1, gp_Pnt, gp_Dir) + +from . import solid +from .primitives import cylinder +from .primitives import sphere +from .primitives import box +from .primitives import cone +from .primitives import wedge +from .primitives import torus +from .primitives import text +from .primitives import revolution +from .primitives import extrusion + +X_axis = gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)) +Y_axis = gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 1, 0)) +Z_axis = gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)) Cylinder = primitives.cylinder.Cylinder Sphere = primitives.sphere.Sphere @@ -34,8 +37,8 @@ Solid = solid.Solid inspectionData = { - 'solidData' : {}, - 'paramData' : {} + 'solidData': {}, + 'paramData': {} } # Value range constants @@ -50,71 +53,84 @@ # Custom data type enum = 1 -_brep_cache_path_ = os.path.join(os.environ['HOME'],'.cadmium','brepcache') +try: + _brep_cache_path_ = os.path.join(os.environ['HOME'], '.cadmium', 'brepcache') +except KeyError: + _brep_cache_path_ = os.path.join(os.environ['USERPROFILE'], '.cadmium', 'brepcache') _brep_caching_enabled_ = True -_font_dir_ = os.path.join(os.environ['HOME'],'.cadmium','fonts') +try: + _font_dir_ = os.path.join(os.environ['HOME'], '.cadmium', 'fonts') +except KeyError: + _font_dir_ = os.path.join(os.environ['USERPROFILE'], '.cadmium', 'fonts') _abs_fontpath_allowed_ = True if not os.path.exists(_brep_cache_path_): - os.makedirs(_brep_cache_path_) + os.makedirs(_brep_cache_path_) + # # Annotation decorators # def description(*arg, **kwdArg): + validArgs = ['shortName', 'summary', 'alignment'] + for k in list(kwdArg.keys()): + if not (k in validArgs): + raise Exception('Invalid argument ' + str(k)) - validArgs = ['shortName','summary', 'alignment'] - for k in kwdArg.keys(): - if not(k in validArgs): - raise Exception('Invalid argument '+str(k)) + if arg: + raise Exception('Only named arguments supported') - if arg: - raise Exception('Only named arguments supported') + import cadmium + cadmium.inspectionData['solidData'] = kwdArg - import cadmium - cadmium.inspectionData['solidData'] = kwdArg + def decorator(func): + return func + + return decorator - def decorator(func): return func - return decorator def param(*arg, **kwdArg): + validArgs = ['name', 'shortName', 'description', + 'valueRange', 'valueType', 'invalidValues', 'validValues', 'endpointInclusion'] + for k in list(kwdArg.keys()): + if not (k in validArgs): + raise Exception('Invalid argument ' + str(k)) + + if arg: + raise Exception('Only named arguments supported') + + import cadmium + if 'valueType' in kwdArg: + if kwdArg['valueType'] == int: + kwdArg['valueType'] = 'int' + elif kwdArg['valueType'] == float: + kwdArg['valueType'] = 'float' + elif kwdArg['valueType'] == str: + kwdArg['valueType'] = 'str' + elif kwdArg['valueType'] == bool: + kwdArg['valueType'] = 'bool' + elif kwdArg['valueType'] == cadmium.enum: + kwdArg['valueType'] = 'enum' + else: + kwdArg['valueType'] = 'unknown' + + cadmium.inspectionData['paramData'][kwdArg['name']] = kwdArg + + def decorator(func): + return func + + return decorator - validArgs = ['name','shortName','description', - 'valueRange','valueType','invalidValues','validValues','endpointInclusion'] - for k in kwdArg.keys(): - if not(k in validArgs): - raise Exception('Invalid argument '+str(k)) - - if arg: - raise Exception('Only named arguments supported') - - import cadmium - if kwdArg.has_key('valueType'): - if kwdArg['valueType'] == int: - kwdArg['valueType'] = 'int' - elif kwdArg['valueType'] == float: - kwdArg['valueType'] = 'float' - elif kwdArg['valueType'] == str: - kwdArg['valueType'] = 'str' - elif kwdArg['valueType'] == bool: - kwdArg['valueType'] = 'bool' - elif kwdArg['valueType'] == cadmium.enum: - kwdArg['valueType'] = 'enum' - else: - kwdArg['valueType'] = 'unknown' - - cadmium.inspectionData['paramData'][kwdArg['name']] = kwdArg - - def decorator(func): return func - return decorator class CadmiumException(BaseException): - ''' - Useful mainly for validating user provided input for Solid instantiations - ''' - def __init__(self, msg): - self.msg = msg - BaseException.__init__(self, msg) + ''' + Useful mainly for validating user provided input for Solid instantiations + ''' + + def __init__(self, msg): + self.msg = msg + BaseException.__init__(self, msg) + def garbage_collect(): - GarbageCollector.garbage.smart_purge() + GarbageCollector.garbage.smart_purge() diff --git a/src/cadmium/primitives/text.py b/src/cadmium/primitives/text.py index 192e275..d969f87 100644 --- a/src/cadmium/primitives/text.py +++ b/src/cadmium/primitives/text.py @@ -7,7 +7,11 @@ import math import urllib -import fontforge +try: + import fontforge +except ImportError: + print("WARNING: could not load fontforge. Not all font capabilities available") + from OCC.gp import gp_Pnt, gp_Vec from OCC.TColgp import TColgp_Array1OfPnt from OCC.Geom import Geom_BezierCurve diff --git a/src/cadmium/solid.py b/src/cadmium/solid.py index f75ca87..47b030f 100644 --- a/src/cadmium/solid.py +++ b/src/cadmium/solid.py @@ -1,10 +1,15 @@ +from __future__ import division +from __future__ import print_function # # Cadmium - Python library for Solid Modelling # Copyright (C) 2011 Jayesh Salvi [jayesh 3dtin com] # -#!/usr/bin/python +# !/usr/bin/python +from builtins import range +from builtins import object +from past.utils import old_div import os from math import pi as math_pi import cadmium @@ -16,398 +21,402 @@ from OCC.gp import * from OCC import Bnd, BRepBndLib -from OCC.Utils.Topology import * +from OCCUtils.Topology import * from OCC.TopoDS import * from OCC.TopAbs import * # For generating Triangle Mesh from OCC.BRepMesh import * from OCC.BRep import * -from OCC.BRepTools import BRepTools +from OCC import BRepTools from OCC.Precision import * -class Solid(): - ''' - Base class for all solids (Primitive and Custom). It's created by passing in Shape as argument. - ''' - def __init__(self, s=None, center=False): - if s: - if type(s) == TopoDS_Shape: - self.shape = s - else: - self.shape = s.shape - else: - self.shape = TopoDS_Shape() - - self.centerTranslation = [0,0,0] - if center: self.centralize() - - def __add__(self, other): - ''' - **Union** with other solid. - ''' - union = BRepAlgoAPI_Fuse(self.shape, other.shape).Shape() - return Solid(union) - - def __mul__(self, other): - ''' - **Intersect** with other solid - ''' - intersection = BRepAlgoAPI_Common(self.shape, other.shape).Shape() - return Solid(intersection) - def __sub__(self, other): - ''' - **Subtract** other solid from this +class Solid(object): ''' - subtraction = BRepAlgoAPI_Cut(self.shape, other.shape).Shape() - return Solid(subtraction) - - def _triangle_is_valid(self, P1,P2,P3): - ''' check wether a triangle is or not valid - ''' - V1 = gp_Vec(P1,P2) - V2 = gp_Vec(P2,P3) - V3 = gp_Vec(P3,P1) - if V1.SquareMagnitude()>1e-10 and V2.SquareMagnitude()>1e-10 and V3.SquareMagnitude()>1e-10: - V1.Cross(V2) - if V1.SquareMagnitude()>1e-10: - return True - else: - print 'Not valid!' - return False - else: - print 'Not valid!' - return False - - def getBoundingBox(self): + Base class for all solids (Primitive and Custom). It's created by passing in Shape as argument. ''' - Returns Bounding Box of this solid - The bounds are returns in an array [xmin, ymin, zmin, xmax, ymax, zmax] - ''' - box = Bnd.Bnd_Box(); - b = BRepBndLib.BRepBndLib(); - b.Add(self.shape, box); - return box.Get() - - def centralize(self): - xmin, ymin, zmin, xmax, ymax, zmax = self.getBoundingBox() - xspan = xmax - xmin - yspan = ymax - ymin - zspan = zmax - zmin - self.centerTranslation = \ - ((-xspan/2.)-xmin, (-yspan/2.)-ymin, (-zspan/2.)-zmin) - self.translate(delta=self.centerTranslation) - - def _vtxkey(self, v): - return '%.4f_%.4f_%.4f'%(v[0], v[1], v[2]) - - def _save_vertex(self, vertex): - key = self._vtxkey(vertex) - if self.vtxmap.has_key(key): - return self.vtxmap[key] - else: - self.vtxmap[key] = self.vcount - self.vertices.append(vertex) - self.vcount += 1 - return self.vcount-1 - - def _save_face(self, indices): - self.faces.append(indices) - - def _reset_mesh(self): - self.vtxmap = {} - self.vcount = 0 - self.faces = [] - self.vertices = [] - - def _compress_write(self, filename): - vstr = '['+','.join(map(lambda x: '[%.2f,%.2f,%.2f]'%\ - (x[0],x[1],x[2]), self.vertices))+']' - fstr = '['+','.join(map(lambda x: '[%d,%d,%d]'%\ - (x[0],x[1],x[2]), self.faces))+']' - minjson = '{"vertices":'+vstr+',"faces":'+fstr+'}' - - plain = open(filename+'.plain', 'w') - plain.write(minjson) - plain.close() - plain = open(filename+'.plain','rb') - - import gzip - compressed = gzip.open(filename, 'wb') - compressed.writelines(plain) - compressed.close() - os.remove(filename+'.plain') - - def _build_mesh(self, precision=0.01): - self._reset_mesh() - BRepMesh_Mesh(self.shape, precision) - faces_iterator = Topo(self.shape).faces() - - for F in faces_iterator: - face_location = F.Location() - facing = BRep_Tool_Triangulation(F,face_location).GetObject() - tab = facing.Nodes() - tri = facing.Triangles() - - for i in range(1,facing.NbTriangles()+1): - trian = tri.Value(i) - if F.Orientation() == TopAbs_REVERSED: - index1, index3, index2 = trian.Get() + def __init__(self, s=None, center=False): + if s: + if type(s) == TopoDS_Shape: + self.shape = s + else: + self.shape = s.shape else: - index1, index2, index3 = trian.Get() - P1 = tab.Value(index1).Transformed(face_location.Transformation()) - P2 = tab.Value(index2).Transformed(face_location.Transformation()) - P3 = tab.Value(index3).Transformed(face_location.Transformation()) - - p1_coord = P1.XYZ().Coord() - p2_coord = P2.XYZ().Coord() - p3_coord = P3.XYZ().Coord() - - if self._triangle_is_valid(P1, P2, P3): - i1 = self._save_vertex(p1_coord) - i2 = self._save_vertex(p2_coord) - i3 = self._save_vertex(p3_coord) - self._save_face([i1,i2,i3]) - - def getMesh(self, precision=0.01): - self._build_mesh(precision) - return { 'vertices':self.vertices, 'faces':self.faces } - - def toJSON(self, filename, compress=False, precision=0.01): - ''' - Writes JSON representation of the mesh - - :param filename: Path of the file to write JSON to - :type filename: str - :param precision: Provides control over quality of exported mesh. Higher the precision value, lower the accuracy of exported mesh, smaller the size of exported file. Lower the precision value, more accurate the exported mesh, bigger the size of exported file. - :type precision: float - ''' - self._build_mesh(precision) - - if compress: - self._compress_write(filename) - else: - open(filename, 'w').write(json.dumps({ - 'vertices':self.vertices,'faces':self.faces})) - - def translate(self, x=0, y=0, z=0, delta=[0,0,0]): - ''' - Translate the solid. - Either provide delta or one of x,y,z. If either x,y or z are non-zero, - delta will be ignored. - - :param delta: Translation vector - :type delta: float - :param x: Translation along X - :type x: float - :param y: Translation along Y - :type y: float - :param z: Translation along Z - :type z: float - ''' - if x != 0 or y != 0 or z != 0: - delta = [x,y,z] - - xform = gp_Trsf() - xform.SetTranslation(gp_Vec(delta[0], delta[1], delta[2])) - brep = BRepBuilderAPI_Transform(self.shape, xform, False) - brep.Build() - self.shape = brep.Shape() - return self - - def rotate(self, axis=cadmium.Z_axis, angle=0): - ''' - Rotate the solid - - :param axis: One of three principal axes (cadmium.X_axis, cadmium.Y_axis, cadmium.Z_Axis) - or a list of floats interpreted as vector - :param angle: in Degrees - :type angle: float - ''' - xform = gp_Trsf() - if type(axis) == type(cadmium.X_axis): - xform.SetRotation(axis, angle*math_pi/180.0); - else: - ax = gp_Ax1(gp_Pnt(0,0,0),gp_Dir(axis[0],axis[1],axis[2])) - xform.SetRotation(ax, angle*math_pi/180.0); - brep = BRepBuilderAPI_Transform(self.shape, xform, False) - brep.Build() - self.shape = brep.Shape() - return self - - def reflect(self, vector): - ''' - Reflect the solid along the vector. Vector is the surface normal of the plane about which - reflection is desired. - :param vector: One of three principal axes (cadmium.X_axis, cadmium.Y_axis, cadmium.Z_Axis) - or a list of floats interpreted as vector - ''' - x = -1 if vector is cadmium.X_axis else 1 - y = -1 if vector is cadmium.Y_axis else 1 - z = -1 if vector is cadmium.Z_axis else 1 - xform = gp_GTrsf() - xform.SetVectorialPart(gp_Mat( - x, 0, 0, - 0, y, 0, - 0, 0, z, - )) - brep = BRepBuilderAPI_GTransform(self.shape, xform, False) - brep.Build() - self.shape = brep.Shape() - return self - - def scale(self, scale=1, scaleX=1, scaleY=1, scaleZ=1): - ''' - Scale the solid. Either provide scale parameter for uniform scaling along - all axis. If scale is not provided, but one of scaleX,scaleY,scaleZ is - provided, the solid is scaled along respective dimensions. - - :param scale: Uniform scale factor along all dimensions - :type scale: float - :param scaleX: Scale factor along X - :type scaleX: float - :param scaleY: Scale factor along Y - :type scaleY: float - :param scaleZ: Scale factor along Z - :type scaleZ: float - ''' - if scale != 1: - scaleX = scaleY = scaleZ = scale - - xform = gp_GTrsf() - xform.SetVectorialPart(gp_Mat( - scaleX, 0, 0, - 0, scaleY, 0, - 0, 0, scaleZ, - )) - brep = BRepBuilderAPI_GTransform(self.shape, xform, False) - brep.Build() - self.shape = brep.Shape() - return self - - def shear(self, xy=0, xz=0, yx=0, yz=0, zx=0, zy=0): - ''' - Shear the solid - - :param xy: Shear along X-axis as function of y - :type xy: float - :param xz: Shear along X-axis as function of z - :type xz: float - :param yx: Shear along Y-axis as function of x - :type yx: float - :param yz: Shear along Y-axis as function of z - :type yz: float - :param zx: Shear along Z-axis as function of x - :type zx: float - :param zy: Shear along Z-axis as function of y - :type zy: float - ''' - xform = gp_Trsf() - xform.SetValues( - 1, xy, xz, 0, - yx, 1, yz, 0, - zx, zy, 1, 0, - Precision_Angular(), Precision_Confusion() - ) - brep = BRepBuilderAPI_GTransform(self.shape, gp_GTrsf(xform), False) - brep.Build() - self.shape = brep.Shape() - return self - - class _Verboseness(object): - ''' Helps suppress verbouse outputs ''' - def __init__(self, verbose=False): - self.verbose = verbose - - def __enter__(self): - if not self.verbose: - import sys - import os - sys.stdout.flush() - self.newstdout = os.dup(1) - self.devnull = os.open('/dev/null', os.O_WRONLY) - os.dup2(self.devnull, 1) - os.close(self.devnull) - sys.stdout = os.fdopen(self.newstdout, 'w') - - def __exit__(self, type, value, traceback): - if not self.verbose: - os.dup2(self.newstdout, 1) - - def toSTEP(self, filename, verbose=False, tolerance=0.001): - ''' - Writes STEP output of the solid - - :param filename: Path of the file to write STEP to - :type filename: str - :param verbose: Choose if you want to see the STEP stats - :type verbose: bool - :param tolerance: Provides control over quality of exported STEP. - :type tolernace: float - ''' - with self._Verboseness(verbose): - stepWriter = STEPControl.STEPControl_Writer() - stepWriter.SetTolerance(tolerance) - if self.shape: - status = stepWriter.Transfer(self.shape, - STEPControl.STEPControl_AsIs) - if status: - stepWriter.Write(filename) - - def fromSTEP(self, filename, verbose=False): - ''' - Imports a STEP file to a solid - - :param filename: Path of the file to write STEP to - :type filename: str - :param verbose: Choose if you want to see the STEP stats - :type verbose: bool - ''' - with self._Verboseness(verbose): - if filename: - stepReader = STEPControl.STEPControl_Reader() - status = stepReader.ReadFile(filename) - if status: - nbr = stepReader.NbRootsForTransfer() - for n in range(1, nbr +1): - stepReader.TransferRoot(n) - nbs = stepReader.NbShapes() - if nbs == 1: - if stepReader.Shape(1): - self.shape = stepReader.Shape(1) - return self - - def toSTL(self, filename, ascii=False, deflection=0.01): - ''' - Writes STL output of the solid - - :param filename: Path of the file to write STL data to - :type filename: str - :param ascii: Choose between ASCII or Binary STL format - :type ascii: bool - :param deflection: Provides control over quality of exported STL. Higher the deflection value, lower the accuracy of exported STL, smaller the size of resulting file. Lower the deflection value, more accurate the exported STL, bigger the size of resulting file. - :type deflection: float - ''' - stl_writer = StlAPI.StlAPI_Writer() - stl_writer.SetASCIIMode(ascii) - stl_writer.SetDeflection(deflection) - stl_writer.Write(self.shape, filename) - - def toBREP(self, filename): - ''' - Write BREP output of the solid - - :param filename: Path of the file to write BREP to - ''' - BRepTools().Write(self.shape, filename) - - def fromBREP(self, filename): - ''' - Load ths solid from BREP file - - :param filename: Path of the file to read BREP from - ''' - BRepTools().Read(self.shape, filename, BRep_Builder()) + self.shape = TopoDS_Shape() + + self.centerTranslation = [0, 0, 0] + if center: self.centralize() + + def __add__(self, other): + ''' + **Union** with other solid. + ''' + union = BRepAlgoAPI_Fuse(self.shape, other.shape).Shape() + return Solid(union) + + def __mul__(self, other): + ''' + **Intersect** with other solid + ''' + intersection = BRepAlgoAPI_Common(self.shape, other.shape).Shape() + return Solid(intersection) + + def __sub__(self, other): + ''' + **Subtract** other solid from this + ''' + subtraction = BRepAlgoAPI_Cut(self.shape, other.shape).Shape() + return Solid(subtraction) + + def _triangle_is_valid(self, P1, P2, P3): + ''' check wether a triangle is or not valid + ''' + V1 = gp_Vec(P1, P2) + V2 = gp_Vec(P2, P3) + V3 = gp_Vec(P3, P1) + if V1.SquareMagnitude() > 1e-10 and V2.SquareMagnitude() > 1e-10 and V3.SquareMagnitude() > 1e-10: + V1.Cross(V2) + if V1.SquareMagnitude() > 1e-10: + return True + else: + print('Not valid!') + return False + else: + print('Not valid!') + return False + + def getBoundingBox(self): + ''' + Returns Bounding Box of this solid + + The bounds are returns in an array [xmin, ymin, zmin, xmax, ymax, zmax] + ''' + box = Bnd.Bnd_Box() + b = OCC.BRepBndLib.brepbndlib() + b.Add(self.shape, box) + return box.Get() + + def centralize(self): + xmin, ymin, zmin, xmax, ymax, zmax = self.getBoundingBox() + xspan = xmax - xmin + yspan = ymax - ymin + zspan = zmax - zmin + self.centerTranslation = \ + ((old_div(-xspan, 2.)) - xmin, (old_div(-yspan, 2.)) - ymin, (old_div(-zspan, 2.)) - zmin) + self.translate(delta=self.centerTranslation) + + def _vtxkey(self, v): + return '%.4f_%.4f_%.4f' % (v[0], v[1], v[2]) + + def _save_vertex(self, vertex): + key = self._vtxkey(vertex) + if key in self.vtxmap: + return self.vtxmap[key] + else: + self.vtxmap[key] = self.vcount + self.vertices.append(vertex) + self.vcount += 1 + return self.vcount - 1 + + def _save_face(self, indices): + self.faces.append(indices) + + def _reset_mesh(self): + self.vtxmap = {} + self.vcount = 0 + self.faces = [] + self.vertices = [] + + def _compress_write(self, filename): + vstr = '[' + ','.join(['[%.2f,%.2f,%.2f]' % \ + (x[0], x[1], x[2]) for x in self.vertices]) + ']' + fstr = '[' + ','.join(['[%d,%d,%d]' % \ + (x[0], x[1], x[2]) for x in self.faces]) + ']' + minjson = '{"vertices":' + vstr + ',"faces":' + fstr + '}' + + plain = open(filename + '.plain', 'w') + plain.write(minjson) + plain.close() + plain = open(filename + '.plain', 'rb') + + import gzip + compressed = gzip.open(filename, 'wb') + compressed.writelines(plain) + compressed.close() + os.remove(filename + '.plain') + + def _build_mesh(self, precision=0.01): + self._reset_mesh() + OCC.BRepMesh.brepmesh_Mesh(self.shape, precision) + + faces_iterator = Topo(self.shape).faces() + + for F in faces_iterator: + face_location = F.Location() + facing = BRep_Tool_Triangulation(F, face_location).GetObject() + tab = facing.Nodes() + tri = facing.Triangles() + + for i in range(1, facing.NbTriangles() + 1): + trian = tri.Value(i) + if F.Orientation() == TopAbs_REVERSED: + index1, index3, index2 = trian.Get() + else: + index1, index2, index3 = trian.Get() + P1 = tab.Value(index1).Transformed(face_location.Transformation()) + P2 = tab.Value(index2).Transformed(face_location.Transformation()) + P3 = tab.Value(index3).Transformed(face_location.Transformation()) + + p1_coord = P1.XYZ().Coord() + p2_coord = P2.XYZ().Coord() + p3_coord = P3.XYZ().Coord() + + if self._triangle_is_valid(P1, P2, P3): + i1 = self._save_vertex(p1_coord) + i2 = self._save_vertex(p2_coord) + i3 = self._save_vertex(p3_coord) + self._save_face([i1, i2, i3]) + + def getMesh(self, precision=0.01): + self._build_mesh(precision) + return {'vertices': self.vertices, 'faces': self.faces} + + def toJSON(self, filename, compress=False, precision=0.01): + ''' + Writes JSON representation of the mesh + + :param filename: Path of the file to write JSON to + :type filename: str + :param precision: Provides control over quality of exported mesh. Higher the precision value, lower the accuracy of exported mesh, smaller the size of exported file. Lower the precision value, more accurate the exported mesh, bigger the size of exported file. + :type precision: float + ''' + self._build_mesh(precision) + + if compress: + self._compress_write(filename) + else: + open(filename, 'w').write(json.dumps({ + 'vertices': self.vertices, 'faces': self.faces})) + + def translate(self, x=0, y=0, z=0, delta=[0, 0, 0]): + ''' + Translate the solid. + Either provide delta or one of x,y,z. If either x,y or z are non-zero, + delta will be ignored. + + :param delta: Translation vector + :type delta: float + :param x: Translation along X + :type x: float + :param y: Translation along Y + :type y: float + :param z: Translation along Z + :type z: float + ''' + if x != 0 or y != 0 or z != 0: + delta = [x, y, z] + + xform = gp_Trsf() + xform.SetTranslation(gp_Vec(delta[0], delta[1], delta[2])) + brep = BRepBuilderAPI_Transform(self.shape, xform, False) + brep.Build() + self.shape = brep.Shape() + return self + + def rotate(self, axis=cadmium.Z_axis, angle=0): + ''' + Rotate the solid + + :param axis: One of three principal axes (cadmium.X_axis, cadmium.Y_axis, cadmium.Z_Axis) + or a list of floats interpreted as vector + :param angle: in Degrees + :type angle: float + ''' + xform = gp_Trsf() + if type(axis) == type(cadmium.X_axis): + xform.SetRotation(axis, angle * math_pi / 180.0); + else: + ax = gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(axis[0], axis[1], axis[2])) + xform.SetRotation(ax, angle * math_pi / 180.0); + brep = BRepBuilderAPI_Transform(self.shape, xform, False) + brep.Build() + self.shape = brep.Shape() + return self + + def reflect(self, vector): + ''' + Reflect the solid along the vector. Vector is the surface normal of the plane about which + reflection is desired. + :param vector: One of three principal axes (cadmium.X_axis, cadmium.Y_axis, cadmium.Z_Axis) + or a list of floats interpreted as vector + ''' + x = -1 if vector is cadmium.X_axis else 1 + y = -1 if vector is cadmium.Y_axis else 1 + z = -1 if vector is cadmium.Z_axis else 1 + xform = gp_GTrsf() + xform.SetVectorialPart(gp_Mat( + x, 0, 0, + 0, y, 0, + 0, 0, z, + )) + brep = BRepBuilderAPI_GTransform(self.shape, xform, False) + brep.Build() + self.shape = brep.Shape() + return self + + def scale(self, scale=1, scaleX=1, scaleY=1, scaleZ=1): + ''' + Scale the solid. Either provide scale parameter for uniform scaling along + all axis. If scale is not provided, but one of scaleX,scaleY,scaleZ is + provided, the solid is scaled along respective dimensions. + + :param scale: Uniform scale factor along all dimensions + :type scale: float + :param scaleX: Scale factor along X + :type scaleX: float + :param scaleY: Scale factor along Y + :type scaleY: float + :param scaleZ: Scale factor along Z + :type scaleZ: float + ''' + if scale != 1: + scaleX = scaleY = scaleZ = scale + + xform = gp_GTrsf() + xform.SetVectorialPart(gp_Mat( + scaleX, 0, 0, + 0, scaleY, 0, + 0, 0, scaleZ, + )) + brep = BRepBuilderAPI_GTransform(self.shape, xform, False) + brep.Build() + self.shape = brep.Shape() + return self + + def shear(self, xy=0, xz=0, yx=0, yz=0, zx=0, zy=0): + ''' + Shear the solid + + :param xy: Shear along X-axis as function of y + :type xy: float + :param xz: Shear along X-axis as function of z + :type xz: float + :param yx: Shear along Y-axis as function of x + :type yx: float + :param yz: Shear along Y-axis as function of z + :type yz: float + :param zx: Shear along Z-axis as function of x + :type zx: float + :param zy: Shear along Z-axis as function of y + :type zy: float + ''' + xform = gp_Trsf() + xform.SetValues( + 1, xy, xz, 0, + yx, 1, yz, 0, + zx, zy, 1, 0, + Precision_Angular(), Precision_Confusion() + ) + brep = BRepBuilderAPI_GTransform(self.shape, gp_GTrsf(xform), False) + brep.Build() + self.shape = brep.Shape() + return self + + class _Verboseness(object): + ''' Helps suppress verbouse outputs ''' + + def __init__(self, verbose=False): + self.verbose = verbose + + def __enter__(self): + if not self.verbose: + import sys + import os + sys.stdout.flush() + self.newstdout = os.dup(1) + self.devnull = os.open('/dev/null', os.O_WRONLY) + os.dup2(self.devnull, 1) + os.close(self.devnull) + sys.stdout = os.fdopen(self.newstdout, 'w') + + def __exit__(self, type, value, traceback): + if not self.verbose: + os.dup2(self.newstdout, 1) + + def toSTEP(self, filename, verbose=False, tolerance=0.001): + ''' + Writes STEP output of the solid + + :param filename: Path of the file to write STEP to + :type filename: str + :param verbose: Choose if you want to see the STEP stats + :type verbose: bool + :param tolerance: Provides control over quality of exported STEP. + :type tolernace: float + ''' + with self._Verboseness(verbose): + stepWriter = STEPControl.STEPControl_Writer() + stepWriter.SetTolerance(tolerance) + if self.shape: + status = stepWriter.Transfer(self.shape, + STEPControl.STEPControl_AsIs) + if status: + stepWriter.Write(filename) + + def fromSTEP(self, filename, verbose=False): + ''' + Imports a STEP file to a solid + + :param filename: Path of the file to write STEP to + :type filename: str + :param verbose: Choose if you want to see the STEP stats + :type verbose: bool + ''' + with self._Verboseness(verbose): + if filename: + stepReader = STEPControl.STEPControl_Reader() + status = stepReader.ReadFile(filename) + if status: + nbr = stepReader.NbRootsForTransfer() + for n in range(1, nbr + 1): + stepReader.TransferRoot(n) + nbs = stepReader.NbShapes() + if nbs == 1: + if stepReader.Shape(1): + self.shape = stepReader.Shape(1) + return self + + def toSTL(self, filename, ascii=False, deflection=0.01): + ''' + Writes STL output of the solid + + :param filename: Path of the file to write STL data to + :type filename: str + :param ascii: Choose between ASCII or Binary STL format + :type ascii: bool + :param deflection: Provides control over quality of exported STL. Higher the deflection value, lower the accuracy of exported STL, smaller the size of resulting file. Lower the deflection value, more accurate the exported STL, bigger the size of resulting file. + :type deflection: float + ''' + stl_writer = StlAPI.StlAPI_Writer() + stl_writer.SetASCIIMode(ascii) + stl_writer.SetDeflection(deflection) + stl_writer.Write(self.shape, filename) + + def toBREP(self, filename): + ''' + Write BREP output of the solid + + :param filename: Path of the file to write BREP to + ''' + BRepTools().Write(self.shape, filename) + + def fromBREP(self, filename): + ''' + Load ths solid from BREP file + + :param filename: Path of the file to read BREP from + ''' + BRepTools().Read(self.shape, filename, BRep_Builder()) diff --git a/src/facade/cadmium.py b/src/facade/cadmium.py index 5c9f683..6025682 100755 --- a/src/facade/cadmium.py +++ b/src/facade/cadmium.py @@ -1,4 +1,6 @@ +from __future__ import print_function +from builtins import object import json A_MIN = 1 @@ -14,133 +16,145 @@ SCALE = 'scale' SHEAR = 'shear' -X_axis = [1,0,0] -Y_axis = [0,1,0] -Z_axis = [0,0,1] - -class Solid(): - - def __init__(self, - solid=None, primitive=None, left=None, right=None, op=None): - - self.solid = None - if solid: - self.solid = solid - elif primitive: - self.primitive = primitive - else: - self.primitive = None - self.left = left - self.right = right - self.op = op - self.transforms = [] - self.params = dict() - - def centralize(self): - pass - - def __add__(self, other): - return Solid(left=self, right=other, op=UNION) - - def __sub__(self, other): - return Solid(left=self, right=other, op=SUBTRACTION) - - def __mul__(self, other): - return Solid(left=self, right=other, op=INTERSECTION) - - def translate(self, x=0, y=0, z=0): - self.transforms.append(dict(kind=TRANSLATION,x=x,y=y,z=z)) - return self - - def rotate(self, axis, angle): - self.transforms.append(dict(kind=ROTATION,axis=axis,angle=angle)) - return self - - def scale(self, sx=1, sy=1, sz=1): - self.transforms.append(dict(kind=SCALE,sx=sx,sy=sy,sz=sz)) - return self - - def shear(self, xy=0, xz=0, yx=0, yz=0, zx=0, zy=0): - self.transforms.append(dict(kind=SHEAR,xy=xy,xz=xz,yx=yx,yz=yz,zx=zx,zy=zy)) - return self - - def toData(self): - if self.solid: - return self.solid.toData() - elif self.primitive: - return dict(kind=self.primitive, - params=self.params,transforms=self.transforms) - else: - return dict(op=self.op,left=self.left.toData(), - right=self.right.toData(), transforms=self.transforms) +X_axis = [1, 0, 0] +Y_axis = [0, 1, 0] +Z_axis = [0, 0, 1] + + +class Solid(object): + def __init__(self, + solid=None, primitive=None, left=None, right=None, op=None): + + self.solid = None + if solid: + self.solid = solid + elif primitive: + self.primitive = primitive + else: + self.primitive = None + self.left = left + self.right = right + self.op = op + self.transforms = [] + self.params = dict() + + def centralize(self): + pass + + def __add__(self, other): + return Solid(left=self, right=other, op=UNION) + + def __sub__(self, other): + return Solid(left=self, right=other, op=SUBTRACTION) + + def __mul__(self, other): + return Solid(left=self, right=other, op=INTERSECTION) + + def translate(self, x=0, y=0, z=0): + self.transforms.append(dict(kind=TRANSLATION, x=x, y=y, z=z)) + return self + + def rotate(self, axis, angle): + self.transforms.append(dict(kind=ROTATION, axis=axis, angle=angle)) + return self + + def scale(self, sx=1, sy=1, sz=1): + self.transforms.append(dict(kind=SCALE, sx=sx, sy=sy, sz=sz)) + return self + + def shear(self, xy=0, xz=0, yx=0, yz=0, zx=0, zy=0): + self.transforms.append(dict(kind=SHEAR, xy=xy, xz=xz, yx=yx, yz=yz, zx=zx, zy=zy)) + return self + + def toData(self): + if self.solid: + return self.solid.toData() + elif self.primitive: + return dict(kind=self.primitive, + params=self.params, transforms=self.transforms) + else: + return dict(op=self.op, left=self.left.toData(), + right=self.right.toData(), transforms=self.transforms) + class Box(Solid): - def __init__(self, x=10,y=10,z=10, center=False): - Solid.__init__(self, primitive='box') - self.params = dict(x=x,y=y,z=z) + def __init__(self, x=10, y=10, z=10, center=False): + Solid.__init__(self, primitive='box') + self.params = dict(x=x, y=y, z=z) + class Cylinder(Solid): - def __init__(self, radius=None, height=None, h=None, r=None, - pie=360, r1=None, r2=None, center=False): - Solid.__init__(self, primitive='cyl') - if radius: r = radius - if height: h = height - self.params = dict(r=r,h=h) + def __init__(self, radius=None, height=None, h=None, r=None, + pie=360, r1=None, r2=None, center=False): + Solid.__init__(self, primitive='cyl') + if radius: r = radius + if height: h = height + self.params = dict(r=r, h=h) + class Cone(Solid): - def __init__(self, radius=None, height=None, h=None, r=None, - pie=360, center=False): - Solid.__init__(self, primitive='con') - if radius: r = radius - if height: h = height - self.params = dict(r=r,h=h) + def __init__(self, radius=None, height=None, h=None, r=None, + pie=360, center=False): + Solid.__init__(self, primitive='con') + if radius: r = radius + if height: h = height + self.params = dict(r=r, h=h) + class Sphere(Solid): - def __init__(self, radius=None, r=None, phi=360, center=False): - Solid.__init__(self, primitive='sph') - if radius: r = radius - self.params = dict(r=r) + def __init__(self, radius=None, r=None, phi=360, center=False): + Solid.__init__(self, primitive='sph') + if radius: r = radius + self.params = dict(r=r) + class Torus(Solid): - def __init__(self, r1=None, r2=None, angle=360, center=False): - Solid.__init__(self, primitive='tor') - self.params = dict(r1=r1, r2=r2, angle=360) + def __init__(self, r1=None, r2=None, angle=360, center=False): + Solid.__init__(self, primitive='tor') + self.params = dict(r1=r1, r2=r2, angle=360) + class Wedge(Solid): - def __init__(self, dx=5, dy=5, dz=5, ltx=0, center=True): - Solid.__init__(self, primitive='wdg') - self.params = dict(dx=dx, dy=dy, dz=dz) + def __init__(self, dx=5, dy=5, dz=5, ltx=0, center=True): + Solid.__init__(self, primitive='wdg') + self.params = dict(dx=dx, dy=dy, dz=dz) + def description(*arg, **kwdArg): - def decorator(func): return func - return decorator + def decorator(func): return func + + return decorator + def param(*arg, **kwdArg): - def decorator(func): return func - return decorator + def decorator(func): return func + + return decorator + def main(): - bA = Box(3,3,3).translate(dy=-0.5) - bB = Box(2,2,2).translate(dy=1) - cyl = Cylinder(radius=0.5,height=7).translate(dy=1)\ - .rotate(axis=X_axis,angle=90) - #s = (cyl*bA)+(cyl*bB) - #s = cyl * (bA+bB) - #s = bA - (bB * cyl) - #s = (bA - bB) + (bA - cyl) - #s = bA * (bB * cyl) - #s = (bA - bB) + (bA * cyl) - #s = bA - (bB - cyl) - #s = (bA * bB) - cyl - #s = bA * (bB - cyl) - s = (bA - cyl) * bB - #s = (bA - cyl) + (bB - cyl) - #s = (bA + bB) - cyl - #s = (bA * cyl) + (bB * cyl) - #s = (bA + bB) * cyl - - #s = bA + bB + cyl - print json.dumps(s.toData(), indent=2) + bA = Box(3, 3, 3).translate(dy=-0.5) + bB = Box(2, 2, 2).translate(dy=1) + cyl = Cylinder(radius=0.5, height=7).translate(dy=1) \ + .rotate(axis=X_axis, angle=90) + # s = (cyl*bA)+(cyl*bB) + # s = cyl * (bA+bB) + # s = bA - (bB * cyl) + # s = (bA - bB) + (bA - cyl) + # s = bA * (bB * cyl) + # s = (bA - bB) + (bA * cyl) + # s = bA - (bB - cyl) + # s = (bA * bB) - cyl + # s = bA * (bB - cyl) + s = (bA - cyl) * bB + # s = (bA - cyl) + (bB - cyl) + # s = (bA + bB) - cyl + # s = (bA * cyl) + (bB * cyl) + # s = (bA + bB) * cyl + + # s = bA + bB + cyl + print(json.dumps(s.toData(), indent=2)) + if __name__ == '__main__': - main() + main() diff --git a/src/facade/compile.py b/src/facade/compile.py index c482cdc..110f57d 100644 --- a/src/facade/compile.py +++ b/src/facade/compile.py @@ -1,28 +1,24 @@ - import os import json import inspect import cadmium -module_names = filter( - lambda x: x != 'cadmium.py' and x != 'compile.py' and x.endswith('.py'), - os.listdir('.')) -module_names = map(lambda x: x.split('.')[0], module_names) +module_names = [x for x in os.listdir('.') if x != 'cadmium.py' and x != 'compile.py' and x.endswith('.py')] +module_names = [x.split('.')[0] for x in module_names] -std_classes = [ c[0] for c in inspect.getmembers(cadmium, inspect.isclass) ] +std_classes = [c[0] for c in inspect.getmembers(cadmium, inspect.isclass)] expressions = {} for module_name in module_names: - mod = __import__(module_name) - classes = filter(lambda x: x[0] not in std_classes, - inspect.getmembers(mod, inspect.isclass)) + mod = __import__(module_name) + classes = [x for x in inspect.getmembers(mod, inspect.isclass) if x[0] not in std_classes] - assert len(classes) == 1 - usolid = classes[0][1]() - expressions[module_name] = dict( - name = classes[0][0], - csg = usolid.toData() - ) + assert len(classes) == 1 + usolid = classes[0][1]() + expressions[module_name] = dict( + name=classes[0][0], + csg=usolid.toData() + ) -open('expressions.js','w').write(json.dumps(expressions, indent=2)) +open('expressions.js', 'w').write(json.dumps(expressions, indent=2)) diff --git a/tests/primitives.py b/tests/primitives.py index cc770d4..0afa525 100644 --- a/tests/primitives.py +++ b/tests/primitives.py @@ -1,100 +1,109 @@ - import os import sys + sys.path.append('../src') from unittest import TestCase import difflib import cadmium + def generate_data(): - cyl = cadmium.Cylinder(r=5, h=10, center=True) - cyl.toSTL('data/cylinder.stl') - cyl.toJSON('data/cylinder.json.gz',compress=True) + cyl = cadmium.Cylinder(r=5, h=10, center=True) + cyl.toSTL('data/cylinder.stl') + cyl.toJSON('data/cylinder.json.gz', compress=True) - box = cadmium.Box(x=4,y=4,z=4, center=True) - box.toSTL('data/box.stl') - box.toJSON('data/box.json.gz', compress=True) + box = cadmium.Box(x=4, y=4, z=4, center=True) + box.toSTL('data/box.stl') + box.toJSON('data/box.json.gz', compress=True) - cone = cadmium.Cone(r=5,h=10, center=True) - cone.toSTL('data/cone.stl') - cone.toJSON('data/cone.json.gz', compress=True) + cone = cadmium.Cone(r=5, h=10, center=True) + cone.toSTL('data/cone.stl') + cone.toJSON('data/cone.json.gz', compress=True) - wedge = cadmium.Wedge(dx=5,dy=5,dz=5, center=True) - wedge.toSTL('data/wedge.stl') - wedge.toJSON('data/wedge.json.gz', compress=True) + wedge = cadmium.Wedge(dx=5, dy=5, dz=5, center=True) + wedge.toSTL('data/wedge.stl') + wedge.toJSON('data/wedge.json.gz', compress=True) - torus = cadmium.Torus(r1=1, r2=.2, center=True) - torus.toSTL('data/torus.stl') - torus.toJSON('data/torus.json.gz', compress=True) + torus = cadmium.Torus(r1=1, r2=.2, center=True) + torus.toSTL('data/torus.stl') + torus.toJSON('data/torus.json.gz', compress=True) + + sph = cadmium.Sphere(r=1, center=True) + sph.toSTL('data/sphere.stl') + sph.toJSON('data/sphere.json.gz', compress=True) - sph = cadmium.Sphere(r=1, center=True) - sph.toSTL('data/sphere.stl') - sph.toJSON('data/sphere.json.gz', compress=True) def are_same(fn1, fn2): - return difflib.SequenceMatcher( - None, open(fn1).read(), open(fn2).read()).ratio() > 0.95 + return difflib.SequenceMatcher( + None, open(fn1).read(), open(fn2).read()).ratio() > 0.95 + class CylinderTest(TestCase): - def test(self): - cyl = cadmium.Cylinder(r=5, h=10, center=True) - cyl.toSTL('cylinder.stl') - self.assertTrue(are_same('cylinder.stl','data/cylinder.stl')) - os.remove('cylinder.stl') - cyl.toJSON('cylinder.json.gz', compress=True) - self.assertTrue(are_same('cylinder.json.gz','data/cylinder.json.gz')) - os.remove('cylinder.json.gz') + def test(self): + cyl = cadmium.Cylinder(r=5, h=10, center=True) + cyl.toSTL('cylinder.stl') + self.assertTrue(are_same('cylinder.stl', 'data/cylinder.stl')) + os.remove('cylinder.stl') + cyl.toJSON('cylinder.json.gz', compress=True) + self.assertTrue(are_same('cylinder.json.gz', 'data/cylinder.json.gz')) + os.remove('cylinder.json.gz') + class BoxTest(TestCase): - def test(self): - box = cadmium.Box(x=4,y=4,z=4, center=True) - box.toSTL('box.stl') - self.assertTrue(are_same('box.stl','data/box.stl')) - os.remove('box.stl') - box.toJSON('box.json.gz', compress=True) - self.assertTrue(are_same('box.json.gz','data/box.json.gz')) - os.remove('box.json.gz') + def test(self): + box = cadmium.Box(x=4, y=4, z=4, center=True) + box.toSTL('box.stl') + self.assertTrue(are_same('box.stl', 'data/box.stl')) + os.remove('box.stl') + box.toJSON('box.json.gz', compress=True) + self.assertTrue(are_same('box.json.gz', 'data/box.json.gz')) + os.remove('box.json.gz') + class ConeTest(TestCase): - def test(self): - cone = cadmium.Cone(r=5,h=10, center=True) - cone.toSTL('cone.stl') - self.assertTrue(are_same('cone.stl','data/cone.stl')) - os.remove('cone.stl') - cone.toJSON('cone.json.gz', compress=True) - self.assertTrue(are_same('cone.json.gz','data/cone.json.gz')) - os.remove('cone.json.gz') + def test(self): + cone = cadmium.Cone(r=5, h=10, center=True) + cone.toSTL('cone.stl') + self.assertTrue(are_same('cone.stl', 'data/cone.stl')) + os.remove('cone.stl') + cone.toJSON('cone.json.gz', compress=True) + self.assertTrue(are_same('cone.json.gz', 'data/cone.json.gz')) + os.remove('cone.json.gz') + class WedgeTest(TestCase): - def test(self): - wedge = cadmium.Wedge(dx=5,dy=5,dz=5, center=True) - wedge.toSTL('wedge.stl') - self.assertTrue(are_same('wedge.stl','data/wedge.stl')) - os.remove('wedge.stl') - wedge.toJSON('wedge.json.gz', compress=True) - self.assertTrue(are_same('wedge.json.gz','data/wedge.json.gz')) - os.remove('wedge.json.gz') + def test(self): + wedge = cadmium.Wedge(dx=5, dy=5, dz=5, center=True) + wedge.toSTL('wedge.stl') + self.assertTrue(are_same('wedge.stl', 'data/wedge.stl')) + os.remove('wedge.stl') + wedge.toJSON('wedge.json.gz', compress=True) + self.assertTrue(are_same('wedge.json.gz', 'data/wedge.json.gz')) + os.remove('wedge.json.gz') + class TorusTest(TestCase): - def test(self): - torus = cadmium.Torus(r1=1, r2=.2, center=True) - torus.toSTL('torus.stl') - self.assertTrue(are_same('torus.stl', 'data/torus.stl')) - os.remove('torus.stl') - torus.toJSON('torus.json.gz', compress=True) - self.assertTrue(are_same('torus.json.gz', 'data/torus.json.gz')) - os.remove('torus.json.gz') + def test(self): + torus = cadmium.Torus(r1=1, r2=.2, center=True) + torus.toSTL('torus.stl') + self.assertTrue(are_same('torus.stl', 'data/torus.stl')) + os.remove('torus.stl') + torus.toJSON('torus.json.gz', compress=True) + self.assertTrue(are_same('torus.json.gz', 'data/torus.json.gz')) + os.remove('torus.json.gz') + class SphereTest(TestCase): - def test(self): - sph = cadmium.Sphere(r=1, center=True) - sph.toSTL('sphere.stl') - self.assertTrue(are_same('sphere.stl','data/sphere.stl')) - os.remove('sphere.stl') - sph.toJSON('sphere.json.gz', compress=True) - self.assertTrue(are_same('sphere.json.gz','data/sphere.json.gz')) - os.remove('sphere.json.gz') + def test(self): + sph = cadmium.Sphere(r=1, center=True) + sph.toSTL('sphere.stl') + self.assertTrue(are_same('sphere.stl', 'data/sphere.stl')) + os.remove('sphere.stl') + sph.toJSON('sphere.json.gz', compress=True) + self.assertTrue(are_same('sphere.json.gz', 'data/sphere.json.gz')) + os.remove('sphere.json.gz') + if __name__ == '__main__': - generate_data() + generate_data()