topologicpy 0.5.8__py3-none-any.whl → 6.0.0__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.
Files changed (94) hide show
  1. topologicpy/Aperture.py +72 -72
  2. topologicpy/Cell.py +2169 -2169
  3. topologicpy/CellComplex.py +1137 -1137
  4. topologicpy/Cluster.py +1288 -1280
  5. topologicpy/Color.py +423 -393
  6. topologicpy/Context.py +79 -79
  7. topologicpy/DGL.py +3213 -3136
  8. topologicpy/Dictionary.py +698 -695
  9. topologicpy/Edge.py +1187 -1187
  10. topologicpy/EnergyModel.py +1180 -1171
  11. topologicpy/Face.py +2141 -2141
  12. topologicpy/Graph.py +7768 -7700
  13. topologicpy/Grid.py +353 -353
  14. topologicpy/Helper.py +507 -507
  15. topologicpy/Honeybee.py +461 -461
  16. topologicpy/Matrix.py +271 -271
  17. topologicpy/Neo4j.py +521 -521
  18. topologicpy/Plotly.py +2 -2
  19. topologicpy/Polyskel.py +541 -541
  20. topologicpy/Shell.py +1768 -1768
  21. topologicpy/Speckle.py +508 -508
  22. topologicpy/Topology.py +7060 -6988
  23. topologicpy/Vector.py +905 -905
  24. topologicpy/Vertex.py +1585 -1585
  25. topologicpy/Wire.py +3050 -3050
  26. topologicpy/__init__.py +22 -38
  27. topologicpy/version.py +1 -0
  28. {topologicpy-0.5.8.dist-info → topologicpy-6.0.0.dist-info}/LICENSE +661 -704
  29. topologicpy-6.0.0.dist-info/METADATA +751 -0
  30. topologicpy-6.0.0.dist-info/RECORD +32 -0
  31. topologicpy/bin/linux/topologic/__init__.py +0 -2
  32. topologicpy/bin/linux/topologic/libTKBO-6bdf205d.so.7.7.0 +0 -0
  33. topologicpy/bin/linux/topologic/libTKBRep-2960a069.so.7.7.0 +0 -0
  34. topologicpy/bin/linux/topologic/libTKBool-c44b74bd.so.7.7.0 +0 -0
  35. topologicpy/bin/linux/topologic/libTKFillet-9a670ba0.so.7.7.0 +0 -0
  36. topologicpy/bin/linux/topologic/libTKG2d-8f31849e.so.7.7.0 +0 -0
  37. topologicpy/bin/linux/topologic/libTKG3d-4c6bce57.so.7.7.0 +0 -0
  38. topologicpy/bin/linux/topologic/libTKGeomAlgo-26066fd9.so.7.7.0 +0 -0
  39. topologicpy/bin/linux/topologic/libTKGeomBase-2116cabe.so.7.7.0 +0 -0
  40. topologicpy/bin/linux/topologic/libTKMath-72572fa8.so.7.7.0 +0 -0
  41. topologicpy/bin/linux/topologic/libTKMesh-2a060427.so.7.7.0 +0 -0
  42. topologicpy/bin/linux/topologic/libTKOffset-6cab68ff.so.7.7.0 +0 -0
  43. topologicpy/bin/linux/topologic/libTKPrim-eb1262b3.so.7.7.0 +0 -0
  44. topologicpy/bin/linux/topologic/libTKShHealing-e67e5cc7.so.7.7.0 +0 -0
  45. topologicpy/bin/linux/topologic/libTKTopAlgo-e4c96c33.so.7.7.0 +0 -0
  46. topologicpy/bin/linux/topologic/libTKernel-fb7fe3b7.so.7.7.0 +0 -0
  47. topologicpy/bin/linux/topologic/libgcc_s-32c1665e.so.1 +0 -0
  48. topologicpy/bin/linux/topologic/libstdc++-672d7b41.so.6.0.30 +0 -0
  49. topologicpy/bin/linux/topologic/topologic.cpython-310-x86_64-linux-gnu.so +0 -0
  50. topologicpy/bin/linux/topologic/topologic.cpython-311-x86_64-linux-gnu.so +0 -0
  51. topologicpy/bin/linux/topologic/topologic.cpython-38-x86_64-linux-gnu.so +0 -0
  52. topologicpy/bin/linux/topologic/topologic.cpython-39-x86_64-linux-gnu.so +0 -0
  53. topologicpy/bin/linux/topologic.libs/libTKBO-6bdf205d.so.7.7.0 +0 -0
  54. topologicpy/bin/linux/topologic.libs/libTKBRep-2960a069.so.7.7.0 +0 -0
  55. topologicpy/bin/linux/topologic.libs/libTKBool-c44b74bd.so.7.7.0 +0 -0
  56. topologicpy/bin/linux/topologic.libs/libTKFillet-9a670ba0.so.7.7.0 +0 -0
  57. topologicpy/bin/linux/topologic.libs/libTKG2d-8f31849e.so.7.7.0 +0 -0
  58. topologicpy/bin/linux/topologic.libs/libTKG3d-4c6bce57.so.7.7.0 +0 -0
  59. topologicpy/bin/linux/topologic.libs/libTKGeomAlgo-26066fd9.so.7.7.0 +0 -0
  60. topologicpy/bin/linux/topologic.libs/libTKGeomBase-2116cabe.so.7.7.0 +0 -0
  61. topologicpy/bin/linux/topologic.libs/libTKMath-72572fa8.so.7.7.0 +0 -0
  62. topologicpy/bin/linux/topologic.libs/libTKMesh-2a060427.so.7.7.0 +0 -0
  63. topologicpy/bin/linux/topologic.libs/libTKOffset-6cab68ff.so.7.7.0 +0 -0
  64. topologicpy/bin/linux/topologic.libs/libTKPrim-eb1262b3.so.7.7.0 +0 -0
  65. topologicpy/bin/linux/topologic.libs/libTKShHealing-e67e5cc7.so.7.7.0 +0 -0
  66. topologicpy/bin/linux/topologic.libs/libTKTopAlgo-e4c96c33.so.7.7.0 +0 -0
  67. topologicpy/bin/linux/topologic.libs/libTKernel-fb7fe3b7.so.7.7.0 +0 -0
  68. topologicpy/bin/linux/topologic.libs/libgcc_s-32c1665e.so.1 +0 -0
  69. topologicpy/bin/linux/topologic.libs/libstdc++-672d7b41.so.6.0.30 +0 -0
  70. topologicpy/bin/macos/topologic/__init__.py +0 -2
  71. topologicpy/bin/windows/topologic/TKBO-f6b191de.dll +0 -0
  72. topologicpy/bin/windows/topologic/TKBRep-e56a600e.dll +0 -0
  73. topologicpy/bin/windows/topologic/TKBool-7b8d47ae.dll +0 -0
  74. topologicpy/bin/windows/topologic/TKFillet-0ddbf0a8.dll +0 -0
  75. topologicpy/bin/windows/topologic/TKG2d-2e2dee3d.dll +0 -0
  76. topologicpy/bin/windows/topologic/TKG3d-6674513d.dll +0 -0
  77. topologicpy/bin/windows/topologic/TKGeomAlgo-d240e370.dll +0 -0
  78. topologicpy/bin/windows/topologic/TKGeomBase-df87aba5.dll +0 -0
  79. topologicpy/bin/windows/topologic/TKMath-45bd625a.dll +0 -0
  80. topologicpy/bin/windows/topologic/TKMesh-d6e826b1.dll +0 -0
  81. topologicpy/bin/windows/topologic/TKOffset-79b9cc94.dll +0 -0
  82. topologicpy/bin/windows/topologic/TKPrim-aa430a86.dll +0 -0
  83. topologicpy/bin/windows/topologic/TKShHealing-bb48be89.dll +0 -0
  84. topologicpy/bin/windows/topologic/TKTopAlgo-7d0d1e22.dll +0 -0
  85. topologicpy/bin/windows/topologic/TKernel-08c8cfbb.dll +0 -0
  86. topologicpy/bin/windows/topologic/__init__.py +0 -2
  87. topologicpy/bin/windows/topologic/topologic.cp310-win_amd64.pyd +0 -0
  88. topologicpy/bin/windows/topologic/topologic.cp311-win_amd64.pyd +0 -0
  89. topologicpy/bin/windows/topologic/topologic.cp38-win_amd64.pyd +0 -0
  90. topologicpy/bin/windows/topologic/topologic.cp39-win_amd64.pyd +0 -0
  91. topologicpy-0.5.8.dist-info/METADATA +0 -96
  92. topologicpy-0.5.8.dist-info/RECORD +0 -91
  93. {topologicpy-0.5.8.dist-info → topologicpy-6.0.0.dist-info}/WHEEL +0 -0
  94. {topologicpy-0.5.8.dist-info → topologicpy-6.0.0.dist-info}/top_level.txt +0 -0
topologicpy/Color.py CHANGED
@@ -1,393 +1,423 @@
1
- # Copyright (C) 2024
2
- # Wassim Jabi <wassim.jabi@gmail.com>
3
- #
4
- # This program is free software: you can redistribute it and/or modify it under
5
- # the terms of the GNU Affero General Public License as published by the Free Software
6
- # Foundation, either version 3 of the License, or (at your option) any later
7
- # version.
8
- #
9
- # This program is distributed in the hope that it will be useful, but WITHOUT
10
- # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
- # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
12
- # details.
13
- #
14
- # You should have received a copy of the GNU Affero General Public License along with
15
- # this program. If not, see <https://www.gnu.org/licenses/>.
16
-
17
- import plotly.colors
18
- import math
19
-
20
- class Color:
21
- @staticmethod
22
- def ByCSSNamedColor(color, alpha: float = None):
23
- """
24
- Creates a Color from a CSS named color string. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
25
-
26
- Parameters
27
- ----------
28
- color : str
29
- A CSS named color.
30
- alpha : float , optional
31
- THe desired alpha (transparency value). The default is None which means no alpha value will be included in the returned list.
32
-
33
- Returns
34
- -------
35
- list
36
- The color expressed as an [r, g, b] or an [r, g, b, a] list.
37
- """
38
- import webcolors
39
- if not alpha == None:
40
- if not 0.0 <= alpha <= 1.0:
41
- print("Color.ByCSSNamedColor - Error: alpha is not within the valid range of 0 to 1. Returning None.")
42
- return None
43
- try:
44
- # Get RGB values from the named CSS color
45
- rgbList = list(webcolors.name_to_rgb(color))
46
- if not alpha == None:
47
- rgbList.append(alpha)
48
- return rgbList
49
-
50
- except ValueError:
51
- print(f"Color.ByCSSNamedColor - Error: '{color}' is not a valid named CSS color. Returning None.")
52
- return None
53
-
54
- @staticmethod
55
- def ByHEX(hex: str, alpha: float = None):
56
- """
57
- Converts a hexadecimal color string to RGB color values.
58
-
59
- Parameters
60
- ----------
61
- hex : str
62
- A hexadecimal color string in the format '#RRGGBB'.
63
- alpha : float , optional
64
- The transparency value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is None
65
- which means no transparency value will be included in the returned color.
66
- Returns
67
- -------
68
- list
69
- The color expressed as an [r, g, b] or an [r, g, b, a] list.
70
-
71
- """
72
- if not isinstance(hex, str):
73
- print("Color.HEXtoRGB - Error: The input hex parameter is not a valid string. Returning None.")
74
- return None
75
- if not alpha == None:
76
- if not 0.0 <= alpha <= 1.0:
77
- print("Color.ByHEX - Error: alpha is not within the valid range of 0 to 1. Returning None.")
78
- return None
79
- hex = hex.lstrip('#')
80
- if len(hex) != 6:
81
- print("Color.HEXtoRGB - Error: Invalid hexadecimal color format. It should be a 6-digit hex value. Returning None.")
82
- return None
83
- r = int(hex[0:2], 16)
84
- g = int(hex[2:4], 16)
85
- b = int(hex[4:6], 16)
86
- rgbList = [r, g, b]
87
- if not alpha == None:
88
- rgbList.append(alpha)
89
- return rgbList
90
-
91
- @staticmethod
92
- def ByValueInRange(value: float = 0.5, minValue: float = 0.0, maxValue: float = 1.0, alpha: float = None, colorScale="viridis"):
93
- """
94
- Returns the r, g, b, (and optionally) a list of numbers representing the red, green, blue and alpha color elements.
95
-
96
- Parameters
97
- ----------
98
- value : float , optional
99
- The input value. The default is 0.5.
100
- minValue : float , optional
101
- the input minimum value. The default is 0.0.
102
- maxValue : float , optional
103
- The input maximum value. The default is 1.0.
104
- alpha : float , optional
105
- The alpha (transparency) value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is 1.0.
106
- useAlpha : bool , optional
107
- If set to True, the returns list includes the alpha value as a fourth element in the list.
108
- colorScale : str , optional
109
- The desired type of plotly color scales to use (e.g. "Viridis", "Plasma"). The default is "Viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/.
110
-
111
- Returns
112
- -------
113
- list
114
- The color expressed as an [r, g, b] or an [r, g, b, a] list.
115
-
116
- """
117
- if not alpha == None:
118
- if not 0.0 <= alpha <= 1.0:
119
- print("Color.ByValueInRange - Error: alpha is not within the valid range of 0 to 1. Returning None.")
120
- return None
121
- # Code based on: https://stackoverflow.com/questions/62710057/access-color-from-plotly-color-scale
122
-
123
- def hex_to_rgb(value):
124
- value = str(value)
125
- value = value.lstrip('#')
126
- lv = len(value)
127
- returnValue = tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
128
- return str(returnValue)
129
-
130
- def get_color(colorscale_name, loc):
131
- from _plotly_utils.basevalidators import ColorscaleValidator
132
- # first parameter: Name of the property being validated
133
- # second parameter: a string, doesn't really matter in our use case
134
- cv = ColorscaleValidator("colorscale", "")
135
- # colorscale will be a list of lists: [[loc1, "rgb1"], [loc2, "rgb2"], ...]
136
- colorscale = cv.validate_coerce(colorscale_name)
137
- if hasattr(loc, "__iter__"):
138
- return [get_continuous_color(colorscale, x) for x in loc]
139
- color = get_continuous_color(colorscale, loc)
140
- color = color.replace("rgb", "")
141
- color = color.replace("(", "")
142
- color = color.replace(")", "")
143
- color = color.split(",")
144
- final_colors = []
145
- for c in color:
146
- final_colors.append(math.floor(float(c)))
147
- return final_colors
148
-
149
- def get_continuous_color(colorscale, intermed):
150
- """
151
- Plotly continuous colorscales assign colors to the range [0, 1]. This function computes the intermediate
152
- color for any value in that range.
153
-
154
- Plotly doesn't make the colorscales directly accessible in a common format.
155
- Some are ready to use:
156
-
157
- colorscale = plotly.colors.PLOTLY_SCALES["Greens"]
158
-
159
- Others are just swatches that need to be constructed into a colorscale:
160
-
161
- viridis_colors, scale = plotly.colors.convert_colors_to_same_type(plotly.colors.sequential.Viridis)
162
- colorscale = plotly.colors.make_colorscale(viridis_colors, scale=scale)
163
-
164
- :param colorscale: A plotly continuous colorscale defined with RGB string colors.
165
- :param intermed: value in the range [0, 1]
166
- :return: color in rgb string format
167
- :rtype: str
168
- """
169
-
170
- if len(colorscale) < 1:
171
- raise ValueError("colorscale must have at least one color")
172
- if intermed <= 0 or len(colorscale) == 1:
173
- c = colorscale[0][1]
174
- return c if c[0] != "#" else hex_to_rgb(c)
175
- if intermed >= 1:
176
- c = colorscale[-1][1]
177
- return c if c[0] != "#" else hex_to_rgb(c)
178
- for cutoff, color in colorscale:
179
- if intermed > cutoff:
180
- low_cutoff, low_color = cutoff, color
181
- else:
182
- high_cutoff, high_color = cutoff, color
183
- break
184
- if (low_color[0] == "#") or (high_color[0] == "#"):
185
- # some color scale names (such as cividis) returns:
186
- # [[loc1, "hex1"], [loc2, "hex2"], ...]
187
- low_color = hex_to_rgb(low_color)
188
- high_color = hex_to_rgb(high_color)
189
- return plotly.colors.find_intermediate_color(
190
- lowcolor=low_color,
191
- highcolor=high_color,
192
- intermed=((intermed - low_cutoff) / (high_cutoff - low_cutoff)),
193
- colortype="rgb",
194
- )
195
-
196
- def get_color_default(ratio):
197
- r = 0.0
198
- g = 0.0
199
- b = 0.0
200
-
201
- finalRatio = ratio;
202
- if (finalRatio < 0.0):
203
- finalRatio = 0.0
204
- elif(finalRatio > 1.0):
205
- finalRatio = 1.0
206
-
207
- if (finalRatio >= 0.0 and finalRatio <= 0.25):
208
- r = 0.0
209
- g = 4.0 * finalRatio
210
- b = 1.0
211
- elif (finalRatio > 0.25 and finalRatio <= 0.5):
212
- r = 0.0
213
- g = 1.0
214
- b = 1.0 - 4.0 * (finalRatio - 0.25)
215
- elif (finalRatio > 0.5 and finalRatio <= 0.75):
216
- r = 4.0*(finalRatio - 0.5);
217
- g = 1.0
218
- b = 0.0
219
- else:
220
- r = 1.0
221
- g = 1.0 - 4.0 * (finalRatio - 0.75)
222
- b = 0.0
223
-
224
- rcom = (max(min(r, 1.0), 0.0))
225
- gcom = (max(min(g, 1.0), 0.0))
226
- bcom = (max(min(b, 1.0), 0.0))
227
-
228
- return [rcom,gcom,bcom]
229
-
230
- if minValue > maxValue:
231
- temp = minValue;
232
- maxValue = minValue
233
- maxValue = temp
234
-
235
- val = value
236
- val = max(min(val,maxValue), minValue) # bracket value to the min and max values
237
- if (maxValue - minValue) != 0:
238
- val = (val - minValue)/(maxValue - minValue)
239
- else:
240
- val = 0
241
- if not colorScale or colorScale.lower() == "default":
242
- rgbList = get_color_default(val)
243
- else:
244
- rgbList = get_color(colorScale, val)
245
- if not alpha == None:
246
- rgbList.append(alpha)
247
- return rgbList
248
-
249
- @staticmethod
250
- def CSSNamedColor(color):
251
- """
252
- Returns the CSS Named color that most closely matches the input color. The input color is assumed to be
253
- in the format [r, g, b]. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
254
-
255
- Parameters
256
- ----------
257
- color : list
258
- The input color. This is assumed to be in the format [r, g, b]
259
-
260
- Returns
261
- -------
262
- str
263
- The CSS named color that most closely matches the input color.
264
- """
265
-
266
- import webcolors
267
- import numbers
268
-
269
- if not isinstance(color, list):
270
- print("Color.CSSNamedColor - Error: The input color parameter is not a valid list. Returning None.")
271
- return None
272
- color = [int(x) for x in color if isinstance(x, numbers.Real)]
273
- if len(color) < 3:
274
- print("Color.CSSNamedColor - Error: The input color parameter does not contain valid r, g, b values. Returning None.")
275
- return None
276
- color = color[0:3]
277
- for x in color:
278
- if not (0 <= x <= 255):
279
- print("Color.CSSNamedColor - Error: The input color parameter does not contain valid r, g, b values. Returning None.")
280
- return None
281
-
282
- def est_color(requested_color):
283
- min_colors = {}
284
- for key, name in webcolors.CSS3_HEX_TO_NAMES.items():
285
- r_c, g_c, b_c = webcolors.hex_to_rgb(key)
286
- rd = (r_c - requested_color[0]) ** 2
287
- gd = (g_c - requested_color[1]) ** 2
288
- bd = (b_c - requested_color[2]) ** 2
289
- min_colors[(rd + gd + bd)] = name
290
- return min_colors[min(min_colors.keys())]
291
-
292
- try:
293
- closest_color_name = webcolors.rgb_to_name(color)
294
- except ValueError:
295
- closest_color_name = est_color(color)
296
- return closest_color_name
297
-
298
- @staticmethod
299
- def CSSNamedColors():
300
- """
301
- Returns a list of all CSS named colors. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
302
-
303
- Parameters
304
- ----------
305
-
306
- Returns
307
- -------
308
- list
309
- The list of all CSS named colors.
310
-
311
- """
312
- # List of CSS named colors
313
- css_named_colors = [
314
- "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque", "black", "blanchedalmond",
315
- "blue", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
316
- "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkgrey",
317
- "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon",
318
- "darkseagreen", "darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet", "deeppink",
319
- "deepskyblue", "dimgray", "dimgrey", "dodgerblue", "firebrick", "floralwhite", "forestgreen", "fuchsia",
320
- "gainsboro", "ghostwhite", "gold", "goldenrod", "gray", "green", "greenyellow", "grey", "honeydew", "hotpink",
321
- "indianred", "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue",
322
- "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey", "lightpink",
323
- "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey", "lightsteelblue",
324
- "lightyellow", "lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine", "mediumblue",
325
- "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
326
- "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite", "navy", "oldlace",
327
- "olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod", "palegreen", "paleturquoise",
328
- "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "purple", "rebeccapurple",
329
- "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna",
330
- "silver", "skyblue", "slateblue", "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan",
331
- "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen"
332
- ]
333
-
334
- return css_named_colors
335
-
336
- @staticmethod
337
- def PlotlyColor(color, alpha=1.0, useAlpha=False):
338
- """
339
- Returns a plotly color string based on the input list of [r, g, b] or [r, g, b, a]. If your list is [r, g, b], you can optionally specify an alpha value
340
-
341
- Parameters
342
- ----------
343
- color : list
344
- The input color list. This is assumed to be in the format [r, g, b] or [r, g, b, a]
345
- alpha : float , optional
346
- The transparency value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is 1.0.
347
- useAlpha : bool , optional
348
- If set to True, the returns list includes the alpha value as a fourth element in the list.
349
-
350
- Returns
351
- -------
352
- str
353
- The plotly color string.
354
-
355
- """
356
- if not isinstance(color, list):
357
- print("Color.PlotlyColor - Error: The input color parameter is not a valid list. Returning None.")
358
- return None
359
- if len(color) < 3:
360
- print("Color.PlotlyColor - Error: The input color parameter contains less than the minimum three elements. Returning None.")
361
- return None
362
- if len(color) == 4:
363
- alpha = color[3]
364
- alpha = min(max(alpha, 0), 1)
365
- if alpha < 1:
366
- useAlpha = True
367
- if useAlpha:
368
- return "rgba("+str(color[0])+","+str(color[1])+","+str(color[2])+","+str(alpha)+")"
369
- return "rgb("+str(color[0])+","+str(color[1])+","+str(color[2])+")"
370
-
371
- @staticmethod
372
- def RGBToHex(rgb):
373
- """
374
- Converts RGB color values to a hexadecimal color string.
375
-
376
- Parameters
377
- ----------
378
- rgb : tuple
379
- A tuple containing three integers representing the RGB values.
380
-
381
- Returns
382
- -------
383
- str
384
- A hexadecimal color string in the format '#RRGGBB'.
385
- """
386
- if not isinstance(rgb, list):
387
- print("Color.RGBToHex - Error: The input rgb parameter is not a valid list. Returning None.")
388
- return None
389
- r, g, b = rgb
390
- hex_value = "#{:02x}{:02x}{:02x}".format(r, g, b)
391
- return hex_value.upper()
392
-
393
-
1
+ # Copyright (C) 2024
2
+ # Wassim Jabi <wassim.jabi@gmail.com>
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify it under
5
+ # the terms of the GNU Affero General Public License as published by the Free Software
6
+ # Foundation, either version 3 of the License, or (at your option) any later
7
+ # version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful, but WITHOUT
10
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
12
+ # details.
13
+ #
14
+ # You should have received a copy of the GNU Affero General Public License along with
15
+ # this program. If not, see <https://www.gnu.org/licenses/>.
16
+
17
+ import plotly.colors
18
+ import math
19
+
20
+ class Color:
21
+ @staticmethod
22
+ def ByCSSNamedColor(color, alpha: float = None):
23
+ """
24
+ Creates a Color from a CSS named color string. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
25
+
26
+ Parameters
27
+ ----------
28
+ color : str
29
+ A CSS named color.
30
+ alpha : float , optional
31
+ THe desired alpha (transparency value). The default is None which means no alpha value will be included in the returned list.
32
+
33
+ Returns
34
+ -------
35
+ list
36
+ The color expressed as an [r, g, b] or an [r, g, b, a] list.
37
+ """
38
+ import warnings
39
+ import os
40
+ try:
41
+ import webcolors
42
+ except:
43
+ print("Color.ByCSSNamedColor - Information: Installing required webcolors library.")
44
+ try:
45
+ os.system("pip install webcolors")
46
+ except:
47
+ os.system("pip install webcolors --user")
48
+ try:
49
+ import webcolors
50
+ print("Color.ByCSSNamedColor - Information: webcolors library installed correctly.")
51
+ except:
52
+ warnings.warn("Color.ByCSSNamedColor - Error: Could not import webcolors library. Please manually install webcolors. Returning None.")
53
+ return None
54
+
55
+ if not alpha == None:
56
+ if not 0.0 <= alpha <= 1.0:
57
+ print("Color.ByCSSNamedColor - Error: alpha is not within the valid range of 0 to 1. Returning None.")
58
+ return None
59
+ try:
60
+ # Get RGB values from the named CSS color
61
+ rgbList = list(webcolors.name_to_rgb(color))
62
+ if not alpha == None:
63
+ rgbList.append(alpha)
64
+ return rgbList
65
+
66
+ except ValueError:
67
+ print(f"Color.ByCSSNamedColor - Error: '{color}' is not a valid named CSS color. Returning None.")
68
+ return None
69
+
70
+ @staticmethod
71
+ def ByHEX(hex: str, alpha: float = None):
72
+ """
73
+ Converts a hexadecimal color string to RGB color values.
74
+
75
+ Parameters
76
+ ----------
77
+ hex : str
78
+ A hexadecimal color string in the format '#RRGGBB'.
79
+ alpha : float , optional
80
+ The transparency value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is None
81
+ which means no transparency value will be included in the returned color.
82
+ Returns
83
+ -------
84
+ list
85
+ The color expressed as an [r, g, b] or an [r, g, b, a] list.
86
+
87
+ """
88
+ if not isinstance(hex, str):
89
+ print("Color.HEXtoRGB - Error: The input hex parameter is not a valid string. Returning None.")
90
+ return None
91
+ if not alpha == None:
92
+ if not 0.0 <= alpha <= 1.0:
93
+ print("Color.ByHEX - Error: alpha is not within the valid range of 0 to 1. Returning None.")
94
+ return None
95
+ hex = hex.lstrip('#')
96
+ if len(hex) != 6:
97
+ print("Color.HEXtoRGB - Error: Invalid hexadecimal color format. It should be a 6-digit hex value. Returning None.")
98
+ return None
99
+ r = int(hex[0:2], 16)
100
+ g = int(hex[2:4], 16)
101
+ b = int(hex[4:6], 16)
102
+ rgbList = [r, g, b]
103
+ if not alpha == None:
104
+ rgbList.append(alpha)
105
+ return rgbList
106
+
107
+ @staticmethod
108
+ def ByValueInRange(value: float = 0.5, minValue: float = 0.0, maxValue: float = 1.0, alpha: float = None, colorScale="viridis"):
109
+ """
110
+ Returns the r, g, b, (and optionally) a list of numbers representing the red, green, blue and alpha color elements.
111
+
112
+ Parameters
113
+ ----------
114
+ value : float , optional
115
+ The input value. The default is 0.5.
116
+ minValue : float , optional
117
+ the input minimum value. The default is 0.0.
118
+ maxValue : float , optional
119
+ The input maximum value. The default is 1.0.
120
+ alpha : float , optional
121
+ The alpha (transparency) value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is 1.0.
122
+ useAlpha : bool , optional
123
+ If set to True, the returns list includes the alpha value as a fourth element in the list.
124
+ colorScale : str , optional
125
+ The desired type of plotly color scales to use (e.g. "Viridis", "Plasma"). The default is "Viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/.
126
+
127
+ Returns
128
+ -------
129
+ list
130
+ The color expressed as an [r, g, b] or an [r, g, b, a] list.
131
+
132
+ """
133
+ if not alpha == None:
134
+ if not 0.0 <= alpha <= 1.0:
135
+ print("Color.ByValueInRange - Error: alpha is not within the valid range of 0 to 1. Returning None.")
136
+ return None
137
+ # Code based on: https://stackoverflow.com/questions/62710057/access-color-from-plotly-color-scale
138
+
139
+ def hex_to_rgb(value):
140
+ value = str(value)
141
+ value = value.lstrip('#')
142
+ lv = len(value)
143
+ returnValue = tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
144
+ return str(returnValue)
145
+
146
+ def get_color(colorscale_name, loc):
147
+ from _plotly_utils.basevalidators import ColorscaleValidator
148
+ # first parameter: Name of the property being validated
149
+ # second parameter: a string, doesn't really matter in our use case
150
+ cv = ColorscaleValidator("colorscale", "")
151
+ # colorscale will be a list of lists: [[loc1, "rgb1"], [loc2, "rgb2"], ...]
152
+ colorscale = cv.validate_coerce(colorscale_name)
153
+ if hasattr(loc, "__iter__"):
154
+ return [get_continuous_color(colorscale, x) for x in loc]
155
+ color = get_continuous_color(colorscale, loc)
156
+ color = color.replace("rgb", "")
157
+ color = color.replace("(", "")
158
+ color = color.replace(")", "")
159
+ color = color.split(",")
160
+ final_colors = []
161
+ for c in color:
162
+ final_colors.append(math.floor(float(c)))
163
+ return final_colors
164
+
165
+ def get_continuous_color(colorscale, intermed):
166
+ """
167
+ Plotly continuous colorscales assign colors to the range [0, 1]. This function computes the intermediate
168
+ color for any value in that range.
169
+
170
+ Plotly doesn't make the colorscales directly accessible in a common format.
171
+ Some are ready to use:
172
+
173
+ colorscale = plotly.colors.PLOTLY_SCALES["Greens"]
174
+
175
+ Others are just swatches that need to be constructed into a colorscale:
176
+
177
+ viridis_colors, scale = plotly.colors.convert_colors_to_same_type(plotly.colors.sequential.Viridis)
178
+ colorscale = plotly.colors.make_colorscale(viridis_colors, scale=scale)
179
+
180
+ :param colorscale: A plotly continuous colorscale defined with RGB string colors.
181
+ :param intermed: value in the range [0, 1]
182
+ :return: color in rgb string format
183
+ :rtype: str
184
+ """
185
+
186
+ if len(colorscale) < 1:
187
+ raise ValueError("colorscale must have at least one color")
188
+ if intermed <= 0 or len(colorscale) == 1:
189
+ c = colorscale[0][1]
190
+ return c if c[0] != "#" else hex_to_rgb(c)
191
+ if intermed >= 1:
192
+ c = colorscale[-1][1]
193
+ return c if c[0] != "#" else hex_to_rgb(c)
194
+ for cutoff, color in colorscale:
195
+ if intermed > cutoff:
196
+ low_cutoff, low_color = cutoff, color
197
+ else:
198
+ high_cutoff, high_color = cutoff, color
199
+ break
200
+ if (low_color[0] == "#") or (high_color[0] == "#"):
201
+ # some color scale names (such as cividis) returns:
202
+ # [[loc1, "hex1"], [loc2, "hex2"], ...]
203
+ low_color = hex_to_rgb(low_color)
204
+ high_color = hex_to_rgb(high_color)
205
+ return plotly.colors.find_intermediate_color(
206
+ lowcolor=low_color,
207
+ highcolor=high_color,
208
+ intermed=((intermed - low_cutoff) / (high_cutoff - low_cutoff)),
209
+ colortype="rgb",
210
+ )
211
+
212
+ def get_color_default(ratio):
213
+ r = 0.0
214
+ g = 0.0
215
+ b = 0.0
216
+
217
+ finalRatio = ratio;
218
+ if (finalRatio < 0.0):
219
+ finalRatio = 0.0
220
+ elif(finalRatio > 1.0):
221
+ finalRatio = 1.0
222
+
223
+ if (finalRatio >= 0.0 and finalRatio <= 0.25):
224
+ r = 0.0
225
+ g = 4.0 * finalRatio
226
+ b = 1.0
227
+ elif (finalRatio > 0.25 and finalRatio <= 0.5):
228
+ r = 0.0
229
+ g = 1.0
230
+ b = 1.0 - 4.0 * (finalRatio - 0.25)
231
+ elif (finalRatio > 0.5 and finalRatio <= 0.75):
232
+ r = 4.0*(finalRatio - 0.5);
233
+ g = 1.0
234
+ b = 0.0
235
+ else:
236
+ r = 1.0
237
+ g = 1.0 - 4.0 * (finalRatio - 0.75)
238
+ b = 0.0
239
+
240
+ rcom = (max(min(r, 1.0), 0.0))
241
+ gcom = (max(min(g, 1.0), 0.0))
242
+ bcom = (max(min(b, 1.0), 0.0))
243
+
244
+ return [rcom,gcom,bcom]
245
+
246
+ if minValue > maxValue:
247
+ temp = minValue;
248
+ maxValue = minValue
249
+ maxValue = temp
250
+
251
+ val = value
252
+ val = max(min(val,maxValue), minValue) # bracket value to the min and max values
253
+ if (maxValue - minValue) != 0:
254
+ val = (val - minValue)/(maxValue - minValue)
255
+ else:
256
+ val = 0
257
+ if not colorScale or colorScale.lower() == "default":
258
+ rgbList = get_color_default(val)
259
+ else:
260
+ rgbList = get_color(colorScale, val)
261
+ if not alpha == None:
262
+ rgbList.append(alpha)
263
+ return rgbList
264
+
265
+ @staticmethod
266
+ def CSSNamedColor(color):
267
+ """
268
+ Returns the CSS Named color that most closely matches the input color. The input color is assumed to be
269
+ in the format [r, g, b]. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
270
+
271
+ Parameters
272
+ ----------
273
+ color : list
274
+ The input color. This is assumed to be in the format [r, g, b]
275
+
276
+ Returns
277
+ -------
278
+ str
279
+ The CSS named color that most closely matches the input color.
280
+ """
281
+ import numbers
282
+ import warnings
283
+ import os
284
+ try:
285
+ import webcolors
286
+ except:
287
+ print("Color.CSSNamedColor - Information: Installing required webcolors library.")
288
+ try:
289
+ os.system("pip install webcolors")
290
+ except:
291
+ os.system("pip install webcolors --user")
292
+ try:
293
+ import webcolors
294
+ print("Color.CSSNamedColor - Information: webcolors library installed correctly.")
295
+ except:
296
+ warnings.warn("Color.CSSNamedColor - Error: Could not import webcolors library. Please manually install webcolors. Returning None.")
297
+ return None
298
+
299
+ if not isinstance(color, list):
300
+ print("Color.CSSNamedColor - Error: The input color parameter is not a valid list. Returning None.")
301
+ return None
302
+ color = [int(x) for x in color if isinstance(x, numbers.Real)]
303
+ if len(color) < 3:
304
+ print("Color.CSSNamedColor - Error: The input color parameter does not contain valid r, g, b values. Returning None.")
305
+ return None
306
+ color = color[0:3]
307
+ for x in color:
308
+ if not (0 <= x <= 255):
309
+ print("Color.CSSNamedColor - Error: The input color parameter does not contain valid r, g, b values. Returning None.")
310
+ return None
311
+
312
+ def est_color(requested_color):
313
+ min_colors = {}
314
+ for key, name in webcolors.CSS3_HEX_TO_NAMES.items():
315
+ r_c, g_c, b_c = webcolors.hex_to_rgb(key)
316
+ rd = (r_c - requested_color[0]) ** 2
317
+ gd = (g_c - requested_color[1]) ** 2
318
+ bd = (b_c - requested_color[2]) ** 2
319
+ min_colors[(rd + gd + bd)] = name
320
+ return min_colors[min(min_colors.keys())]
321
+
322
+ try:
323
+ closest_color_name = webcolors.rgb_to_name(color)
324
+ except ValueError:
325
+ closest_color_name = est_color(color)
326
+ return closest_color_name
327
+
328
+ @staticmethod
329
+ def CSSNamedColors():
330
+ """
331
+ Returns a list of all CSS named colors. See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
332
+
333
+ Parameters
334
+ ----------
335
+
336
+ Returns
337
+ -------
338
+ list
339
+ The list of all CSS named colors.
340
+
341
+ """
342
+ # List of CSS named colors
343
+ css_named_colors = [
344
+ "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque", "black", "blanchedalmond",
345
+ "blue", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
346
+ "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkgrey",
347
+ "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon",
348
+ "darkseagreen", "darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet", "deeppink",
349
+ "deepskyblue", "dimgray", "dimgrey", "dodgerblue", "firebrick", "floralwhite", "forestgreen", "fuchsia",
350
+ "gainsboro", "ghostwhite", "gold", "goldenrod", "gray", "green", "greenyellow", "grey", "honeydew", "hotpink",
351
+ "indianred", "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue",
352
+ "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey", "lightpink",
353
+ "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey", "lightsteelblue",
354
+ "lightyellow", "lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine", "mediumblue",
355
+ "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
356
+ "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite", "navy", "oldlace",
357
+ "olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod", "palegreen", "paleturquoise",
358
+ "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "purple", "rebeccapurple",
359
+ "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna",
360
+ "silver", "skyblue", "slateblue", "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan",
361
+ "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen"
362
+ ]
363
+
364
+ return css_named_colors
365
+
366
+ @staticmethod
367
+ def PlotlyColor(color, alpha=1.0, useAlpha=False):
368
+ """
369
+ Returns a plotly color string based on the input list of [r, g, b] or [r, g, b, a]. If your list is [r, g, b], you can optionally specify an alpha value
370
+
371
+ Parameters
372
+ ----------
373
+ color : list
374
+ The input color list. This is assumed to be in the format [r, g, b] or [r, g, b, a]
375
+ alpha : float , optional
376
+ The transparency value. 0.0 means the color is fully transparent, 1.0 means the color is fully opaque. The default is 1.0.
377
+ useAlpha : bool , optional
378
+ If set to True, the returns list includes the alpha value as a fourth element in the list.
379
+
380
+ Returns
381
+ -------
382
+ str
383
+ The plotly color string.
384
+
385
+ """
386
+ if not isinstance(color, list):
387
+ print("Color.PlotlyColor - Error: The input color parameter is not a valid list. Returning None.")
388
+ return None
389
+ if len(color) < 3:
390
+ print("Color.PlotlyColor - Error: The input color parameter contains less than the minimum three elements. Returning None.")
391
+ return None
392
+ if len(color) == 4:
393
+ alpha = color[3]
394
+ alpha = min(max(alpha, 0), 1)
395
+ if alpha < 1:
396
+ useAlpha = True
397
+ if useAlpha:
398
+ return "rgba("+str(color[0])+","+str(color[1])+","+str(color[2])+","+str(alpha)+")"
399
+ return "rgb("+str(color[0])+","+str(color[1])+","+str(color[2])+")"
400
+
401
+ @staticmethod
402
+ def RGBToHex(rgb):
403
+ """
404
+ Converts RGB color values to a hexadecimal color string.
405
+
406
+ Parameters
407
+ ----------
408
+ rgb : tuple
409
+ A tuple containing three integers representing the RGB values.
410
+
411
+ Returns
412
+ -------
413
+ str
414
+ A hexadecimal color string in the format '#RRGGBB'.
415
+ """
416
+ if not isinstance(rgb, list):
417
+ print("Color.RGBToHex - Error: The input rgb parameter is not a valid list. Returning None.")
418
+ return None
419
+ r, g, b = rgb
420
+ hex_value = "#{:02x}{:02x}{:02x}".format(r, g, b)
421
+ return hex_value.upper()
422
+
423
+