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:
I created the plane by selecting 3 points of the mesh and fill them to form a face.
Scaled the plane in order to be bigger than the area that I'm trying to intersect (I scale to 20x bigger).
Created a boolean modifier to intersect the created plane with the mesh.
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[0].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[0].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'>
The Euler angles are not the same, here is an image of both planes after applying the rotation.
What am I doing wrong?
get_face_euler_angle(triangular_plane_obj)
function code is $\endgroup$Euler((0, 0, 1))
$\endgroup$