scigraphs-engine 0.2.0__tar.gz → 0.2.1__tar.gz

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 (69) hide show
  1. {scigraphs_engine-0.2.0/scigraphs_engine.egg-info → scigraphs_engine-0.2.1}/PKG-INFO +1 -1
  2. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/pyproject.toml +1 -1
  3. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/__init__.py +1 -1
  4. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/edge_styles.py +50 -13
  5. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1/scigraphs_engine.egg-info}/PKG-INFO +1 -1
  6. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/LICENSE +0 -0
  7. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/README.md +0 -0
  8. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/_camera_math.py +0 -0
  9. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/adaptive.py +0 -0
  10. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/api.py +0 -0
  11. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/__init__.py +0 -0
  12. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/__init__.py +0 -0
  13. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/buffers.py +0 -0
  14. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/camera.py +0 -0
  15. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/device.py +0 -0
  16. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/pipelines.py +0 -0
  17. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/render.py +0 -0
  18. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/renderer.py +0 -0
  19. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/shaders.py +0 -0
  20. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/target.py +0 -0
  21. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/arrow.json +0 -0
  22. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/arrow.wgsl +0 -0
  23. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/arrow_flat.json +0 -0
  24. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/arrow_flat.wgsl +0 -0
  25. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/heb_line.json +0 -0
  26. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/heb_line.wgsl +0 -0
  27. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/line_f.json +0 -0
  28. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/line_f.wgsl +0 -0
  29. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/ribbon.json +0 -0
  30. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/ribbon.wgsl +0 -0
  31. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/ribbon_f.json +0 -0
  32. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/ribbon_f.wgsl +0 -0
  33. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/ribbon_id.json +0 -0
  34. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/ribbon_id.wgsl +0 -0
  35. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/round_point.json +0 -0
  36. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/round_point.wgsl +0 -0
  37. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/round_point_f.json +0 -0
  38. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/round_point_f.wgsl +0 -0
  39. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/smooth_color.json +0 -0
  40. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/smooth_color.wgsl +0 -0
  41. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/sphere.json +0 -0
  42. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/sphere.wgsl +0 -0
  43. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/sphere_f.json +0 -0
  44. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/sphere_f.wgsl +0 -0
  45. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/sphere_id.json +0 -0
  46. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/sphere_id.wgsl +0 -0
  47. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/uniform_color.json +0 -0
  48. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/backends/wgpu/wgsl/uniform_color.wgsl +0 -0
  49. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/blocks.py +0 -0
  50. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/bundling/__init__.py +0 -0
  51. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/bundling/fdeb.py +0 -0
  52. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/bundling/mingle.py +0 -0
  53. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/bundling/routed.py +0 -0
  54. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/bundling/sbeb.py +0 -0
  55. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/channels.py +0 -0
  56. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/communities.py +0 -0
  57. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/filters.py +0 -0
  58. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/gpu_compute.py +0 -0
  59. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/lod.py +0 -0
  60. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/mesh.py +0 -0
  61. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/palette.py +0 -0
  62. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/settings.py +0 -0
  63. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/simplify.py +0 -0
  64. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine/source.py +0 -0
  65. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine.egg-info/SOURCES.txt +0 -0
  66. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine.egg-info/dependency_links.txt +0 -0
  67. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine.egg-info/requires.txt +0 -0
  68. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/scigraphs_engine.egg-info/top_level.txt +0 -0
  69. {scigraphs_engine-0.2.0 → scigraphs_engine-0.2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scigraphs-engine
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Blender-free graph visualization engine: filters, backbones, bundling, PNG render.
5
5
  Author: José Marín
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "scigraphs-engine"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Blender-free graph visualization engine: filters, backbones, bundling, PNG render."
9
9
  readme = "README.md"
10
10
  # Floor matches Blender 4.1+ / 5.x Python.
@@ -4,7 +4,7 @@
4
4
 
5
5
  import importlib as _importlib
6
6
 
7
- __version__ = "0.2.0"
7
+ __version__ = "0.2.1"
8
8
 
9
9
  from .mesh import ( # noqa: F401
10
10
  DrawState, MeshGroup, MeshSpec, ShaderRef, LINES, POINTS, TRIS,
@@ -117,18 +117,43 @@ def _canonical_flip(edges):
117
117
  return np.where(edges[:, 0] <= edges[:, 1], 1.0, -1.0)
118
118
 
119
119
 
120
- def _perp_unit(d_unit, flip=None):
120
+ def drawing_normal(points, flatness=0.02):
121
+ """The normal of the plane a layout lies in, or None when it is truly 3D."""
122
+ pts = np.asarray(points, dtype=np.float64)
123
+ if pts.ndim != 2 or pts.shape[0] < 3:
124
+ return None
125
+ centred = pts - pts.mean(axis=0)
126
+ scale = np.abs(centred).max()
127
+ if scale <= 0:
128
+ return None
129
+ try:
130
+ _u, s, vh = np.linalg.svd(centred / scale, full_matrices=False)
131
+ except np.linalg.LinAlgError:
132
+ return None
133
+ if s[0] <= 0 or s[-1] / s[0] > flatness:
134
+ return None
135
+ return vh[-1] / max(np.linalg.norm(vh[-1]), 1e-12)
136
+
137
+
138
+ def _perp_unit(d_unit, flip=None, normal=None):
121
139
  """Perpendicular of the canonical direction. Taken from the stored one it
122
140
  flips for (v, u) and cancels against the direction sign, drawing the
123
- reciprocal edge on top of (u, v)."""
141
+ reciprocal edge on top of (u, v).
142
+ """
124
143
  if flip is not None:
125
144
  d_unit = d_unit * flip[:, None]
126
- perp = np.cross(d_unit, _Z_UP)
145
+ axis = _Z_UP if normal is None else np.asarray(normal, dtype=float)
146
+ perp = np.cross(d_unit, axis)
127
147
  ln = np.linalg.norm(perp, axis=1)
128
- vertical = ln < 1e-10
129
- if vertical.any():
130
- perp[vertical] = np.cross(d_unit[vertical], _X_AXIS)
148
+ degenerate = ln < 1e-10
149
+ if degenerate.any():
150
+ fallback = _X_AXIS if normal is None else _Z_UP
151
+ perp[degenerate] = np.cross(d_unit[degenerate], fallback)
131
152
  ln = np.linalg.norm(perp, axis=1)
153
+ still = ln < 1e-10
154
+ if still.any():
155
+ perp[still] = np.cross(d_unit[still], _X_AXIS)
156
+ ln = np.linalg.norm(perp, axis=1)
132
157
  ln = np.maximum(ln, 1e-12)
133
158
  return perp / ln[:, None]
134
159
 
@@ -176,11 +201,13 @@ def _bezier_quadratic(p0, c, p1, t):
176
201
  + (t**2)[None, :, None] * p1[:, None]
177
202
 
178
203
 
179
- def _curved_points(a, b, params, edge_ids, use_cubic, t, flip=None):
204
+ def _curved_points(a, b, params, edge_ids, use_cubic, t, flip=None,
205
+ normal=None):
180
206
  length = np.linalg.norm(b - a, axis=1)
181
207
  offs = length * params["curvature"] * 0.5
182
208
  sign = _direction_sign(a, b, params["direction"], edge_ids, flip)
183
- perp = _perp_unit((b - a) / np.maximum(length, 1e-12)[:, None], flip)
209
+ perp = _perp_unit((b - a) / np.maximum(length, 1e-12)[:, None], flip,
210
+ normal=normal)
184
211
  perp = perp * (offs * sign)[:, None]
185
212
  if use_cubic:
186
213
  c1 = a + (b - a) * 0.25 + perp * 0.5
@@ -189,7 +216,7 @@ def _curved_points(a, b, params, edge_ids, use_cubic, t, flip=None):
189
216
  return _bezier_quadratic(a, (a + b) * 0.5 + perp, b, t)
190
217
 
191
218
 
192
- def _arc_points(a, b, params, edge_ids, t, flip=None):
219
+ def _arc_points(a, b, params, edge_ids, t, flip=None, normal=None):
193
220
  chord = np.linalg.norm(b - a, axis=1)
194
221
  sagitta = chord * params["curvature"] * 0.5
195
222
  flat = sagitta <= 1e-3
@@ -197,7 +224,8 @@ def _arc_points(a, b, params, edge_ids, t, flip=None):
197
224
  flat, 1.0, chord**2 / np.maximum(8.0 * sagitta, 1e-12) + sagitta / 2.0
198
225
  )
199
226
  sign = _direction_sign(a, b, params["direction"], edge_ids, flip)
200
- perp = _perp_unit((b - a) / np.maximum(chord, 1e-12)[:, None], flip)
227
+ perp = _perp_unit((b - a) / np.maximum(chord, 1e-12)[:, None], flip,
228
+ normal=normal)
201
229
  perp = perp * sign[:, None]
202
230
  mid = (a + b) * 0.5
203
231
  center = mid - perp * (radius - sagitta)[:, None]
@@ -473,13 +501,15 @@ def tessellate(coords, edges, params, edge_widths=None, hierarchy=None,
473
501
  plain_rows, w_plain = plain_rows[order], w_plain[order]
474
502
  flip = flip[order]
475
503
 
504
+ plane = drawing_normal(coords)
505
+
476
506
  # CURVED rides a cubic; QUADRATIC and TAPERED ride a quadratic.
477
507
  if (style in ('CURVED', 'QUADRATIC', 'TAPERED')
478
508
  and params["curvature"] >= 1e-3):
479
509
  pts = _curved_points(a, b, params, plain_ids,
480
- style == 'CURVED', t, flip)
510
+ style == 'CURVED', t, flip, normal=plane)
481
511
  elif style == 'ARC' and params["curvature"] >= 1e-3:
482
- pts = _arc_points(a, b, params, plain_ids, t, flip)
512
+ pts = _arc_points(a, b, params, plain_ids, t, flip, normal=plane)
483
513
  elif style == 'ORTHOGONAL':
484
514
  pts = _orthogonal_points(a, b, params)
485
515
  t = np.linspace(0.0, 1.0, pts.shape[1])
@@ -487,7 +517,14 @@ def tessellate(coords, edges, params, edge_widths=None, hierarchy=None,
487
517
  if plain.shape[0] <= BUNDLE_MAX_EDGES:
488
518
  pts = _bundled_points(a, b, params, t)
489
519
  else:
490
- pts = _curved_points(a, b, params, plain_ids, True, t, flip)
520
+ import warnings
521
+ warnings.warn(
522
+ f"BUNDLED: {plain.shape[0]} edges exceeds "
523
+ f"BUNDLE_MAX_EDGES={BUNDLE_MAX_EDGES}; drawing cubic "
524
+ f"Bezier curves instead of bundles",
525
+ RuntimeWarning, stacklevel=2)
526
+ pts = _curved_points(a, b, params, plain_ids, True, t, flip,
527
+ normal=plane)
491
528
  elif heb:
492
529
  pts = _heb_points(a, b, plain[:, 0], plain[:, 1], hierarchy,
493
530
  params, t.size)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scigraphs-engine
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Blender-free graph visualization engine: filters, backbones, bundling, PNG render.
5
5
  Author: José Marín
6
6
  License: MIT License