From c81e9ee82cb99351ac10fb079dedf9e100472e56 Mon Sep 17 00:00:00 2001 From: Chris Mackey Date: Thu, 3 Oct 2024 13:30:14 -0700 Subject: [PATCH] fix(create): Ensure data trees work correctly for Straight Skeleton --- .../json/HB_Straight_Skeleton.json | 4 ++-- .../src/HB Straight Skeleton.py | 15 +++++---------- .../user_objects/HB Straight Skeleton.ghuser | Bin 4987 -> 4939 bytes 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/honeybee_grasshopper_core/json/HB_Straight_Skeleton.json b/honeybee_grasshopper_core/json/HB_Straight_Skeleton.json index 0125d64..f781b07 100644 --- a/honeybee_grasshopper_core/json/HB_Straight_Skeleton.json +++ b/honeybee_grasshopper_core/json/HB_Straight_Skeleton.json @@ -1,5 +1,5 @@ { - "version": "1.8.2", + "version": "1.8.3", "nickname": "Skeleton", "outputs": [ [ @@ -43,7 +43,7 @@ } ], "subcategory": "0 :: Create", - "code": "\ntry: # import the core ladybug_geometry dependencies\n from ladybug_geometry.geometry3d import LineSegment3D\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_geometry:\\n\\t{}'.format(e))\n\ntry: # import the core ladybug_geometry dependencies\n from ladybug_geometry_polyskel.polyskel import skeleton_as_edge_list\n from ladybug_geometry_polyskel.polysplit import perimeter_core_subfaces_and_skeleton\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_geometry:\\n\\t{}'.format(e))\n\ntry: # import the ladybug_{{cad}} dependencies\n from ladybug_{{cad}}.config import tolerance\n from ladybug_{{cad}}.togeometry import to_face3d\n from ladybug_{{cad}}.fromgeometry import from_face3d, from_linesegment3d\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, list_to_data_tree\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\nif all_required_inputs(ghenv.Component):\n # extract the straight skeleton and sub-faces from the geometry\n skeleton, perim_poly, core_poly = [], [], []\n for face in to_face3d(_floor_geo):\n face = face.remove_colinear_vertices(tolerance)\n if offset_ is not None and offset_ > 0:\n skel, perim, core = perimeter_core_subfaces_and_skeleton(\n face, offset_, tolerance)\n skeleton.append([from_linesegment3d(lin) for lin in skel])\n perim_poly.append([from_face3d(p) for p in perim])\n core_poly.append([from_face3d(c) for c in core])\n else:\n skel_2d = skeleton_as_edge_list(\n face.boundary_polygon2d, face.hole_polygon2d,\n tolerance, intersect=True)\n skel_3d = []\n for seg in skel_2d:\n verts_3d = tuple(face.plane.xy_to_xyz(pt) for pt in seg.vertices)\n skel_3d.append(LineSegment3D.from_end_points(*verts_3d))\n skeleton.append([from_linesegment3d(lin) for lin in skel_3d])\n\n # convert outputs to data trees\n skeleton = list_to_data_tree(skeleton)\n perim_poly = list_to_data_tree(perim_poly)\n core_poly = list_to_data_tree(core_poly)\n", + "code": "\ntry: # import the core ladybug_geometry dependencies\n from ladybug_geometry.geometry3d import LineSegment3D\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_geometry:\\n\\t{}'.format(e))\n\ntry: # import the core ladybug_geometry dependencies\n from ladybug_geometry_polyskel.polyskel import skeleton_as_edge_list\n from ladybug_geometry_polyskel.polysplit import perimeter_core_subfaces_and_skeleton\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_geometry:\\n\\t{}'.format(e))\n\ntry: # import the ladybug_{{cad}} dependencies\n from ladybug_{{cad}}.config import tolerance\n from ladybug_{{cad}}.togeometry import to_face3d\n from ladybug_{{cad}}.fromgeometry import from_face3d, from_linesegment3d\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, list_to_data_tree\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\nif all_required_inputs(ghenv.Component):\n # extract the straight skeleton and sub-faces from the geometry\n skeleton, perim_poly, core_poly = [], [], []\n for face in to_face3d(_floor_geo):\n face = face.remove_colinear_vertices(tolerance)\n if offset_ is not None and offset_ > 0:\n skel, perim, core = perimeter_core_subfaces_and_skeleton(\n face, offset_, tolerance)\n skeleton.extend([from_linesegment3d(lin) for lin in skel])\n perim_poly.extend([from_face3d(p) for p in perim])\n core_poly.extend([from_face3d(c) for c in core])\n else:\n skel_2d = skeleton_as_edge_list(\n face.boundary_polygon2d, face.hole_polygon2d,\n tolerance, intersect=True)\n skel_3d = []\n for seg in skel_2d:\n verts_3d = tuple(face.plane.xy_to_xyz(pt) for pt in seg.vertices)\n skel_3d.append(LineSegment3D.from_end_points(*verts_3d))\n skeleton.extend([from_linesegment3d(lin) for lin in skel_3d])\n", "category": "Honeybee", "name": "HB Straight Skeleton", "description": "Get the straight skeleton and core/perimeter sub-faces for any planar geometry.\n_\nThis component uses a modified version of the the polyskel package\n(https://github.com/Botffy/polyskel) by Armin Scipiades (aka. @Bottfy),\nwhich is, itself, a Python implementation of the straight skeleton\nalgorithm as described by Felkel and Obdrzalek in their 1998 conference paper\nStraight skeleton implementation\n(https://github.com/Botffy/polyskel/blob/master/doc/StraightSkeletonImplementation.pdf).\n-" diff --git a/honeybee_grasshopper_core/src/HB Straight Skeleton.py b/honeybee_grasshopper_core/src/HB Straight Skeleton.py index e6bf0a9..389715e 100644 --- a/honeybee_grasshopper_core/src/HB Straight Skeleton.py +++ b/honeybee_grasshopper_core/src/HB Straight Skeleton.py @@ -40,7 +40,7 @@ ghenv.Component.Name = 'HB Straight Skeleton' ghenv.Component.NickName = 'Skeleton' -ghenv.Component.Message = '1.8.2' +ghenv.Component.Message = '1.8.3' ghenv.Component.Category = 'Honeybee' ghenv.Component.SubCategory = '0 :: Create' ghenv.Component.AdditionalHelpFromDocStrings = '0' @@ -73,9 +73,9 @@ if offset_ is not None and offset_ > 0: skel, perim, core = perimeter_core_subfaces_and_skeleton( face, offset_, tolerance) - skeleton.append([from_linesegment3d(lin) for lin in skel]) - perim_poly.append([from_face3d(p) for p in perim]) - core_poly.append([from_face3d(c) for c in core]) + skeleton.extend([from_linesegment3d(lin) for lin in skel]) + perim_poly.extend([from_face3d(p) for p in perim]) + core_poly.extend([from_face3d(c) for c in core]) else: skel_2d = skeleton_as_edge_list( face.boundary_polygon2d, face.hole_polygon2d, @@ -84,9 +84,4 @@ for seg in skel_2d: verts_3d = tuple(face.plane.xy_to_xyz(pt) for pt in seg.vertices) skel_3d.append(LineSegment3D.from_end_points(*verts_3d)) - skeleton.append([from_linesegment3d(lin) for lin in skel_3d]) - - # convert outputs to data trees - skeleton = list_to_data_tree(skeleton) - perim_poly = list_to_data_tree(perim_poly) - core_poly = list_to_data_tree(core_poly) + skeleton.extend([from_linesegment3d(lin) for lin in skel_3d]) diff --git a/honeybee_grasshopper_core/user_objects/HB Straight Skeleton.ghuser b/honeybee_grasshopper_core/user_objects/HB Straight Skeleton.ghuser index 0af17767309814b6ac5e3a0201ba2c666ed55c91..14d028d1cc2c3be75c77c9588795dc884526b537 100644 GIT binary patch literal 4939 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 F{{>Kb!rcG>