Source code for ladybug_rhino.bakeobjects

"""Functions to bake entire Ladybug objects into the Rhino scene.

The methods here are intended to help translate groups of geometry that are
commonly generated by several objects in Ladybug core (ie. legends, compasses,
visualization sets, etc.)
"""
import json

from ladybug_geometry.geometry3d import Mesh3D
from ladybug.graphic import GraphicContainer
from ladybug_display.geometry3d import  DisplayText3D
from ladybug_display.visualization import AnalysisGeometry

from .config import units_system
from .color import color_to_color
from .bakegeometry import bake_point2d, bake_vector2d, bake_ray2d, \
    bake_linesegment2d, bake_arc2d, bake_polygon2d, bake_polyline2d, bake_mesh2d, \
    bake_point3d, bake_vector3d, bake_ray3d, bake_linesegment3d, bake_plane, \
    bake_arc3d, bake_polyline3d, bake_mesh3d, bake_face3d, bake_polyface3d, \
    bake_sphere, bake_cone, bake_cylinder, _get_layer, _get_attributes
from .bakedisplay import bake_display_point2d, bake_display_vector2d, \
    bake_display_ray2d, bake_display_linesegment2d, bake_display_arc2d, \
    bake_display_polygon2d, bake_display_polyline2d, bake_display_mesh2d, \
    bake_display_text3d, bake_display_vector3d, bake_display_point3d, \
    bake_display_ray3d, bake_display_linesegment3d, bake_display_arc3d, \
    bake_display_polyline3d, bake_display_plane, bake_display_mesh3d, \
    bake_display_face3d, bake_display_polyface3d, bake_display_sphere, \
    bake_display_cone, bake_display_cylinder

try:
    import System
except ImportError as e:
    raise ImportError("Failed to import Windows/.NET libraries\n{}".format(e))

try:
    import Rhino.DocObjects as docobj
    from Rhino import RhinoDoc as rhdoc
except ImportError as e:
    raise ImportError("Failed to import Rhino document attributes.\n{}".format(e))

BAKE_MAPPER = {
    'Vector2D': bake_vector2d,
    'Point2D': bake_point2d,
    'Ray2D': bake_ray2d,
    'LineSegment2D': bake_linesegment2d,
    'Arc2D': bake_arc2d,
    'Polyline2D': bake_polyline2d,
    'Polygon2D': bake_polygon2d,
    'Mesh2D': bake_mesh2d,
    'Vector3D': bake_vector3d,
    'Point3D': bake_point3d,
    'Ray3D': bake_ray3d,
    'LineSegment3D': bake_linesegment3d,
    'Arc3D': bake_arc3d,
    'Polyline3D': bake_polyline3d,
    'Plane': bake_plane,
    'Mesh3D': bake_mesh3d,
    'Face3D': bake_face3d,
    'Polyface3D': bake_polyface3d,
    'Sphere': bake_sphere,
    'Cone': bake_cone,
    'Cylinder': bake_cylinder,
    'DisplayVector2D': bake_display_vector2d,
    'DisplayPoint2D': bake_display_point2d,
    'DisplayRay2D': bake_display_ray2d,
    'DisplayLineSegment2D': bake_display_linesegment2d,
    'DisplayPolyline2D': bake_display_polyline2d,
    'DisplayArc2D': bake_display_arc2d,
    'DisplayPolygon2D': bake_display_polygon2d,
    'DisplayMesh2D': bake_display_mesh2d,
    'DisplayVector3D': bake_display_vector3d,
    'DisplayPoint3D': bake_display_point3d,
    'DisplayRay3D': bake_display_ray3d,
    'DisplayPlane': bake_display_plane,
    'DisplayLineSegment3D': bake_display_linesegment3d,
    'DisplayPolyline3D': bake_display_polyline3d,
    'DisplayArc3D': bake_display_arc3d,
    'DisplayFace3D': bake_display_face3d,
    'DisplayMesh3D': bake_display_mesh3d,
    'DisplayPolyface3D': bake_display_polyface3d,
    'DisplaySphere': bake_display_sphere,
    'DisplayCone': bake_display_cone,
    'DisplayCylinder': bake_display_cylinder,
    'DisplayText3D': bake_display_text3d
}


[docs]def bake_legend(legend, layer_name=None): """Add a Ladybug Legend object to the Rhino scene. Args: legend: A Ladybug Legend object to be added to the Rhino scene. layer_name: Optional text string for the layer name on which to place the legend. If None, text will be added to the current layer. Returns: A list of IDs that point to the objects in the Rhino scene in the following order: - legend_mesh -- A colored mesh for the legend. - legend_title -- A text object for the legend title. - legend_text -- Text objects for the rest of the legend text. """ # bake the legend mesh legend_mesh = bake_mesh3d(legend.segment_mesh, layer_name) # translate the legend text _height = legend.legend_parameters.text_height _font = legend.legend_parameters.font if legend.legend_parameters.continuous_legend is False: legend_text = [ DisplayText3D(txt, loc, _height, None, _font, 'Left', 'Bottom') for txt, loc in zip(legend.segment_text, legend.segment_text_location)] elif legend.legend_parameters.vertical is True: legend_text = [ DisplayText3D(txt, loc, _height, None, _font, 'Left', 'Center') for txt, loc in zip(legend.segment_text, legend.segment_text_location)] else: legend_text = [ DisplayText3D(txt, loc, _height, None, _font, 'Center', 'Bottom') for txt, loc in zip(legend.segment_text, legend.segment_text_location)] legend_title = DisplayText3D( legend.title, legend.title_location, _height, None, _font) legend_text.insert(0, legend_title) # bake the text objects legend_text_guids = [] for txt_obj in legend_text: legend_text_guids.append(bake_display_text3d(txt_obj, layer_name)) return [legend_mesh] + legend_text_guids
[docs]def bake_analysis(analysis, layer_name=None, bake_3d_legend=False, min_point=None, max_point=None): """Add a Ladybug Display AnalysisGeometry object to the Rhino scene. Args: analysis: A Ladybug Display AnalysisGeometry object to be added to the Rhino scene. layer_name: Optional text string for the parent layer name on which to place the AnalysisGeometry. The actual layer of the context will always have a name that aligns with the AnalysisGeometry.display_name. bake_3d_legend: A Boolean to note whether the AnalysisGeometry should be baked with 3D legends for any AnalysisGeometries it includes. (Default: False). min_point: An optional Point3D to override the default min point that are used to generate the legend. (Default: None). max_point: An optional Point3D to override the default max point that are used to generate the legend. (Default: None). Returns: A list of IDs that point to the objects in the Rhino scene. """ doc = rhdoc.ActiveDoc # get attributes corresponding to the layer layer_name = analysis.display_name if layer_name is None else \ '{}::{}'.format(layer_name, analysis.display_name) min_pt = analysis.min_point if min_point is None else min_point max_pt = analysis.max_point if max_point is None else max_point # generate the colors that correspond to the values obj_ids = [] for i, data in enumerate(analysis.data_sets): # get properties used for all analysis geometries objs_to_group = [] graphic = GraphicContainer( data.values, min_pt, max_pt, data.legend_parameters, data.data_type, data.unit) colors = graphic.value_colors sub_layer_name = layer_name if data.data_type is None else \ '{}::{}'.format(layer_name, data.data_type.name) layer_index = _get_layer(sub_layer_name) # translate the analysis geometry using the matching method if analysis.matching_method == 'faces': c_count = 0 for mesh in analysis.geometry: mesh.colors = colors[c_count:c_count + len(mesh.faces)] c_count += len(mesh.faces) bake_func = bake_mesh3d if isinstance(mesh, Mesh3D) else bake_mesh2d objs_to_group.append(bake_func(mesh, layer_name=layer_index)) elif analysis.matching_method == 'vertices': c_count = 0 for mesh in analysis.geometry: mesh.colors = colors[c_count:c_count + len(mesh.vertices)] c_count += len(mesh.vertices) bake_func = bake_mesh3d if isinstance(mesh, Mesh3D) else bake_mesh2d objs_to_group.append(bake_func(mesh, layer_name=layer_index)) else: # one color per geometry object bake_func = BAKE_MAPPER[analysis.geometry[0].__class__.__name__] for geo_obj, col in zip(analysis.geometry, colors): attrib = _get_attributes(layer_index) attrib.ColorSource = docobj.ObjectColorSource.ColorFromObject attrib.ObjectColor = color_to_color(col) objs_to_group.append(bake_func(geo_obj, attributes=attrib)) # group the objects, and add JSON of values to layer user data group_table = doc.Groups # group table group_table.Add(sub_layer_name, objs_to_group) layer_table = doc.Layers # layer table layer_obj = layer_table[layer_index] layer_obj.UserDictionary.Set('vis_data', json.dumps(data.to_dict())) layer_obj.UserDictionary.Set('guids', System.Array[System.Guid](objs_to_group)) if i != analysis.active_data: # hide the inactive data layer layer_obj.IsVisible = False # add geometry to the global list and bake the legend if requested obj_ids.extend(objs_to_group) if bake_3d_legend: obj_ids.extend(bake_legend(graphic.legend, layer_index)) # hide the layer if it is hidden if analysis.hidden: layer_table = doc.Layers # layer table layer_index = _get_layer(layer_name) layer_obj = layer_table[layer_index] layer_obj.IsVisible = False return obj_ids
[docs]def bake_context(context, layer_name=None): """Add a Ladybug Display ContextGeometry object to the Rhino scene. Args: context: A Ladybug Display ContextGeometry object to be added to the Rhino scene. layer_name: Optional text string for the parent layer name on which to place the ContextGeometry. The actual layer of the context will always have a name that aligns with the ContextGeometry.display_name. Returns: A list of IDs that point to the objects in the Rhino scene. """ doc = rhdoc.ActiveDoc # get attributes corresponding to the layer layer_name = context.display_name if layer_name is None else \ '{}::{}'.format(layer_name, context.display_name) layer_index = _get_layer(layer_name) # hide the layer if it is hidden if context.hidden: layer_table = doc.Layers # layer table layer_obj = layer_table[layer_index] layer_obj.IsVisible = False # loop through the objects and add them to the scene obj_ids = [] for geo_obj in context.geometry: bake_func = BAKE_MAPPER[geo_obj.__class__.__name__] obj_ids.append(bake_func(geo_obj, layer_index)) return obj_ids
[docs]def bake_visualization_set(vis_set, bake_3d_legend=False): """Add a Ladybug Display VisualizationSet object to the Rhino scene. Args: context_geometry: A Ladybug VisualizationSet object to be added to the Rhino scene. bake_3d_legend: A Boolean to note whether the VisualizationSet should be baked with 3D legends for any AnalysisGeometries it includes. (Default: False). Returns: A list of IDs that point to the objects in the Rhino scene. """ # convert the visualization set to model units if necessary units_sys = units_system() if vis_set.units is not None and units_sys is not None \ and vis_set.units != units_sys: vis_set.convert_to_units(units_sys) # bake all of the geometries obj_ids = [] for geo in vis_set.geometry: if isinstance(geo, AnalysisGeometry): a_objs = bake_analysis( geo, vis_set.display_name, bake_3d_legend, vis_set.min_point, vis_set.max_point) obj_ids.extend(a_objs) else: # translate it as ContextGeometry obj_ids.extend(bake_context(geo, vis_set.display_name)) return obj_ids