2
$\begingroup$

I have created an operator to calculate the surface area of the currently selected faces. To enable selection across objects, it loops through context.selected_objects. Then for each of these, it reads the polygons with select=true.

The problem is, when I change my face selection manually in the viewport and re-run the operator, the select attribute doesn't seem to be changing in real-time. There's some delay, and as a result my plugin is reporting an inaccurate result.

Code is as follows:

class Utils():
    def get_selected_object_faces(object):
        mesh = object.data
        polys = mesh.polygons
        return [poly for poly in polys if poly.select]
    
    def sq_meters_to_sq_feet(sq_meters, precision):
        return round(sq_meters * 10.7639, precision);

class Calc_Selected_Face_Area(bpy.types.Operator):
    """Calculates Selected Face Area"""
    bl_idname = "interior.calc_selected_face_area"
    bl_label = ""
    
    def execute(self, context):
        selected_polys = []
        for selected_object in context.selected_objects:
          print(selected_object)
          selected_polys += Utils.get_selected_object_faces(selected_object)
        
        poly_areas = [Utils.sq_meters_to_sq_feet(poly.area, precision=0) for poly in selected_polys]
        print(poly_areas)
        context.scene.shared_props.areas = ", ".join([str(int(poly)) for poly in poly_areas])
        
        return {'FINISHED'}

See specifically in the get_selected_object_faces method. The poly.select attirbute I'm reading in there, doesn't seem to be updating immediately when I change my face selection in the UI.

Any tips would be appreciated. New to Python and Blender.

The full script if you want to try it out.

$\endgroup$
5
  • $\begingroup$ Pretty much a duplicate re select not being reflected in mesh data while in edit mode. If it is to be an operator for use in edit mode would recommend using bmesh. (or you will need to update the mesh from edit mesh by some means) $\endgroup$
    – batFINGER
    Oct 1 '21 at 23:21
  • $\begingroup$ Thank you @batFINGER I will give a try using bmesh. $\endgroup$ Oct 1 '21 at 23:23
  • $\begingroup$ Possible dupe blender.stackexchange.com/questions/128549/… (Note in the detail of alias's answer the need to toggle edit mode) There is also Object.update_from_edit_mode() ... however if to be used in edit mode would go for edit bmesh and using a live selection. $\endgroup$
    – batFINGER
    Oct 1 '21 at 23:26
  • $\begingroup$ Do you know how to get all of the objects being edited? In my case I am doing multi-object editing. $\endgroup$ Oct 1 '21 at 23:36
  • $\begingroup$ blender.stackexchange.com/questions/160909/… $\endgroup$
    – batFINGER
    Oct 1 '21 at 23:41
4
$\begingroup$

Numpy version.

Changes made in edit mode are not reflected in mesh data until changing back to object mode . How can I check face selection in edit mode If you do not wish to toggle the mode, consider using an edit mode bmesh, especially if the areas are going to be used to update the mesh.

As an example thought I'd add a numpy version, as well as demonstrate use of Object.update_from_editmode() Equiv would be a toggle mode operator if in edit mode, to update selection

EDIT: For working on an evaluated mesh it will require the edit mode toggle via operator. Otherwise the objects are not evaluated correctly whilst in edit mode.

For an unmodified untransformed method can update the mesh via

if context.mode == 'EDIT_MESH':
    for ob in context.selected_objects:
        if ob.mode == 'EDIT':
            ob.update_from_editmode()

Quick rundown of code.

  • For selected objects in edit mode update their meshes
  • Get the modified and transformed mesh of each selected object from depsgraph
  • Calculate the area, add to total area
  • Make all the calculations in native unit, can convert the result(s) when required. ie $c * a + c * b = c * (a + b)$

Test code.

import bpy
import numpy as np

from bpy import context

toggle = context.mode == 'EDIT_MESH'
if toggle:
    bpy.ops.object.editmode_toggle()

print("-" * 44)
dg = context.evaluated_depsgraph_get()
all_areas = []        
for ob in context.selected_objects:
    me = dg.objects[ob.name].data
    me.transform(ob.matrix_world) # globalize coords
    data = np.empty(len(me.polygons))
    me.polygons.foreach_get("select", data)
    selection = data.astype(bool)
    me.polygons.foreach_get("area", data)
    areas = data
    
    print(ob.name)
    print("Total ob area", np.sum(areas))
    print(np.count_nonzero(selection), "/", len(selection))
    all_areas.append(np.sum(areas[selection]))
    print("Selected area", all_areas[-1], "sq bu")
    #bpy.data.meshes.remove(me)
    print()

print("Total Area Selected")
print(sum(all_areas), "sq bu")
    
if toggle:
    bpy.ops.object.editmode_toggle()  

Test Run.

Default cube copied 3 times. One scaled double, another scaled half. Each given a subsurf modifier. One face selected on each.


Cube
Total ob area 8.114266633987427
4 / 24
Selected area 1.3523776829242706 sq bu

DoubleCube
Total ob area 32.457067370414734
4 / 24
Selected area 5.409510254859924 sq bu

HalfCube
Total ob area 2.0285658836364746
4 / 24
Selected area 0.3380943015217781 sq bu

Total Area Selected
7.099982239305973 sq bu

        
$\endgroup$
1
  • $\begingroup$ Cool I just forced a toggle to object mode and back to edit mode in the script. It would be a benefit to make it work in edit mode only (and live update the results) but I can't get too hung up on it because we have deadlines. Appreciate the help. $\endgroup$ Oct 4 '21 at 21:25

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.