From: Timothy S. <pe...@us...> - 2004-07-16 11:45:28
|
Update of /cvsroot/pyode/pyode/xode In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv24225/xode Modified Files: body.py joint.py node.py parser.py transform.py Added Files: errors.py geom.py Removed Files: xode.py Log Message: xode/ * Added Geom parser. * Fixed some mistakes in transform.py. * Moved exception classes from parser.py to errors.py. examples/ * Added the xml encoding attribute to the XODE document in tutorial2.py * Added an example to demonstrate XODE's transform features. tests/ * Added Geom tests to test_xode.py. --- NEW FILE: geom.py --- # XODE Importer for PyODE # Copyright (C) 2004 Timothy Stranex """ XODE Geom Parser @author: U{Timothy Stranex<mailto:ti...@st...>} """ import ode import errors, node, joint, body, transform class Geom(node.TreeNode): """ Represents an C{ode.Geom} object and corresponds to the <geom> tag. """ def __init__(self, name, parent): node.TreeNode.__init__(self, name, parent) self._space = self.getFirstAncestor(ode.Space).getODEObject() self._transformed = False try: self._body = self.getFirstAncestor(ode.Body) except node.AncestorNotFoundError: self._body = None def takeParser(self, parser): """ Handle further parsing. It should be called immediately after the <geom> tag has been encountered. """ self._parser = parser self._parser.push(startElement=self._startElement, endElement=self._endElement) def _startElement(self, name, attrs): nodeName = attrs.get('name', None) if (name == 'transform'): t = transform.Transform() t.takeParser(self._parser, self, attrs) self._transformed = True elif (name == 'box'): self._parseGeomBox(attrs) elif (name == 'cappedCylinder'): self._parseGeomCCylinder(attrs) elif (name == 'cone'): raise NotImplementedError() elif (name == 'cylinder'): raise NotImplementedError() elif (name == 'plane'): self._parseGeomPlane(attrs) elif (name == 'ray'): self._parseGeomRay(attrs) elif (name == 'sphere'): self._parseGeomSphere(attrs) elif (name == 'trimesh'): raise NotImplementedError() elif (name == 'geom'): g = Geom(nodeName, self) g.takeParser(self._parser) elif (name == 'body'): b = body.Body(nodeName, self, attrs) b.takeParser(self._parser) elif (name == 'joint'): j = joint.Joint(nodename, self) j.takeParser(self._parser) elif (name == 'jointgroup'): pass elif (name == 'ext'): pass else: raise errors.ChildError('geom', name) def _endElement(self, name): if (name == 'geom'): obj = self.getODEObject() if (obj is None): raise errors.InvalidError('No geom type element found.') if (not obj.placeable()): # Non-placeable geoms cannot be attached to bodies or be # transformed self._parser.pop() return if (self._body is None): # The Geom is independant so it can have its own transform t = self.getTransform() obj.setPosition(t.getPosition()) obj.setRotation(t.getRotation()) elif (self._transformed): # The Geom is attached to a body so to transform it, it must # by placed in a GeomTransform and its transform is relative # to the body. t = self.getTransform(self._body) obj.setPosition(t.getPosition()) obj.setRotation(t.getRotation()) trans = ode.GeomTransform(self._space) trans.setGeom(obj) trans.setBody(self._body.getODEObject()) self.setODEObject(trans) else: obj.setBody(self._body.getODEObject()) self._parser.pop() def _parseGeomBox(self, attrs): def start(name, attrs): if (name == 'ext'): pass else: raise errors.ChildError('box', name) def end(name): if (name == 'box'): self._parser.pop() lx = float(attrs['sizex']) ly = float(attrs['sizey']) lz = float(attrs['sizez']) self.setODEObject(ode.GeomBox(self._space, (lx, ly, lz))) self._parser.push(startElement=start, endElement=end) def _parseGeomCCylinder(self, attrs): def start(name, attrs): if (name == 'ext'): pass else: raise errors.ChildError('cappedCylinder', name) def end(name): if (name == 'cappedCylinder'): self._parser.pop() radius = float(attrs['radius']) length = float(attrs['length']) self.setODEObject(ode.GeomCCylinder(self._space, radius, length)) self._parser.push(startElement=start, endElement=end) def _parseGeomSphere(self, attrs): def start(name, attrs): if (name == 'ext'): pass else: raise errors.ChildError('sphere', name) def end(name): if (name == 'sphere'): self._parser.pop() radius = float(attrs['radius']) self.setODEObject(ode.GeomSphere(self._space, radius)) self._parser.push(startElement=start, endElement=end) def _parseGeomPlane(self, attrs): def start(name, attrs): if (name == 'ext'): pass else: raise errors.ChildError('plane', name) def end(name): if (name == 'plane'): self._parser.pop() a = float(attrs['a']) b = float(attrs['b']) c = float(attrs['c']) d = float(attrs['d']) self.setODEObject(ode.GeomPlane(self._space, (a, b, c), d)) self._parser.push(startElement=start, endElement=end) def _parseGeomRay(self, attrs): def start(name, attrs): if (name == 'ext'): pass else: raise errors.ChildError('ray', name) def end(name): if (name == 'ray'): self._parser.pop() length = float(attrs['length']) self.setODEObject(ode.GeomRay(self._space, length)) self._parser.push(startElement=start, endElement=end) Index: parser.py =================================================================== RCS file: /cvsroot/pyode/pyode/xode/parser.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** parser.py 7 Jul 2004 11:46:32 -0000 1.1 --- parser.py 16 Jul 2004 11:45:17 -0000 1.2 *************** *** 53,62 **** import ode import xml.parsers.expat ! import transform, node, body, joint ! ! class InvalidError(Exception): ! """ ! Raised when an XODE document is invalid. ! """ class Parser: --- 53,57 ---- import ode import xml.parsers.expat ! import errors, transform, node, body, joint, geom class Parser: *************** *** 100,104 **** self._root.takeParser(self) else: ! raise InvalidError('Root element must be <xode>.') def parseVector(self, attrs): --- 95,99 ---- self._root.takeParser(self) else: ! raise errors.InvalidError('Root element must be <xode>.') def parseVector(self, attrs): *************** *** 109,114 **** @rtype: tuple ! @raise InvalidError: If the attributes don't correspond to a valid ! vector. """ --- 104,109 ---- @rtype: tuple ! @raise errors.InvalidError: If the attributes don't correspond to a ! valid vector. """ *************** *** 116,122 **** vec = float(attrs['x']), float(attrs['y']), float(attrs['z']) except ValueError: ! raise InvalidError('Vector attributes must be numbers.') except KeyError: ! raise InvalidError('Vector must have x, y and z attributes.') else: return vec --- 111,117 ---- vec = float(attrs['x']), float(attrs['y']), float(attrs['z']) except ValueError: ! raise errors.InvalidError('Vector attributes must be numbers.') except KeyError: ! raise errors.InvalidError('Vector must have x, y and z attributes.') else: return vec *************** *** 132,136 **** @rtype: instance of L{node.TreeNode} ! @raise InvalidException: If document is invalid. """ --- 127,131 ---- @rtype: instance of L{node.TreeNode} ! @raise errors.InvalidError: If document is invalid. """ *************** *** 148,152 **** @rtype: instance of L{node.TreeNode} ! @raise InvalidException: If document is invalid. """ --- 143,147 ---- @rtype: instance of L{node.TreeNode} ! @raise errors.InvalidError: If document is invalid. """ *************** *** 181,186 **** pass else: ! raise InvalidError('%s is not a valid child of <xode>.' % ! repr(name)) def _endElement(self, name): --- 176,180 ---- pass else: ! raise errors.ChildError('xode', name) def _endElement(self, name): *************** *** 222,227 **** pass else: ! raise InvalidError('%s is not a valid child of <world>' % ! repr(name)) def _endElement(self, name): --- 216,220 ---- pass else: ! raise errors.ChildError('world', name) def _endElement(self, name): *************** *** 258,263 **** t.takeParser(self._parser, self, attrs) elif (name == 'geom'): ! # parse geom ! pass elif (name == 'group'): # parse group --- 251,256 ---- t.takeParser(self._parser, self, attrs) elif (name == 'geom'): ! g = geom.Geom(nodeName, self) ! g.takeParser(self._parser) elif (name == 'group'): # parse group *************** *** 276,281 **** pass else: ! raise InvalidError('%s is not a valid child of <space>.' % ! repr(name)) def _endElement(self, name): --- 269,273 ---- pass else: ! raise errors.ChildError('space', name) def _endElement(self, name): --- xode.py DELETED --- Index: joint.py =================================================================== RCS file: /cvsroot/pyode/pyode/xode/joint.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** joint.py 7 Jul 2004 11:46:32 -0000 1.1 --- joint.py 16 Jul 2004 11:45:17 -0000 1.2 *************** *** 8,12 **** import ode ! import node, parser class Joint(node.TreeNode): --- 8,12 ---- import ode ! import node, errors class Joint(node.TreeNode): *************** *** 41,49 **** link = root.namedChild(name).getODEObject() except KeyError: ! raise parser.InvalidError('Joint link must reference an already '\ ! 'parsed body.') if (not isinstance(link, ode.Body)): ! raise parser.InvalidError('Joint link must reference a body.') return link --- 41,49 ---- link = root.namedChild(name).getODEObject() except KeyError: ! raise errors.InvalidError('Joint link must reference an already '\ ! 'parsed body.') if (not isinstance(link, ode.Body)): ! raise errors.InvalidError('Joint link must reference a body.') return link *************** *** 64,68 **** if (link1 is link2): ! raise parser.InvalidError('Joint requires two objects.') return link1, link2 --- 64,68 ---- if (link1 is link2): ! raise errors.InvalidError('Joint requires two objects.') return link1, link2 *************** *** 101,111 **** pass else: ! raise parser.InvalidError('%s is not a valid child of <joint>.' % ! repr(name)) def _endElement(self, name): if (name == 'joint'): if (self.getODEObject() is None): ! raise parser.InvalidError('No joint type element found.') self._parser.pop() --- 101,110 ---- pass else: ! raise errors.ChildError('joint', name) def _endElement(self, name): if (name == 'joint'): if (self.getODEObject() is None): ! raise errors.InvalidError('No joint type element found.') self._parser.pop() *************** *** 117,122 **** anchor[0] = self._parser.parseVector(attrs) else: ! raise parser.InvalidError('%s is not a valid child of <ball>.' % ! repr(name)) def end(name): --- 116,120 ---- anchor[0] = self._parser.parseVector(attrs) else: ! raise errors.ChildError('ball', name) def end(name): Index: transform.py =================================================================== RCS file: /cvsroot/pyode/pyode/xode/transform.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** transform.py 7 Jul 2004 11:46:32 -0000 1.1 --- transform.py 16 Jul 2004 11:45:17 -0000 1.2 *************** *** 24,27 **** --- 24,28 ---- [0, 0, 0, 0]] self.setIdentity() + self._absolute = False def takeParser(self, parser, node, attrs): *************** *** 41,44 **** --- 42,50 ---- self._scale = float(attrs.get('scale', 1.0)) + self._absolute = (attrs.get('absolute', 'false') == 'true') + + self._position = None + self._euler = None + self._node = node self._parser = parser *************** *** 48,59 **** def _startElement(self, name, attrs): if (name == 'matrix4f'): - self.setIdentity() for r in range(4): for c in range(4): self.m[r][c] = float(attrs['m%i%i' % (r, c)]) elif (name == 'position'): ! self.translate(float(attrs['x']), ! float(attrs['y']), ! float(attrs['z'])) elif (name == 'euler'): coeff = 1 --- 54,64 ---- def _startElement(self, name, attrs): if (name == 'matrix4f'): for r in range(4): for c in range(4): self.m[r][c] = float(attrs['m%i%i' % (r, c)]) elif (name == 'position'): ! self._position = (float(attrs['x']), ! float(attrs['y']), ! float(attrs['z'])) elif (name == 'euler'): coeff = 1 *************** *** 64,76 **** y = coeff * float(attrs['y']) z = coeff * float(attrs['z']) ! ! self.rotate(x, y, z) def _endElement(self, name): if (name == 'transform'): self.scale(self._scale, self._scale, self._scale) self._node.setNodeTransform(self) self._parser.pop() def setIdentity(self): """ --- 69,104 ---- y = coeff * float(attrs['y']) z = coeff * float(attrs['z']) ! ! self._euler = (x, y, z) ! elif (name == 'rotation'): ! pass ! else: ! import parser ! raise parser.InvalidError("%s is not a valid child of <transform>."% ! repr(name)) def _endElement(self, name): if (name == 'transform'): + if (self._euler): + x, y, z = self._euler + self.rotate(x, y, z) + if (self._position): + x, y, z = self._position + self.translate(x, y, z) + self.scale(self._scale, self._scale, self._scale) + self._node.setNodeTransform(self) self._parser.pop() + def isAbsolute(self): + """ + @return: True if this transform is to be absolute rather than + relative to another. + @rtype: bool + """ + + return self._absolute + def setIdentity(self): """ *************** *** 142,146 **** @type z: float """ ! rx = Transform() rx.m[1][1] = math.cos(x) --- 170,174 ---- @type z: float """ ! rx = Transform() rx.m[1][1] = math.cos(x) *************** *** 150,157 **** ry = Transform() ! ry.m[0][0] = math.cos(x) ! ry.m[0][2] = -math.sin(x) ! ry.m[2][0] = math.sin(x) ! ry.m[2][2] = math.cos(x) rz = Transform() --- 178,185 ---- ry = Transform() ! ry.m[0][0] = math.cos(y) ! ry.m[0][2] = -math.sin(y) ! ry.m[2][0] = math.sin(y) ! ry.m[2][2] = math.cos(y) rz = Transform() *************** *** 161,165 **** rz.m[1][1] = math.cos(z) ! r = self * rz * ry * rz self.m = r.m --- 189,193 ---- rz.m[1][1] = math.cos(z) ! r = self * rx * ry * rz self.m = r.m --- NEW FILE: errors.py --- # XODE Importer for PyODE # Copyright (C) 2004 Timothy Stranex """ XODE Exceptions @author: U{Timothy Stranex<mailto:ti...@st...>} """ class InvalidError(Exception): """ Raised when an XODE document is invalid. """ class ChildError(InvalidError): """ Raised when an invalid child element is found. @ivar parent: The parent element. @type parent: str @ivar child: The invalid child element. @type child: str """ def __init__(self, parent, child): self.parent = parent self.child = child def __str__(self): return '<%s> is not a valid child of <%s>.' % (self.child, self.parent) class MissingElementError(InvalidError): """ Raised when a child element is missing. @ivar parent: The parent element. @type parent: str @ivar child: The missing child element. @type child: str """ def __init__(self, parent, child): self.parent = parent self.child = child def __str__(self): return 'Missing child <%s> of <%s>.' % (self.child, self.parent) Index: body.py =================================================================== RCS file: /cvsroot/pyode/pyode/xode/body.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** body.py 7 Jul 2004 11:46:32 -0000 1.1 --- body.py 16 Jul 2004 11:45:17 -0000 1.2 *************** *** 8,12 **** import ode ! import parser, node, joint, transform class Body(node.TreeNode): --- 8,12 ---- import ode ! import errors, node, joint, transform, geom class Body(node.TreeNode): *************** *** 22,27 **** enabled = attrs.get('enabled', 'true') if (enabled not in ['true', 'false']): ! raise parser.InvalidError("Enabled attribute must be either 'true' or " \ ! "'false'.") else: if (enabled == 'false'): --- 22,27 ---- enabled = attrs.get('enabled', 'true') if (enabled not in ['true', 'false']): ! raise errors.InvalidError("Enabled attribute must be either 'true'"\ ! " or 'false'.") else: if (enabled == 'false'): *************** *** 77,85 **** float(attrs['zaxis'])) except KeyError: ! raise parser.InvalidError('finiteRotation element must have' \ ' xaxis, yaxis and zaxis attributes') if (mode not in [0, 1]): ! raise parser.InvalidError('finiteRotation mode attribute must' \ ' be either 0 or 1.') --- 77,85 ---- float(attrs['zaxis'])) except KeyError: ! raise errors.InvalidError('finiteRotation element must have' \ ' xaxis, yaxis and zaxis attributes') if (mode not in [0, 1]): ! raise errors.InvalidError('finiteRotation mode attribute must' \ ' be either 0 or 1.') *************** *** 96,99 **** --- 96,109 ---- j = joint.Joint(nodeName, self) j.takeParser(self._parser) + elif (name == 'body'): + b = Body(nodeName, self, attrs) + b.takeParser(self._parser) + elif (name == 'geom'): + g = geom.Geom(nodeName, self) + g.takeParser(self._parser) + elif (name == 'transform'): # so it doesn't raise ChildError + pass + else: + raise errors.ChildError('body', name) def _endElement(self, name): *************** *** 146,151 **** mass.takeParser(self._parser) else: ! raise parser.InvalidError('%s is not a valid child of <mass>' % ! repr(name)) def _endElement(self, name): --- 156,160 ---- mass.takeParser(self._parser) else: ! raise errors.ChildError('mass', name) def _endElement(self, name): Index: node.py =================================================================== RCS file: /cvsroot/pyode/pyode/xode/node.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** node.py 7 Jul 2004 11:46:32 -0000 1.1 --- node.py 16 Jul 2004 11:45:17 -0000 1.2 *************** *** 97,102 **** return self._transform ! def getTransform(self): """ @return: The absolute transform at this node. @rtype: instance of L{transform.Transform} --- 97,111 ---- return self._transform ! def getTransform(self, untilAncestor=None): """ + Calculates the absolute transform at this node. It calculates the + transforms recursively from the root node. If C{untilAncestor} is + passed, the transform is calculated relative to it. If C{untilAncestor} + is passed but is not an ancestor of this node, the transform is + calculated from the root node as if C{None} was passed. + + @param untilAncestor: The ancestor to calculate the transform from. + @type untilAncestor: instance of L{TreeNode} + @return: The absolute transform at this node. @rtype: instance of L{transform.Transform} *************** *** 106,113 **** t = self.getNodeTransform() ! if (p is not None): ! return p.getTransform() * t ! else: return t def getName(self): --- 115,122 ---- t = self.getNodeTransform() ! if ((p is None) or (t.isAbsolute()) or (p is untilAncestor)): return t + else: + return p.getTransform(untilAncestor) * t def getName(self): |