Commits

Ed Blake committed c7e8afc

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

  • Participants
  • Parent commits 0ff351f

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?

File 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))

File py2scad/base.py

     """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)
 

File 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

File py2scad/tests/__init__.py

Empty file added.

File 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

File 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,)

File 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."""