1
$\begingroup$

I have a problem with some files I've received. I have two sets of models, compartment bounding files (just empty boxes that represent a space) and some geometry that will go into those spaces. Worth mentioning I'm not very experiences with python or blender.

I am trying to write a script to loop through each bound file, and within that loop also loop through every piece of internal geometry to check whether or not the geometry is inside the current bound file. If a model is inside the bound, I want to write the bounding box name and the model name to a CSV, which I have figured out. I need to also account for bounding shapes such as L and H, and I'm not sure BVH can do this or not.

Here is what I have so far, based on what I found in another thread. The problem is this currently only checks for intersecting meshes on faces, verts, or edges I believe, and is not returning any models that are inside the bound box, but not touching the bound box.

import bpy
import mathutils
import bmesh
import csv

scene =  bpy.context.scene

BVHTree = mathutils.bvhtree.BVHTree

componentList = [obj for obj in bpy.data.collections['Collection'].objects]

boundsList = [obj for obj in bpy.data.collections['Bounds'].objects]

#show objects that are intersecting
def intersection_check():
    #create and open a csv
    csvfile = open(outputCSV, 'w')
    wr = csv.writer(csvfile, quoting=csv.QUOTE_ALL)
    
    #check every bounds object for intersection with every component object
    for bound in boundsList:
        for component in componentList:
            print()
            if bound == component:
                continue

            #create bmesh objects
            bm1 = bmesh.new()
            bm2 = bmesh.new()

            #fill bmesh data from objects
            bm1.from_mesh(scene.objects[bound.name].data)
            bm2.from_mesh(scene.objects[component.name].data)

            #applying location to the bmeshes
            bm1.transform(scene.objects[bound.name].matrix_world)
            bm2.transform(scene.objects[component.name].matrix_world)

            #make BVH tree from BMesh of objects
            bound_BVHtree = BVHTree.FromBMesh(bm1)
            component_BVHtree = BVHTree.FromBMesh(bm2)

            #get intersecting pairs
            inter = bound_BVHtree.overlap(component_BVHtree)

            #if list is empty, no objects are touching
            if inter != []:
                print(bound.name + " and " + component.name + " are touching!")
                wr.writerow([bound.name, component.name])
                
    # close the csv
    csvfile.close()

Update 7/21/2021:

I've found a method that works on a smaller subset of geometry but not my entire set, for some reason. I've also reduced runtime significantly with a few tweaks to the loops and how the component points are grabbed. Unfortunately I'm unable to share the files I'm working with, so I apologize for that.

Can anyone see any reason I'd be getting false positives with this? I grabbed the are_inside function from another post and I'm not completely sure how it works.

New code:

import bpy
import os
import mathutils
from mathutils import Vector
import bmesh
import csv

context = bpy.context

scene =  context.scene

BVHTree = mathutils.bvhtree.BVHTree

componentList = [obj for obj in bpy.data.collections['Collection'].objects]

boundList = [obj for obj in bpy.data.collections['Bounds'].objects]

def are_inside(points, bm):
    """
    input: 
        points
        - a list of vectors (can also be tuples/lists)
        bm
        - a manifold bmesh with verts and (edge/faces) for which the 
          normals are calculated already. (add bm.normal_update() otherwise)
    returns:
        a list
        - a mask lists with True if the point is inside the bmesh, False otherwise
    """

    rpoints = []
    addp = rpoints.append
    bvh = BVHTree.FromBMesh(bm, epsilon=0.00001)

    # return points on polygons
    for point in points:
        fco, normal, _, _ = bvh.find_nearest(point)
        p2 = fco - Vector(point)
        v = p2.dot(normal)
        addp(not v < 0.0)  # addp(v >= 0.0) ?

    return rpoints


def intersection_check(boundList, componentList):
    #create and open a csv
    csvfile = open(outputCSV, 'w', newline='')
    wr = csv.writer(csvfile, quoting=csv.QUOTE_ALL)

    #check every bound for intersection with every other object
    for bound in boundList:
        
        #create bmesh objects
        bm1 = bmesh.new()

        #fill bmesh data from objects
        bm1.from_mesh(scene.objects[bound.name].data)

        #applying location to the bmeshes
        bm1.transform(scene.objects[bound.name].matrix_world)

        bm1.normal_update()
        
        for component in componentList:
            #print()
            if bound == component:
                continue

            p = [scene.objects[component.name].location]
            
            if True in are_inside(p, bm1):
                print(component.name + " is inside " + bound.name + "!")
                wr.writerow([bound.name, component.name])

    print('Intersection Check Complete')     
    csvfile.close()
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.