# Overlapping faces : how to create new faces (interception and complements) with Python API?

Suppose I have a mesh with two overlapping faces (in general, all vertices are different):

import bpy
import bmesh
if bpy.context.mode != 'OBJECT':
bpy.ops.object.mode_set()
# Create circle #1
location=(0.0, 0.0, 0.0), fill_type='NGON')
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action="DESELECT")
# Create circle #2
location=(1.0, 2.0, 0.0), fill_type='NGON')
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action="DESELECT")
# Apply all transformations
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)


If possible, how can I create a new mesh with three faces : one face associated with the interception, and two faces representing the original faces with the interception removed.

I tried to use bmesh.utils.face_split_edgenet, but do not fully understand how to define "edgenet". At least, I can get it working. If I run

bm = bmesh.new()
bm.from_mesh(context.object.data)
bm.select_mode = {'FACE'}
bm.faces.ensure_lookup_table()
tmp = bmesh.utils.face_split_edgenet(bm.faces[0], bm.faces[1].edges)


"tmp" seems to be the same as bm.faces[0].

My goal is to assign different colors to 'area in circle #1, but not in circle #2', 'area in circle #2, but not circle #1', and 'both in circle #1 and circle #2'.

Bmesh script

For the case of overlapping circles.

Define two circles on z = 0 plane with centres and radii c1.xy, r1 and c2.xy, r2.

Add them as bmesh circles, edges only no fill. Select the edges of circle 1. Circle 1 needs to be at (0, 0), edit to translate to and back if need be.

The distance between the centres is R the length of the vector N = c2 - c1. Using the triangle with sides R, r1, r2. apply the cosine rule (given we have SSS (side, side, side) to get angle "alpha" opposite r2. rotate the r1' length unit vectorN by +/- alpha around Z axis to get the two points of intersection **

Using N as the normal and one of these points as a plane coordinate can bisect the circles, and move the cut points to their respective "perfect" circle intersection points. Notice the operators to add emties at the intersection points, will show that they rarely fall on an edge of what is really a regular ngon

Remove doubles.

Fill all edges on either side of and on plane to give non intersection parts.

The non selected edges on circle 1 side plus the selected edges of circle two side give the edges intersection in the middle.

Pass the bmesh contextual create operator the edges. Akin to pressing the F key in UI.

Assign materials 0 1 to circles and 2 to intersection.

Realign the normals.

Test script. run in object mode, first add some simple mesh at (0, 0, 0), assign it three materials, select and run script

import bpy
import bmesh
from mathutils import Vector, Matrix
from mathutils.geometry import distance_point_to_plane as dp2p
from math import sin, cos, acos, degrees, radians, pi
bm = bmesh.new()

me = bpy.context.object.data

segments = 25
# circle 1
c1 = Vector((0, 0))
r1 = 8

bmesh.ops.create_circle(bm,
segments=segments,
matrix=Matrix.Translation(c1.to_3d())
)
for e in bm.edges:
print(e)
e.tag = True
e.select = True
# circle 2
c2 = Vector((4, 5))
r2 = 9

bmesh.ops.create_circle(bm,
segments=segments,
matrix=Matrix.Translation(c2.to_3d())
)

N = (c2 - c1).to_3d()
R = N.length
R2 = N.length_squared

# half angles

a1 = (R2 + r2 * r2 - r1 * r1) / (2 * R * r2)
a2 = (R2 + r1 * r1 - r2 * r2) / (2 * R * r1)
a = acos(a2)
# circle 1
rot = Matrix.Rotation(a, 3, 'Z')
loc1 = r1 * (rot * N.normalized())

rot = Matrix.Rotation(-a, 3, 'Z')
loc2 = r1 * (rot * N.normalized())
# three cases

ret = bmesh.ops.bisect_plane(bm,
plane_co=loc1,
plane_no=N,
geom=bm.edges[:])

for v in ret['geom_cut']:
if (v.co - loc1).length < (v.co - loc2).length:
v.co = loc1
else:
v.co = loc2
bmesh.ops.remove_doubles(bm, verts=ret["geom_cut"], dist=0.001)

c1_geom = [e for e in bm.edges
if all(dp2p(v.co, loc1, N) <= 0.001 for v in e.verts)]
bmesh.ops.contextual_create(bm, geom=c1_geom)

c2_geom = [e for e in bm.edges
if all(dp2p(v.co, loc1, N) >= -0.001 for v in e.verts)]
bmesh.ops.contextual_create(bm, geom=c2_geom)
# intersection
all = set(bm.edges)
tagged = set(e for e in bm.edges if e.select)
untagged = all - tagged
inter = list((set(c1_geom) - tagged).union(set(c2_geom) - untagged))
for e in bm.edges:
e.select = e in inter
ret = bmesh.ops.contextual_create(bm, geom=inter)

#recalc normals
bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
for f in bm.faces:
f.material_index = f.index

bm.to_mesh(me)
me.update()


Notes:

Tagged edges seemed to be lost after remove doubles bmesh op, had to use select instead.

Perform some tests to see if circles intersect.

if r1 + r2 == R: # touching
# fill both circles
pass
elif r1 + r2 > R: # intersecting
# do above
pass
else:
print("circles don't intersect")
`

As in a previous question, and this one I've used bmesh rather than operators. See Python performance with Blender operators Your script in this question (using 9 operators) could be achieved with 3 operators add circle x 2 and join. However I would recommend not using them at all if possible.

For non circular shapes getting tricky with solidify and boolean modifiers might get the job done.