Example #1
0
class ParticleManager:
    _particle_types = {}

    @classmethod
    def register_particle_type(cls, type, name):
        cls._particle_types[name] = type

    @classmethod
    def unregister_particle_type(cls, name):
        del cls._particle_types[name]

    def __init__(self, context):
        self.obj = context.active_object
        self.particles = []
        self.edges = []
        self.draw_layer = grease_draw.StrokeLayer(context)
        self.kd_tree = None

    def create_particle(self, type, location, radius=0.03):
        if type in self._particle_types:
            p = self._particle_types[type](radius, location, self)
            self.particles.append(p)
            return p
        else:
            raise KeyError("No such particle registered: %s" % type)

    def build_kd_tree(self):
        self.kd_tree = KDTree(len(self.particles))
        for index, particle in enumerate(self.particles):
            self.kd_tree.insert(particle.location, index)
        self.kd_tree.balance()

    def nearest_n_particles(self, location, n):
        for location, index, distance in self.kd_tree.find_n(location, n):
            particle = self.particles[index]
            yield particle, distance

    def nearest_n_tag_particles(self, location, n, tag):
        def tag_filter(i):
            return self.particles[i].tag is tag

        for location, index, distance in self.kd_tree.find_n(
                location, n, filter=tag_filter):
            particle = self.particles[index]
            yield particle, distance

    def step(self, speed):
        for particle in self.particles:
            particle.step(speed)

    def sample_obj(self, location):
        return self.obj.closest_point_on_mesh(location)
Example #2
0
def connect_verts(bm, z_idx, v1, verts2_bm, connections, max_rho):
    tree = KDTree(len(verts2_bm))
    for i, v2 in enumerate(verts2_bm):
        tree.insert(v2.co, i)
    tree.balance()

    for co, i, dist in tree.find_n(v1.co, connections):
        if dist <= max_rho:
            v2 = verts2_bm[i]
            if bm.edges.get((v1, v2)) is None:
                bm.edges.new((v1, v2))
                bm.edges.ensure_lookup_table()
Example #3
0
    def repeal_particles(self, iterations=20, factor=0.01):
        particles = list(self.particles)
        tree = KDTree(len(particles))
        for index, particle in enumerate(particles):
            tree.insert(particle.co, index)
        tree.balance()

        for i in range(iterations):
            new_tree = KDTree(len(self.particles))
            for index, particle in enumerate(particles):
                if particle.tag in {"SHARP", "GREASE"}:
                    continue

                d = Vector()

                for loc, other_index, dist in tree.find_n(particle.co, 3):
                    if dist == 0:
                        continue
                    other = particles[other_index]
                    vec = particle.co - other.co

                    d += (vec / (dist ** 3))

                    if not self.triangle_mode:
                        u = particle.dir
                        v = u.cross(particle.normal)
                        for vec in (u + v, u - v, -u + v, -u - v):
                            vec *= particle.radius
                            vec += other.co
                            vec -= particle.co
                            dist = vec.length
                            d -= vec * 0.3 / (dist ** 3)

                d.normalize()
                location, normal, dir, s, c = self.field.sample_point(particle.co + (d * factor * particle.radius))
                if location:
                    particle.co = location
                    particle.normal = normal
                    self.grid.update(particle)
                    particle.dir = dir

                new_tree.insert(particle.co, index)
            new_tree.balance()
            tree = new_tree

            yield i
Example #4
0
def unique_points(points, eps=1e-4):
    kdt = KDTree(len(points))
    for i, p in enumerate(points):
        kdt.insert(p, i)
    kdt.balance()
    unique = []
    repeating = []
    mask = []
    for p in points:
        found = kdt.find_n(p, 2)
        if len(found) > 1:
            loc, idx, distance = found[1]
            ok = distance > eps
            mask.append(ok)
            if ok:
                unique.append(p)
            else:
                repeating.append(p)
    return mask, unique, repeating
    def finish(self, context):
        #ray cast the entire grid into
        
        if 'Posterior Plane' in bpy.data.objects:
            Plane = bpy.data.objects['Posterior Plane']
            Plane.hide = False
                
        else:
            me = bpy.data.meshes.new('Posterior Plane')
            Plane = bpy.data.objects.new('Posterior Plane', me)
            context.scene.objects.link(Plane)
        
        
        pbme = bmesh.new()
        pbme.verts.ensure_lookup_table()
        pbme.edges.ensure_lookup_table()
        pbme.faces.ensure_lookup_table()
        bmesh.ops.create_grid(pbme, x_segments = 200, y_segments = 200, size = 39.9)
        pbme.to_mesh(Plane.data)
        
        pt, pno = calculate_plane(self.crv.b_pts)
        
        if self.splint.jaw_type == 'MANDIBLE':
            Zw = Vector((0,0,-1))
            Xw = Vector((1,0,0))
            Yw = Vector((0,-1,1))
            
        else:
            Zw = Vector((0,0,1))
            Xw = Vector((1,0,0))
            Yw = Vector((0,1,0))
            
        Z = pno
        Z.normalize()
        
        if Zw.dot(Z) < 0:
            Z *= -1
            
        Y = Z.cross(Xw)
        X = Y.cross(Z)
            
        
        R = Matrix.Identity(3)  #make the columns of matrix U, V, W
        R[0][0], R[0][1], R[0][2]  = X[0] ,Y[0],  Z[0]
        R[1][0], R[1][1], R[1][2]  = X[1], Y[1],  Z[1]
        R[2][0] ,R[2][1], R[2][2]  = X[2], Y[2],  Z[2]
        
        R = R.to_4x4()
        T = Matrix.Translation(pt - 5 * Z)
        
        Plane.matrix_world = T * R
    
        pmx = Plane.matrix_world
        ipmx = pmx.inverted()
        
        bme_pln = bmesh.new()
        bme_pln.from_mesh(Plane.data)
        bme_pln.verts.ensure_lookup_table()
        bme_pln.edges.ensure_lookup_table()
        bme_pln.faces.ensure_lookup_table()
        bvh = BVHTree.FromBMesh(bme_pln)
        
        
        #we are going to raycast the user world coordinate points
        #into a grid, and identify all points in the grid from the local Z direction
        #Then we will store the local location of the user picked coordinate in a dictionary
        key_verts = {}
        
        for loc in self.crv.b_pts:

            res = bvh.ray_cast(ipmx * loc, -Z, 30)
            if res[0] != None:
                
                f = bme_pln.faces[res[2]]
                for v in f.verts:
                    key_verts[v] = ipmx * loc
                    v.select_set(True)
                
                continue
            
            res = bvh.ray_cast(ipmx * loc, Z, 30)
            if res[0] != None:
                
                f = bme_pln.faces[res[2]]
                for v in f.verts:
                    key_verts[v] = ipmx * loc
                    v.select_set(True)
                
                continue
        
        #bme_pln.to_mesh(Plane.data)
        #bme_pln.free()
        #return
        kdtree = KDTree(len(key_verts))
        for v in key_verts.keys():
            kdtree.insert(v.co, v.index)
        
        kdtree.balance()
        
        #raycast  the shell if we can
        raycast_shell = False
        if 'Splint Shell' in bpy.data.objects:
            shell = bpy.data.objects.get('Splint Shell')
            bvh_shell = BVHTree.FromObject(shell, context.scene)
            mx_shell = shell.matrix_world
            imx_shell = mx_shell.inverted()
            Z_shell = imx_shell.to_3x3()*Z
            raycast_shell = True
            
        
        right_side = set()
        left_side = set()
        ray_casted = set()
        
        to_delete = set()
        
        for v in bme_pln.verts:
            if v in key_verts:
                v.co[2] = key_verts[v][2]
               
                if v.co[1] > 0:
                    left_side.add(v)
                else:
                    right_side.add(v)
                continue
                
            results = kdtree.find_range(v.co, .5)
            if len(results):
                N = len(results)
                r_total = 0
                v_new = Vector((0,0,0))
                for res in results:
                    r_total += 1/res[2]
                    v_new += (1/res[2]) * key_verts[bme_pln.verts[res[1]]]
                        
                v_new *= 1/r_total
                v.co[2] = v_new[2]
                if v.co[1] > 0:
                    left_side.add(v)
                else:
                    right_side.add(v)
                continue
                        
            results = kdtree.find_range(v.co, 6)
            if len(results):
                N = len(results)
                r_total = 0
                v_new = Vector((0,0,0))
                for res in results:
                    r_total += (1/res[2])**2
                    v_new += ((1/res[2])**2) * key_verts[bme_pln.verts[res[1]]]
                        
                v_new *= 1/r_total
                v.co[2] = v_new[2]
                if v.co[1] > 0:
                    left_side.add(v)
                else:
                    right_side.add(v)
                continue
            
            loc, no, index, d = bvh_shell.ray_cast(imx_shell * pmx * v.co, Z_shell)
            if loc:
                
                ray_casted.add(v)
                results = kdtree.find_n(v.co, 4)
                N = len(results)
                r_total = 0
                v_new = Vector((0,0,0))
                for res in results:
                    r_total += (1/res[2])**2
                    v_new += ((1/res[2])**2) * key_verts[bme_pln.verts[res[1]]]
                        
                v_new *= 1/r_total
                v.co[2] = v_new[2]
                continue

        total_verts = ray_casted | left_side | right_side
        
        ant_left = max(left_side, key = lambda x: x.co[0])
        ant_right = max(right_side, key = lambda x: x.co[0])
        
        new_verts = set()
        dilation_verts = set()  
        for v in total_verts:
            for ed in v.link_edges:
                v_new = ed.other_vert(v)
                if v_new in total_verts or v_new in new_verts: 
                    continue
                else:
                    new_verts.add(v_new)
                    
        print('adding %i new verts' % len(new_verts))
        
        
        total_verts.update(new_verts)
        dilation_verts.update(new_verts)
        
        #for v in ray_casted:
        #    if v.co[1] > 0:
        #        if v.co[0] > ant_left.co[0]:
        #            to_delete.add(v)
        #    else:
        #        if v.co[0] > ant_right.co[0]:
        #            to_delete.add(v)
        
        #print('added %i ray_casted' % len(ray_casted))
        #total_verts = ray_casted | left_side | right_side
        #total_verts.difference_update(to_delete)       
        
        #new_verts = set()   
        #for v in total_verts:
        #    for ed in v.link_edges:
        #        v_new = ed.other_vert(v)
        #        if v_new in total_verts: continue
                
        #        if v_new.co[1] > 0 and v_new.co[0] < ant_left.co[0]:
        #            if v in to_delete:
        #                new_verts.add(v)
        #        if v_new.co[1] <= 0 and v_new.co[0] < ant_right.co[0]:
        #            if v in to_delete:
        #                new_verts.add(v)   
        
        #to_delete.difference_update(new_verts)
        
        #print('adding %i new verts' % len(new_verts))   
        for j in range(0,3):
            newer_verts = set()  
            for v in new_verts:
                for ed in v.link_edges:
                    v_new = ed.other_vert(v)
                    if v_new in total_verts or v_new in newer_verts:
                        continue
                     
                    newer_verts.add(v_new)
                    
            
                       
            total_verts.update(newer_verts)
            dilation_verts.update(newer_verts)
            new_verts = newer_verts
        
        to_delete = set(bme_pln.verts[:]) - total_verts
        
        #filter out anteior dilation
        for v in dilation_verts:
            
            if v.co[1] > 0 and v.co[0] > ant_left.co[0]:
                to_delete.add(v)
                continue
            if v.co[1] <= 0 and v.co[0] > ant_right.co[0]:
                to_delete.add(v)
                continue
                
             
            results = kdtree.find_n(v.co, 4)
            N = len(results)
            r_total = 0
            v_new = Vector((0,0,0))
            for res in results:
                r_total += (1/res[2])**2
                v_new += ((1/res[2])**2) * key_verts[bme_pln.verts[res[1]]]
                        
            v_new *= 1/r_total
            v.co[2] = v_new[2]
            
        #filter out anteior dilation
        for v in ray_casted:
            if v.co[1] > 0 and v.co[0] > ant_left.co[0]:
                to_delete.add(v)
                continue
            if v.co[1] <= 0 and v.co[0] > ant_right.co[0]:
                to_delete.add(v)
                continue
                            
        bmesh.ops.delete(bme_pln, geom = list(to_delete), context = 1)
        bme_pln.to_mesh(Plane.data)
        Plane.data.update()
        
        smod = Plane.modifiers.new('Smooth', type = 'SMOOTH')
        smod.iterations = 5
        smod.factor = 1
        
        self.splint.ops_string += 'Mark Posterior Cusps:'
Example #6
0
class ParticleManager:
    def __init__(self, obj):
        self.particles = []
        self.obj = obj
        self.field = vector_fields.FrameField(obj)

        self.inv_mat = obj.matrix_world.inverted()

        self.bm = self.field.bm
        self.kd_tree = KDTree(0)
        self.kd_tree.balance()
        self.draw_obj = draw_3d.DrawObject()

        self.triangle_mode = False

    def build_field(self, context, use_gp, x_mirror):
        self.field.build_major_curvatures()
        frame = get_gp_frame(context)
        if frame:
            self.field.from_grease_pencil(frame,
                                          mat=self.inv_mat,
                                          x_mirror=x_mirror)
            self.field.marching_growth()
            self.field.smooth(2)

        else:
            self.field.erase_part(2)
            self.field.marching_growth()
            self.field.smooth()

    def build_kdtree(self):
        tree = KDTree(len(self.particles))
        for id, p in enumerate(self.particles):
            tree.insert(p.location, id)
        tree.balance()
        self.kd_tree = tree

    def initialize_particles_from_gp(self, resolution, adaptive, context):
        scale = max(self.obj.dimensions) / max(self.obj.scale)
        target_resolution = scale / resolution
        frame = get_gp_frame(context)
        created_particles = 0
        if not frame:
            return created_particles
        for stroke in frame.strokes:
            co = self.inv_mat * stroke.points[0].co
            last_particle = self.create_particle(Partile, co)
            last_particle.target_resolution = target_resolution
            last_particle.radius = target_resolution / (
                last_particle.last_hit.curvature * adaptive + (1 - adaptive))
            last_particle.adaptive = adaptive
            created_particles += 1
            for point in stroke.points:
                co = self.inv_mat * point.co
                if (co - last_particle.location
                    ).length >= last_particle.radius * 2:
                    last_particle = self.create_particle(Partile, co)
                    last_particle.target_resolution = target_resolution
                    last_particle.radius = target_resolution / (
                        last_particle.last_hit.curvature * adaptive +
                        (1 - adaptive))
                    last_particle.adaptive = adaptive
                    created_particles += 1
        return created_particles

    def initialize_from_features(self,
                                 verts,
                                 resolution=20,
                                 adaptive=0,
                                 count=50):
        scale = max(self.obj.dimensions) / max(self.obj.scale)
        target_resolution = scale / resolution
        verts = sorted(self.field.bm.verts,
                       key=lambda v: self.field.sharpness_field.get(
                           v.index, float("inf")),
                       reverse=True)
        for i in range(min(count, len(self.field.bm.verts))):
            vert = verts[i]
            co = vert.co.copy()
            p1 = self.create_particle(Partile, co)
            p1.radius = target_resolution
            p1.target_resolution = target_resolution
            p1.adaptive = adaptive

    def initialize_from_verts(self, verts, adaptive):
        for vert in verts:
            p = self.create_particle(Partile, vert.co)
            p.adaptive = adaptive

    def initialize_grid(self,
                        verts,
                        resolution=20,
                        use_x_mirror=True,
                        adaptive=0):
        particle_locations = set()
        scale = max(self.obj.dimensions)
        target_resolution = 1 / ((1 / scale) * resolution)
        for vert in verts:
            co = vert.co.copy()
            co /= scale
            co *= resolution
            x = int(co.x)
            y = int(co.y)
            z = int(co.z)
            if use_x_mirror:
                if x > 0:
                    particle_locations.add((x, y, z))
            else:
                particle_locations.add((x, y, z))

        for location in particle_locations:
            co = Vector(location)
            co *= scale
            co /= resolution
            hit = self.sample_surface(co)
            p1 = self.create_particle(Partile, hit.co)
            p1.adaptive = adaptive
            p1.target_resolution = target_resolution
            if use_x_mirror:
                hit.co.x *= -1
                p2 = self.create_particle(Partile, hit.co)
                p2.adaptive = adaptive
                p2.target_resolution = target_resolution
                p1.counter_pair, p2.counter_pair = p2, p1

    def mirror_particles(self, any_side=False):
        new_particles = []
        for particle in self.particles:
            if particle.location.x > particle.radius or any_side:
                co = particle.location.copy()
                co.x *= -1
                p1 = particle
                p2 = Partile(co, self)
                p2.radius = p1.radius
                p2.tag = p1.tag
                p2.adaptive = p1.adaptive
                p2.target_resolution = p1.target_resolution
                p1.counter_pair, p2.counter_pair = p2, p1
                new_particles.append(p2)
                new_particles.append(p1)

            elif -particle.radius * 0.5 < particle.location.x < particle.radius * 0.5:
                new_particles.append(particle)
                particle.lock_x = True

        self.particles = new_particles
        self.build_kdtree()

    def create_particle(self, type, location, prepend=False):
        p = type(location, self)
        if not prepend:
            self.particles.append(p)
        else:
            self.particles.insert(0, p)
        return p

    def remove_particle(self, particle):
        self.particles.remove(particle)

    def step(
        self,
        speed,
    ):
        new_tree = KDTree(len(self.particles))
        self.draw_obj.commands.clear()
        for id, particle in enumerate(self.particles):
            particle.step(speed)
            particle.draw()
            new_tree.insert(particle.location, id)
        new_tree.balance()
        self.kd_tree = new_tree

    def spread_step(self):
        count = 0
        new_particles = []
        self.draw_obj.commands.clear()

        for particle in self.particles:
            new_particles += particle.spread()
            count += len(new_particles)

        for particle in self.particles:
            if not particle.tag == "REMOVE":
                new_particles.append(particle)
        self.particles = new_particles

        new_tree = KDTree(len(self.particles))
        for id, particle in enumerate(self.particles):
            particle.draw()
            new_tree.insert(particle.location, id)
        new_tree.balance()
        self.kd_tree = new_tree

        return count

    def get_nearest(self, location, n):
        for location, index, dist in self.kd_tree.find_n(location, n):
            yield self.particles[index], dist

    def sample_surface(self, location):
        return self.field.sample_point(location)

    def draw(self):
        self.draw_obj.commands.clear()
        for particle in self.particles:
            particle.draw()

    def simplify_mesh(self, bm):
        class Ownership:
            def __init__(self, particle, dist):
                self.particle = particle
                self.distance = dist
                self.valid = False

        bmesh.ops.triangulate(bm, faces=bm.faces)
        last_edges = float("+inf")
        while True:
            edges = set()
            for edge in bm.edges:
                le = (edge.verts[0].co - edge.verts[1].co).length_squared
                center = edge.verts[0].co + edge.verts[1].co
                center /= 2
                for p, dist in self.get_nearest(center, 1):
                    if p.radius**2 < le:
                        edges.add(edge)
            if not len(edges) < last_edges:
                break
            last_edges = len(edges)
            bmesh.ops.subdivide_edges(bm, edges=list(edges), cuts=1)
            bmesh.ops.triangulate(bm, faces=bm.faces)

        bm.faces.ensure_lookup_table()
        bm.verts.ensure_lookup_table()
        tree = KDTree(len(bm.verts))
        for vert in bm.verts:
            tree.insert(vert.co, vert.index)
        tree.balance()

        ownership_mapping = {}
        ownership_validation_front = set()

        for vert in bm.verts:
            for p, dist in self.get_nearest(vert.co, 1):
                ownership_mapping[vert] = Ownership(p, dist)

        for particle in self.particles:
            location, index, dist = tree.find(particle.location)
            vert = bm.verts[index]
            if vert in ownership_mapping:
                if ownership_mapping[vert].particle == particle:
                    ownership_mapping[vert].valid = True
                    ownership_validation_front.add(vert)

        while True:
            new_front = set()
            for vert in ownership_validation_front:
                for edge in vert.link_edges:
                    other_vert = edge.other_vert(vert)
                    if other_vert not in ownership_mapping:
                        continue
                    if ownership_mapping[other_vert].valid:
                        continue
                    if other_vert in ownership_mapping:
                        if ownership_mapping[
                                vert].particle is ownership_mapping[
                                    other_vert].particle:
                            new_front.add(other_vert)
                            ownership_mapping[other_vert].valid = True
            ownership_validation_front = new_front
            if not new_front:
                break

        new_bm = bmesh.new()
        for particle in self.particles:
            particle.vert = new_bm.verts.new(particle.location)

        for face in bm.faces:
            connections = set()
            for vert in face.verts:
                if vert in ownership_mapping:
                    if ownership_mapping[vert].valid:
                        p = ownership_mapping[vert].particle
                        connections.add(p)
            if len(connections) == 3:
                try:
                    new_bm.faces.new(
                        [particle.vert for particle in connections])
                except ValueError:
                    pass
        while True:
            stop = True
            for vert in new_bm.verts:
                if len(vert.link_edges) < 3:
                    new_bm.verts.remove(vert)
                    stop = False
            if stop:
                break

        bmesh.ops.holes_fill(new_bm, edges=new_bm.edges)
        bmesh.ops.triangulate(new_bm, faces=new_bm.faces)
        bmesh.ops.recalc_face_normals(new_bm, faces=new_bm.faces)
        if not self.triangle_mode:
            bmesh.ops.join_triangles(new_bm,
                                     faces=new_bm.faces,
                                     angle_face_threshold=1.0,
                                     angle_shape_threshold=3.14)

        return new_bm