Commits

Ed Blake committed c7e8afc

Finished draft implimentation of assemblies and variables.
Started unit tests (sort of).
Added TODO, modified README.

Comments (0)

Files changed (9)

 enables you write .scad files using python. py2scad is meant to be used in
 conjuction with Clifford Wolff's great OpenSCAD program (http://openscad.org/).
 Use py2scad to create the .scad version of your part and OpenSCAD to view your
-part as you create it. 
+part as you create it.
 
-Note, there are still some features that I need to implement: 
-
-* The fn, fa, fs character should work on the global level, but you can yet set
-  them for individual primatives as you can in openscad. 
-
-* The lookup table function hasn't been implemented. This will be especiallly 
-  useful for creating animations. 
+See TODO file for features under consideration/construction.
 
 Installations instructions can be found in the INSTALL file.
 
-Author: Will Dickson IO Rodeo Inc. 
+Author: Will Dickson IO Rodeo Inc.
 
 py2scad may be freely distributed and modified in accordance with the Apache
 2.0  License.
-
+############################ General ############################
+
+More code documentation and comments
+Remove unused attributes
+Remove excessive type enforcement
+Add routines to run OpenSCAD and produce output
+consolidate objects?
+fix naming?
+How does scad animation work?
+
+############################# Will ##############################
+
+Implement lookup table function, this will be especiallly useful
+    for creating animations.
+Add fn, fa, fs controls for individual primatives.
+
+############################## Ed ###############################
+
+Create unit-tests written for all modules
+Create more accessable examples (tutorial?)
+Add machinery for including/using scad library files
+Add transformation arguments to primitives?

examples/assembly_with_variables.py

+"""
+An example of creating a parameterized assembly and using variables to make the
+Scad output file more readable.
+"""
+import sys, os
+from py2scad import *
+
+# Setup the path to the Scad executable
+scad = '' # Default to False
+if sys.platform == 'darwin':
+    scad = "/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD"
+elif sys.platform == "win32":
+    scad = r'"D:\Program files\OpenSCAD\OpenSCAD.exe"'
+
+outfilename = "assembly.scad"
+
+# This is just an example, so we are going to make pointless mounting plate!
+
+########################### Variables ###########################
+# Assume the user of the script might want to play with w/h/d and edge offsets
+vars = Variables(width=50.0, height=25.0, depth=2.0, inset=3.0)
+# Hole sizes are fixed though
+bolt_dia = 3.0
+hole_dia = 10.0
+tol = 0.25 # Gap tolerance
+
+########################### Assembly ############################
+# Make a faux m3 socket head bolt assembly
+m3_bolt = Assembly(name="m3bolt", parameters=["length"],
+            obj=Union([
+                # socket cylinder
+                Translate(Cylinder(h=bolt_dia, r1=bolt_dia), [0,0,bolt_dia/2]),
+                # threaded shaft
+                Translate(Cylinder(h="length+1", r1=bolt_dia/2), [0,0,"-length/2"])
+                ]),
+            comment="Generate an m3 bolt at the specific length."
+            )
+
+############################# Part ##############################
+# The part is just a cube with some holes, and transparent bolts in the holes
+part = Difference([
+    Cube(size=[vars.width, vars.height, vars.depth]),
+    # Bolt holes
+    Translate(Cylinder(h='2+'+vars.depth, r1=(bolt_dia/2)+tol),
+              ["({0.width}/2)-{0.inset}".format(vars),
+               "({0.height}/2)-{0.inset}".format(vars),
+               0]
+            ),
+    Translate(Cylinder(h='2+'+vars.depth, r1=(bolt_dia/2)+tol),
+              ["(-{0.width}/2)+{0.inset}".format(vars),
+               "({0.height}/2)-{0.inset}".format(vars),
+               0]
+            ),
+    Translate(Cylinder(h='2+'+vars.depth, r1=(bolt_dia/2)+tol),
+              ["(-{0.width}/2)+{0.inset}".format(vars),
+               "(-{0.height}/2)+{0.inset}".format(vars),
+               0]
+            ),
+    Translate(Cylinder(h='2+'+vars.depth, r1=(bolt_dia/2)+tol),
+              ["({0.width}/2)-{0.inset}".format(vars),
+               "(-{0.height}/2)+{0.inset}".format(vars),
+               0]
+            ),
+    # Bolts
+    Union([
+        Translate(m3_bolt(16),
+                  ["({0.width}/2)-{0.inset}".format(vars),
+                   "({0.height}/2)-{0.inset}".format(vars),
+                   0]
+                ),
+        Translate(m3_bolt(16),
+                  ["(-{0.width}/2)+{0.inset}".format(vars),
+                   "({0.height}/2)-{0.inset}".format(vars),
+                   0]
+                ),
+        Translate(m3_bolt(16),
+                  ["(-{0.width}/2)+{0.inset}".format(vars),
+                   "(-{0.height}/2)+{0.inset}".format(vars),
+                   0]
+                ),
+        Translate(m3_bolt(16),
+                  ["({0.width}/2)-{0.inset}".format(vars),
+                   "(-{0.height}/2)+{0.inset}".format(vars),
+                   0]
+                )],
+        mod='%'
+    ),
+    # Center hole
+    Cylinder(h=vars['depth']+2, r1=hole_dia/2)
+
+])
+
+
+design = SCAD_Prog()
+design.add([vars, m3_bolt, part])
+design.write(outfilename)
+
+if scad: # If we can find scad
+    # Generate a stl file in the same dir with the same namd as the scad file
+    os.system('{scad} -s {stl} {file}'.format(scad=scad,
+                                              stl=outfilename.replace('.scad', '.stl'),
+                                              file=outfilename))
     """Wrapper for Openscad program."""
 
     def __init__(self, fn=None, fa=None, fs=None):
-        self.module_list = []
         self.objlist = []
         # Global facet settings
         self.fn = fn
         self.fa = fa
         self.fs = fs
 
-    def module(self, obj, **kwargs):
-        """Do something terribly clever to parameterize an object..."""
-        pass
-
     def add(self, obj):
         """Add a scad object to this program container."""
         if type(obj) == list:
         if not self.fs == None:
             rtn_str = '%s$fs = %d;\n'%(rtn_str, self.fs)
 
-        for mod in self.module_list:
-            rtn_str = "{0}{1}\n\n".format(rtn_str, mod)
-
         for obj in self.objlist:
             rtn_str = '%s%s\n\n'%(rtn_str,obj)
 

py2scad/primitives.py

     # Attribute getter returns the varaible name
     # Attribute setter sets variable value
     # cmd_str method returns variable definition in scad syntax
-    def __init__(self, **kwargs):
+    def __init__(self, comment='', **kwargs):
+        self.comment = comment
+        if not comment:
+            self.comment = "Named variables //\n"
         dict.__init__(self, kwargs)
         self._initialised = True
 
 
     def cmd_str(self, tab_level=0):
         tab_str = ' '*utility.TAB_WIDTH*tab_level
-        rtn_str = '\n'
+        rtn_str = ''
         for k,v in self.items():
-            rtn_str += '{0} = {1};\n'.format(k, utility.val_to_str(v))
+            rtn_str += '{0}{1} = {2};\n'.format(tab_str, k, utility.val_to_str(v))
         return rtn_str + '\n'
 
+    def __str__(self, tab_level=0):
+        tab_str = ' '*utility.TAB_WIDTH*tab_level
+        comment = ''
+        if self.comment:
+            comment = tab_str + '// ' + self.comment + '\n'
+        return '\n{0}{1}'.format(comment, self.cmd_str(tab_level=tab_level))
+
+
 # 3D primitives ---------------------------------------------------------------
 
 class Cube(base.SCAD_Object):
 
     def __init__(self, points, faces, center=True, *args, **kwargs):
         base.SCAD_Object.__init__(self, center=center, *args, **kwargs)
-        self.points = [utility.float_list3(x) for x in points]
-        self.faces = [utility.float_list(x) for x in faces]
+        self.points = points
+        self.faces = faces
 
     def cmd_str(self,tab_level=0):
         tab_str0 = ' '*utility.TAB_WIDTH*tab_level
 
     def __init__(self, points, paths, *args, **kwargs):
         base.SCAD_Object.__init__(self, *args, **kwargs)
-        self.points = [utility.float_list2(p) for p in points]
-        self.paths = [utility.float_list(p) for p in paths]
+        self.points = points
+        self.paths = paths
 
     def cmd_str(self,tab_level=0):
         tab_str0 = ' '*utility.TAB_WIDTH*tab_level

py2scad/tests/__init__.py

Empty file added.

py2scad/tests/base_test.py

+"""
+Copyright (c) 2010 Ed Blake <kitsu.eb@gmail.com>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+import unittest
+from ..base import *
+
+class TestSCAD_Prog(unittest.TestCase):
+    """Test the program object."""
+    def setup(self):
+        pass
+
+    def test_write(self):
+        """Check that write produces a valid program."""
+        pass
+
+if __name__ == "__main__":
+    # Run test suite
+    pass

py2scad/transforms.py

         base.SCAD_CMP_Object.__init__(self, obj, **kwargs)
         self.name = name
         # Filter out any non-string arguments (unicode removed from py3)
-        self.args = tuple([arg for arg in args if type(arg) == str])
+        self.args = parameters
 
     def cmd_str(self,tab_level=0):
         """Outputs the module signature."""
         """Returns a string calling this module with provided arguments."""
         if len(args) > len(self.args):
             raise TypeError("{0}() takes exactly {1} argument(s) ({2} given)".format(self.name, len(self.args), len(args)))
-        return "{0}({1});".format(self.name, ', '.join(arg for arg in args))
+        return "{0}({1});".format(self.name,
+                ', '.join(utility.val_to_str(arg) for arg in args))
 
 # 3D transformations ---------------------------------------------------------
 
     """Scale contained object along local x,y,z."""
     def __init__(self,obj,v=[1.0,1.0,1.0], *args, **kwargs):
         base.SCAD_CMP_Object.__init__(self, obj, *args, **kwargs)
-        self.v = utility.float_list3(v)
+        self.v = v
 
     def cmd_str(self,tab_level=0):
         v_str = utility.val_to_str(self.v)
 
     def __init__(self, obj, v=[1.0,0.0,0.0], a=None, *args, **kwargs):
         base.SCAD_CMP_Object.__init__(self, obj, *args, **kwargs)
-        self.v = utility.float_list3(v)
+        self.v = v
         self.a = a
 
     def cmd_str(self,tab_level=0):
 
     def __init__(self,obj,v=[0.0,0.0,0.0], *args, **kwargs):
         base.SCAD_CMP_Object.__init__(self, obj, *args, **kwargs)
-        self.v = utility.float_list3(v)
+        self.v = v
 
     def cmd_str(self,tab_level=0):
         v_str = utility.val_to_str(self.v)
 
     def __init__(self,obj,v=[1.0,0.0,0.0], *args, **kwargs):
         base.SCAD_CMP_Object.__init__(self, obj, *args, **kwargs)
-        self.v = utility.float_list3(v)
+        self.v = v
 
     def cmd_str(self,tab_level=0):
         v_str = utility.val_to_str(self.v)
     def __init__(self,obj,h=1, twist=0, center=True, convexity=5,
                  slices=None, *args, **kwargs):
         base.SCAD_CMP_Object.__init__(self,obj,center=center,mod=mod, *args, **kwargs)
-        self.h = float(h)
-        self.twist = float(twist)
-        self.convexity = int(convexity)
+        self.h = h
+        self.twist = twist
+        self.convexity = convexity
         try:
-            self.slices = int(slices)
+            self.slices = slices
         except TypeError:
             self.slices = None
 
                  convexity=10, twist=0, *args, **kwargs):
         base.SCAD_Object.__init__(self, center=center, *args, **kwargs)
         self.filename = filename
-        self.height = float(height)
+        self.height = height
         self.layer = layer
-        self.twist = float(twist)
-        self.convexity = int(convexity)
+        self.twist = twist
+        self.convexity = convexity
 
     def cmd_str(self, tab_level=0):
         arg_str = 'file="%s"'%(self.filename,)

py2scad/utility.py

 RAD2DEG = math.radians
 
 # Utility functions -----------------------------------------------------------
-
+""" Depreciated!
 def float_list2(v):
     _v = float_list(v)
     assert len(_v) == 2, 'v must be convertable to a length2 list'
     _v = list(v)
     _v = [float(x) for x in _v]
     return _v
+"""
 
 def val_to_str(val ,tab_level=0):
     """Ensure misc values are nicely formatted."""