fairyfly-core 0.2.3__py3-none-any.whl → 0.2.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
fairyfly/colorobj.py ADDED
@@ -0,0 +1,346 @@
1
+ # coding=utf-8
2
+ """Module for coloring geometry with attributes."""
3
+ from __future__ import division
4
+
5
+ from .shape import Shape
6
+ from .boundary import Boundary
7
+ from .search import get_attr_nested
8
+
9
+ from ladybug.graphic import GraphicContainer
10
+ from ladybug.legend import LegendParameters, LegendParametersCategorized
11
+ from ladybug_geometry.geometry3d.pointvector import Point3D
12
+
13
+
14
+ class _ColorObject(object):
15
+ """Base class for visualization objects.
16
+
17
+ Properties:
18
+ * legend_parameters
19
+ * attr_name
20
+ * attr_name_end
21
+ * attributes
22
+ * attributes_unique
23
+ * attributes_original
24
+ * min_point
25
+ * max_point
26
+ * graphic_container
27
+ """
28
+ __slots__ = ('_attr_name', '_legend_parameters', '_attr_name_end',
29
+ '_attributes', '_attributes_unique', '_attributes_original',
30
+ '_min_point', '_max_point')
31
+
32
+ def __init__(self, legend_parameters=None):
33
+ """Initialize ColorObject."""
34
+ # assign the legend parameters of this object
35
+ self.legend_parameters = legend_parameters
36
+
37
+ self._attr_name = None
38
+ self._attr_name_end = None
39
+ self._attributes = None
40
+ self._attributes_unique = None
41
+ self._attributes_original = None
42
+ self._min_point = None
43
+ self._max_point = None
44
+
45
+ @property
46
+ def legend_parameters(self):
47
+ """Get or set the legend parameters."""
48
+ return self._legend_parameters
49
+
50
+ @legend_parameters.setter
51
+ def legend_parameters(self, value):
52
+ if value is not None:
53
+ assert isinstance(value, LegendParameters) and not \
54
+ isinstance(value, LegendParametersCategorized), \
55
+ 'Expected LegendParameters. Got {}.'.format(type(value))
56
+ self._legend_parameters = value
57
+ else:
58
+ self._legend_parameters = LegendParameters()
59
+
60
+ @property
61
+ def attr_name(self):
62
+ """Get a text string of an attribute that the input objects should have."""
63
+ return self._attr_name
64
+
65
+ @property
66
+ def attr_name_end(self):
67
+ """Get text for the last attribute in the attr_name.
68
+
69
+ Useful when attr_name is nested.
70
+ """
71
+ return self._attr_name_end
72
+
73
+ @property
74
+ def attributes(self):
75
+ """Get a tuple of text for the attributes assigned to the objects.
76
+
77
+ If the input attr_name is a valid attribute for the object but None is
78
+ assigned, the output will be 'None'. If the input attr_name is not valid
79
+ for the input object, 'N/A' will be returned.
80
+ """
81
+ return self._attributes
82
+
83
+ @property
84
+ def attributes_unique(self):
85
+ """Get a tuple of text for the unique attributes assigned to the objects."""
86
+ return self._attributes_unique
87
+
88
+ @property
89
+ def attributes_original(self):
90
+ """Get a tuple of objects for the attributes assigned to the objects.
91
+
92
+ These will follow the original object typing of the attribute and won't
93
+ be strings like the attributes.
94
+ """
95
+ return self._attributes_original
96
+
97
+ @property
98
+ def min_point(self):
99
+ """Get a Point3D for the minimum of the box around the objects."""
100
+ return self._min_point
101
+
102
+ @property
103
+ def max_point(self):
104
+ """Get a Point3D for the maximum of the box around the objects."""
105
+ return self._max_point
106
+
107
+ @property
108
+ def graphic_container(self):
109
+ """Get a ladybug GraphicContainer that relates to this object.
110
+
111
+ The GraphicContainer possesses almost all things needed to visualize the
112
+ ColorShapes object including the legend, value_colors, etc.
113
+ """
114
+ # produce a range of values from the collected attributes
115
+ attr_dict = {i: val for i, val in enumerate(self.attributes_unique)}
116
+ attr_dict_rev = {val: i for i, val in attr_dict.items()}
117
+ try:
118
+ values = tuple(attr_dict_rev[r_attr] for r_attr in self.attributes)
119
+ except KeyError: # possibly caused by float cast to -0.0
120
+ values = []
121
+ for r_attr in self.attributes:
122
+ if r_attr == '-0.0':
123
+ values.append(attr_dict_rev['0.0'])
124
+ else:
125
+ values.append(attr_dict_rev[r_attr])
126
+
127
+ # produce legend parameters with an ordinal dict for the attributes
128
+ l_par = self.legend_parameters.duplicate()
129
+ if l_par.is_segment_count_default:
130
+ l_par.segment_count = len(self.attributes_unique)
131
+ l_par.ordinal_dictionary = attr_dict
132
+ if l_par.is_title_default:
133
+ l_par.title = self.attr_name_end.replace('_', ' ').title()
134
+
135
+ return GraphicContainer(values, self.min_point, self.max_point, l_par)
136
+
137
+ def _process_attribute_name(self, attr_name):
138
+ """Process the attribute name and assign it to this object."""
139
+ self._attr_name = str(attr_name)
140
+ at_split = self._attr_name.split('.')
141
+ if len(at_split) == 1:
142
+ self._attr_name_end = at_split[-1]
143
+ elif at_split[-1] == 'display_name':
144
+ self._attr_name_end = at_split[-2]
145
+ elif at_split[-1] == '__name__' and at_split[-2] == '__class__':
146
+ self._attr_name_end = at_split[-3]
147
+ else:
148
+ self._attr_name_end = at_split[-1]
149
+
150
+ def _process_attributes(self, ff_objs):
151
+ """Process the attributes of fairyfly objects."""
152
+ nd = self.legend_parameters.decimal_count
153
+ attributes = [get_attr_nested(obj, self._attr_name, nd, False)
154
+ for obj in ff_objs]
155
+ attributes_unique = set(attributes)
156
+ float_attr = [atr for atr in attributes_unique if isinstance(atr, float)]
157
+ str_attr = [str(atr) for atr in attributes_unique if not isinstance(atr, float)]
158
+ float_attr.sort()
159
+ str_attr.sort()
160
+ self._attributes = tuple(str(val) for val in attributes)
161
+ self._attributes_unique = tuple(str_attr) + tuple(str(val) for val in float_attr)
162
+ self._attributes_original = \
163
+ tuple(get_attr_nested(obj, self._attr_name, cast_to_str=False)
164
+ for obj in ff_objs)
165
+
166
+ def _calculate_min_max(self, ff_objs):
167
+ """Calculate maximum and minimum Point3D for a set of shapes."""
168
+ st_rm_min, st_rm_max = ff_objs[0].min, ff_objs[0].max
169
+ min_pt = [st_rm_min.x, st_rm_min.y, st_rm_min.z]
170
+ max_pt = [st_rm_max.x, st_rm_max.y, st_rm_max.z]
171
+
172
+ for shape in ff_objs[1:]:
173
+ rm_min, rm_max = shape.min, shape.max
174
+ if rm_min.x < min_pt[0]:
175
+ min_pt[0] = rm_min.x
176
+ if rm_min.y < min_pt[1]:
177
+ min_pt[1] = rm_min.y
178
+ if rm_min.z < min_pt[2]:
179
+ min_pt[2] = rm_min.z
180
+ if rm_max.x > max_pt[0]:
181
+ max_pt[0] = rm_max.x
182
+ if rm_max.y > max_pt[1]:
183
+ max_pt[1] = rm_max.y
184
+ if rm_max.z > max_pt[2]:
185
+ max_pt[2] = rm_max.z
186
+
187
+ self._min_point = Point3D(min_pt[0], min_pt[1], min_pt[2])
188
+ self._max_point = Point3D(max_pt[0], max_pt[1], max_pt[2])
189
+
190
+ def ToString(self):
191
+ """Overwrite .NET ToString."""
192
+ return self.__repr__()
193
+
194
+
195
+ class ColorShape(_ColorObject):
196
+ """Object for visualizing shape attributes.
197
+
198
+ Args:
199
+ shapes: An array of fairyfly Shapes, which will be colored with the attribute.
200
+ attr_name: A text string of an attribute that the input shapes should have.
201
+ This can have '.' that separate the nested attributes from one another.
202
+ For example, 'properties.therm.materials'.
203
+ legend_parameters: An optional LegendParameter object to change the display
204
+ of the ColorShape (Default: None).
205
+
206
+ Properties:
207
+ * shapes
208
+ * attr_name
209
+ * legend_parameters
210
+ * attr_name_end
211
+ * attributes
212
+ * attributes_unique
213
+ * attributes_original
214
+ * geometry
215
+ * graphic_container
216
+ * min_point
217
+ * max_point
218
+ """
219
+ __slots__ = ('_shapes',)
220
+
221
+ def __init__(self, shapes, attr_name, legend_parameters=None):
222
+ """Initialize ColorShape."""
223
+ try: # check the input shapes
224
+ shapes = tuple(shapes)
225
+ except TypeError:
226
+ raise TypeError('Input shapes must be an array. Got {}.'.format(type(shapes)))
227
+ assert len(shapes) > 0, 'ColorShapes must have at least one shape.'
228
+ for shape in shapes:
229
+ assert isinstance(shape, Shape), 'Expected fairyfly Shape for ' \
230
+ 'ColorShape shapes. Got {}.'.format(type(shape))
231
+ self._shapes = shapes
232
+ self._calculate_min_max(shapes)
233
+
234
+ # assign the legend parameters of this object
235
+ self.legend_parameters = legend_parameters
236
+
237
+ # get the attributes of the input shapes
238
+ self._process_attribute_name(attr_name)
239
+ self._process_attributes(shapes)
240
+
241
+ @property
242
+ def shapes(self):
243
+ """Get a tuple of fairyfly Shapes assigned to this object."""
244
+ return self._shapes
245
+
246
+ @property
247
+ def geometry(self):
248
+ """Get a nested array with each sub-array having the Face3D of each shape."""
249
+ return [s.geometry for s in self.shapes]
250
+
251
+ def __repr__(self):
252
+ """Color Shape representation."""
253
+ return 'Color Shape:\n{} Shapes\n{}'.format(len(self.shapes), self.attr_name_end)
254
+
255
+
256
+ class ColorBoundary(_ColorObject):
257
+ """Object for visualizing boundary attributes.
258
+
259
+ Args:
260
+ boundaries: An array of fairyfly Boundaries which will be colored with
261
+ their attributes.
262
+ attr_name: A text string of an attribute that the input faces should have.
263
+ This can have '.' that separate the nested attributes from one another.
264
+ For example, 'properties.therm.condition.temperature'.
265
+ legend_parameters: An optional LegendParameter object to change the display
266
+ of the ColorBoundary (Default: None).
267
+
268
+ Properties:
269
+ * boundaries
270
+ * attr_name
271
+ * legend_parameters
272
+ * attr_name_end
273
+ * attributes_unique
274
+ * attributes
275
+ * attributes_original
276
+ * flat_geometry
277
+ * graphic_container
278
+ * min_point
279
+ * max_point
280
+ """
281
+ __slots__ = ('_boundaries',)
282
+
283
+ def __init__(self, boundaries, attr_name, legend_parameters=None):
284
+ """Initialize ColorBoundary."""
285
+ try: # check the input boundaries
286
+ boundaries = tuple(boundaries)
287
+ except TypeError:
288
+ raise TypeError(
289
+ 'Input boundaries must be an array. Got {}.'.format(type(boundaries)))
290
+ assert len(boundaries) > 0, 'ColorBoundary must have at least one boundary.'
291
+ for bound in boundaries:
292
+ assert isinstance(bound, Boundary), 'Expected fairyfly Boundary for ' \
293
+ 'ColorBoundary. Got {}.'.format(type(bound))
294
+ self._boundaries = boundaries
295
+ self._calculate_min_max(boundaries)
296
+
297
+ # assign the legend parameters of this object
298
+ self.legend_parameters = legend_parameters
299
+
300
+ # get the attributes of the input faces
301
+ self._process_attribute_name(attr_name)
302
+ self._process_attributes(boundaries)
303
+
304
+ @property
305
+ def boundaries(self):
306
+ """Get the fairyfly Boundaries assigned to this object.
307
+ """
308
+ return self._boundaries
309
+
310
+ @property
311
+ def attributes(self):
312
+ """Get a tuple of text for the attributes assigned to the objects.
313
+
314
+ If the input attr_name is a valid attribute for the object but None is
315
+ assigned, the output will be 'None'. If the input attr_name is not valid
316
+ for the input object, 'N/A' will be returned.
317
+ """
318
+ flat_attr = []
319
+ for bnd, attrib in zip(self._boundaries, self._attributes):
320
+ flat_attr.extend([attrib] * len(bnd))
321
+ return flat_attr
322
+
323
+ @property
324
+ def attributes_original(self):
325
+ """Get a tuple of objects for the attributes assigned to the objects.
326
+
327
+ These will follow the original object typing of the attribute and won't
328
+ be strings like the attributes.
329
+ """
330
+ flat_attr = []
331
+ for bnd, attrib in zip(self._boundaries, self._attributes_original):
332
+ flat_attr.extend([attrib] * len(bnd))
333
+ return flat_attr
334
+
335
+ @property
336
+ def flat_geometry(self):
337
+ """Get an array of LineSegment3D on this object.
338
+
339
+ The geometries here align with the attributes and graphic_container colors.
340
+ """
341
+ return [lin for bound in self._boundaries for lin in bound]
342
+
343
+ def __repr__(self):
344
+ """Color Shape representation."""
345
+ return 'Color Boundary:\n{} Boundary\n{}'.format(
346
+ len(self.boundaries), self.attr_name_end)
fairyfly/dictutil.py ADDED
@@ -0,0 +1,40 @@
1
+ # coding=utf-8
2
+ """Utilities to convert any dictionary to Python objects.
3
+
4
+ Note that importing this module will import almost all modules within the
5
+ library in order to be able to re-serialize almost any dictionary produced
6
+ from the library.
7
+ """
8
+ from fairyfly.model import Model
9
+ from fairyfly.shape import Shape
10
+ from fairyfly.boundary import Boundary
11
+
12
+
13
+ def dict_to_object(fairyfly_dict, raise_exception=True):
14
+ """Re-serialize a dictionary of almost any object within fairyfly.
15
+
16
+ This includes any Model, Shape or Boundary object.
17
+
18
+ Args:
19
+ fairyfly_dict: A dictionary of any Fairyfly object. Note
20
+ that this should be a non-abridged dictionary to be valid.
21
+ raise_exception: Boolean to note whether an exception should be raised
22
+ if the object is not identified as a part of fairyfly.
23
+ Default: True.
24
+
25
+ Returns:
26
+ A Python object derived from the input fairyfly_dict.
27
+ """
28
+ try: # get the type key from the dictionary
29
+ obj_type = fairyfly_dict['type']
30
+ except KeyError:
31
+ raise ValueError('Fairyfly dictionary lacks required "type" key.')
32
+
33
+ if obj_type == 'Model':
34
+ return Model.from_dict(fairyfly_dict)
35
+ elif obj_type == 'Shape':
36
+ return Shape.from_dict(fairyfly_dict)
37
+ elif obj_type == 'Boundary':
38
+ return Boundary.from_dict(fairyfly_dict)
39
+ elif raise_exception:
40
+ raise ValueError('{} is not a recognized fairyfly object'.format(obj_type))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fairyfly-core
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: A library to represent construction detail geometry for environmental simulation.
5
5
  Home-page: https://github.com/ladybug-tools/fairyfly-core
6
6
  Author: Ladybug Tools
@@ -4,8 +4,10 @@ fairyfly/_base.py,sha256=AVtekYu0x4nXrCC7SBhHwpTV4TptE7gWDf8X6dS93iY,11502
4
4
  fairyfly/_lockable.py,sha256=jmTVsXEWmYyEHpL3WsQZ_ItGOVVlJht1z3NFn07DIp0,3707
5
5
  fairyfly/boundary.py,sha256=e-O-3IxgU0KbZX3BbW6R1DmFaFZgLMghsaab9NqvHJk,13056
6
6
  fairyfly/checkdup.py,sha256=wO_LN6Mr72i-tlrGxy1qbvfzCwz5gDcDxXfaQumkz7o,8207
7
+ fairyfly/colorobj.py,sha256=zTs9m3xPr5m75IbPukPE0t4kLKAaiuLFJMVbosah7c0,12700
7
8
  fairyfly/config.json,sha256=Sd5gBLwKM8e88InpFFzYG8UB77WHlfQPT4jap09D6s4,131
8
9
  fairyfly/config.py,sha256=VfQ-vRX-q1CLja4xFMGVte-FpdFStdMXj98WKpmMOFA,10633
10
+ fairyfly/dictutil.py,sha256=nVwoWG4FsoV9nYjQxI3jBxVeZcdy2TQd1FKhEquVLR8,1469
9
11
  fairyfly/extensionutil.py,sha256=5MQV0gZBc6_vfbVr4j8l_8JslMsj4bBbsZ4yqZ2VPG4,2716
10
12
  fairyfly/logutil.py,sha256=Urxl0U14-qffIt6NiGLT6pV-796oAGUvnnf2ypj1j9Q,2564
11
13
  fairyfly/model.py,sha256=2S7yGWZCNa7To1CyAhZ44IgIgBcMveu1t6hTfSo4czk,44282
@@ -20,9 +22,9 @@ fairyfly/writer/__init__.py,sha256=D4FdE2LUVCMvfud3qL2UquH1uCz3EGlKOEMgstMjtXc,2
20
22
  fairyfly/writer/boundary.py,sha256=skpfcFyxWLvjTSStbCh5f4wehSHeI-eKMgoeSCzvO7Q,190
21
23
  fairyfly/writer/model.py,sha256=6Y15NVGCjmYcBdimxyQWnE8AAhQAJi6A7QfMi4P49M4,181
22
24
  fairyfly/writer/shape.py,sha256=pMlLbk3WkrNPbNvowDQ_JFgNkhHNOlqL52TMZKB7zKo,181
23
- fairyfly_core-0.2.3.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
24
- fairyfly_core-0.2.3.dist-info/METADATA,sha256=bdmVF7kM_c_Z11yK7yUSPekfDjcego07zbLHr59iF8Q,3291
25
- fairyfly_core-0.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
- fairyfly_core-0.2.3.dist-info/entry_points.txt,sha256=KM3hAPV0ivux3CUx1-pDT0BL1WSUFt2jmT4i_MFbdzc,47
27
- fairyfly_core-0.2.3.dist-info/top_level.txt,sha256=8ZUsvZOSB3g5enmv1R0vxJilNeW8Uft5KrTL0rUNj7k,9
28
- fairyfly_core-0.2.3.dist-info/RECORD,,
25
+ fairyfly_core-0.2.5.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
26
+ fairyfly_core-0.2.5.dist-info/METADATA,sha256=DAuN25_S3DfCvtXoKd0zkgToyAQ1nMQ1b0QANsC4kQk,3291
27
+ fairyfly_core-0.2.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
28
+ fairyfly_core-0.2.5.dist-info/entry_points.txt,sha256=KM3hAPV0ivux3CUx1-pDT0BL1WSUFt2jmT4i_MFbdzc,47
29
+ fairyfly_core-0.2.5.dist-info/top_level.txt,sha256=8ZUsvZOSB3g5enmv1R0vxJilNeW8Uft5KrTL0rUNj7k,9
30
+ fairyfly_core-0.2.5.dist-info/RECORD,,