import bpy import bmesh from mathutils import Vector from collections import defaultdict from dataclasses import dataclass def sprint(*text): screen = bpy.data.screens['Scripting'] for area in screen.areas: if area.type != "CONSOLE": continue override = {'screen': screen, 'area': area} with bpy.context.temp_override(**override): bpy.ops.console.scrollback_append(text=" ".join(map(str, text))) print = sprint def face_indicators(light: Vector, position: list[Vector], polygon_normal: list[Vector], mesh: bpy.types.Mesh, # outputs indicators: list[float]): for i in range(len(mesh.polygons)): n = polygon_normal[i] p = position[mesh.polygons[i].a] indicator = dot(n, (light - p)) indicators[i] = indicator @dataclass class Edge: a: int # vertex index b: int # vertex index @dataclass class PolygonIndex: a: int b: int @dataclass class EdgePolygon: edge: Edge polygon_index: PolygonIndex def build_edge_polygons(mesh: bpy.types.Mesh): by_edge = defaultdict(list) for i, polygon in enumerate(mesh.polygons): for edge in polygon.edge_keys: by_edge[frozenset(edge)].append(i) assert all(len(p) == 2 for p in by_edge.values()) return [ EdgePolygon(Edge(*edge), PolygonIndex(*polygons)) for edge, polygons in by_edge.items() ] def object_silhouette(indicators: list[float], mesh: bpy.types.Mesh, # outputs edge_indices: list[int]): ix = 0 edge_polygons = build_edge_polygons(mesh) for i in range(len(edge_polygons)): ep = edge_polygons[i] if (indicators[ep.polygon_index.a] > 0) != (indicators[ep.polygon_index.b]): edge_indices[ix] = i ix += 1 return ix def select_edge(bm, edge): for e in bm.edges: if frozenset((e.verts[0].index, e.verts[1].index)) == frozenset(edge): e.select = True def select(): light = bpy.data.objects['Light'] torus = bpy.data.objects['Torus'] position = [0] * len( obj = bpy.context.edit_object bm = bmesh.from_edit_mesh(obj.data) bmesh.update_edit_mesh(obj.data)