cactuz 0.0.9

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Torsten Sprenger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,331 @@
1
+ # cactuz
2
+
3
+ A modern Svelte library for visualizing hierarchical data structures using the *CactusTree* algorithm with hierarchical edge bundling.
4
+
5
+ <div align="center">
6
+ <img src="https://github.com/spren9er/cactuz/blob/main/docs/images/cactus_tree_simple.png?raw=true" alt="cactus-tree-simple" width="75%" height="75%">
7
+ </div>
8
+
9
+ ## Overview
10
+
11
+ The library **cactuz** is based on the research paper *[CactusTree: A Tree Drawing Approach for Hierarchical Edge Bundling](https://ieeexplore.ieee.org/document/8031596)* by Tommy Dang and Angus Forbes. This implementation provides both a ready-to-use Svelte component and a standalone layout algorithm for creating interactive tree visualizations.
12
+
13
+ See [cactuz.spren9er.de](https://cactuz.spren9er.de) for a live demo and interactive playground.
14
+
15
+ ## Features
16
+
17
+ - **Fractal-based Tree Layout** - Recursively stacks child nodes on parent nodes
18
+ - **Hierarchical Edge Bundling** - Groups related connections for cleaner visualization
19
+ - **Highly Customizable** - Extensive styling and behavior options
20
+ - **Interactive** - Pan, zoom, hover effects, and link filtering
21
+ - **Depth-based Styling** - Configure appearance for different tree levels
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install cactuz
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```svelte
32
+ <script>
33
+ import { CactusTree } from 'cactuz';
34
+
35
+ const nodes = [
36
+ { id: 'root', name: 'Root', parent: null },
37
+ { id: 'child1', name: 'Child 1', parent: 'root' },
38
+ { id: 'child2', name: 'Child 2', parent: 'root' },
39
+ { id: 'leaf1', name: 'Leaf 1', parent: 'child1' },
40
+ { id: 'leaf2', name: 'Leaf 2', parent: 'child1' },
41
+ ];
42
+
43
+ const links = [{ source: 'leaf1', target: 'leaf2' }];
44
+ </script>
45
+
46
+ <CactusTree width={800} height={600} {nodes} {links} />
47
+ ```
48
+
49
+ ## API Reference
50
+
51
+ ### CactusTree Component
52
+
53
+ #### Props
54
+
55
+ | Prop | Type | Required | Default | Description |
56
+ | ---------- | --------- | -------- | ------- | ---------------------------------- |
57
+ | `width` | `number` | yes | - | Canvas width in pixels |
58
+ | `height` | `number` | yes | - | Canvas height in pixels |
59
+ | `nodes` | `Node[]` | yes | - | Array of hierarchical nodes |
60
+ | `links` | `Link[]` | no | `[]` | Array of connections between nodes |
61
+ | `options` | `Options` | no | `{}` | Layout and behavior configuration |
62
+ | `styles` | `Styles` | no | `{}` | Visual styling configuration |
63
+ | `pannable` | `boolean` | no | `true` | Enable pan interaction |
64
+ | `zoomable` | `boolean` | no | `true` | Enable zoom interaction |
65
+
66
+ #### Node Structure
67
+
68
+ ```typescript
69
+ interface Node {
70
+ id: string; // Unique identifier
71
+ name: string; // Display name
72
+ parent: string | null; // Parent node ID (null for root)
73
+ weight?: number; // Optional explicit weight
74
+ }
75
+ ```
76
+
77
+ #### Link Structure
78
+
79
+ ```typescript
80
+ interface Link {
81
+ source: string; // Source node ID
82
+ target: string; // Target node ID
83
+ }
84
+ ```
85
+
86
+ #### Options
87
+
88
+ ```typescript
89
+ interface Options {
90
+ overlap?: number; // Node overlap factor (-inf to 1, default: 0.5)
91
+ arcSpan?: number; // Arc span in radians (default: 5π/4)
92
+ sizeGrowthRate?: number; // Size growth rate (default: 0.75)
93
+ orientation?: number; // Root orientation in radians (default: π/2)
94
+ zoom?: number; // Zoom level (default: 1.0)
95
+ }
96
+ ```
97
+
98
+ #### Styles
99
+
100
+ ```typescript
101
+ interface Styles {
102
+ // Node appearance
103
+ fill?: string; // Node fill color (default: '#efefef')
104
+ fillOpacity?: number; // Node fill opacity (default: 1)
105
+ stroke?: string; // Node stroke color (default: '#333333')
106
+ strokeWidth?: number; // Node stroke width (default: 1)
107
+ strokeOpacity?: number; // Node stroke opacity (default: 1)
108
+
109
+ // Labels
110
+ label?: string; // Label color (default: '#333333')
111
+ labelFontFamily?: string; // Label font (default: 'monospace')
112
+ labelLink?: string; // Label link line color (default: '#333333')
113
+ labelLinkWidth?: number; // Label link line width (default: 0.5)
114
+ labelLinkPadding?: number; // Gap between circle and link start in pixels (default: 0)
115
+ labelLinkLength?: number; // Length of visible link line in pixels (default: 5)
116
+ labelPadding?: number; // Padding around label text in pixels (default: 1)
117
+ labelMinFontSize?: number; // Minimum label font size (default: 8)
118
+ labelMaxFontSize?: number; // Maximum label font size (default: 14)
119
+ labelLimit?: number; // Maximum number of labels to show (default: 30)
120
+ // Shows labels for the N largest nodes by radius (all types)
121
+ // Set to 0 to hide all labels (except when hovering)
122
+
123
+ // Connections
124
+ line?: string; // Tree line color (default: '#333333')
125
+ lineWidth?: number; // Tree line width (default: 1)
126
+ edge?: string; // Link color (default: '#ff6b6b')
127
+ edgeWidth?: number; // Link width (default: 1)
128
+ edgeOpacity?: number; // Link opacity (default: 0.1, full opacity when hovering leaf nodes with links)
129
+
130
+ // Hover effects
131
+ highlight?: boolean; // Enable hover effects (default: true)
132
+ highlightFill?: string; // Hover fill color (default: '#ffcc99')
133
+ highlightStroke?: string; // Hover stroke color (default: '#ff6600')
134
+
135
+ // Depth-specific styling
136
+ depths?: DepthStyle[]; // Per-depth style overrides
137
+ }
138
+ ```
139
+
140
+ #### Depth-Specific Styling
141
+
142
+ ```typescript
143
+ interface DepthStyle {
144
+ depth: number; // Tree depth (0 = root, -1 = leaves)
145
+ fill?: string;
146
+ fillOpacity?: number;
147
+ stroke?: string;
148
+ strokeWidth?: number;
149
+ strokeOpacity?: number;
150
+ label?: string;
151
+ labelFontFamily?: string;
152
+ line?: string;
153
+ lineWidth?: number;
154
+ highlight?: boolean;
155
+ highlightFill?: string;
156
+ highlightStroke?: string;
157
+ }
158
+ ```
159
+
160
+ ### CactusLayout Class
161
+
162
+ For custom implementations or non-Svelte environments, you can use the layout algorithm directly:
163
+
164
+ ```javascript
165
+ import { CactusLayout } from 'cactuz';
166
+
167
+ const layout = new CactusLayout(
168
+ 800, // width
169
+ 600, // height
170
+ 1.0, // zoom
171
+ 0.5, // overlap
172
+ Math.PI, // arcSpan
173
+ 0.75, // sizeGrowthRate
174
+ );
175
+
176
+ const nodeData = layout.render(nodes, 400, 300, -Math.PI / 2);
177
+ ```
178
+
179
+ #### Constructor
180
+
181
+ ```typescript
182
+ new CactusLayout(
183
+ width: number, // Target width
184
+ height: number, // Target height
185
+ zoom?: number, // Zoom factor (default: 1)
186
+ overlap?: number, // Overlap factor (default: 0)
187
+ arcSpan?: number, // Arc span (default: π)
188
+ sizeGrowthRate?: number // Size growth rate (default: 0.75)
189
+ )
190
+ ```
191
+
192
+ #### Methods
193
+
194
+ ##### `render(nodes, startX, startY, startAngle)`
195
+
196
+ Computes the layout and returns positioned node data.
197
+
198
+ **Parameters:**
199
+
200
+ - `nodes`: Array of node objects or flat node array
201
+ - `startX`: Starting X coordinate (usually width/2)
202
+ - `startY`: Starting Y coordinate (usually height/2)
203
+ - `startAngle`: Starting angle in radians (default: π/2)
204
+
205
+ **Returns:**
206
+
207
+ ```typescript
208
+ NodeData[] // Array of positioned nodes
209
+ ```
210
+
211
+ ##### `NodeData Structure`
212
+
213
+ ```typescript
214
+ interface NodeData {
215
+ x: number; // X coordinate
216
+ y: number; // Y coordinate
217
+ radius: number; // Node radius
218
+ node: Node; // Original node reference
219
+ isLeaf: boolean; // Whether this is a leaf node
220
+ depth: number; // Depth in hierarchy (0 = root)
221
+ angle: number; // Angle from parent (radians)
222
+ }
223
+ ```
224
+
225
+ ## Advanced Usage
226
+
227
+ ### Custom Styling Example
228
+
229
+ ```svelte
230
+ <script>
231
+ import { CactusTree } from 'cactuz';
232
+
233
+ const styles = {
234
+ fill: '#f0f8ff',
235
+ stroke: '#4682b4',
236
+ strokeWidth: 2,
237
+ label: '#2c3e50',
238
+ labelFontFamily: 'Arial, sans-serif',
239
+ labelLimit: 100,
240
+ edge: '#e74c3c',
241
+ edgeWidth: 3,
242
+ highlightFill: '#ffd700',
243
+ highlightStroke: '#ff8c00',
244
+ depths: [
245
+ {
246
+ depth: 0, // Root styling
247
+ fill: '#2c3e50',
248
+ stroke: '#ecf0f1',
249
+ label: '#ecf0f1',
250
+ },
251
+ {
252
+ depth: -1, // Leaf styling
253
+ fill: '#e74c3c',
254
+ stroke: '#c0392b',
255
+ label: '#2c3e50',
256
+ },
257
+ ],
258
+ };
259
+ </script>
260
+
261
+ <CactusTree {width} {height} {nodes} {links} {styles} />
262
+ ```
263
+
264
+ ### Interactive Features
265
+
266
+ The component provides several interactive features:
267
+
268
+ - **Pan**: Click and drag to pan the visualization
269
+ - **Zoom**: Use mouse wheel to zoom in/out
270
+ - **Hover**: Hover over nodes to highlight connections
271
+
272
+ ## Examples
273
+
274
+ ### Basic Tree
275
+
276
+ ```svelte
277
+ <CactusTree
278
+ width={600}
279
+ height={400}
280
+ nodes={[
281
+ { id: 'a', name: 'Root', parent: null },
282
+ { id: 'b', name: 'Branch 1', parent: 'a' },
283
+ { id: 'c', name: 'Branch 2', parent: 'a' },
284
+ { id: 'd', name: 'Leaf 1', parent: 'b' },
285
+ { id: 'e', name: 'Leaf 2', parent: 'b' },
286
+ ]}
287
+ />
288
+ ```
289
+
290
+ ### With Edge Bundling
291
+
292
+ ```svelte
293
+ <CactusTree
294
+ width={800}
295
+ height={600}
296
+ {nodes}
297
+ links={[
298
+ { source: 'leaf1', target: 'leaf3' },
299
+ { source: 'leaf2', target: 'leaf4' },
300
+ ]}
301
+ styles={{
302
+ edge: '#3498db',
303
+ edgeOpacity: 0.3,
304
+ edgeWidth: 2,
305
+ }}
306
+ />
307
+ ```
308
+
309
+ ### Negative Overlap and Link Filtering
310
+
311
+
312
+ ```svelte
313
+ <CactusTree
314
+ width={1000}
315
+ height={800}
316
+ {nodes}
317
+ options={{
318
+ overlap: -1.1, // Gaps between nodes
319
+ arcSpan: 2 * Math.PI, // Full circle layout
320
+ orientation: 7 / 9 * Math.PI, // Leftward growth
321
+ zoom: 0.7 // Zoom out to see full chart
322
+ }}
323
+ />
324
+ ```
325
+
326
+ <div align="center">
327
+ <img src="https://github.com/spren9er/cactuz/blob/main/docs/images/cactus_tree_advanced.png?raw=true" alt="cactus-tree-advanced" width="75%" height="75%">
328
+ </div>
329
+
330
+ For a negative overlap parameter, nodes are connected by links.
331
+ Also, when hovering over leaf nodes, only the links connected to that node are shown, while all other links are hidden. This allows for better readability in dense visualizations.