|  | import bpy
from mathutils import Vector, Euler
from math import pi
import json
import os
import bpy_extras
import bmesh
import time
class ModalTimerOperator(bpy.types.Operator):
    """Operator which runs its self from a timer"""
    bl_idname = "wm.modal_timer_operator"
    bl_label = "Modal Timer Operator"
    _timer = None
    index = 0
    
    def menu_func(self, context):
        self.layout.operator(ModalTimerOperator.bl_idname)
    def modal(self, context, event):
        scene = context.scene
        if self.index >= len(self.sides):
            self.cancel(context)
            return {'CANCELLED'}
        if event.type == 'TIMER':
            print("HOLY COW!!!")
            self.capture(self.sides[self.index])
            self.index += 1
            if self.index < len(self.sides):
                self.position_camera(self.sides[self.index])
            else:
                self.export_data()
        return {'PASS_THROUGH'}
    def execute(self, context):
        print("execute!")
        self.c = bpy.data.objects["cam"]
        self.scene = bpy.context.scene
        selected = bpy.context.selected_objects[0]
        bpy.ops.object.mode_set(mode="OBJECT")
        bpy.ops.object.transform_apply( rotation = True )
        self.data = {}
        self.b = selected
        FRONT = [0, 1, 2, "Front"]
        SIDE = [1, 1, 2, "Side"]
        TOP = [0, 0, 1, "Top"]
        ANGLED = [0.5, 1, 2, "Angled"]
        self.sides = [FRONT, SIDE, TOP, ANGLED]
        path = "/tmp/macrovision/"
        media_path = "/home/crux/furry/macrovision/media/"
        media_folder = "buildings/Houses/"
        os.makedirs(path, exist_ok=True)
        os.makedirs(os.path.join(media_path, media_folder), exist_ok=True)
        wm = context.window_manager
        self._timer = wm.event_timer_add(0.25, window=context.window)
        wm.modal_handler_add(self)
        self.position_camera(self.sides[self.index])
        return {'RUNNING_MODAL'}
    def cancel(self, context):
        wm = context.window_manager
        wm.event_timer_remove(self._timer)
    def getView3dAreaAndRegion(self, context):
      for area in context.screen.areas: 
          if area.type == "VIEW_3D":    
            for region in area.regions:
              if region.type == "WINDOW":
                print("Found WINDOW") 
                return area, region
    def select_border(self, context, view3dAreaAndRegion=None, extend=True):
      if not view3dAreaAndRegion:
        view3dAreaAndRegion = self.getView3dAreaAndRegion(context)
        print(view3dAreaAndRegion)
      view3dArea, view3dRegion = view3dAreaAndRegion
      override = context.copy()
      override['area'] = view3dArea
      override['region'] = view3dRegion
      bpy.ops.view3d.select_box(override,xmin=0,xmax=view3dArea.width,ymin=0,ymax=view3dArea.height,mode='SET')
    def position_camera(self, angles):
        bpy.ops.object.mode_set(mode="OBJECT")
        local_bbox_center = 0.125 * sum((Vector(box) for box in self.b.bound_box), Vector())
        global_bbox_center = self.b.matrix_world @ local_bbox_center
        self.c.data.ortho_scale = max(self.b.dimensions) * 1.3
        self.c.location = global_bbox_center
        self.c.rotation_euler = Euler([angles[1] * pi / 2, 0, angles[0] * pi / 2])
        rot = self.c.rotation_euler.to_matrix()
        rot.invert()
        self.c.location = self.c.location + Vector([0, 0, 100]) @ rot
        self.data[angles[3]] = self.b.dimensions[angles[2]]
    
    def capture(self, angles):
        bpy.ops.object.mode_set(mode="EDIT")
        bpy.ops.mesh.select_all(action="SELECT")
        bpy.ops.mesh.sort_elements(type='VIEW_ZAXIS', elements={'FACE'}, reverse=False)
        bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
        polygons = []
        edges = []
        
        self.select_border(bpy.context)
        bm.faces.ensure_lookup_table()
        bm.verts.ensure_lookup_table()
        bm.edges.ensure_lookup_table()
        for face in bm.faces:
            if not face.select:
                continue
            verts = []
            for vert in face.verts:
                co = bpy_extras.object_utils.world_to_camera_view(self.scene, self.c, vert.co)
                verts.append([co[0], co[1]])
            verts.append(verts[0])
            polygons.append({"verts": verts, "type": "bright" if face.material_index == 1 else "dark"})
            for edge in face.edges:
                if not edge.select:
                    continue
                co1 = bpy_extras.object_utils.world_to_camera_view(self.scene, self.c, edge.verts[0].co)
                co2 = bpy_extras.object_utils.world_to_camera_view(self.scene, self.c, edge.verts[1].co)
                polygons.append({"type": "edge", "verts": [
                    [co1[0], co1[1]],
                    [co2[0], co2[1]]
                ]})
        bm.free()
        bpy.ops.object.mode_set(mode="OBJECT")
            
        with open(f"/tmp/polygons-{angles[3]}.json", "w", encoding="utf-8") as file:
            json.dump({"polygons": polygons}, file)
            
    def export_data(self):
        with open(f"/tmp/data-{self.b.name}.json", "w", encoding="utf-8") as file:
            json.dump({"name": self.b.name, "views": self.data}, file)
def register():
    bpy.utils.register_class(ModalTimerOperator)
    bpy.types.VIEW3D_MT_object.append(ModalTimerOperator.menu_func)
def unregister():
    bpy.utils.unregister_class(ModalTimerOperator)
register()
 |