3
$\begingroup$

I have one single mesh object that consists of many little, randomly placed but non-overlapping circles, like so (top view):

enter image description here

The object was created by looping the bmesh.ops.create_cone(... ) -operator. I need to calculate the surface of the (parts of) circles that are within the square boundaries drawn in the image, which is in this case a square of radius 2 centered around the origin. I found this command in related questions

area = sum(f.calc_area() for f in bm.faces if ...),

which works well if I leave out the if statement, but I don't know how I can include a statement to cut away all pieces of circles that are outside of the bounding box.

Edit: @batFINGER I made a minimal working example of two non-overlapping circles of radius 2. For the sake of clarity I did not include the part of the code that puts them in random places, but I just defined the centers manually in ccValids. Each circle has 64 segments, so since both circles belong to the same mesh data, Blender considers this single mesh object, consisting of two circles, to have 128 faces that are all shaped like pieces of pie.

ccValids = [(-1,2,0), (2,-2,0)] 

bm = bmesh.new()
for c in ccValids:
    m = Matrix()
    bmesh.ops.create_cone( 
        bm, 
        diameter1 = cRad, 
        diameter2 = 0, 
        depth     = 0, 
        segments  = 64,
        matrix    = m.Translation( c )
    )

l_d = bpy.data.meshes.new('LeafsM')
bm.to_mesh( l_d )
l_o = bpy.data.objects.new('LeafsO', l_d )
bpy.context.scene.objects.link( l_o )

Now let's say I want to know what part of the total surface of these two circles lies inside the square of radius 2, that is shown in the camera view below:

enter image description here

Clearly the parts of interest are not segment-shaped, so it won't be useful to base the calculation on the counting on segments. Is there a way to perhaps "split" segments (thus the 128 faces) in an inside and an outside part first, and then count the surfaces of the parts that are inside?

$\endgroup$
4
  • $\begingroup$ @batFINGER I try to understand but I don't get it. There seem to be 128 faces in these two circles but I don't know what's their shape and how they are used to build up the circles. I am trying to remove those faces of which I suspect that they're outside of the box, in order to discover what the faces look like, but it seems I cannot use a f.remove() command. $\endgroup$
    – Gnub
    Oct 17, 2017 at 16:07
  • $\begingroup$ I meant remove(f), does not work $\endgroup$
    – Gnub
    Oct 17, 2017 at 16:21
  • $\begingroup$ I made them by looping bmesh.create_cone like the example lTousky showed in blender.stackexchange.com/questions/91679/… , with segments = 64. Since I made 2 circles to keep an overview, there are now 128 of them and they all belong to the same mesh data. I only want the surface of those parts within some square boundaries, but to be sure I get the correct result I now try to remove the parts of circles that are supposed to be outside, to first see if I would really calculate the correct surface. $\endgroup$
    – Gnub
    Oct 17, 2017 at 19:21
  • $\begingroup$ I can add a sample file tomorrow morning when I get back to work, I don't have the file here right now. $\endgroup$
    – Gnub
    Oct 17, 2017 at 19:24

1 Answer 1

2
$\begingroup$

Boolean Intersection modifier.

Given you are chopping aligned cylinders with an aligned box, the boolean modifier would be a much better way to go IMO.

Add a cube to the scene as the bounding box, scale in z to make sure it is taller than cylinders in z. Add a boolean intersection modifier to cylinders using cube as bounds. A copy of the cylinders mesh with the modifiers applied is calculated using Object.to_mesh(...) This mesh can be loaded into a bmesh, to do face area calculations.

To only calculate the areas of the circular tops, would need to look at face normals being parallel to z axis or similar.

enter image description here Result of running script. The result mesh on left in place, result in edit mode on right. The top face area is 2.5704, which is 64.26% of the square area (4).

Test script

import bpy
import bmesh
from mathutils import Vector
TOL = 0.0001 # anle test
up = Vector((0, 0, 1))

context = bpy.context
scene = context.scene
obj = context.object
# add a new default cube
bpy.ops.mesh.primitive_cube_add(location=(0, 0, 0))
cube = context.object
cube.scale.z = 4
# add a boolean modifer to the obj
bmod = obj.modifiers.new(name="bbox", type='BOOLEAN')
bmod.operation = 'INTERSECT'
bmod.object = cube

scene.update()
# create a copy
copy_mesh = obj.to_mesh(scene, True, settings='PREVIEW')
bm = bmesh.new()
bm.from_mesh(copy_mesh)
area = sum(f.calc_area() for f in bm.faces 
        if f.normal.angle(up) < TOL)
print("Top face area:", area)
bm.free()
# clean up... 
obj.modifiers.remove(bmod)

#scene.objects.unlink(cube)
# uncomment below to see mesh.
cube.data = copy_mesh
cube.scale.z = 1

enter image description here Test run on cones. Bottom faces normals matched with -Z axis. The sum of the areas of bottom edges of the mesh is 3.04279, 70.70% of a 2 x 2 square.

$\endgroup$
5
  • $\begingroup$ The script did work first time I ran it, but now I keep getting an error message on bmod = obj.modifiers.new(name="bbox", type='BOOLEAN') : val: BooleanModifier.object ID type does not support assignment to its self. $\endgroup$
    – Gnub
    Oct 19, 2017 at 9:54
  • 1
    $\begingroup$ select your spotty cylinder mesh in object mode, then run script. $\endgroup$
    – batFINGER
    Oct 19, 2017 at 9:56
  • $\begingroup$ Ashamed of my ignorance.. I get no more error but now the calculated area is always zero. $\endgroup$
    – Gnub
    Oct 19, 2017 at 10:24
  • $\begingroup$ I think I know why: my circles are in fact cones of zero height, the intersection results in a mesh without surfaces. Is there a way to calculate what's inside the cube? $\endgroup$
    – Gnub
    Oct 19, 2017 at 10:46
  • $\begingroup$ Remove all the boolean modifiers on spotty cone object. (unremoved from crashing edit mode runs) If the circle face of the cone is on the bottom, you can match it against down = (0, 0, -1) instead of up. $\endgroup$
    – batFINGER
    Oct 19, 2017 at 14:38

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .