From 504ab493b75aead78f3877957cfc140ee5c2919d Mon Sep 17 00:00:00 2001 From: jvasile <jvasile@cb376a5e-1013-0410-a455-b6b1f9ac8223> Date: Thu, 17 Jul 2008 13:10:40 +0000 Subject: Big changes to the class stack. Breaks v0.1 compatibility, but that is ok git-svn-id: https://reprap.svn.sourceforge.net/svnroot/reprap@1760 cb376a5e-1013-0410-a455-b6b1f9ac8223 --- trunk/users/vasile/brlcad/brlcad.py | 358 ++++++++++++++++++++----- trunk/users/vasile/brlcad/brlcad_mechanical.py | 29 +- 2 files changed, 320 insertions(+), 67 deletions(-) (limited to 'trunk/users/vasile/brlcad') diff --git a/trunk/users/vasile/brlcad/brlcad.py b/trunk/users/vasile/brlcad/brlcad.py index 5a28f812..afa0ec2a 100644 --- a/trunk/users/vasile/brlcad/brlcad.py +++ b/trunk/users/vasile/brlcad/brlcad.py @@ -15,6 +15,8 @@ license = '''## This is a freed work; you can redistribute it and/or modify it import sys, os +VERSION = 0.2 + class Vector: def __init__(self,*args): if len(args) == 1: @@ -40,80 +42,279 @@ def unique_name(stub, suffix): unique_names[full] += 1 return stub + '.' + str(unique_names[full]) + suffix -def build_name(stub, suffix, **kwargs): +def build_name(stub, ending, **kwargs): + '''There are a lot of kwargs options to control how objects are named. + + If you specify a name with 'name', the object will bear that exact + name, with no suffix or unique number. You are responsible for + making sure the name is unique. + + 'suffix' is added to names. It's usually a single letter + indicating s for shape or r for region. + + 'basename' overrides the stub. To this, a unique number and the + suffix will be added. + + 'unique_name' can be set to False, and that will turn off the numbering.''' + + if 'name' in kwargs: + return kwargs['name'] + if 'suffix' in kwargs: suffix = kwargs['suffix'] + else: + suffix = ending if suffix: suffix = '.' + suffix - if 'name' in kwargs: + + if 'basename' in kwargs: if 'unique_name' in kwargs and kwargs['unique_name']==False: - return kwargs['name'] + suffix + return kwargs['basename'] + suffix else: - return unique_name(kwargs['name'], suffix) + return unique_name(kwargs['basename'], suffix) else: return unique_name(stub, suffix) -class Statement: +class Statement(): + isstatement = True + name='' # statements don't have names, but this keeps group from complaining def __init__(self, statement, args=[]): - args = list(args) - for i in range(len(args)): - if type(args[i]) == tuple or type(args[i]) == list: - args[i] = Vector(args[i]) - self.args = args + + self.args=[] + if type(args) == str: + self.args.append(args) + else: + for i in range(len(args)): + if type(args[i]) == tuple or type(args[i]) == list: + self.args.append(Vector(args[i])) + else: + self.args.append(args[i]) + self.statement = statement + def __str__(self): - return '%s %s' % (self.statement, ' '.join([str(s) for s in self.args])) + return '%s %s\n' % (self.statement, ' '.join([str(s) for s in self.args])) -class Script(): - '''A script is just a list of statements to send to mged. - TODO: Maybe this class should derive from list?''' - def __init__(self, *statements): - self.statements = list(statements) - self.index = len(statements) +class Shape(): + isshape = True + def __init__(self, args=[], **kwargs): + '''A Shape is any physical item we can depict with brl-cad, from a + primitive to a screw to a hole to an entire machine. + + Each element in args is a Statement or a Shape that makes this + item. + + kwargs['rotate'] = rotate vector or a tuple containing rotate + vector and rotate vertex. + + kwargs['scale'] - not implemented + kwargs['translate'] - not implemented + + Specify the Shape by instantiating it along with the + statements and shapes that comprise it. Typically, you'll use + rotate or translate after instantiating to move it into + position. + + self.children stores a list of Shapes that comprise this one. + + TODO: Every shape needs a default vertex for translation and + rotation purposes. ''' + + self.name = build_name('shape', 's', **kwargs) + + if not hasattr(self, 'statements'): + self.statements = [] + + if not hasattr(self, 'children'): + self.children = [] # an array of Shapes + + for a in args: + if hasattr(a, 'isstatement'): + self.statements.append(a) + elif hasattr(a, 'isshape'): + self.children.append(a) + elif type(a) == type('str'): + self.statements.append(Statement(a)) + else: + print >>sys.stderr, self.name, 'contains ', type(a) + sys.exit(2) + + Shape.guess_vertex(self) + + if not hasattr(self, 'vertex'): + sys.stderr.write('Shape (%s) needs vertex\n' % (self.name)) + sys.exit(2) + + if 'group' in kwargs and kwargs['group']: + self.group() + + if 'rotate' in kwargs: + if type(kwargs['rotate'] == tuple) or type(kwargs['rotate'] == list): + if len(kwargs['rotate']) == 3: + self.rotate(kwargs['rotate']) + elif len(kwargs['rotate']) == 2: + self.rotate(kwargs['rotate'][0],kwargs['rotate'][1]) + else: + print >>sys.stderr, ('%s: rotate takes 1 vector or 2 vectors in a tuple.' % + (self.name)) + sys.exit(2) + else: + print >>sys.stderr, '%s: rotate takes 1 or 2 tuples/lists.' % (self.name) + sys.exit(2) + def __str__(self): - return ''.join(['%s\n' % (s) for s in self.statements]) + '''Accessing an Object as a string will yield all the statements and + the children's statements.''' - def __iter__(self): - return self - def next(self): - if self.index == len(statements): - raise StopIteration - self.index = self.index + 1 - return self.statements[self.index] + return ''.join([str(c) for c in self.children]) + \ + ''.join([str(s) for s in self.statements]) - def append(self, *statements): - for i in statements: - self.statements.append(i) - return self + def combination(self, command): + '''Unimplemented and/or Untested: Add a statement to the stack making this + a combination. Populate self.name with the name of that + combination.''' + + self.statements.append(Statement('c', (command,))) + + def group(self): + '''Take all the Shapes that comprise this item, and make a group of + self.name. Append the group statement to self. + + If kwargs['group']==True, this will be done as part of __init__''' + + self.statements.append(Kill(self.name)) + + args = [self.name] + for c in self.children: + if hasattr(c, 'isshape'): + args.append(c.name) + + if len(args) < 2: + print >>sys.stderr, self.name, 'needs more items to group' + sys.exit(2) + + self.statements.append(Statement('g', args)) + return self.name -class Shape(Statement): + def guess_vertex(self): + '''If vertex doesn't exist, adopt vertex of first shape in children. + + If this doesn't find the right vertex, you'll have to set it + manually *before* you call init. Otherwise, init will + complain about lack of a vertex and halt.''' + + if not hasattr(self, 'vertex'): + for c in self.children: + if hasattr(c, 'vertex'): + self.vertex = c.vertex + return self.vertex + + def rotate(self, rotation, vertex=None): + if vertex == None: + vertex = self.vertex + + for c in self.children: + if hasattr(c, 'rotate'): + c.rotate(rotation, vertex) + + def translate(self, translate, vertex=None): + '''translate is a tuple containing the amount to move along each axis. + vertex is the point from which we move. All other Shapes and + Shapes that make up this Shape are moved relative to the + vertex.''' + + if vertex == None: + vertex = self.vertex + + for c in self.children: + if hasattr(c, 'rotate'): + c.translate(translate, vertex) + + +class Primitive(Shape): + "Primitive shapes executed in one or more statements." + isprimitive=True def __init__(self, shape, args=[], **kwargs): - "Primitive shapes" self.name = build_name(shape, 's', **kwargs) - Statement.__init__(self,'in %s %s' % (self.name, shape),args) - -class Region(Statement): - def __init__(self, region_command, **kwargs): - self.name = build_name('region', 'r', **kwargs) - Statement.__init__(self,"r", (self.name, region_command)) - -class Group(Statement): - '''*shapes is a list of objects or object names to be added to the - group. This class respects all the Statement keyword arguments, - such as name, suffix and unique_name.''' - def __init__(self, *shapes, **kwargs): - shapes = list(shapes) - self.name = build_name('group', 'g', **kwargs) - for i in range(len(shapes)): - if not isinstance(shapes[i], basestring): - shapes[i] = shapes[i].name - Statement.__init__(self, 'g', (self.name, ' '.join(shapes))) - -class Comment(Statement): - def __init__(self, comment): - Statement.__init__(self,"\n##", (comment,)) + self.statements = [] + + self.guess_vertex(args) + + Shape.__init__(self, ( Kill(self.name), + Statement('in %s %s' % (self.name, shape),args),), + name=self.name, **kwargs) + + def guess_vertex(self, args): + ## Guess at the vertex. If this won't find the right vertex, + ## you'll have to set it manually. + i = 0 + while not hasattr(self, 'vertex') and i < len(args): + if type(args[i]) == tuple and len(args[i]) == 3: + self.vertex = args[i] + i += 1 + + def rotate(self, rotation, vertex=None): + if vertex == None: + vertex = self.vertex + + xrot, yrot, zrot = rotation + xvert, yvert, zvert = vertex + + self.statements.append('sed %s\nkeypoint %f %f %f\nrot %f %f %f\naccept\n' % ( + self.name, xvert, yvert, zvert, xrot, yrot, zrot)) + + def translate(self, xtra, ytra=None, ztra=None): + '''Translate shape to new coordinates. + + Note that this is different from MGED's internal translate + statement. MGED moves the shape to the indicated coordinates. + This function moves the shape relative to the current + coordinates. translate(1, 2, -3) moves the shape 1 to the + right, 2 up and 3 back.''' + + if type(xtra) == tuple: + xtra, ytra, ztra = xtra + + self.statements.append('sed %s\ntra %f %f %f\naccept' % ( + self.name, xtra, ytra, ztra)) + +class Cone(Primitive): + def __init__(self, vertex, height_vector, base_radius, top_radius, **kwargs): + Primitive.__init__(self, "trc", (vertex, height_vector, base_radius, top_radius), **kwargs) + +class Cylinder(Primitive): + def __init__(self, vertex, height_vector, radius, **kwargs): + Primitive.__init__(self, "rcc", (vertex, height_vector, radius), **kwargs) + +class Box(Primitive): + def __init__(self, xmin, xmax, ymin, ymax, zmin, zmax, **kwargs): + self.vertex = (xmin, ymin, zmin) + if not 'basename' in kwargs: + kwargs['basename'] = 'box' + Primitive.__init__(self, "rpp", (xmin, xmax, ymin, ymax, zmin, zmax), **kwargs) + +class Arb8(Primitive): + def __init__(self, v1, v2, v3, v4, v5, v6, v7, v8, **kwargs): + Primitive.__init__(self, "arb8", (v1, v2, v3, v4, v5, v6, v7, v8), **kwargs) + + +## Implement some simplistic commands +from string import Template +commands = { +'Comment':"##", +'Exit':'exit', +'Kill':'kill', +'Killall':'killall', +'Killtree': 'killtree', +'Quit':'quit', +'Source':'source', +} +for c in commands: + exec Template('''class $command(Statement): + def __init__(self, arg=''): + Statement.__init__(self, '$brl', arg)''').substitute(command = c, brl = commands[c]) class Sed(Statement): ## Enter editing mode @@ -122,7 +323,7 @@ class Sed(Statement): class Title(Statement): ## TODO: escape quotes def __init__(self, title, args=[]): - Statement.__init__(self,"title", (title,)) + Statement.__init__(self,"title", title) class Units(Statement): def __init__(self, units): @@ -134,19 +335,44 @@ class Units(Statement): if not units in good_units: sys.stdout.write('Unknown units! Ignoring units statement. Defaulting to mm.\n') units = 'mm' - Statement.__init__(self,"units", (units,)) + Statement.__init__(self,"units", units) -class Cone(Shape): - def __init__(self, vertex, height_vector, base_radius, top_radius, **kwargs): - Shape.__init__(self, "trc", (vertex, height_vector, base_radius, top_radius), **kwargs) +class Script(): + '''A script is just a list of statements to send to mged. + TODO: Maybe this class should derive from list?''' -class Cylinder(Shape): - def __init__(self, vertex, height_vector, radius, **kwargs): - Shape.__init__(self, "rcc", (vertex, height_vector, radius), **kwargs) + def __init__(self, *statements): + self.statements = list(statements) + def __str__(self): + return ''.join(['%s' % (s) for s in self.statements]) -class Box(Shape): - def __init__(self, xmin, xmax, ymin, ymax, zmin, zmax, **kwargs): - Shape.__init__(self, "rpp", (xmin, xmax, ymin, ymax, zmin, zmax), **kwargs) + def append(self, *statements): + for i in statements: + self.statements.append(i) + return self + + +class Box_rounded_edge_corners(Shape): + def __init__(self, xmin, xmax, ymin, ymax, zmin, zmax, radius, **kwargs): + if xmin > xmax: + xmin, xmax = xmax, xmin + if ymin > ymax: + ymin, ymax = ymax, ymin + if zmin > zmax: + zmin, zmax = zmax, zmin + + if radius > (xmax-xmin) / 2 or radius > (zmax-zmin)/2: + sys.stdout.write('Radius %f too large!\n' % (float(radius))) + sys.exit(2) + + step = radius + + Shape.__init__(self, [ + Box(xmin+step, xmax-step, ymin, ymax, zmin, zmax), + Box(xmin, xmax, ymin, ymax, zmin+step, zmax-step), + Cylinder((xmin+step, ymin, zmin+step), (0, ymax-ymin, 0), radius), + Cylinder((xmin+step, ymin, zmax-step), (0, ymax-ymin, 0), radius), + Cylinder((xmax-step, ymin, zmin+step), (0, ymax-ymin, 0), radius), + Cylinder((xmax-step, ymin, zmax-step), (0, ymax-ymin, 0), radius) + ], group=True, basename='brec', suffix='t') -#class Box_rounded_edge_corners(Script): -# def __init__(self, vertex, height diff --git a/trunk/users/vasile/brlcad/brlcad_mechanical.py b/trunk/users/vasile/brlcad/brlcad_mechanical.py index a5e80caa..482b4923 100644 --- a/trunk/users/vasile/brlcad/brlcad_mechanical.py +++ b/trunk/users/vasile/brlcad/brlcad_mechanical.py @@ -13,7 +13,34 @@ ## to the Free Software Foundation, Inc., 51 Franklin Street, 5th ## Floor, Boston, MA 02110-1301 USA -import brlcad +from brlcad import * + +class stepper_motor(Shape): + '''Vertex is center point box, along the center of the cylinder. + + TODO: implement mounting holes''' + + def __init__(self, vertex, base_height, base_width, + mount_hole_rad, + motor_height, motor_rad, + spindle_height, spindle_rad, **kwargs): + + x, y, z = vertex + by = base_height + my = motor_height + + bw = float(base_width) / 2 + + self.vertex = vertex + + Shape.__init__(self, [ + Comment('Stepper Motor'), + + Cylinder((x, y+by, z), (0,motor_height,0), motor_rad), + Cylinder((x, y+by+my, z), (0,spindle_height,0), spindle_rad), + Box_rounded_edge_corners(x-bw,x+bw, y,y+by, z-bw,z+bw, bw/10), + ], basename='stepper', suffix='', group=True, **kwargs) + class Threaded_rod(): '''Threaded rod. Horrible detail right now''' -- cgit v1.2.3