Skip to content

Commit

Permalink
feat(dict): Adding to_dict and from_dict methods to all geometry obj…
Browse files Browse the repository at this point in the history
…ects

There are also a few minor tweks here like a bug fix to the Plane __init__ method and the exposing of a staticmethod on Polyface3D to get outward-facing faces.
  • Loading branch information
chriswmackey authored and Chris Mackey committed Jun 4, 2019
1 parent 693cae6 commit 00d15c9
Show file tree
Hide file tree
Showing 22 changed files with 510 additions and 58 deletions.
16 changes: 16 additions & 0 deletions ladybug_geometry/geometry2d/_1d.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ def __init__(self, p, v):
self._p = p
self._v = v

@classmethod
def from_dict(cls, data):
"""Create a LineSegment2D/Ray2D from a dictionary.
Args:
data: {
"p": {"x": 10, "y": 0},
"v": {"x": 10, "y": 10}}
"""
return cls(Point2D.from_dict(data['p']), Vector2D.from_dict(data['v']))

@property
def p(self):
"""Base point."""
Expand Down Expand Up @@ -75,6 +86,11 @@ def duplicate(self):
"""Get a copy of this object."""
return self.__copy__()

def to_dict(self):
"""Get LineSegment2D/Ray2D as a dictionary."""
return {'p': self.p.to_dict(),
'v': self.v.to_dict()}

def __copy__(self):
return self.__class__(self.p, self.v)

Expand Down
31 changes: 31 additions & 0 deletions ladybug_geometry/geometry2d/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
from .polygon import Polygon2D
from ._2d import Base2DIn2D

try:
from ladybug.color import Color
except ImportError:
Color = None
print('Failed to import ladybug Color.\n'
'Importing mesh colors in from_dict methods will not be availabe.')

try:
from itertools import izip as zip # python 2
except ImportError:
Expand Down Expand Up @@ -58,6 +65,22 @@ def __init__(self, vertices, faces, colors=None):
self._face_areas = None
self._face_centroids = None

@classmethod
def from_dict(cls, data):
"""Create a Mesh2D from a dictionary.
Args:
data: {
"vertices": [{"x": 0, "y": 0}, {"x": 10, "y": 0}, {"x": 0, "y": 10}],
"faces": [(0, 1, 2)]
}
"""
colors = None
if Color is not None and 'colors' in data and data['colors'] is not None:
colors = tuple(Color.from_dict(col) for col in data['colors'])
return cls(tuple(Point2D.from_dict(pt) for pt in data['vertices']),
data['faces'], colors)

@classmethod
def from_face_vertices(cls, faces, purge=True):
"""Create a mesh from a list of faces with each face defined by a list of Point2Ds.
Expand Down Expand Up @@ -334,6 +357,14 @@ def scale(self, factor, origin=None):
_verts = tuple(pt.scale(factor, origin) for pt in self.vertices)
return self._mesh_scale(_verts, factor)

def to_dict(self):
"""Get Mesh2D as a dictionary."""
colors = None
if self.colors is not None:
colors = [col.to_dict() for col in self.colors]
return {'vertices': [pt.to_dict() for pt in self.vertices],
'faces': self.faces, 'colors': colors}

def _face_area(self, face):
"""Return the area of a face."""
return Mesh2D._get_area(tuple(self._vertices[i] for i in face))
Expand Down
16 changes: 16 additions & 0 deletions ladybug_geometry/geometry2d/pointvector.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ def __init__(self, x=0, y=0):
self._x = x
self._y = y

@classmethod
def from_dict(cls, data):
"""Create a Vector2D/Point2D from a dictionary.
Args:
data: {
"x": 10,
"y": 0}
"""
return cls(data['x'], data['y'])

@property
def x(self):
"""Get the X coordinate."""
Expand Down Expand Up @@ -114,6 +125,11 @@ def duplicate(self):
"""Get a copy of this vector."""
return self.__copy__()

def to_dict(self):
"""Get Vector2D/Point2D as a dictionary."""
return {'x': self.x,
'y': self.y}

@staticmethod
def _rotate(vec, angle):
"""Hidden rotation method used by both Point2D and Vector2D."""
Expand Down
15 changes: 15 additions & 0 deletions ladybug_geometry/geometry2d/polygon.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ def __init__(self, vertices):
self._is_convex = None
self._is_self_intersecting = None

@classmethod
def from_dict(cls, data):
"""Create a Polygon2D from a dictionary.
Args:
data: {
"vertices": [{"x": 0, "y": 0}, {"x": 10, "y": 0}, {"x": 0, "y": 10}]
}
"""
return cls(tuple(Point2D.from_dict(pt) for pt in data['vertices']))

@classmethod
def from_rectangle(cls, base_point, height_vector, base, height):
"""Initialize Polygon2D from rectangle parameters.
Expand Down Expand Up @@ -603,6 +614,10 @@ def is_polygon_outside(self, polygon):
return False
return True

def to_dict(self):
"""Get Polygon2D as a dictionary."""
return {'vertices': [pt.to_dict() for pt in self.vertices]}

@staticmethod
def _segments_from_vertices(vertices):
_segs = []
Expand Down
16 changes: 16 additions & 0 deletions ladybug_geometry/geometry3d/_1d.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ def __init__(self, p, v):
self._p = p
self._v = v

@classmethod
def from_dict(cls, data):
"""Create a LineSegment3D/Ray3D from a dictionary.
Args:
data: {
"p": {"x": 10, "y": 0, "z": 0},
"v": {"x": 10, "y": 10, "z": 0}}
"""
return cls(Point3D.from_dict(data['p']), Vector3D.from_dict(data['v']))

@property
def p(self):
"""Base point."""
Expand Down Expand Up @@ -100,6 +111,11 @@ def duplicate(self):
"""Get a copy of this object."""
return self.__copy__()

def to_dict(self):
"""Get LineSegment3D/Ray3D as a dictionary."""
return {'p': self.p.to_dict(),
'v': self.v.to_dict()}

def __copy__(self):
return self.__class__(self.p, self.v)

Expand Down
44 changes: 42 additions & 2 deletions ladybug_geometry/geometry3d/face.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,32 @@ def __init__(self, vertices, plane):
self._is_convex = None
self._is_self_intersecting = None

@classmethod
def from_dict(cls, data):
"""Create a Face3D from a dictionary.
Args:
data: {
"boundary": [{"x": 0, "y": 0, "z": 0}, {"x": 10, "y": 0, "z": 0},
{"x": 0, "y": 10, "z": 0}],
"plane": {"n": {"x": 0, "y": 0, "z": 1}, "o": {"x": 0, "y": 0, "z": 0},
"x": {"x": 1, "y": 0, "z": 0}}
"holes": [[{"x": 2, "y": 2, "z": 0}, {"x": 5, "y": 2, "z": 0},
{"x": 2, "y": 5, "z": 0}]],
}
"""
holes = None
if 'holes' in data and data['holes'] is not None:
holes = data['holes']
if holes is None:
return cls(tuple(Point3D.from_dict(pt) for pt in data['boundary']),
Plane.from_dict(data['plane']))
else:
return cls.from_shape_with_holes(
[Point3D.from_dict(pt) for pt in data['boundary']],
[[Point3D.from_dict(pt) for pt in hole] for hole in holes],
Plane.from_dict(data['plane']))

@classmethod
def from_vertices(cls, vertices):
"""Initialize Face3D from only a list of vertices.
Expand Down Expand Up @@ -1309,6 +1335,16 @@ def sub_rectangles_from_rectangle(base_plane, parent_base, parent_height, ratio,
base_plane)]
return final_faces

def to_dict(self):
"""Get Face3D as a dictionary."""
if not self.has_holes:
return {'boundary': [pt.to_dict() for pt in self.boundary],
'plane': self.plane.to_dict()}
else:
return {'boundary': [pt.to_dict() for pt in self.boundary],
'plane': self.plane.to_dict(),
'holes': [[pt.to_dict() for pt in hole] for hole in self.holes]}

def _check_number_mesh_grid(self, input, name):
assert isinstance(input, (float, int)), '{} for Face3D.get_mesh_grid' \
' must be a number. Got {}.'.format(name, type(input))
Expand Down Expand Up @@ -1495,8 +1531,12 @@ def _plane_from_vertices(vertices):
return Plane(n, pt1)

def __copy__(self):
_new_poly = Face3D(self.vertices, self.plane)
return _new_poly
_new_face = Face3D(self.vertices, self.plane)
self._transfer_properties(_new_face)
_new_face._holes = self._holes
_new_face._polygon2d = self._polygon2d
_new_face._mesh2d = self._mesh2d
return _new_face

def __repr__(self):
return 'Face3D ({} vertices)'.format(len(self))
31 changes: 31 additions & 0 deletions ladybug_geometry/geometry3d/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
from .plane import Plane
from ._2d import Base2DIn3D

try:
from ladybug.color import Color
except ImportError:
Color = None
print('Failed to import ladybug Color.\n'
'Importing mesh colors in from_dict methods will not be availabe.')

try:
from itertools import izip as zip # python 2
except ImportError:
Expand Down Expand Up @@ -61,6 +68,22 @@ def __init__(self, vertices, faces, colors=None):
self._face_normals = None
self._vertex_normals = None

@classmethod
def from_dict(cls, data):
"""Create a Mesh3D from a dictionary.
Args:
data: {
"vertices": [{"x": 0, "y": 0}, {"x": 10, "y": 0}, {"x": 0, "y": 10}],
"faces": [(0, 1, 2)]
}
"""
colors = None
if Color is not None and 'colors' in data and data['colors'] is not None:
colors = tuple(Color.from_dict(col) for col in data['colors'])
return cls(tuple(Point3D.from_dict(pt) for pt in data['vertices']),
data['faces'], colors)

@classmethod
def from_face_vertices(cls, faces, purge=True):
"""Create a mesh from a list of faces with each face defined by a list of Point3Ds.
Expand Down Expand Up @@ -234,6 +257,14 @@ def scale(self, factor, origin=None):
_verts = tuple(pt.scale(factor, origin) for pt in self.vertices)
return self._mesh_scale(_verts, factor)

def to_dict(self):
"""Get Mesh3D as a dictionary."""
colors = None
if self.colors is not None:
colors = [col.to_dict() for col in self.colors]
return {'vertices': [pt.to_dict() for pt in self.vertices],
'faces': self.faces, 'colors': colors}

def _calculate_face_areas_and_normals(self):
"""Calculate face areas and normals from vertices."""
_f_norm = []
Expand Down
25 changes: 24 additions & 1 deletion ladybug_geometry/geometry3d/plane.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ def __init__(self, n=Vector3D(0, 0, 1), o=Point3D(0, 0, 0), x=None):
if self._n.x == 0 and self._n.y == 0:
self._x = Vector3D(1, 0, 0)
else:
self._x = Vector3D(self._n.y, -self._n.x, 0)
x = Vector3D(self._n.y, -self._n.x, 0)
x = x.normalize()
self._x = x
else:
assert isinstance(x, Vector3D), \
"Expected Vector3D for plane X-axis. Got {}.".format(type(x))
Expand All @@ -56,6 +58,21 @@ def __init__(self, n=Vector3D(0, 0, 1), o=Point3D(0, 0, 0), x=None):
self._x = x
self._y = self._n.cross(self._x)

@classmethod
def from_dict(cls, data):
"""Create a Plane from a dictionary.
Args:
data: {
"n": {"x": 0, "y": 0, "z": 1},
"o": {"x": 0, "y": 10, "z": 0},
"x": {"x": 1, "y": 0, "z": 0}}
"""
x = None
if 'x' in data and data['x'] is not None:
x = Vector3D.from_dict(data['x'])
return cls(Vector3D.from_dict(data['n']), Point3D.from_dict(data['o']), x)

@classmethod
def from_three_points(cls, o, p2, p3):
"""Initialize a Plane from three Point3D objects that are not co-linear.
Expand Down Expand Up @@ -307,6 +324,12 @@ def duplicate(self):
"""Get a copy of this object."""
return self.__copy__()

def to_dict(self):
"""Get Plane as a dictionary."""
return {'n': self.n.to_dict(),
'o': self.o.to_dict(),
'x': self.x.to_dict()}

def __copy__(self):
return self.__class__(self.n, self.o)

Expand Down
18 changes: 18 additions & 0 deletions ladybug_geometry/geometry3d/pointvector.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ def __init__(self, x=0, y=0, z=0):
self._y = y
self._z = z

@classmethod
def from_dict(cls, data):
"""Create a Vector3D/Point3D from a dictionary.
Args:
data: {
"x": 10,
"y": 0,
"z": 0}
"""
return cls(data['x'], data['y'], data['z'])

@property
def x(self):
"""Get the X coordinate."""
Expand Down Expand Up @@ -117,6 +129,12 @@ def duplicate(self):
"""Get a copy of this vector."""
return self.__copy__()

def to_dict(self):
"""Get Vector3D/Point3D as a dictionary."""
return {'x': self.x,
'y': self.y,
'z': self.z}

@staticmethod
def _reflect(vec, normal):
"""Hidden reflection method used by both Point3D and Vector3D."""
Expand Down
Loading

0 comments on commit 00d15c9

Please sign in to comment.