diff --git a/ladybug_grasshopper/icon/LB View Factors.png b/ladybug_grasshopper/icon/LB View Factors.png index f3d0e11..c8b9752 100644 Binary files a/ladybug_grasshopper/icon/LB View Factors.png and b/ladybug_grasshopper/icon/LB View Factors.png differ diff --git a/ladybug_grasshopper/icon/LB View Percent.png b/ladybug_grasshopper/icon/LB View Percent.png index 83dcd21..284791d 100644 Binary files a/ladybug_grasshopper/icon/LB View Percent.png and b/ladybug_grasshopper/icon/LB View Percent.png differ diff --git a/ladybug_grasshopper/icon/LB View Rose.png b/ladybug_grasshopper/icon/LB View Rose.png new file mode 100644 index 0000000..332b978 Binary files /dev/null and b/ladybug_grasshopper/icon/LB View Rose.png differ diff --git a/ladybug_grasshopper/json/LB_View_Factors.json b/ladybug_grasshopper/json/LB_View_Factors.json index ecafcac..aca575c 100644 --- a/ladybug_grasshopper/json/LB_View_Factors.json +++ b/ladybug_grasshopper/json/LB_View_Factors.json @@ -1,5 +1,5 @@ { - "version": "1.7.0", + "version": "1.7.1", "nickname": "ViewFactors", "outputs": [ [ diff --git a/ladybug_grasshopper/json/LB_View_Percent.json b/ladybug_grasshopper/json/LB_View_Percent.json index 19db818..05c0772 100644 --- a/ladybug_grasshopper/json/LB_View_Percent.json +++ b/ladybug_grasshopper/json/LB_View_Percent.json @@ -1,5 +1,5 @@ { - "version": "1.7.0", + "version": "1.7.1", "nickname": "ViewPercent", "outputs": [ [ @@ -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 }, @@ -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 }, @@ -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-" diff --git a/ladybug_grasshopper/json/LB_View_Rose.json b/ladybug_grasshopper/json/LB_View_Rose.json new file mode 100644 index 0000000..21c2825 --- /dev/null +++ b/ladybug_grasshopper/json/LB_View_Rose.json @@ -0,0 +1,85 @@ +{ + "version": "1.7.0", + "nickname": "ViewRose", + "outputs": [ + [ + { + "access": "None", + "name": "view_vecs", + "description": "A list of vectors which are projected from each of the points\nto evaluate view.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "results", + "description": "A list of numbers that aligns with the vertices of the mesh. Each number\nindicates the distance from the _center_pt at which the view is\nblocked from a particular direction.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "mesh", + "description": "A colored mesh representing the visible area from the viewpoint past\nthe _context geometry. Colors indicate how open the view is from a\ngiven direction.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "legend", + "description": "A legend that correspond to the colors of the mesh and shows the distance\nat which vectors are blocked.", + "type": null, + "default": null + } + ] + ], + "inputs": [ + { + "access": "list", + "name": "_context", + "description": "Rhino Breps or Meshes representing context geometry that can block\nthe view around the center point.", + "type": "GeometryBase", + "default": null + }, + { + "access": "item", + "name": "_center_pt", + "description": "A point for the center of the view rose from which view openness\nwill be evaluated.", + "type": "Point3d", + "default": null + }, + { + "access": "item", + "name": "_radius_", + "description": "A number for the radius of the view rose in Rhino model units. This is\nalso used to evaluate the distance at which context is no longer\nable to block the view from the center point. This value should\ntypically be increased if the view rose does not extend past the\n_context geometry. (Default: 100 meters in the current Rhino model\nunits system).", + "type": "double", + "default": null + }, + { + "access": "item", + "name": "_view_type_", + "description": "Text or an integer representing the type of view analysis to conduct.\nChoose from the following options. (Default: 0 - HorizontalRadial)\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.", + "type": "string", + "default": null + }, + { + "access": "item", + "name": "_resolution_", + "description": "A positive integer for the number of times that the original\nview vectors are subdivided. For a circle, 1 indicates that 72\nevenly-spaced vectors are used to describe a circle, 2 indicates\nthat 144 vectors describe a circle, and each successive value will\nroughly double the number of view vectors used. For a dome, 1 indicates\nthat 1225 are used to describe the dome, 2 indicates that 5040\nview vectors describe the some and each successive value will\nroughly quadruple the number of view vectors used. Setting this to\na high value will result in a more accurate analysis but will take\nlonger to run. (Default: 1).", + "type": "int", + "default": null + }, + { + "access": "item", + "name": "legend_par_", + "description": "Optional legend parameters from the \"LB Legend Parameters\"\nthat will be used to customize the display of the results.", + "type": "System.Object", + "default": null + } + ], + "subcategory": "3 :: Analyze Geometry", + "code": "\ntry:\n from ladybug_geometry.geometry3d import Mesh3D\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_geometry:\\n\\t{}'.format(e))\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, units_abbreviation\n from ladybug_{{cad}}.togeometry import to_point3d\n from ladybug_{{cad}}.fromgeometry import from_mesh3d, from_vector3d\n from ladybug_{{cad}}.fromobjects import legend_objects\n from ladybug_{{cad}}.intersect import join_geometry_to_mesh, \\\n intersect_mesh_rays_distance\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs\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 None: (0, view_sphere.horizontal_circle_view_mesh),\n 'HorizontalRadial': (0, view_sphere.horizontal_circle_view_mesh),\n 'Horizontal30DegreeOffset': (1, view_sphere.horizontal_radial_view_mesh),\n 'Spherical': (2, view_sphere.sphere_view_mesh),\n 'SkyExposure': (3, view_sphere.dome_view_mesh),\n '0': (0, view_sphere.horizontal_circle_view_mesh),\n '1': (1, view_sphere.horizontal_radial_view_mesh),\n '2': (2, view_sphere.sphere_view_mesh),\n '3': (3, view_sphere.dome_view_mesh)\n}\n\n\nif all_required_inputs(ghenv.Component):\n # process the view_type_ and set the default values\n vt_int, view_method = VIEW_TYPES[_view_type_]\n _radius_ = int(100 / conversion_to_meters()) if _radius_ is None else _radius_\n resolution_ = _resolution_ if _resolution_ is not None else 1\n az_count = 72 * resolution_\n alt_count = 6 * resolution_ if vt_int == 1 else 18 * resolution_\n\n # get the view vectors and mesh based on the inputs\n lb_point = to_point3d(_center_pt)\n if vt_int == 0:\n study_mesh, lb_vecs = view_method(\n center_point=lb_point, radius=_radius_, azimuth_count=az_count)\n else:\n study_mesh, lb_vecs = view_method(\n center_point=lb_point, radius=_radius_,\n azimuth_count=az_count, altitude_count=alt_count)\n view_vecs = [from_vector3d(pt) for pt in lb_vecs]\n\n # process the input geometry\n shade_mesh = join_geometry_to_mesh(_context)\n\n # intersect the rays with the mesh\n results = intersect_mesh_rays_distance(shade_mesh, _center_pt, view_vecs, _radius_)\n\n # adjust the mesh based on the distance\n move_vecs = [vec * -(_radius_ - dist) for vec, dist, in zip(lb_vecs, results)]\n new_verts = [lb_point] if vt_int in (0, 1) else []\n iter_verts = study_mesh.vertices[1:] if vt_int in (0, 1) else study_mesh.vertices\n for pt, mv in zip(iter_verts, move_vecs):\n new_verts.append(pt.move(mv))\n study_mesh = Mesh3D(new_verts, study_mesh.faces)\n\n # add a value at the start to align with the vertices\n if vt_int in (0, 1):\n avg_val = sum(results) / len(results)\n results.insert(0, avg_val)\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 = units_abbreviation()\n if legend_par_ is None or legend_par_.are_colors_default:\n graphic.legend_parameters.colors = Colorset.view_study()\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 Rose", + "description": "Visualize the view openness around a given point as a colored mesh that fills a\ncircle, sphere, or hemisphere (depending on the specified view type).\n_\nThe input context will block the view, resulting in a decresed view rose size\nand a change in the view rose color.\n-" +} \ No newline at end of file diff --git a/ladybug_grasshopper/src/LB View Factors.py b/ladybug_grasshopper/src/LB View Factors.py index c94cdb7..2ce6c62 100644 --- a/ladybug_grasshopper/src/LB View Factors.py +++ b/ladybug_grasshopper/src/LB View Factors.py @@ -71,10 +71,10 @@ ghenv.Component.Name = 'LB View Factors' ghenv.Component.NickName = 'ViewFactors' -ghenv.Component.Message = '1.7.0' +ghenv.Component.Message = '1.7.1' ghenv.Component.Category = 'Ladybug' ghenv.Component.SubCategory = '3 :: Analyze Geometry' -ghenv.Component.AdditionalHelpFromDocStrings = '0' +ghenv.Component.AdditionalHelpFromDocStrings = '2' try: from ladybug.viewsphere import view_sphere diff --git a/ladybug_grasshopper/src/LB View Percent.py b/ladybug_grasshopper/src/LB View Percent.py index 07b9ac4..0e23b59 100644 --- a/ladybug_grasshopper/src/LB View Percent.py +++ b/ladybug_grasshopper/src/LB View Percent.py @@ -29,9 +29,9 @@ 0 - HorizontalRadial - The percentage of the 360 horizontal view plane that is not blocked by the context geometry. _ - 1 - Horizonta30DegreeOffset - The percentage of the 360 horizontal + 1 - Horizontal30DegreeOffset - The percentage of the 360 horizontal view band bounded on top and bottom by a 30 degree offset from - the horizontal plane. 30 degress corresponds roughly to the + the horizontal plane. 30 degrees corresponds roughly to the vertical limit of human peripheral vision. _ 2 - Spherical - The percentage of the sphere surrounding each of @@ -110,8 +110,8 @@ the percentage of the view_vecs that are not blocked by context geometry. mesh: A colored mesh of the test _geometry representing the percentage of the input _geometry's view that is not blocked by context. - legend: A legend showing the number of hours that correspond to the colors - of the mesh. + legend: A legend that correspond to the colors of the mesh and shows the percentage + of the view_vecs that are not blocked by context geometry. title: A text object for the study title. int_mtx: A Matrix object that can be connected to the "LB Deconstruct Matrix" component to obtain detailed vector-by-vector results of the study. @@ -125,7 +125,7 @@ ghenv.Component.Name = "LB View Percent" ghenv.Component.NickName = 'ViewPercent' -ghenv.Component.Message = '1.7.0' +ghenv.Component.Message = '1.7.1' ghenv.Component.Category = 'Ladybug' ghenv.Component.SubCategory = '3 :: Analyze Geometry' ghenv.Component.AdditionalHelpFromDocStrings = '2' @@ -159,7 +159,7 @@ # dictionary to record all available view types VIEW_TYPES = { 'HorizontalRadial': 'Horizontal Radial', - 'Horizonta30DegreeOffset': 'Horizontal 30-Degree Offset', + 'Horizontal30DegreeOffset': 'Horizontal 30-Degree Offset', 'Spherical': 'Spherical', 'SkyExposure': 'Sky Exposure', 'SkyView': 'Sky View', diff --git a/ladybug_grasshopper/src/LB View Rose.py b/ladybug_grasshopper/src/LB View Rose.py new file mode 100644 index 0000000..9cacfb7 --- /dev/null +++ b/ladybug_grasshopper/src/LB View Rose.py @@ -0,0 +1,167 @@ +# Ladybug: A Plugin for Environmental Analysis (GPL) +# This file is part of Ladybug. +# +# Copyright (c) 2023, Ladybug Tools. +# You should have received a copy of the GNU Affero General Public License +# along with Ladybug; If not, see . +# +# @license AGPL-3.0-or-later + +""" +Visualize the view openness around a given point as a colored mesh that fills a +circle, sphere, or hemisphere (depending on the specified view type). +_ +The input context will block the view, resulting in a decresed view rose size +and a change in the view rose color. +- + + Args: + _context: Rhino Breps or Meshes representing context geometry that can block + the view around the center point. + _center_pt: A point for the center of the view rose from which view openness + will be evaluated. + _radius_: A number for the radius of the view rose in Rhino model units. This is + also used to evaluate the distance at which context is no longer + able to block the view from the center point. This value should + typically be increased if the view rose does not extend past the + _context geometry. (Default: 100 meters in the current Rhino model + units system). + _view_type_: Text or an integer representing the type of view analysis to conduct. + Choose from the following options. (Default: 0 - HorizontalRadial) + _ + 0 - HorizontalRadial - The percentage of the 360 horizontal view + plane that is not blocked by the context geometry. + _ + 1 - Horizontal30DegreeOffset - The percentage of the 360 horizontal + view band bounded on top and bottom by a 30 degree offset from + the horizontal plane. 30 degrees corresponds roughly to the + vertical limit of human peripheral vision. + _ + 2 - Spherical - The percentage of the sphere surrounding each of + the test points that is not blocked by context geometry. This + is equivalent to a solid angle and gives equal weight to all + portions of the sphere. + _ + 3 - SkyExposure - The percentage of the sky that is visible from + each of the the test points. + _resolution_: A positive integer for the number of times that the original + view vectors are subdivided. For a circle, 1 indicates that 72 + evenly-spaced vectors are used to describe a circle, 2 indicates + that 144 vectors describe a circle, and each successive value will + roughly double the number of view vectors used. For a dome, 1 indicates + that 1225 are used to describe the dome, 2 indicates that 5040 + view vectors describe the some and each successive value will + roughly quadruple the number of view vectors used. Setting this to + a high value will result in a more accurate analysis but will take + longer to run. (Default: 1). + legend_par_: Optional legend parameters from the "LB Legend Parameters" + that will be used to customize the display of the results. + + + Returns: + report: ... + view_vecs: A list of vectors which are projected from each of the points + to evaluate view. + results: A list of numbers that aligns with the vertices of the mesh. Each number + indicates the distance from the _center_pt at which the view is + blocked from a particular direction. + mesh: A colored mesh representing the visible area from the viewpoint past + the _context geometry. Colors indicate how open the view is from a + given direction. + legend: A legend that correspond to the colors of the mesh and shows the distance + at which vectors are blocked. +""" + +ghenv.Component.Name = 'LB View Rose' +ghenv.Component.NickName = 'ViewRose' +ghenv.Component.Message = '1.7.0' +ghenv.Component.Category = 'Ladybug' +ghenv.Component.SubCategory = '3 :: Analyze Geometry' +ghenv.Component.AdditionalHelpFromDocStrings = '2' + +try: + from ladybug_geometry.geometry3d import Mesh3D +except ImportError as e: + raise ImportError('\nFailed to import ladybug_geometry:\n\t{}'.format(e)) + +try: + from ladybug.viewsphere import view_sphere + from ladybug.color import Colorset + from ladybug.graphic import GraphicContainer +except ImportError as e: + raise ImportError('\nFailed to import ladybug:\n\t{}'.format(e)) + +try: + from ladybug_rhino.config import conversion_to_meters, units_abbreviation + from ladybug_rhino.togeometry import to_point3d + from ladybug_rhino.fromgeometry import from_mesh3d, from_vector3d + from ladybug_rhino.fromobjects import legend_objects + from ladybug_rhino.intersect import join_geometry_to_mesh, \ + intersect_mesh_rays_distance + from ladybug_rhino.grasshopper import all_required_inputs +except ImportError as e: + raise ImportError('\nFailed to import ladybug_rhino:\n\t{}'.format(e)) + + +# dictionary to record all available view types +VIEW_TYPES = { + None: (0, view_sphere.horizontal_circle_view_mesh), + 'HorizontalRadial': (0, view_sphere.horizontal_circle_view_mesh), + 'Horizontal30DegreeOffset': (1, view_sphere.horizontal_radial_view_mesh), + 'Spherical': (2, view_sphere.sphere_view_mesh), + 'SkyExposure': (3, view_sphere.dome_view_mesh), + '0': (0, view_sphere.horizontal_circle_view_mesh), + '1': (1, view_sphere.horizontal_radial_view_mesh), + '2': (2, view_sphere.sphere_view_mesh), + '3': (3, view_sphere.dome_view_mesh) +} + + +if all_required_inputs(ghenv.Component): + # process the view_type_ and set the default values + vt_int, view_method = VIEW_TYPES[_view_type_] + _radius_ = int(100 / conversion_to_meters()) if _radius_ is None else _radius_ + resolution_ = _resolution_ if _resolution_ is not None else 1 + az_count = 72 * resolution_ + alt_count = 6 * resolution_ if vt_int == 1 else 18 * resolution_ + + # get the view vectors and mesh based on the inputs + lb_point = to_point3d(_center_pt) + if vt_int == 0: + study_mesh, lb_vecs = view_method( + center_point=lb_point, radius=_radius_, azimuth_count=az_count) + else: + study_mesh, lb_vecs = view_method( + center_point=lb_point, radius=_radius_, + azimuth_count=az_count, altitude_count=alt_count) + view_vecs = [from_vector3d(pt) for pt in lb_vecs] + + # process the input geometry + shade_mesh = join_geometry_to_mesh(_context) + + # intersect the rays with the mesh + results = intersect_mesh_rays_distance(shade_mesh, _center_pt, view_vecs, _radius_) + + # adjust the mesh based on the distance + move_vecs = [vec * -(_radius_ - dist) for vec, dist, in zip(lb_vecs, results)] + new_verts = [lb_point] if vt_int in (0, 1) else [] + iter_verts = study_mesh.vertices[1:] if vt_int in (0, 1) else study_mesh.vertices + for pt, mv in zip(iter_verts, move_vecs): + new_verts.append(pt.move(mv)) + study_mesh = Mesh3D(new_verts, study_mesh.faces) + + # add a value at the start to align with the vertices + if vt_int in (0, 1): + avg_val = sum(results) / len(results) + results.insert(0, avg_val) + + # create the mesh and legend outputs + graphic = GraphicContainer(results, study_mesh.min, study_mesh.max, legend_par_) + graphic.legend_parameters.title = units_abbreviation() + if legend_par_ is None or legend_par_.are_colors_default: + graphic.legend_parameters.colors = Colorset.view_study() + + # create all of the visual outputs + study_mesh.colors = graphic.value_colors + mesh = from_mesh3d(study_mesh) + legend = legend_objects(graphic.legend) diff --git a/ladybug_grasshopper/user_objects/LB View Factors.ghuser b/ladybug_grasshopper/user_objects/LB View Factors.ghuser index 738d728..cb01e64 100644 Binary files a/ladybug_grasshopper/user_objects/LB View Factors.ghuser and b/ladybug_grasshopper/user_objects/LB View Factors.ghuser differ diff --git a/ladybug_grasshopper/user_objects/LB View Percent.ghuser b/ladybug_grasshopper/user_objects/LB View Percent.ghuser index 1873d0b..7906b7f 100644 Binary files a/ladybug_grasshopper/user_objects/LB View Percent.ghuser and b/ladybug_grasshopper/user_objects/LB View Percent.ghuser differ diff --git a/ladybug_grasshopper/user_objects/LB View Rose.ghuser b/ladybug_grasshopper/user_objects/LB View Rose.ghuser new file mode 100644 index 0000000..4424d8e Binary files /dev/null and b/ladybug_grasshopper/user_objects/LB View Rose.ghuser differ diff --git a/samples/view_rose.gh b/samples/view_rose.gh new file mode 100644 index 0000000..dd53337 Binary files /dev/null and b/samples/view_rose.gh differ