All pastes #332906 Raw Edit

Grom Dallur

public python v1 · immutable
#332906 ·published 2007-01-30 14:04 UTC
rendered paste body
## grom is free software; you can redistribute it and/or modify## it under the terms of the GNU General Public License as published by the## Free Software Foundation; either version 2 of the License, or (at your## option) any later version.  stl-to-dxf is distributed in the hope that it## will be useful, but WITHOUT ANY WARRANTY; without even the implied## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See## the GNU General Public License for more details.  You should have## received a copy of the GNU General Public License along with stl-to-dxf; if## not, write to the Free Software Foundation, Inc., 59 Temple Place,## Suite 330, Boston, MA 02111-1307 USA## ## grom is Copyright (C) 2006 Jarl Stefansson## jarl@dallur.comfrom math import sqrt################################################################## Make it so that all Operators are compatible with all numeric types# Make it so that all Operators are compatible with all objects# Make it so that you can create all objects by passing things in lists# Filter which objects types are allowed to be passed to operators# Use lambda to enable creation of groups passing multiple# Sync method names with those in vpython or create aliases# Verify that when one of the x/y/z attributes in a point is change the data stuff changes to################################################################class CartesianZero(object):    """ A singleton representing 0,0,0 in a cartesian coordinate system"""    # Since we use zero coordinate a whole lot and call it often it makes sense to use a singleton for performance    class __impl:        """ The singleton interface """        pass        __instance = None        # Any point of cartesian zero is equal to cartesian zero    def __Point__(self, which = None, other = None):        return self        def __init__(self):        """ Create singleton instance """        self.x = 0.0        self.y = 0.0        self.z = 0.0        # Check whether we already have an instance        if CartesianZero.__instance is None:            # Create and remember instance            CartesianZero.__instance = CartesianZero.__impl()        # Store instance reference as the only member in the handle        self.__dict__['_CartesianZero__instance'] = CartesianZero.__instanceclass Primitive(object):    """Generic Geometric Object Class"""    def __init__(self):        shared_init()    # Stores init parameters which are shared by all objects derrived from Primitive    def shared_init(self):        self.closed = None        self.color = None        self.index = 0            # Iter enables indexing and accessing of values in a list like style    def __iter__(self):        return self    # Getitem is the function called to use the indexing capability offereed by iter    def __getitem__(self, key):            return self.data[key]            # Next enables getting the data values one by one by iterating over them    def next(self):        if self.index == len(self.data):            self.index = 0            raise StopIteration        returnData = self.data[self.index]        self.index += 1        return returnData            # An error thrown when a __Point__ method is passed an object it is unable to handle    def PointTypeError(self):        raise TypeError , 'Unable to convert ' + str(type(self)) + ' to a Point object'        sys.exit(1)            # A generic distance method to get the distance between any two points    def distance(self, other = CartesianZero()):        return self.calculate_distance(other)    # Returnes False if any of the objects passed as *other is not a numeric type    def isnumerical(self, *other):        for input_value in other:            if not isinstance(input_value, (int, float, long)):                return False                break        return True    # Advaned flattener which handles all iterable object instances correctly including dictionaries    def flatten(self, other):        """Recursively Flatten numerical data from iterable data types to a list, ignore non-numerical data types except Classes that have __iter__ methods"""        result = []        for item in other:            if isinstance(item, (list, tuple, Primitive)):                result.extend(self.flatten(item))            elif isinstance(item, dict):                result.extend(self.flatten(item.items()))            elif self.isnumerical(item):                result.append(item)        return result    # Advanced flattener which does not flatten Object Instances but only type instances (list/tuple/dict), this is usefull for retaining object relationships    def flattenTypes(self, other):        result = []        for item in other:            if isinstance(item, Primitive) or self.isnumerical(item):                result.append(item)            elif isinstance(item, (list, tuple)):                result.extend(self.flattenTypes(item))            elif isinstance(item, dict):                result.extend(self.flatten(item.items()))            else:                raise TypeError , 'Object type ' + str(type(item)) + ' is not a valid argument for ' + str(type(self))        return result    def inputHandler(self, *other):        """Parses input objects/types and delegates the work of processing them to parseTypes which in turn uses iterateArguments"""        return self.parseTypes(other)    def parseTypes(self, other):        """Parses all types of iterable types/objects and returns an intelligently parsed set of Points"""        result = []        NewGroupFlag = True        # These things are a bit tricky, self recursing for loop which handles different types in a different way        for item in other:            if isinstance(item, Primitive):                result.append(item)            elif self.isnumerical(item) or isinstance(item, str):                if NewGroupFlag == True:                    # Data in a dictionary, first priority is data with  x/y/z as key values                    if isinstance(other, dict):                        coordinates = {'x':0.0,'y':0.0,'z':0.0}                        for key in ['x','X','y','Y','z','Z']:                            try:                                coordinates[key] = float(other[key])                                NewGroupFlag = False                            except KeyError:                               pass                        # If there were no x/y/z values in the keys table we parse the values in the orcer x/y/z which is traditional                        if NewGroupFlag == True:                            result.extend(self.iterateArguments(other.values(), coordinates ))                            NewGroupFlag = False                        elif NewGroupFlag == False:                            result.append(Point(coordinates['x'],coordinates['y'],coordinates['z']))                    else:                        # Contents of tuples/lists is handled here                        NewGroupFlag = False                        result.extend(self.iterateArguments(other))            elif isinstance(item, (list, tuple)):                # Recursively call myself to recurse into lists/tuples                result.extend(self.parseTypes(item))            elif isinstance(item, dict):                # Recursively call myself to recurse into dictionaries                result.extend(self.parseTypes(item))            else:                raise TypeError , 'Object type ' + str(type(item)) + ' is not a valid argument for ' + str(type(self))        return result    def iterateArguments(self, normalList, coordinates = {'x':0.0,'y':0.0,'z':0.0}):        """Takes a list/dict/tuple with coordinates in the form of {'x':0.0,'y':0.0,'z':0.0} and a list of normalized numerical values and returns a list of Point objects"""        # Make sure we aer dealing with a list because of wrap-around issues with slicing tuples        normalList = list(normalList)        result = []        iterations = 0                #Try to parse values in pairs of 3, if there are less we try 2, if that fails we use the one field only, order is assumed to be x,y,z        while iterations < len(normalList):            coordinates = {'x':0.0,'y':0.0,'z':0.0}             try:                coordinates = {'x':0.0,'y':0.0,'z':0.0}                 coordinates['x'] = normalList[iterations]                coordinates['y'] = normalList[iterations+1]                coordinates['z'] = normalList[iterations+2]            except IndexError:                try:                    coordinates['x'] = normalList[iterations]                    coordinates['y'] = normalList[iterations+1]                except IndexError:                    try:                        coordinates['x'] = normalList[iterations]                    except IndexError:                        # This is one of those "FUNNY" errors :P                        raise IndexError , 'This should only happen if for some reason a numerical/string value does not contain a value :P'            iterations += 3            result.append(Point(coordinates['x'],coordinates['y'],coordinates['z']))                    return result class Point(Primitive):    """A vertex/knot/vector/point object for a point in 3D space"""    # The native input is 0-3 floating point values, any other input will be directed to the inputHandler()        # When you get a point from a point it always returns itself    def __Point__(self, which = None, other = None):        return self    # Initial settings    def __init__(self, *arguments):        # Include shared init settings from Primitive        self.shared_init()        # If we don't get exactly three numerical arguments we let the inputHandler sort everything out        if len(arguments) == 3 and self.isnumerical(arguments[0]) and self.isnumerical(arguments[1]) and self.isnumerical(arguments[2]):            self.x = float(arguments[0])            self.y = float(arguments[1])            self.z = float(arguments[2])            self.data = [self.x,self.y,self.z]        else:            inputHandler(arguments)    # Return properly formatted string    def __str__(self):        return ' Point(' + str(self.x) + ', ' + str(self.y) + ', ' + str(self.z) + ')'    # Add two Points     def __add__(self, other):        return Point(self.x + other.__Point__().x, self.y + other.__Point__().y, self.z + other.__Point__().z)    # Subtract two Points    def __sub__(self, other):        return Point(self.x - other.__Point__().x, self.y - other.__Point__().y, self.z - other.__Point__().z)    # Dot product    def __mul__(self, other):        return Point(self.x * other.__Point__().x, self.y * other.__Point__().y, self.z * other.__Point__().z)    # Scalar multiplication    def __rmul__(self, other):        return Point(other.__Point__().y * self.x +  other.__Point__().y * self.y + other.__Point__().y * self.z)    # Division    def __div__(self, other):        return Point(self.x / other.__Point__().x , self.y / other.__Point__().y , self.z / other.__Point__().z)    # Modulo    def __mod__(self, other):        return Point(self.x % other.__Point__().x, self.y % other.__Point__().y, self.z % other.__Point__().z)    # Exponentiation    def __pow__(self, other):        return Point(self.x ** other.__Point__().x, self.y ** other.__Point__().y, self.z ** other.__Point__().z)    # Negative    def __neg__(self):        return Point(neg(self.x), neg(self.y), neg(self.z))    # Positive     def __pos__(self):        return Point(pos(self.x), pos(self.y), pos(self.z))    # Absolute    def __abs__(self):        return Point(abs(self.x), abs(self.y), abs(self.z))    # Compare two Points, 0 for same, 1 for self larger, -1 for other larger    def __cmp__(self, other):        if (self.x - other.x) == 0 and (self.y - other.y) == 0 and (self.z - other.z) == 0:            return 0        elif self.x > other.x and self.y > other.y and self.z > other.z:            return 1        else:            return -1            # Length of the "Vector" between 0,0,0 and the Point    def length(self):        return calculate_distance(self)    # Returns a point which is mid way between the two points     def average(self, other):        return Point(self.x - (self.x - other.x) / 2 ,self.y - (self.y - other.y) / 2 ,self.z - (self.z - other.z) / 2)    # Calculate the distances correctly to any object from a point, int(0)  is assumed to be Point(0,0,0)    def calculate_distance(self, other = CartesianZero()):            return sqrt((self.x - other.__Point__("start").x)**2 + (self.x - other.__Point__("start").y)**2 + (self.z - other.__Point__("start").z)**2)        class Line(Primitive):    """A line/ray/vector which is defined by two Points"""    # Return a specific point from a line, first/last/middle or closest to a point, if closest is invoked without a point it returns the closest to 0,0,0    def __Point__(self, which = 0, other = CartesianZero()):        if which in ["first", "start", "0", (0), [0]]:            return self.point0        elif which in ["last", "end", "-1", (-1), [-1]]:            return self.point1        elif which in ["center", "middle"]:            return self.point0.average(self.point1)        elif which in ["closest", "nearest", "near"] and isinstance(other, Point) or isinstance(other, CartesianZero):            # faulty logic, should return the point on the line that is closest but not the closest defining point            if self.point0.distance(other) > self.point1.distance(other):                return self.point0            else:                return self.point1        else:            PointTypeError()                # Initial settings    def __init__(self, point0, point1):        # Include shared init settings from Primitive        self.shared_init()        self.point0 = point0        self.point1 = point1        self.data = point0, point1    # Return properly formatted string    def __str__(self):        return ' Line[' + str(self[0]) + str(self[1]) + ']'    # Length of a line or the distance from an ray start to the vector defining point    def length(self):        return calculate_distance(self.point0,self.point1)class Curve(Primitive):    """A non-straight line defined as a NURBS curve"""    # All curves should be defined as NURBS curve knot and knot-vectors, aka Series of non-connected Rays/Lines    # Return a specific point from a curve, first/last/middle or closest to a point    def ___Point__(self, which = 0, other = CartesianZero()):        if which in ["first", "start", "0", (0), [0]]:            return self[0]        elif which in ["last", "end", "-1", (-1), [-1]]:            return self[-1]        elif which in ["center", "middle"]:            # Missing implementation            return self[0]        elif which in ["closest", "nearest", "near"] and isinstance(other, Point) or isinstance(other, CartesianZero):            # Missing implementation            return self[0]        else:            PointTypeError()                # Initial settings    def __init__(self, lines):        self.shared_init()        self.data = lines    # Return properly formatted string    def __str__(self):        return_string = list()        for id in range(len(self.data)):            return_string.append(self[id].__str__())        return ' Curve{' + str(return_string) + '}'    # Enable appending to the curve    def append(self, lines):        for line in lines:            self.data.append(line)    class Polyform(Primitive):    """A Geometric object consisting of any number of points listed in sequence"""    # A polyform is essentially a non-interpolated polyline which can be a solid    # Return a specific point from a polyform, first/last/middle or closest to a point    def __Point__(self, which = 0, other = CartesianZero()):        if which in ["first", "start", "0", (0), [0]]:            return self[0]        elif which in ["last", "end", "-1", (-1), [-1]]:            return self[-1]        elif which in ["center", "middle"]:            # Missing implementation            return self[0]        elif which in ["closest", "nearest", "near"] and isinstance(other, Point) or isinstance(other, CartesianZero):            # Missing implementation            return self[0]        else:            PointTypeError()                # Initial settings    def __init__(self, points):        # Include shared init settings from Primitive        self.shared_init()        self.data = points            # Return properly formatted string    def __str__(self):        return_string = list()        for id in range(len(self.data)):            return_string.append(self.data[id].__str__())        return str(return_string)    # Enable appending to the polyform    def append(self, point):        self.polyform.append(point)class Group(Primitive):    """A collection of Polyforms, Curves and Lines"""    # A group contains objects with a permanently fixed position relative to each other    # All objects including itself(group) can be nested within a group    # Groups where all objects form closed paths are considered to be solid objects or surfaces    # Return a specific point from a curve, first/last/middle or closest to a point    def __Point__(self, which, other = CartesianZero()):        if which in ["first", "start", "0", (0), [0]]:            newinstance = self            while isinstance(newinstance, Group):                newinstance = newinstance[0]            return newinstance.__Point__[0]        elif which in ["last", "end", "-1", (-1), [-1]]:            newinstance = self            while isinstance(newinstance, Group):                newinstance = newinstance[-1]            return newinstance.__Point__[-1]        elif which in ["center", "middle"]:            # Missing implementation            return self[0]        elif which in ["closest", "nearest", "near"] and isinstance(other, Point) or isinstance(other, CartesianZero):            # Missing implementation            return self[0]        else:            PointTypeError()                #Initial settings    def __init__(self, geoObjects):        # Include shared init settings from Primitive        self.shared_init()        self.data = geoObjects    # Return properly formatted string    def __str__(self):        return_string = list()        for id in range(len(self.data)):            return_string.append(self[id].__str__())        return ' Group<' + str(return_string) + '>'    # Enable appending to groups    def append(self, geoObject):        self.data.append(geoObject)################# TEST #####################print ""print "###################### TEST ###########################"print "Create Objects"print "     Create 10 points"mypoint0 = Point(0,0,0)mypoint1 = Point(10,10,10)mypoint2 = Point(20,20,20)mypoint3 = Point(30,20,30)mypoint4 = Point(40,20,30)mypoint5 = Point(50,20,30)mypoint6 = Point(60,20,30)mypoint7 = Point(70,20,30)mypoint8 = Point(80,20,30)mypoint9 = Point(90,20,30)print "     Create 5 lines"myline0 = Line(mypoint0,mypoint1)myline1 = Line(mypoint2,mypoint3)myline2 = Line(mypoint4,mypoint5)myline3 = Line(mypoint6,mypoint7)myline4 = Line(mypoint8,mypoint9)print "     Create 2 curves, one with 2 lines and the other with 3 lines"mycurve0 = Curve([myline0,myline1])mycurve1 = Curve([myline2,myline3,myline4])print "     Create 2 polyforms, one with 3 points and the other with 7 points"mypolyform0 = Polyform([mypoint0,mypoint1,mypoint2])mypolyform1 = Polyform([mypoint3,mypoint4,mypoint5,mypoint6,mypoint7,mypoint8,mypoint9])print "     Create 2 groups, one with 3 points, 2 lines, one curve and one polyline, the other everything including the other group"mygroup0 = Group([mypoint0,mypoint1,mypoint2,myline0,myline1,mycurve0,mypolyform0])mygroup1 = Group([mypoint0,mypoint1,mypoint2,mypoint3,mypoint4,mypoint5,mypoint6,mypoint7,mypoint8,mypoint9,myline0,myline1,myline2,myline3,myline4,mycurve0,mycurve1,mypolyform0,mypolyform1,mygroup0])print "Create two lists, a tuple and a dictionary, put list1 in another list, join it and list0 with tuple0 in a list, join these with dict0 in a tuple with mypoint3"mylist0 = [1,2,3]mylist1 = [4,5,6]mytuple0 = (7,8,9)mydict0 = {'jack': 4098, 'sape': 4139}flatteningtuple     = (([mylist0,mytuple0,[mylist1]],mydict0,mypoint3))print "Pretty formatting objects for display"newList = []for value in mypoint0.flattenTypes(flatteningtuple):    newList.append(str(value))mydict1 = {'x':1,'y':3,'z':7}mydict2 = {'a':0,'b':3,'c':7}mydict3 = {'a':0,'b':3}mydict4 = {'a':4}mydict5 = {'n':1,'r':3,'q':7,'a':5}mytuple = (1,45,2)mylist = [1,44,33]mylist1 = [67,47]mystring = "dsdfd"myint = 2myfloat = 23.6mylong = long(23423434232342343343234)def printPointInType(argument):    for item in (mypoint1.inputHandler(argument)):        return str(item)print ""print "Cast types to string"print "     mypoint0 is: " + str(mypoint0)print "     myline0 is: " + str(myline0)print "     mycurve0 is: " + str(mycurve0)print "     mypolyform0 is: " + str(mypolyform0)print "     mygroup0 is: " + str(mygroup0)print ""print "Test the __iter__ methods"print "     first float of mypoint3 is: " + str(mypoint3[0])print "     first point of myline3 is: " + str(myline3[0])print "     first line of mycurve1 is: " + str(mycurve1[0])print "     first point of mypolyform1 is: " + str(mypolyform1[0])print "     first object of mygroup1 is: " + str(mygroup1[0])print ""print "Complex Calc Test, mypoint1 multiplied by the first point in myline1 divided by mypoint9 to the power of mypoint1"print "     " + str(mypoint1 * myline1 / mypoint9 ** mypoint1)print ""print "Distance Calculations"print "     distance from mypoint0 to myline3: " + str(mypoint0.distance(myline3))print "     distance from mypoint0 to start of myline3 (should be same as before): " + str(mypoint0.distance(myline3[0]))print "     distance from mypoint1 to the middle of myline4: " + str(mypoint1.distance(myline4.__Point__("center")))print "     distance from mypoint9 to the closest point of myline1: " + str(mypoint9.distance(myline1.__Point__("closest")))print ""print "Flattening of hierarchies"print "     flattened the tuple of different sequential data types looks like: " + str(mypoint3.flatten(flatteningtuple))print "     flattened mygroup0 looks like: " + str(mygroup0.flatten(mygroup0))print "     flattened mygroup1 looks like: " + str(mygroup1.flatten(mygroup1))print "     flattening types only with flattenTypes looks like: " + str(mypoint0.flattenTypes(flatteningtuple))print "     flattening types only with flattenTypes with str formatting looks like: " + str(newList)print "Testing the InputHandler" print "     point from 3 int list: " + printPointInType(mylist)print "     point from 2 int list: " + printPointInType(mylist1)print "     point from 3 int tuple: " + printPointInType(mytuple)print "     point from 3 int dict in x,y,z format: " + printPointInType(mydict1)print "     point from 2 int dict in a,b,c, format: " + printPointInType(mydict2)print "     point from 2 int dict in a,b format:" + printPointInType(mydict3)print "     point from 1 int dict in 'a' format: " + printPointInType(mydict4)print "     point from 4 int dict in n,r,q,a format" + printPointInType(mydict5)print "     point from 1 int: " + printPointInType(myint)print "     point from 1 float: " + printPointInType(myfloat)print "     point from 1 long: " + printPointInType(mylong)