Source code for honeybee_vtk.to_vtk

"""Functions to translate HB Model objects to PolyData or JoinedPolyData.

honeybee-vtk provides a wrapper for vtkPolyData or vtkAppendPolyData which are used
instead of the original objects to add additional fields that are needed to provide
additional functionalities in honeybee-vtk. See ``types`` module for more information.

There are two sets of functions in this module:

    - create_x : These methods create an object from other objects. For instance
        create_polyline creates a polyline for a list of points.
    - convert_x: These methods convert a LBT object directly to a PolyData. For instance
        convert_polyline converts a Polyline3D to a VTK-based Polyline.

"""

from typing import List

import vtk

from ladybug_geometry.geometry3d import Face3D, Mesh3D, Point3D, Vector3D, Polyline3D
from honeybee.room import Room
from honeybee.face import Face
from honeybee.aperture import Aperture
from honeybee.door import Door
from honeybee.shade import Shade
from honeybee_radiance.sensorgrid import SensorGrid

from .types import PolyData, JoinedPolyData
from .vtkjs.schema import SensorGridOptions
from ._geometry import _radial_grid


[docs]def convert_face_3d(face: Face3D) -> PolyData: """Convert a ladybug_geometry.Face3D to vtkPolyData.""" # check if geometry has holes or is convex # mesh them and use convert_mesh if face.has_holes or not face.is_convex: return convert_mesh(face.triangulated_mesh3d) # create a PolyData from face points points = vtk.vtkPoints() polygon = vtk.vtkPolygon() cells = vtk.vtkCellArray() vertices_count = len(face.vertices) polygon.GetPointIds().SetNumberOfIds(vertices_count) for ver in face.vertices: points.InsertNextPoint(*ver) for count in range(vertices_count): polygon.GetPointIds().SetId(count, count) cells.InsertNextCell(polygon) face_vtk = PolyData() face_vtk.SetPoints(points) face_vtk.SetPolys(cells) return face_vtk
[docs]def convert_mesh(mesh: Mesh3D) -> PolyData: """Convert a ladybug_geometry.Mesh to vtkPolyData.""" points = vtk.vtkPoints() polygon = vtk.vtkPolygon() cells = vtk.vtkCellArray() for ver in mesh.vertices: points.InsertNextPoint(*ver) for face in mesh.faces: polygon.GetPointIds().SetNumberOfIds(len(face)) for count, i in enumerate(face): polygon.GetPointIds().SetId(count, i) cells.InsertNextCell(polygon) grid_vtk = PolyData() grid_vtk.SetPoints(points) grid_vtk.SetPolys(cells) return grid_vtk
[docs]def convert_sensor_grid( sensor_grid: SensorGrid, load_option: SensorGridOptions = SensorGridOptions.Mesh, angle: int = None, radius: float = None) -> PolyData: """Convert a honeybee-radiance sensor grid to a vtkPolyData.""" # Change the mode from mesh to point if mesh is not provided. # This will allow to keep the mesh option as default and avoid failure with # models that only provide the sensor points. if load_option == SensorGridOptions.Mesh and sensor_grid.mesh is None: print( f'{sensor_grid.display_name} sensor grid does not include mesh faces. ' 'The sensor grid will be loaded as points.' ) load_option = SensorGridOptions.Sensors if load_option == SensorGridOptions.Sensors: points = [ap.pos for ap in sensor_grid.sensors] grid_data = convert_points(points) elif load_option == SensorGridOptions.RadialGrid: grid_data = convert_mesh(_radial_grid(sensor_grid, angle, radius)) elif load_option == SensorGridOptions.Mesh: grid_data = convert_mesh(sensor_grid.mesh) else: raise ValueError(f'{load_option} is not a valid SensorGridOption.') grid_data.identifier = sensor_grid.identifier grid_data.name = sensor_grid.display_name grid_data.type = 'Grid' return grid_data
[docs]def convert_shade(shade: Shade) -> PolyData: polydata = convert_face_3d(shade.geometry) metadata = polydata._get_metadata(shade) polydata._add_metadata(metadata) return polydata
[docs]def convert_aperture(aperture: Aperture) -> List[PolyData]: polydata = convert_face_3d(aperture.geometry) metadata = polydata._get_metadata(aperture) polydata._add_metadata(metadata) data = [polydata] shades = [] if aperture.outdoor_shades: shades.extend(aperture.outdoor_shades) if aperture.indoor_shades: shades.extend(aperture.indoor_shades) if shades: for shade in shades: polydata = convert_shade(shade) data.append(polydata) return data
[docs]def convert_door(door: Door) -> List[PolyData]: polydata = convert_face_3d(door.geometry) metadata = polydata._get_metadata(door) polydata._add_metadata(metadata) data = [polydata] return data
[docs]def convert_face(face: Face) -> List[PolyData]: """Convert a HBFace to a PolyData.""" polydata = convert_face_3d(face.punched_geometry) metadata = polydata._get_metadata(face) polydata._add_metadata(metadata) data = [polydata] if face.apertures: for aperture in face.apertures: data.extend(convert_aperture(aperture)) if face.doors: for door in face.doors: data.extend(convert_door(door)) return data
[docs]def convert_room(room: Room) -> List[PolyData]: """Convert HB Room to PolyData.""" data = [] for face in room.faces: data.extend(convert_face(face)) for shade in room.indoor_shades: polydata = convert_shade(shade) data.append(polydata) for shade in room.outdoor_shades: polydata = convert_shade(shade) data.append(polydata) return data
[docs]def convert_points(points: List[Point3D]) -> PolyData: """Export a list of points to VTK. Args: points: A list of Point3D. Returns: A vtk object with multiple VTK point objects. """ vtk_points = vtk.vtkPoints() vtk_vertices = vtk.vtkCellArray() for point in points: vtk_points.InsertNextPoint(tuple(point)) vtk_vertices.InsertNextCell(len(points), list(range(len(points)))) polydata = PolyData() polydata.SetPoints(vtk_points) polydata.SetVerts(vtk_vertices) polydata.Modified() return polydata
def _create_lines( start_points: List[Point3D], vectors: List[Vector3D]) -> vtk.vtkPolyData: """Create a line from start and end point.""" # Create a vtkPoints container and store the points for all the lines lines_data = vtk.vtkPolyData() pts = vtk.vtkPoints() for st_pt, vector in zip(start_points, vectors): end_pt = st_pt.move(vector) pts.InsertNextPoint(st_pt) pts.InsertNextPoint(end_pt) # add all the points to lines dataset lines_data.SetPoints(pts) lines = vtk.vtkCellArray() # create lines based on points for count in range(len(start_points)): line = vtk.vtkLine() # the second 0 is the index of p0 in linesPolyData's points line.GetPointIds().SetId(0, 2 * count) # the second 1 is the index of P1 in linesPolyData's points line.GetPointIds().SetId(1, (2 * count) + 1) lines.InsertNextCell(line) lines_data.SetLines(lines) return lines_data def _create_cone( center: Point3D, vector: Vector3D, radius: float = 0.1, height: float = 0.3, resolution: int = 2 ) -> PolyData: cone_poly = vtk.vtkPolyData() # Parameters for the cone cone_source = vtk.vtkConeSource() cone_source.SetResolution(resolution) cone_source.SetRadius(radius) cone_source.SetHeight(height) cone_source.SetDirection(tuple(vector)) cone_source.SetCenter(tuple(center)) cone_source.Update() cone_poly.ShallowCopy(cone_source.GetOutput()) return cone_poly # TODO: Add an optional input for data
[docs]def create_arrow(start_points: List[Point3D], vectors: List[Vector3D]) -> JoinedPolyData: """Create arrows from point and vector.""" assert len(start_points) == len(vectors), \ 'Number of start points must match the number of vectors.' cones = [] lines = _create_lines(start_points, vectors) for st_pt, vector in zip(start_points, vectors): end_pt = st_pt.move(vector) cones.append(_create_cone(end_pt, vector)) return JoinedPolyData.from_polydata([lines] + cones)
[docs]def create_polyline(points: List[Point3D]) -> PolyData: """Create a polyline from a list of points.""" # Create a vtkPoints container and store the points for all the lines pts = vtk.vtkPoints() for pt in points: pts.InsertNextPoint(tuple(pt)) # add all the points to lines dataset polyline = vtk.vtkPolyLine() polyline.GetPointIds().SetNumberOfIds(len(points)) for i in range(len(points)): polyline.GetPointIds().SetId(i, i) # Create a cell array to store the lines in and add the lines to it cells = vtk.vtkCellArray() cells.InsertNextCell(polyline) # Create a polydata to store everything in polydata = PolyData() # Add the points to the dataset polydata.SetPoints(pts) # Add the lines to the dataset polydata.SetLines(cells) return polydata
[docs]def convert_polyline(polyline: Polyline3D) -> PolyData: return create_polyline(polyline.vertices)