I'm using Blender 2.82 and I'm trying to take body measurements from a 3d mesh of a human body. Right now I'm concentrating on measuring the perimeter of a certain body part, like waist, bust, hips, etc. To do that, I'm calculating the intersection between the mesh and a plane using a boolean modifier. But I'm not being successful.
This is my process so far:
The problem is that when I change to object view and view the result of the modifier, it doesn't look like an intersection at all on the top half of the mesh, but it works completely fine for the bottom half:
I thought that this was strange, so I tested the very same process but creating a plane using the function bpy.ops.mesh.primitive_plane_add() with the default location and rotation. The result of the intersection with this method is exactly what I want!
This brings me to my question number 1: Why doesn't my triangular plane intersect the mesh the same way as this last plane does? If it did, my problem was solved because I'm already able to correctly measure the perimeter of the intersection.
Since my 3 point plane was not working, I decided to extract the location/rotation of the triangular plane that I created and apply it to the new plane, but the problem is that at the end the rotation is not equal to both meshes.
Here is my code:
from mathutils import Vector,Matrix,Euler from math import atan2, degrees, radians import numpy as np import bmesh import bpy def get_face_euler_angle(obj): normal = obj.data.polygons.normal normal_vec = obj.matrix_world @ normal euler = obj.matrix_world.to_euler() return Euler(normal_vec) def setOriginToGeometry(object): bpy.context.view_layer.objects.active = object bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') def intersection(): bpy.ops.object.mode_set(mode='OBJECT') mesh = bpy.context.object # default vertex indices - for testing i0 = 5873 i1 = 6702 i2 = 9576 mesh.data.vertices[i0].select = True mesh.data.vertices[i1].select = True mesh.data.vertices[i2].select = True # Global coordinates of the variables p0 = list(mesh.matrix_world @ mesh.data.vertices[i0].co) p1 = list(mesh.matrix_world @ mesh.data.vertices[i1].co) p2 = list(mesh.matrix_world @ mesh.data.vertices[i2].co) p_avg = np.mean([p0, p1, p2], axis=0) # Create a triangular face defined by the 3 selected vertices triangular_plane = bpy.data.meshes.new("Plane") triangular_plane.from_pydata([p0, p1, p2], [[0,1], [0,2], [1,2]], [[0,1,2]]) triangular_plane.update() triangular_plane_obj = bpy.data.objects.new("Plane", triangular_plane) bpy.data.collections.objects.link(triangular_plane_obj) # Select plane to scale bpy.ops.object.mode_set(mode='OBJECT') mesh.data.vertices[i0].select = False mesh.data.vertices[i1].select = False mesh.data.vertices[i2].select = False setOriginToGeometry(triangular_plane_obj) # Change origin to the center of the plane to scale setOriginToGeometry(triangular_plane_obj) bpy.ops.object.mode_set(mode='EDIT') # Scale the plane # https://blender.stackexchange.com/questions/58397/scaling-works-differently-in-gui-and-from-script # # It's a context issue and you need to Override the Context. # The pivot point is a member of the 3D_VIEW space. In code below would be area.spaces.active.pivot_point # Example of overriding the context which "fools" the operator into thinking it is being run from the 3D # view, not the text editor. override = bpy.context.copy() for area in bpy.context.screen.areas: if area.type == 'VIEW_3D': override["area"] = area override["space_data"] = area.spaces.active override["region"] = area.regions[-1] break bpy.ops.transform.resize(override, value=(20.0,20.0,20.0), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False, snap_target='MEDIAN') # Boolean modifier on triangular plane (intersect) modifier = triangular_plane_obj.modifiers.new(name='modifier_intersect', type='BOOLEAN') modifier.object = mesh modifier.operation = 'INTERSECT' # Now it's possible to see the effect that the boolean modifier has on the mesh # Since the modifier doesn' work, I want to add a new plane to the scene with the same location and # rotation as triangular_plane_obj triangular_plane_euler = get_face_euler_angle(triangular_plane_obj) print('triangular_plane_obj Euler', triangular_plane_euler) # Create Plane.001 bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.mesh.primitive_plane_add(enter_editmode=False, location=p_avg, rotation=triangular_plane_euler) plane = bpy.data.objects['Plane.001'] plane_euler = get_face_euler_angle(plane) print('plane_obj Euler', plane_euler) # Code to measure the perimeter
The output is
triangular_plane_obj Euler <Euler (x=0.2274, y=0.0125, z=-0.9737), order='XYZ'> plane_obj Euler <Euler (x=-0.1665, y=-0.0987, z=1.6984), order='XYZ'>
What am I doing wrong?