Skip to content

Commit

Permalink
feat(geometry): Add component for view rose
Browse files Browse the repository at this point in the history
  • Loading branch information
chriswmackey committed Feb 14, 2024
1 parent 738d02b commit c8e044f
Show file tree
Hide file tree
Showing 13 changed files with 265 additions and 13 deletions.
Binary file modified ladybug_grasshopper/icon/LB View Factors.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified ladybug_grasshopper/icon/LB View Percent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ladybug_grasshopper/icon/LB View Rose.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion ladybug_grasshopper/json/LB_View_Factors.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.7.0",
"version": "1.7.1",
"nickname": "ViewFactors",
"outputs": [
[
Expand Down
8 changes: 4 additions & 4 deletions ladybug_grasshopper/json/LB_View_Percent.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.7.0",
"version": "1.7.1",
"nickname": "ViewPercent",
"outputs": [
[
Expand Down Expand Up @@ -34,7 +34,7 @@
{
"access": "None",
"name": "legend",
"description": "A legend showing the number of hours that correspond to the colors\nof the mesh.",
"description": "A legend that correspond to the colors of the mesh and shows the percentage\nof the view_vecs that are not blocked by context geometry.",
"type": null,
"default": null
},
Expand All @@ -58,7 +58,7 @@
{
"access": "item",
"name": "_view_type",
"description": "Text or an integer representing the type of view analysis\nto conduct. Choose from the following options.\n_\n0 - HorizontalRadial - The percentage of the 360 horizontal view\nplane that is not blocked by the context geometry.\n_\n1 - Horizonta30DegreeOffset - The percentage of the 360 horizontal\nview band bounded on top and bottom by a 30 degree offset from\nthe horizontal plane. 30 degress corresponds roughly to the\nvertical limit of human peripheral vision.\n_\n2 - Spherical - The percentage of the sphere surrounding each of\nthe test points that is not blocked by context geometry. This\nis equivalent to a solid angle and gives equal weight to all\nportions of the sphere.\n_\n3 - SkyExposure - The percentage of the sky that is visible from\neach of the the test points. This is distinct from SkyView,\nwhich is the amount of sky seen by a surface. SkyExposure is\nequivalent to a solid angle and gives equal weight to all\nportions of the sky.\n_\n4 - SkyView - The percentage of the sky that is visible from the\n_geometry surfaces. This is distinct from SkyExposure, which\ntreats each part of the sky with equal weight. SkyView weights\nthe portions of the sky according to thier projection into\nthe plane of the surface being evaluated. So SkyView for a\nhorizontal surface would give more importance to the sky\npatches that are overhead vs. those near the horizon.",
"description": "Text or an integer representing the type of view analysis\nto conduct. Choose from the following options.\n_\n0 - HorizontalRadial - The percentage of the 360 horizontal view\nplane that is not blocked by the context geometry.\n_\n1 - Horizontal30DegreeOffset - The percentage of the 360 horizontal\nview band bounded on top and bottom by a 30 degree offset from\nthe horizontal plane. 30 degrees corresponds roughly to the\nvertical limit of human peripheral vision.\n_\n2 - Spherical - The percentage of the sphere surrounding each of\nthe test points that is not blocked by context geometry. This\nis equivalent to a solid angle and gives equal weight to all\nportions of the sphere.\n_\n3 - SkyExposure - The percentage of the sky that is visible from\neach of the the test points. This is distinct from SkyView,\nwhich is the amount of sky seen by a surface. SkyExposure is\nequivalent to a solid angle and gives equal weight to all\nportions of the sky.\n_\n4 - SkyView - The percentage of the sky that is visible from the\n_geometry surfaces. This is distinct from SkyExposure, which\ntreats each part of the sky with equal weight. SkyView weights\nthe portions of the sky according to thier projection into\nthe plane of the surface being evaluated. So SkyView for a\nhorizontal surface would give more importance to the sky\npatches that are overhead vs. those near the horizon.",
"type": "string",
"default": null
},
Expand Down Expand Up @@ -127,7 +127,7 @@
}
],
"subcategory": "3 :: Analyze Geometry",
"code": "\nimport math\ntry: # python 2\n from itertools import izip as zip\nexcept ImportError: # python 3\n pass\n\ntry:\n from ladybug.viewsphere import view_sphere\n from ladybug.color import Colorset\n from ladybug.graphic import GraphicContainer\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.config import conversion_to_meters\n from ladybug_{{cad}}.togeometry import to_joined_gridded_mesh3d, to_vector3d\n from ladybug_{{cad}}.fromgeometry import from_mesh3d, from_point3d, from_vector3d\n from ladybug_{{cad}}.fromobjects import legend_objects\n from ladybug_{{cad}}.text import text_objects\n from ladybug_{{cad}}.intersect import join_geometry_to_mesh, intersect_mesh_rays\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, hide_output, \\\n show_output, objectify_output, recommended_processor_count\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\n# dictionary to record all available view types\nVIEW_TYPES = {\n 'HorizontalRadial': 'Horizontal Radial',\n 'Horizonta30DegreeOffset': 'Horizontal 30-Degree Offset',\n 'Spherical': 'Spherical',\n 'SkyExposure': 'Sky Exposure',\n 'SkyView': 'Sky View',\n '0': 'Horizontal Radial',\n '1': 'Horizontal 30-Degree Offset',\n '2': 'Spherical',\n '3': 'Sky Exposure',\n '4': 'Sky View'\n}\n\n\nif all_required_inputs(ghenv.Component) and _run:\n # process the view_type_ and set the default values\n vt_str = VIEW_TYPES[_view_type]\n _resolution_ = _resolution_ if _resolution_ is not None else 1\n _offset_dist_ = _offset_dist_ if _offset_dist_ is not None \\\n else 0.1 / conversion_to_meters()\n if _geo_block_ is None:\n _geo_block_ = True if vt_str in ('Sky Exposure', 'Sky View') else False\n workers = _cpu_count_ if _cpu_count_ is not None else recommended_processor_count()\n\n # create the gridded mesh from the geometry\n study_mesh = to_joined_gridded_mesh3d(_geometry, _grid_size)\n points = [from_point3d(pt.move(vec * _offset_dist_)) for pt, vec in\n zip(study_mesh.face_centroids, study_mesh.face_normals)]\n hide_output(ghenv.Component, 1)\n\n # get the view vectors based on the view type\n patch_wghts = None\n if vt_str == 'Horizontal Radial':\n lb_vecs = view_sphere.horizontal_radial_vectors(30 * _resolution_)\n elif vt_str == 'Horizontal 30-Degree Offset':\n patch_mesh, lb_vecs = view_sphere.horizontal_radial_patches(30, _resolution_)\n patch_wghts = view_sphere.horizontal_radial_patch_weights(30, _resolution_)\n elif vt_str == 'Spherical':\n patch_mesh, lb_vecs = view_sphere.sphere_patches(_resolution_)\n patch_wghts = view_sphere.sphere_patch_weights(_resolution_)\n else:\n patch_mesh, lb_vecs = view_sphere.dome_patches(_resolution_)\n patch_wghts = view_sphere.dome_patch_weights(_resolution_)\n view_vecs = [from_vector3d(pt) for pt in lb_vecs]\n\n # mesh the geometry and context\n shade_mesh = join_geometry_to_mesh(_geometry + context_) if _geo_block_ \\\n else join_geometry_to_mesh(context_)\n\n # intersect the rays with the mesh\n if vt_str == 'Sky View': # account for the normals of the surface\n normals = [from_vector3d(vec) for vec in study_mesh.face_normals]\n int_matrix, angles = intersect_mesh_rays(\n shade_mesh, points, view_vecs, normals, cpu_count=workers)\n else:\n int_matrix, angles = intersect_mesh_rays(\n shade_mesh, points, view_vecs, cpu_count=workers)\n\n # compute the results\n int_mtx = objectify_output('View Intersection Matrix', int_matrix)\n vec_count = len(view_vecs)\n results = []\n if vt_str == 'Sky View': # weight intersections by angle before output\n for int_vals, angles in zip(int_matrix, angles):\n w_res = (ival * 2 * math.cos(ang) for ival, ang in zip(int_vals, angles))\n weight_result = sum(r * w for r, w in zip(w_res, patch_wghts))\n results.append(weight_result * 100 / vec_count)\n else:\n if patch_wghts:\n for int_list in int_matrix:\n weight_result = sum(r * w for r, w in zip(int_list, patch_wghts))\n results.append(weight_result * 100 / vec_count)\n else:\n results = [sum(int_list) * 100 / vec_count for int_list in int_matrix]\n\n # create the mesh and legend outputs\n graphic = GraphicContainer(results, study_mesh.min, study_mesh.max, legend_par_)\n graphic.legend_parameters.title = '%'\n if legend_par_ is None or legend_par_.are_colors_default:\n graphic.legend_parameters.colors = Colorset.view_study()\n title_txt = vt_str if vt_str in ('Sky Exposure', 'Sky View') else \\\n '{} View'.format(vt_str)\n title = text_objects(\n title_txt, graphic.lower_title_location,\n graphic.legend_parameters.text_height * 1.5,\n graphic.legend_parameters.font)\n\n # create all of the visual outputs\n study_mesh.colors = graphic.value_colors\n mesh = from_mesh3d(study_mesh)\n legend = legend_objects(graphic.legend)\n",
"code": "\nimport math\ntry: # python 2\n from itertools import izip as zip\nexcept ImportError: # python 3\n pass\n\ntry:\n from ladybug.viewsphere import view_sphere\n from ladybug.color import Colorset\n from ladybug.graphic import GraphicContainer\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.config import conversion_to_meters\n from ladybug_{{cad}}.togeometry import to_joined_gridded_mesh3d, to_vector3d\n from ladybug_{{cad}}.fromgeometry import from_mesh3d, from_point3d, from_vector3d\n from ladybug_{{cad}}.fromobjects import legend_objects\n from ladybug_{{cad}}.text import text_objects\n from ladybug_{{cad}}.intersect import join_geometry_to_mesh, intersect_mesh_rays\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, hide_output, \\\n show_output, objectify_output, recommended_processor_count\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\n# dictionary to record all available view types\nVIEW_TYPES = {\n 'HorizontalRadial': 'Horizontal Radial',\n 'Horizontal30DegreeOffset': 'Horizontal 30-Degree Offset',\n 'Spherical': 'Spherical',\n 'SkyExposure': 'Sky Exposure',\n 'SkyView': 'Sky View',\n '0': 'Horizontal Radial',\n '1': 'Horizontal 30-Degree Offset',\n '2': 'Spherical',\n '3': 'Sky Exposure',\n '4': 'Sky View'\n}\n\n\nif all_required_inputs(ghenv.Component) and _run:\n # process the view_type_ and set the default values\n vt_str = VIEW_TYPES[_view_type]\n _resolution_ = _resolution_ if _resolution_ is not None else 1\n _offset_dist_ = _offset_dist_ if _offset_dist_ is not None \\\n else 0.1 / conversion_to_meters()\n if _geo_block_ is None:\n _geo_block_ = True if vt_str in ('Sky Exposure', 'Sky View') else False\n workers = _cpu_count_ if _cpu_count_ is not None else recommended_processor_count()\n\n # create the gridded mesh from the geometry\n study_mesh = to_joined_gridded_mesh3d(_geometry, _grid_size)\n points = [from_point3d(pt.move(vec * _offset_dist_)) for pt, vec in\n zip(study_mesh.face_centroids, study_mesh.face_normals)]\n hide_output(ghenv.Component, 1)\n\n # get the view vectors based on the view type\n patch_wghts = None\n if vt_str == 'Horizontal Radial':\n lb_vecs = view_sphere.horizontal_radial_vectors(30 * _resolution_)\n elif vt_str == 'Horizontal 30-Degree Offset':\n patch_mesh, lb_vecs = view_sphere.horizontal_radial_patches(30, _resolution_)\n patch_wghts = view_sphere.horizontal_radial_patch_weights(30, _resolution_)\n elif vt_str == 'Spherical':\n patch_mesh, lb_vecs = view_sphere.sphere_patches(_resolution_)\n patch_wghts = view_sphere.sphere_patch_weights(_resolution_)\n else:\n patch_mesh, lb_vecs = view_sphere.dome_patches(_resolution_)\n patch_wghts = view_sphere.dome_patch_weights(_resolution_)\n view_vecs = [from_vector3d(pt) for pt in lb_vecs]\n\n # mesh the geometry and context\n shade_mesh = join_geometry_to_mesh(_geometry + context_) if _geo_block_ \\\n else join_geometry_to_mesh(context_)\n\n # intersect the rays with the mesh\n if vt_str == 'Sky View': # account for the normals of the surface\n normals = [from_vector3d(vec) for vec in study_mesh.face_normals]\n int_matrix, angles = intersect_mesh_rays(\n shade_mesh, points, view_vecs, normals, cpu_count=workers)\n else:\n int_matrix, angles = intersect_mesh_rays(\n shade_mesh, points, view_vecs, cpu_count=workers)\n\n # compute the results\n int_mtx = objectify_output('View Intersection Matrix', int_matrix)\n vec_count = len(view_vecs)\n results = []\n if vt_str == 'Sky View': # weight intersections by angle before output\n for int_vals, angles in zip(int_matrix, angles):\n w_res = (ival * 2 * math.cos(ang) for ival, ang in zip(int_vals, angles))\n weight_result = sum(r * w for r, w in zip(w_res, patch_wghts))\n results.append(weight_result * 100 / vec_count)\n else:\n if patch_wghts:\n for int_list in int_matrix:\n weight_result = sum(r * w for r, w in zip(int_list, patch_wghts))\n results.append(weight_result * 100 / vec_count)\n else:\n results = [sum(int_list) * 100 / vec_count for int_list in int_matrix]\n\n # create the mesh and legend outputs\n graphic = GraphicContainer(results, study_mesh.min, study_mesh.max, legend_par_)\n graphic.legend_parameters.title = '%'\n if legend_par_ is None or legend_par_.are_colors_default:\n graphic.legend_parameters.colors = Colorset.view_study()\n title_txt = vt_str if vt_str in ('Sky Exposure', 'Sky View') else \\\n '{} View'.format(vt_str)\n title = text_objects(\n title_txt, graphic.lower_title_location,\n graphic.legend_parameters.text_height * 1.5,\n graphic.legend_parameters.font)\n\n # create all of the visual outputs\n study_mesh.colors = graphic.value_colors\n mesh = from_mesh3d(study_mesh)\n legend = legend_objects(graphic.legend)\n",
"category": "Ladybug",
"name": "LB View Percent",
"description": "Evaluate the percent view to the outdoors or sky from input geometry through context.\n_\nSuch view calculations can be used to estimate the quality of a view to the\noutdoors from a given location on the indoors. They can also be used on\nthe outdoors to evaluate the openness of street canyons to the sky, which has\nimplications for the pedestrian expereince as well as the rate of radiant heat\nloss from urban surfaces and the sky at night.\n_\nNote that this component uses the CAD environment's ray intersection methods,\nwhich can be fast for geometries with low complexity but does not scale well\nfor complex geometries or many test points. For such complex studies,\nhoneybee-radiance should be used.\n-"
Expand Down
Loading

0 comments on commit c8e044f

Please sign in to comment.