LAK132 icon

csg.py

LAK132 | PRO | 03/06/17 12:52:40 AM UTC | 0 ⭐ | 405 👁️ | Never ⏰ | []
Python |

9.96 KB

|

None

|

0 👍

/

0 👎

class CSG(object):
    def __init__(self):
        self.polygons = []
    
    def fromPolygons(polygons):
        csg = CSG()
        csg.polygons = polygons
        return csg
        
    def clone(self):
        csg = CSG()
        csg.polygons = copy.deepcopy(self.polygons)
        return csg
    
    def toPolygons(self):
        return self.polygons
        
    
    def union(self, csg):
        a = CSG_Node(self.clone().polygons)
        b = CSG_Node(csg.clone().polygons)
        a.clipTo(b)
        b.clipTo(a)
        b.invert()
        b.clipTo(a)
        b.invert()
        a.build(b.allPolygons())
        return CSG.fromPolygons(a.allPolygons())
        
    def subtract(self, csg):
        a = CSG_Node(self.clone().polygons)
        b = CSG_Node(csg.clone().polygons)
        a.invert()
        a.clipTo(b)
        b.clipTo(a)
        b.invert()
        b.clipTo(a)
        b.invert()
        a.build(b.allPolygons())
        a.invert()
        return CSG.fromPolygons(a.allPolygons())
        
    def intersect(self, csg):
        a = CSG_Node(self.clone().polygons)
        b = CSG_Node(csg.clone().polygons)
        a.invert()
        b.clipTo(a)
        b.invert()
        a.clipTo(b)
        b.clipTo(a)
        a.build(b.allPolygons())
        a.invert()
        CSG.fromPolygons(a.allPolygons())
        
    def inverse(self):
        csg = self.clone()
        for poly in csg.polygons:
            poly.flip()
        return csg
        
    def CSG_Cube(self, origin, size):
        s = [1.0, 1.0, 1.0]
        if len(size) == 3:
            s[0] = size[0]
            s[1] = size[1]
            s[2] = size[2]
        elif 'x' in size:
            s[0] = size.x
            s[1] = size.y
            s[2] = size.z
        c = CSG_Vector(origin)
        xNorm = 1 if s[0] >= 0 else -1
        yNorm = 1 if s[1] >= 0 else -1
        zNorm = 1 if s[2] >= 0 else -1
        a = CSG.fromPolygons([
            [[0, 4, 6, 2], [-xNorm, 0, 0]],
            [[1, 3, 7, 5], [xNorm, 0, 0]],
            [[0, 1, 5, 4], [0, -yNorm, 0]],
            [[2, 6, 7, 3], [0, yNorm, 0]],
            [[0, 2, 3, 1], [0, 0, -zNorm]],
            [[4, 5, 7, 6], [0, 0, zNorm]]])
        b = []
        for poly in a.toPolygons():
            d = []
            for i in poly[0]:
                pos = CSG_Vector(
                    c.x + (s[0] if i & 1 != 0 else 0),
                    c.y + (s[1] if i & 2 != 0 else 0),
                    c.z + (s[2] if i & 4 != 0 else 0)
                )
                d.append(CSG_Vertex(pos, CSG_Vector(poly[1])))
            b.append(CSG_Polygon(d, None))
        self.polygons = b
               
class CSG_Vector(object):
    def __init__(self, *arg):
        if len(arg) == 3:
            self.x = arg[0]
            self.y = arg[1]
            self.z = arg[2]
        elif hasattr(arg[0], 'x'):
            self.x = arg[0].x
            self.y = arg[0].y
            self.z = arg[0].z
        else:
            self.x = arg[0][0]
            self.y = arg[0][1]
            self.z = arg[0][2]
        
    def clone(self):
        return CSG_Vector(self.x, self.y, self.z)
    
    def negated(self):
        return CSG_Vector(-sefl.x, -self.y, -self.z)
    
    def plus(self, a):
        return CSG_Vector(self.x + a.x, self.y + a.y, self.z + a.z)
    
    def minus(self, a):
        return CSG_Vector(self.x - a.x, self.y - a.y, self.z - a.z)
    
    def times(self, a):
        return CSG_Vector(self.x * a, self.y * a, self.z * a)
    
    def dividedBy(self, a):
        return CSG_Vector(self.x / a, self.y / a, self.z / a)
    
    def dot(self, a):
        return ((self.x * a.x) + (self.y * a.y) + (self.z * a.z))
    
    def lerp(self, a, t):
        return self.plus(a.minus(self).times(t))
        
    def length(self):
        return math.sqrt(self.dot(self))
        
    def unit(self):
        return self.dividedBy(self.length())
        
    def cross(self, a):
        return CSG_Vector((self.y * a.z) - (self.z * a.y), (self.z * a.x) - (self.x * a.z), (self.x * a.y) - (self.y * a.x))
    
class CSG_Vertex(object):
    def __init__(self, pos, normal):
        self.pos = CSG_Vector(pos)
        self.normal = CSG_Vector(normal)
    
    def clone(self):
        return CSG_Vertex(copy.deepcopy(self.pos), copy.deepcopy(self.normal))
        
    def flip(self):
        self.normal = self.normal.negated()
        
    def interpolate(self, other, t):
        return CSG_Vertex(self.pos.lerp(other.pos, t), self.normal.lerp(other.normal, t))
        
class CSG_Plane(object):
    def __init__(self, normal, w):
        self.normal = normal
        self.w = w
        
    EPSILON = 1e-5
       
    def fromPoints(a, b, c):
        n = b.minus(a).cross(c.minus(a)).unit()
        return CSG_Plane(n, n.dot(a))
        
    def clone(self):
        return CSG_Plane(copy.deepcopy(self.normal), copy.copy(self.w))
        
    def flip(self):
        self.normal = self.normal.negated()
        self.w = -self.w
        
    def splitPolygon(self, polygon, coplanarFront, coplanarBack, front, back):
        COPLANAR = 0
        FRONT = 1
        BACK = 2
        SPANNING = 3
        
        polygonType = 0
        types = []
        for index in range(len(polygon.vertices)):
            t = self.normal.dot(polygon.vertices[index].pos) - self.w
            type = (BACK if t < -GSC_Plane.EPSILON else (FRONT if t > CSG_Plane.EPSILON else COPLANAR))
            polygonType |= type
            types.append(type)
        
        if polygonType == COPLANAR:
            if self.normal.dot(polygon.plane.normal) > 0:
                coplanarFront.append(polygon)   #These should modify the list outside of this scope as well
            else:
                coplanarBack.append(polygon)
        elif polygonType == FRONT:
            front.append(polygon)   
        elif polygonType == BACK:
            back.append(polygon)
        elif polygonType == SPANNING:
            f = []
            b = []
            for index in range(len(polygon.vertices)):
                j = (i + 1) % len(polygon.vertices)
                ti = types[i]
                tj = types[j]
                vi = polygon.vertices[i]
                vj = polygon.vertices[j]
                if ti != BACK:  
                    f.append(vi)
                if ti != FRONT:
                    b.append(vi.clone() if ti != BACK else vi)
                if (ti | tj) == SPANNING:
                    t = (self.w + self.normal.dot(vi.pos)) / self.normal.dot(vj.pos.minus(vi.pos))
                    v = vi.interpolated(vj, t)
                    f.append(v)
                    b.append(v.clone())
            if len(f) >= 3:
                front.append(CSG_Polygon(f, polygon.shared))
            if len(b) >= 3:
                back.append(CSG_Polygon(b, polygon.shared))
                
class CSG_Polygon(object):
    def __init__(self, vertices, shared):
        self.vertices = vertices
        self.shared = shared
        self.plane = CSG_Plane.fromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos)
    
    def clone(self):
        vertices = copy.deepcopy(self.vertices)
        return CSG_Polygon(vertices, self.shared)
    
    def flip(self):
        self.vertices.reverse()
        for vert in self.vertices:
            vert.flip()
        self.plane.flip()
        
class CSG_Node(object):
    def __init__(self, *args):
        self.plane = None
        self.front = None
        self.back = None
        self.polygons = []
        if len(args) == 1:
            self.build(args[0])
    
    def clone(self):
        node = CSG_Node()
        node.plane = None if self.plane is None else self.plane.clone()
        node.front = None if self.front is None else self.front.clone()
        node.back = None if self.back is None else self.back.clone()
        node.polygons = copy.deepcopy(self.polygons)
        return node
        
    def invert(self):
        for index in range(len(self.polygons)):
            self.polygons[index].flip()
        self.plane.flip()
        if self.front is not None:
            self.front.invert()
        if self.back is not None:
            self.back.invert()
        temp = self.front
        self.front = self.back
        self.back = temp
        
    def clipPolygon(self, polygons):
        if self.plane is None:
            return polygons[:]
        front = []
        back = []
        for index in range(len(polygons)):
            self.plane.splitPolygon(polygons[index], front, back, front, back)
        if self.front is not None:
            front = self.front.clipPolygons(front)
        if self.back is not None:
            back = self.back.clipPolygons(back)
        else:
            back = []
        front += back
        return front
        
    def clipTo(self, bsp):
        self.polygons = bsp.clipPolygons(self.polygons)
        if self.front is not None:
            self.front.clipTo(bsp)
        if self.back is not None:
            self.back.cliptTo(bsp)
            
    def allPolygons(self):
        polygons = self.polygons[:]
        if self.front is not None:
            polygons += self.front.allPolygons()
        if self.back is not None:
            polygons += self.back.allPolygons()
        return polygons
    
    def build(self, polygons):
        if not len(polygons):
            return
        if self.plane is None:
            self.plane = polygons[0].plane.clone()
        front = []
        back = []
        for index in range(len(polygons)):
            self.plane.splitPolygon(polygons[i], self.polygons, self.polygons, front, back)
        if len(front):
            if self.front is None:
                self.front = CSG_Node()
                self.front.build(front)
        if len(back):
            if self.back is None:
                self.back = CSG_Node()
                self.back.build(back)

Comments