pict-section-flow 1.4.0 → 2.0.1

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 (164) hide show
  1. package/package.json +7 -2
  2. package/source/Pict-Section-Flow.js +20 -14
  3. package/source/providers/PictProvider-Flow-Background.js +303 -0
  4. package/source/providers/PictProvider-Flow-CSS.js +73 -7
  5. package/source/providers/PictProvider-Flow-Geometry.js +11 -421
  6. package/source/providers/PictProvider-Flow-Icons.js +12 -0
  7. package/source/providers/PictProvider-Flow-Layouts.js +107 -0
  8. package/source/services/PictService-Flow-ConnectionRenderer.js +1 -1
  9. package/source/services/PictService-Flow-CursorManager.js +113 -0
  10. package/source/services/PictService-Flow-InteractionManager.js +439 -59
  11. package/source/services/PictService-Flow-Layout.js +21 -16
  12. package/source/services/PictService-Flow-PathGenerator.js +30 -417
  13. package/source/services/PictService-Flow-RenderManager.js +23 -3
  14. package/source/services/PictService-Flow-ViewportManager.js +102 -0
  15. package/source/views/PictView-Flow-FloatingToolbar.js +5 -1
  16. package/source/views/PictView-Flow-Node.js +29 -0
  17. package/source/views/PictView-Flow-Toolbar.js +50 -3
  18. package/source/views/PictView-Flow.js +591 -2
  19. package/.claude/launch.json +0 -11
  20. package/docs/.nojekyll +0 -0
  21. package/docs/Architecture.md +0 -163
  22. package/docs/Custom-Styling.md +0 -275
  23. package/docs/Data_Model.md +0 -149
  24. package/docs/Event_System.md +0 -156
  25. package/docs/Getting_Started.md +0 -237
  26. package/docs/Implementation_Reference.md +0 -528
  27. package/docs/Layout_Persistence.md +0 -117
  28. package/docs/README.md +0 -103
  29. package/docs/Theme_Integration.md +0 -150
  30. package/docs/_brand.json +0 -18
  31. package/docs/_cover.md +0 -17
  32. package/docs/_playground.json +0 -24
  33. package/docs/_sidebar.md +0 -57
  34. package/docs/_topbar.md +0 -8
  35. package/docs/_version.json +0 -7
  36. package/docs/api/PictFlowCard.md +0 -216
  37. package/docs/api/PictFlowCardPropertiesPanel.md +0 -235
  38. package/docs/api/addConnection.md +0 -101
  39. package/docs/api/addNode.md +0 -137
  40. package/docs/api/autoLayout.md +0 -77
  41. package/docs/api/getFlowData.md +0 -112
  42. package/docs/api/marshalToView.md +0 -95
  43. package/docs/api/openPanel.md +0 -128
  44. package/docs/api/registerHandler.md +0 -174
  45. package/docs/api/registerNodeType.md +0 -142
  46. package/docs/api/removeConnection.md +0 -57
  47. package/docs/api/removeNode.md +0 -80
  48. package/docs/api/saveLayout.md +0 -152
  49. package/docs/api/screenToSVGCoords.md +0 -68
  50. package/docs/api/selectNode.md +0 -116
  51. package/docs/api/setTheme.md +0 -168
  52. package/docs/api/setZoom.md +0 -97
  53. package/docs/api/toggleFullscreen.md +0 -68
  54. package/docs/card-help/EACH.md +0 -19
  55. package/docs/card-help/FREAD.md +0 -24
  56. package/docs/card-help/FWRITE.md +0 -24
  57. package/docs/card-help/GET.md +0 -22
  58. package/docs/card-help/ITE.md +0 -23
  59. package/docs/card-help/LOG.md +0 -23
  60. package/docs/card-help/NOTE.md +0 -17
  61. package/docs/card-help/PREV.md +0 -18
  62. package/docs/card-help/SET.md +0 -27
  63. package/docs/card-help/SPKL.md +0 -22
  64. package/docs/card-help/STAT.md +0 -23
  65. package/docs/card-help/SW.md +0 -25
  66. package/docs/diagrams/architecture-at-a-glance.excalidraw +0 -4270
  67. package/docs/diagrams/architecture-at-a-glance.mmd +0 -30
  68. package/docs/diagrams/architecture-at-a-glance.svg +0 -2
  69. package/docs/diagrams/data-flow.excalidraw +0 -1451
  70. package/docs/diagrams/data-flow.mmd +0 -17
  71. package/docs/diagrams/data-flow.svg +0 -2
  72. package/docs/diagrams/high-level-design.excalidraw +0 -5767
  73. package/docs/diagrams/high-level-design.mmd +0 -86
  74. package/docs/diagrams/high-level-design.svg +0 -2
  75. package/docs/diagrams/relationships.excalidraw +0 -3852
  76. package/docs/diagrams/relationships.mmd +0 -9
  77. package/docs/diagrams/relationships.svg +0 -2
  78. package/docs/diagrams/service-initialization-sequence.excalidraw +0 -1466
  79. package/docs/diagrams/service-initialization-sequence.mmd +0 -19
  80. package/docs/diagrams/service-initialization-sequence.svg +0 -2
  81. package/docs/diagrams/svg-layer-structure.excalidraw +0 -1060
  82. package/docs/diagrams/svg-layer-structure.mmd +0 -18
  83. package/docs/diagrams/svg-layer-structure.svg +0 -2
  84. package/docs/examples/README.md +0 -9
  85. package/docs/examples/simple_cards/README.md +0 -677
  86. package/docs/examples/simple_cards/css/flowexample.css +0 -65
  87. package/docs/examples/simple_cards/index.html +0 -32
  88. package/docs/examples/simple_cards/js/pict.min.js +0 -12
  89. package/docs/examples/simple_cards/pict-section-flow-example-simple-cards.compatible.min.js +0 -1
  90. package/docs/index.html +0 -38
  91. package/docs/playground/app.json +0 -6
  92. package/docs/playground/appdata.json +0 -85
  93. package/docs/playground/application.js +0 -23
  94. package/docs/playground/pict.json +0 -17
  95. package/docs/playground/runtime/pict-application.min.js +0 -2
  96. package/docs/playground/runtime/pict-section-flow.min.js +0 -2
  97. package/docs/playground/runtime/pict-section-modal.min.js +0 -2
  98. package/docs/playground/runtime/pict.min.js +0 -12
  99. package/docs/retold-catalog.json +0 -244
  100. package/docs/retold-keyword-index.json +0 -26028
  101. package/example_applications/simple_cards/css/flowexample.css +0 -65
  102. package/example_applications/simple_cards/html/index.html +0 -32
  103. package/example_applications/simple_cards/package.json +0 -52
  104. package/example_applications/simple_cards/source/Pict-Application-FlowExample-Configuration.json +0 -15
  105. package/example_applications/simple_cards/source/Pict-Application-FlowExample.js +0 -539
  106. package/example_applications/simple_cards/source/card-help-content.js +0 -16
  107. package/example_applications/simple_cards/source/cards/FlowCard-Comment.js +0 -38
  108. package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +0 -44
  109. package/example_applications/simple_cards/source/cards/FlowCard-Each.js +0 -38
  110. package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +0 -56
  111. package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +0 -50
  112. package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +0 -37
  113. package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +0 -49
  114. package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +0 -55
  115. package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +0 -97
  116. package/example_applications/simple_cards/source/cards/FlowCard-Sparkline.js +0 -100
  117. package/example_applications/simple_cards/source/cards/FlowCard-StatusMonitor.js +0 -46
  118. package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +0 -39
  119. package/example_applications/simple_cards/source/providers/PictRouter-FlowExample-Configuration.json +0 -22
  120. package/example_applications/simple_cards/source/sample-flows.js +0 -410
  121. package/example_applications/simple_cards/source/views/PictView-FlowExample-About.js +0 -184
  122. package/example_applications/simple_cards/source/views/PictView-FlowExample-BottomBar.js +0 -77
  123. package/example_applications/simple_cards/source/views/PictView-FlowExample-Documentation.js +0 -325
  124. package/example_applications/simple_cards/source/views/PictView-FlowExample-FileWriteInfo.js +0 -59
  125. package/example_applications/simple_cards/source/views/PictView-FlowExample-Layout.js +0 -90
  126. package/example_applications/simple_cards/source/views/PictView-FlowExample-MainWorkspace.js +0 -453
  127. package/example_applications/simple_cards/source/views/PictView-FlowExample-TopBar.js +0 -95
  128. package/scripts/generate-card-help.js +0 -214
  129. package/source/providers/edges/Edge-Bezier.js +0 -41
  130. package/source/providers/edges/Edge-Orthogonal.js +0 -37
  131. package/source/providers/edges/Edge-OrthogonalSnap.js +0 -72
  132. package/source/providers/edges/Edge-Perimeter-Linear.js +0 -31
  133. package/source/providers/edges/Edge-Perimeter-Orthogonal.js +0 -39
  134. package/source/providers/edges/Edge-Perimeter.js +0 -48
  135. package/source/providers/edges/Edge-PerimeterMath.js +0 -92
  136. package/source/providers/edges/Edge-Straight.js +0 -24
  137. package/source/providers/layouts/Layout-Circular.js +0 -203
  138. package/source/providers/layouts/Layout-Coerce.js +0 -40
  139. package/source/providers/layouts/Layout-Columnar.js +0 -134
  140. package/source/providers/layouts/Layout-Custom.js +0 -27
  141. package/source/providers/layouts/Layout-ForcedFromCenter.js +0 -256
  142. package/source/providers/layouts/Layout-Grid.js +0 -134
  143. package/source/providers/layouts/Layout-Layered.js +0 -155
  144. package/source/providers/layouts/Layout-Rank.js +0 -141
  145. package/source/providers/layouts/Layout-Staggered.js +0 -131
  146. package/source/providers/layouts/Layout-Tabular.js +0 -94
  147. package/test/CardPalette_tests.js +0 -43
  148. package/test/ConnectionHandleManager_tests.js +0 -717
  149. package/test/ConnectionRenderer_tests.js +0 -591
  150. package/test/ConnectionStyle_tests.js +0 -90
  151. package/test/DataManager_tests.js +0 -859
  152. package/test/Geometry_tests.js +0 -767
  153. package/test/InteractionManager_tests.js +0 -279
  154. package/test/Layout_tests.js +0 -1604
  155. package/test/NodeView_tests.js +0 -66
  156. package/test/PanelManager_tests.js +0 -172
  157. package/test/PathGenerator_tests.js +0 -978
  158. package/test/PortRenderer_tests.js +0 -376
  159. package/test/RenderManager_tests.js +0 -756
  160. package/test/Renderer_tests.js +0 -133
  161. package/test/SelectionManager_tests.js +0 -185
  162. package/test/StylePresets_tests.js +0 -153
  163. package/test/ToolbarExtraButtons_tests.js +0 -138
  164. package/test/UndirectedConnections_tests.js +0 -70
@@ -1,203 +0,0 @@
1
- const libCoerce = require('./Layout-Coerce.js');
2
-
3
- /**
4
- * Layout-Circular
5
- *
6
- * Concentric-ring layout.
7
- *
8
- * Two modes, decided by the presence of connections:
9
- *
10
- * With connections: BFS from the root nodes (in-degree 0; or all
11
- * nodes if every node has incoming edges). Each BFS depth becomes
12
- * one ring. Roots cluster near the center, leaves on the outer rings.
13
- *
14
- * Without connections: all nodes share a single ring with equal
15
- * angular spacing.
16
- *
17
- * Within each ring, nodes are placed at equal angles starting from
18
- * `StartAngle` (in degrees, 0 = +X axis), going clockwise or
19
- * counter-clockwise per `Direction`.
20
- */
21
- module.exports =
22
- {
23
- Name: 'Circular',
24
- Label: 'Circular',
25
- Description: 'Concentric rings; uses connections for ring assignment when available.',
26
- DefaultEdgeTheme: 'Perimeter',
27
-
28
- Apply: function (pNodes, pConnections, pParameters)
29
- {
30
- if (!pNodes || pNodes.length === 0) return;
31
-
32
- let tmpParams = pParameters || {};
33
- let tmpSpacing = libCoerce.toFloat(tmpParams.Spacing, 1.0);
34
- let tmpCenterX = libCoerce.toFloat(tmpParams.CenterX, 1000);
35
- let tmpCenterY = libCoerce.toFloat(tmpParams.CenterY, 750);
36
- let tmpRingSpacing = libCoerce.toFloat(tmpParams.RingSpacing, 220) * tmpSpacing;
37
- let tmpInnerRadius = libCoerce.toFloat(tmpParams.InnerRadius, 0) * tmpSpacing;
38
- let tmpStartAngle = libCoerce.toFloat(tmpParams.StartAngle, -90);
39
- let tmpDirection = (tmpParams.Direction === 'ccw') ? 'ccw' : 'cw';
40
-
41
- let tmpConnections = Array.isArray(pConnections) ? pConnections : [];
42
-
43
- // Build in-degree map and adjacency
44
- let tmpInDegree = {};
45
- let tmpAdjacency = {};
46
- let tmpNodeMap = {};
47
- for (let i = 0; i < pNodes.length; i++)
48
- {
49
- tmpNodeMap[pNodes[i].Hash] = pNodes[i];
50
- tmpInDegree[pNodes[i].Hash] = 0;
51
- tmpAdjacency[pNodes[i].Hash] = [];
52
- }
53
- for (let i = 0; i < tmpConnections.length; i++)
54
- {
55
- let tmpConn = tmpConnections[i];
56
- if (tmpInDegree.hasOwnProperty(tmpConn.TargetNodeHash))
57
- {
58
- tmpInDegree[tmpConn.TargetNodeHash]++;
59
- }
60
- if (tmpAdjacency.hasOwnProperty(tmpConn.SourceNodeHash))
61
- {
62
- tmpAdjacency[tmpConn.SourceNodeHash].push(tmpConn.TargetNodeHash);
63
- }
64
- }
65
-
66
- // Identify roots
67
- let tmpRoots = [];
68
- for (let i = 0; i < pNodes.length; i++)
69
- {
70
- if (tmpInDegree[pNodes[i].Hash] === 0)
71
- {
72
- tmpRoots.push(pNodes[i].Hash);
73
- }
74
- }
75
-
76
- let tmpRings = [];
77
-
78
- if (tmpConnections.length === 0 || tmpRoots.length === 0)
79
- {
80
- tmpRings.push(pNodes.map((pNode) => pNode.Hash));
81
- }
82
- else
83
- {
84
- // BFS by depth
85
- let tmpVisited = {};
86
- let tmpCurrent = tmpRoots.slice();
87
- while (tmpCurrent.length > 0)
88
- {
89
- let tmpRing = [];
90
- let tmpNext = [];
91
- for (let i = 0; i < tmpCurrent.length; i++)
92
- {
93
- let tmpHash = tmpCurrent[i];
94
- if (tmpVisited[tmpHash]) continue;
95
- tmpVisited[tmpHash] = true;
96
- tmpRing.push(tmpHash);
97
-
98
- let tmpChildren = tmpAdjacency[tmpHash] || [];
99
- for (let j = 0; j < tmpChildren.length; j++)
100
- {
101
- if (!tmpVisited[tmpChildren[j]])
102
- {
103
- tmpNext.push(tmpChildren[j]);
104
- }
105
- }
106
- }
107
- if (tmpRing.length > 0) tmpRings.push(tmpRing);
108
- tmpCurrent = tmpNext;
109
- }
110
-
111
- let tmpRemaining = [];
112
- for (let i = 0; i < pNodes.length; i++)
113
- {
114
- if (!tmpVisited[pNodes[i].Hash]) tmpRemaining.push(pNodes[i].Hash);
115
- }
116
- if (tmpRemaining.length > 0) tmpRings.push(tmpRemaining);
117
- }
118
-
119
- // Place nodes
120
- let tmpAngleSign = (tmpDirection === 'cw') ? 1 : -1;
121
- let tmpStartRad = tmpStartAngle * Math.PI / 180;
122
-
123
- for (let tmpRingIdx = 0; tmpRingIdx < tmpRings.length; tmpRingIdx++)
124
- {
125
- let tmpRing = tmpRings[tmpRingIdx];
126
- let tmpRadius = tmpInnerRadius + tmpRingIdx * tmpRingSpacing;
127
-
128
- if (tmpRing.length === 1 && tmpRingIdx === 0 && tmpInnerRadius === 0)
129
- {
130
- let tmpNode = tmpNodeMap[tmpRing[0]];
131
- if (tmpNode)
132
- {
133
- tmpNode.X = tmpCenterX - (tmpNode.Width || 180) / 2;
134
- tmpNode.Y = tmpCenterY - (tmpNode.Height || 80) / 2;
135
- }
136
- continue;
137
- }
138
-
139
- let tmpAngleStep = (2 * Math.PI) / tmpRing.length;
140
- for (let i = 0; i < tmpRing.length; i++)
141
- {
142
- let tmpNode = tmpNodeMap[tmpRing[i]];
143
- if (!tmpNode) continue;
144
- let tmpAngle = tmpStartRad + tmpAngleSign * i * tmpAngleStep;
145
- let tmpW = tmpNode.Width || 180;
146
- let tmpH = tmpNode.Height || 80;
147
- tmpNode.X = tmpCenterX + Math.cos(tmpAngle) * tmpRadius - tmpW / 2;
148
- tmpNode.Y = tmpCenterY + Math.sin(tmpAngle) * tmpRadius - tmpH / 2;
149
- }
150
- }
151
- },
152
-
153
- DefaultParameters:
154
- {
155
- Spacing: 1.0,
156
- CenterX: 1000,
157
- CenterY: 750,
158
- RingSpacing: 220,
159
- InnerRadius: 0,
160
- StartAngle: -90,
161
- Direction: 'cw'
162
- },
163
-
164
- ParameterSchema:
165
- {
166
- Spacing: { Type: 'PreciseNumber', Label: 'Spacing (multiplier)', Default: 1.0, Min: 0.1, Max: 5 },
167
- CenterX: { Type: 'PreciseNumber', Label: 'Center X', Default: 1000, Min: -10000, Max: 10000 },
168
- CenterY: { Type: 'PreciseNumber', Label: 'Center Y', Default: 750, Min: -10000, Max: 10000 },
169
- RingSpacing: { Type: 'PreciseNumber', Label: 'Ring spacing', Default: 220, Min: 1, Max: 5000 },
170
- InnerRadius: { Type: 'PreciseNumber', Label: 'Inner radius', Default: 0, Min: 0, Max: 5000 },
171
- StartAngle: { Type: 'PreciseNumber', Label: 'Start angle (deg)', Default: -90, Min: -360, Max: 360 },
172
- Direction: { Type: 'enum', Label: 'Direction', Default: 'cw', Options: ['cw', 'ccw'] }
173
- },
174
-
175
- ParameterManifest:
176
- {
177
- Scope: 'PictFlowLayout-Circular',
178
- Sections:
179
- [
180
- { Name: 'Circular Parameters', Hash: 'PFLCircularSection', Groups: [{ Name: 'Defaults', Hash: 'PFLCircularGroup' }] }
181
- ],
182
- Descriptors:
183
- {
184
- 'PictFlowLayoutEditor.Parameters.Spacing':
185
- { Name: 'Spacing (multiplier)', Hash: 'Spacing', DataType: 'PreciseNumber', Default: 1.0, PictForm: { Section: 'PFLCircularSection', Group: 'PFLCircularGroup', Row: 0, Width: 12, Min: 0.1, Max: 5 } },
186
- 'PictFlowLayoutEditor.Parameters.CenterX':
187
- { Name: 'Center X', Hash: 'CenterX', DataType: 'PreciseNumber', Default: 1000, PictForm: { Section: 'PFLCircularSection', Group: 'PFLCircularGroup', Row: 1, Width: 6, Min: -10000, Max: 10000 } },
188
- 'PictFlowLayoutEditor.Parameters.CenterY':
189
- { Name: 'Center Y', Hash: 'CenterY', DataType: 'PreciseNumber', Default: 750, PictForm: { Section: 'PFLCircularSection', Group: 'PFLCircularGroup', Row: 1, Width: 6, Min: -10000, Max: 10000 } },
190
- 'PictFlowLayoutEditor.Parameters.RingSpacing':
191
- { Name: 'Ring spacing', Hash: 'RingSpacing', DataType: 'PreciseNumber', Default: 220, PictForm: { Section: 'PFLCircularSection', Group: 'PFLCircularGroup', Row: 2, Width: 6, Min: 1, Max: 5000 } },
192
- 'PictFlowLayoutEditor.Parameters.InnerRadius':
193
- { Name: 'Inner radius', Hash: 'InnerRadius', DataType: 'PreciseNumber', Default: 0, PictForm: { Section: 'PFLCircularSection', Group: 'PFLCircularGroup', Row: 2, Width: 6, Min: 0, Max: 5000 } },
194
- 'PictFlowLayoutEditor.Parameters.StartAngle':
195
- { Name: 'Start angle (deg)', Hash: 'StartAngle', DataType: 'PreciseNumber', Default: -90, PictForm: { Section: 'PFLCircularSection', Group: 'PFLCircularGroup', Row: 3, Width: 6, Min: -360, Max: 360 } },
196
- 'PictFlowLayoutEditor.Parameters.Direction':
197
- {
198
- Name: 'Direction', Hash: 'Direction', DataType: 'String', Default: 'cw',
199
- PictForm: { Section: 'PFLCircularSection', Group: 'PFLCircularGroup', Row: 3, Width: 6, InputType: 'Option', SelectOptions: [{ Value: 'cw', Name: 'Clockwise' }, { Value: 'ccw', Name: 'Counter-clockwise' }] }
200
- }
201
- }
202
- }
203
- };
@@ -1,40 +0,0 @@
1
- /**
2
- * Layout-Coerce
3
- *
4
- * Tiny coercion helpers for layout-algorithm parameters. Manyfest's
5
- * `PreciseNumber` DataType stores values as strings (so big.js /
6
- * fable.ExpressionParser can do arbitrary-precision math on them
7
- * without float drift). The simulation code in each layout algorithm
8
- * uses native JS Math, so we coerce at the entry point.
9
- *
10
- * Apply functions accept either format (number or string) and use
11
- * these helpers to normalize.
12
- */
13
-
14
- function _toFloat(pValue, pDefault)
15
- {
16
- if (typeof pValue === 'number' && !isNaN(pValue)) return pValue;
17
- if (typeof pValue === 'string' && pValue !== '')
18
- {
19
- let tmpNum = parseFloat(pValue);
20
- if (!isNaN(tmpNum)) return tmpNum;
21
- }
22
- return pDefault;
23
- }
24
-
25
- function _toInt(pValue, pDefault)
26
- {
27
- if (typeof pValue === 'number' && !isNaN(pValue)) return Math.floor(pValue);
28
- if (typeof pValue === 'string' && pValue !== '')
29
- {
30
- let tmpNum = parseInt(pValue, 10);
31
- if (!isNaN(tmpNum)) return tmpNum;
32
- }
33
- return pDefault;
34
- }
35
-
36
- module.exports =
37
- {
38
- toFloat: _toFloat,
39
- toInt: _toInt
40
- };
@@ -1,134 +0,0 @@
1
- const libCoerce = require('./Layout-Coerce.js');
2
-
3
- /**
4
- * Layout-Columnar
5
- *
6
- * N-column layout with deterministic fill order. Distinct from Grid in
7
- * two ways:
8
- * - Columns is always explicit (no 'auto')
9
- * - FillOrder controls whether nodes flow row-first or column-first
10
- *
11
- * The column count is `Number` (integer index); spacing/origin values
12
- * are `PreciseNumber` so they survive solver chains.
13
- */
14
- module.exports =
15
- {
16
- Name: 'Columnar',
17
- Label: 'Columnar (N Columns)',
18
- Description: 'Explicit N columns; flow row-first or column-first.',
19
- DefaultEdgeTheme: 'Orthogonal',
20
-
21
- Apply: function (pNodes, pConnections, pParameters)
22
- {
23
- if (!pNodes || pNodes.length === 0) return;
24
-
25
- let tmpParams = pParameters || {};
26
- let tmpSpacing = libCoerce.toFloat(tmpParams.Spacing, 1.0);
27
- let tmpColumns = Math.max(1, libCoerce.toInt(tmpParams.Columns, 3));
28
- let tmpColumnSpacing = libCoerce.toFloat(tmpParams.ColumnSpacing, 40) * tmpSpacing;
29
- let tmpRowSpacing = libCoerce.toFloat(tmpParams.RowSpacing, 40) * tmpSpacing;
30
- let tmpStartX = libCoerce.toFloat(tmpParams.StartX, 100);
31
- let tmpStartY = libCoerce.toFloat(tmpParams.StartY, 100);
32
- let tmpFillOrder = (tmpParams.FillOrder === 'column') ? 'column' : 'row';
33
- let tmpOrderBy = tmpParams.OrderBy || 'index';
34
-
35
- // Compute cell dimensions from largest node
36
- let tmpMaxWidth = 0;
37
- let tmpMaxHeight = 0;
38
- for (let i = 0; i < pNodes.length; i++)
39
- {
40
- tmpMaxWidth = Math.max(tmpMaxWidth, pNodes[i].Width || 180);
41
- tmpMaxHeight = Math.max(tmpMaxHeight, pNodes[i].Height || 80);
42
- }
43
- let tmpCellWidth = tmpMaxWidth + tmpColumnSpacing;
44
- let tmpCellHeight = tmpMaxHeight + tmpRowSpacing;
45
-
46
- let tmpOrdered = pNodes.slice();
47
- if (tmpOrderBy === 'hash')
48
- {
49
- tmpOrdered.sort((pA, pB) => String(pA.Hash).localeCompare(String(pB.Hash)));
50
- }
51
- else if (tmpOrderBy === 'title')
52
- {
53
- tmpOrdered.sort((pA, pB) => String(pA.Title || pA.Hash).localeCompare(String(pB.Title || pB.Hash)));
54
- }
55
-
56
- let tmpRows = Math.ceil(tmpOrdered.length / tmpColumns);
57
-
58
- for (let i = 0; i < tmpOrdered.length; i++)
59
- {
60
- let tmpRow;
61
- let tmpCol;
62
- if (tmpFillOrder === 'column')
63
- {
64
- tmpCol = Math.floor(i / tmpRows);
65
- tmpRow = i % tmpRows;
66
- }
67
- else
68
- {
69
- tmpRow = Math.floor(i / tmpColumns);
70
- tmpCol = i % tmpColumns;
71
- }
72
- tmpOrdered[i].X = tmpStartX + tmpCol * tmpCellWidth;
73
- tmpOrdered[i].Y = tmpStartY + tmpRow * tmpCellHeight;
74
- }
75
- },
76
-
77
- DefaultParameters:
78
- {
79
- Spacing: 1.0,
80
- Columns: 3,
81
- ColumnSpacing: 40,
82
- RowSpacing: 40,
83
- StartX: 100,
84
- StartY: 100,
85
- FillOrder: 'row',
86
- OrderBy: 'index'
87
- },
88
-
89
- ParameterSchema:
90
- {
91
- Spacing: { Type: 'PreciseNumber', Label: 'Spacing (multiplier)', Default: 1.0, Min: 0.1, Max: 5 },
92
- Columns: { Type: 'Number', Label: 'Columns', Default: 3, Min: 1, Max: 50 },
93
- ColumnSpacing: { Type: 'PreciseNumber', Label: 'Column spacing', Default: 40, Min: 0, Max: 1000 },
94
- RowSpacing: { Type: 'PreciseNumber', Label: 'Row spacing', Default: 40, Min: 0, Max: 1000 },
95
- StartX: { Type: 'PreciseNumber', Label: 'Start X', Default: 100, Min: -10000, Max: 10000 },
96
- StartY: { Type: 'PreciseNumber', Label: 'Start Y', Default: 100, Min: -10000, Max: 10000 },
97
- FillOrder: { Type: 'enum', Label: 'Fill order', Default: 'row', Options: ['row', 'column'] },
98
- OrderBy: { Type: 'enum', Label: 'Order by', Default: 'index', Options: ['index', 'hash', 'title'] }
99
- },
100
-
101
- ParameterManifest:
102
- {
103
- Scope: 'PictFlowLayout-Columnar',
104
- Sections:
105
- [
106
- { Name: 'Columnar Parameters', Hash: 'PFLColumnarSection', Groups: [{ Name: 'Defaults', Hash: 'PFLColumnarGroup' }] }
107
- ],
108
- Descriptors:
109
- {
110
- 'PictFlowLayoutEditor.Parameters.Spacing':
111
- { Name: 'Spacing (multiplier)', Hash: 'Spacing', DataType: 'PreciseNumber', Default: 1.0, PictForm: { Section: 'PFLColumnarSection', Group: 'PFLColumnarGroup', Row: 0, Width: 12, Min: 0.1, Max: 5 } },
112
- 'PictFlowLayoutEditor.Parameters.Columns':
113
- { Name: 'Columns', Hash: 'Columns', DataType: 'Number', Default: 3, PictForm: { Section: 'PFLColumnarSection', Group: 'PFLColumnarGroup', Row: 1, Width: 6, Min: 1, Max: 50 } },
114
- 'PictFlowLayoutEditor.Parameters.FillOrder':
115
- {
116
- Name: 'Fill order', Hash: 'FillOrder', DataType: 'String', Default: 'row',
117
- PictForm: { Section: 'PFLColumnarSection', Group: 'PFLColumnarGroup', Row: 1, Width: 6, InputType: 'Option', SelectOptions: [{ Value: 'row', Name: 'Row-first' }, { Value: 'column', Name: 'Column-first' }] }
118
- },
119
- 'PictFlowLayoutEditor.Parameters.ColumnSpacing':
120
- { Name: 'Column spacing', Hash: 'ColumnSpacing', DataType: 'PreciseNumber', Default: 40, PictForm: { Section: 'PFLColumnarSection', Group: 'PFLColumnarGroup', Row: 2, Width: 6, Min: 0, Max: 1000 } },
121
- 'PictFlowLayoutEditor.Parameters.RowSpacing':
122
- { Name: 'Row spacing', Hash: 'RowSpacing', DataType: 'PreciseNumber', Default: 40, PictForm: { Section: 'PFLColumnarSection', Group: 'PFLColumnarGroup', Row: 2, Width: 6, Min: 0, Max: 1000 } },
123
- 'PictFlowLayoutEditor.Parameters.StartX':
124
- { Name: 'Start X', Hash: 'StartX', DataType: 'PreciseNumber', Default: 100, PictForm: { Section: 'PFLColumnarSection', Group: 'PFLColumnarGroup', Row: 3, Width: 6, Min: -10000, Max: 10000 } },
125
- 'PictFlowLayoutEditor.Parameters.StartY':
126
- { Name: 'Start Y', Hash: 'StartY', DataType: 'PreciseNumber', Default: 100, PictForm: { Section: 'PFLColumnarSection', Group: 'PFLColumnarGroup', Row: 3, Width: 6, Min: -10000, Max: 10000 } },
127
- 'PictFlowLayoutEditor.Parameters.OrderBy':
128
- {
129
- Name: 'Order by', Hash: 'OrderBy', DataType: 'String', Default: 'index',
130
- PictForm: { Section: 'PFLColumnarSection', Group: 'PFLColumnarGroup', Row: 4, Width: 12, InputType: 'Option', SelectOptions: [{ Value: 'index', Name: 'Index' }, { Value: 'hash', Name: 'Hash' }, { Value: 'title', Name: 'Title' }] }
131
- }
132
- }
133
- }
134
- };
@@ -1,27 +0,0 @@
1
- /**
2
- * Layout-Custom
3
- *
4
- * No-op layout algorithm. Preserves the X/Y values currently on each
5
- * node so users can hand-place nodes without an algorithm clobbering
6
- * positions on the next render.
7
- *
8
- * Selecting "Custom" with auto-apply enabled is effectively a no-op
9
- * on every structural change — useful as a way to disable the
10
- * configured algorithm without unsetting it.
11
- */
12
- module.exports =
13
- {
14
- Name: 'Custom',
15
- Label: 'Custom (Hand-placed)',
16
- Description: 'Preserve hand-placed positions. No automatic arrangement.',
17
- DefaultEdgeTheme: 'Bezier',
18
-
19
- Apply: function (pNodes, pConnections, pParameters)
20
- {
21
- // Intentionally empty — Custom is a no-op.
22
- },
23
-
24
- DefaultParameters: {},
25
-
26
- ParameterSchema: {}
27
- };
@@ -1,256 +0,0 @@
1
- const libCoerce = require('./Layout-Coerce.js');
2
-
3
- /**
4
- * Layout-ForcedFromCenter
5
- *
6
- * Force-directed simulation in the Fruchterman-Reingold style:
7
- * - Spring (attractive) forces along each connection
8
- * - Coulomb-style repulsion between every pair of nodes
9
- * - Center-attraction force pulling all nodes toward (CenterX, CenterY)
10
- * - Cooling schedule: max displacement per iteration shrinks over time
11
- *
12
- * Deterministic by default — initial positions for unplaced nodes are
13
- * generated from a seedable Mulberry32 PRNG (inline implementation, no
14
- * new dependencies). Tests pin `Seed` to assert byte-identical output.
15
- *
16
- * Performance: O(n^2) per iteration. With Iterations=200 this is fine
17
- * for ~100 nodes; keep ForcedFromCenter for moderate-sized graphs.
18
- *
19
- * Numeric parameters (other than Iterations and Seed) are typed
20
- * `PreciseNumber` so they round-trip cleanly through the
21
- * ExpressionParser; the simulation coerces back to JS floats via
22
- * Layout-Coerce. Iterations and Seed stay `Number` because they're
23
- * loop counters / bitwise-PRNG state.
24
- */
25
- module.exports =
26
- {
27
- Name: 'ForcedFromCenter',
28
- Label: 'Forced from Center',
29
- Description: 'Spring + repulsion simulation pulling toward a center point.',
30
- DefaultEdgeTheme: 'Bezier',
31
-
32
- Apply: function (pNodes, pConnections, pParameters)
33
- {
34
- if (!pNodes || pNodes.length === 0) return;
35
-
36
- let tmpParams = pParameters || {};
37
- let tmpSpacing = libCoerce.toFloat(tmpParams.Spacing, 1.0);
38
- let tmpIterations = libCoerce.toInt(tmpParams.Iterations, 200);
39
- let tmpCenterX = libCoerce.toFloat(tmpParams.CenterX, 1000);
40
- let tmpCenterY = libCoerce.toFloat(tmpParams.CenterY, 750);
41
- let tmpSpringLength = libCoerce.toFloat(tmpParams.SpringLength, 200) * tmpSpacing;
42
- let tmpSpringStiffness = libCoerce.toFloat(tmpParams.SpringStiffness, 0.05);
43
- let tmpRepulsion = libCoerce.toFloat(tmpParams.Repulsion, 8000);
44
- let tmpCenterAttraction = libCoerce.toFloat(tmpParams.CenterAttraction, 0.01);
45
- let tmpCoolingFactor = libCoerce.toFloat(tmpParams.CoolingFactor, 0.95);
46
- let tmpInitialTemperature = libCoerce.toFloat(tmpParams.InitialTemperature, 100);
47
- let tmpSeed = libCoerce.toInt(tmpParams.Seed, 42);
48
- let tmpPreservePositions = !!tmpParams.PreservePositions;
49
- let tmpInitialSpread = libCoerce.toFloat(tmpParams.InitialSpread, 400);
50
-
51
- let tmpConnections = Array.isArray(pConnections) ? pConnections : [];
52
-
53
- // Mulberry32 seeded PRNG — deterministic initial-position generator.
54
- let tmpRand = _makeMulberry32(tmpSeed >>> 0);
55
-
56
- // Initial positions
57
- for (let i = 0; i < pNodes.length; i++)
58
- {
59
- let tmpNode = pNodes[i];
60
- let tmpHasPosition = (typeof tmpNode.X === 'number' && typeof tmpNode.Y === 'number');
61
- if (tmpPreservePositions && tmpHasPosition) continue;
62
- tmpNode.X = tmpCenterX + (tmpRand() - 0.5) * tmpInitialSpread;
63
- tmpNode.Y = tmpCenterY + (tmpRand() - 0.5) * tmpInitialSpread;
64
- }
65
-
66
- // Index nodes by hash for connection lookup
67
- let tmpNodeMap = {};
68
- for (let i = 0; i < pNodes.length; i++)
69
- {
70
- tmpNodeMap[pNodes[i].Hash] = pNodes[i];
71
- }
72
-
73
- let tmpTemperature = tmpInitialTemperature;
74
-
75
- for (let tmpIter = 0; tmpIter < tmpIterations; tmpIter++)
76
- {
77
- // Force accumulators
78
- let tmpForceX = new Array(pNodes.length).fill(0);
79
- let tmpForceY = new Array(pNodes.length).fill(0);
80
-
81
- // Repulsion between every pair of nodes
82
- for (let i = 0; i < pNodes.length; i++)
83
- {
84
- let tmpA = pNodes[i];
85
- for (let j = i + 1; j < pNodes.length; j++)
86
- {
87
- let tmpB = pNodes[j];
88
- let tmpDX = tmpA.X - tmpB.X;
89
- let tmpDY = tmpA.Y - tmpB.Y;
90
- let tmpDistSq = tmpDX * tmpDX + tmpDY * tmpDY;
91
- if (tmpDistSq < 1) tmpDistSq = 1; // avoid singularities
92
- let tmpDist = Math.sqrt(tmpDistSq);
93
- let tmpForce = tmpRepulsion / tmpDistSq;
94
- let tmpFX = (tmpDX / tmpDist) * tmpForce;
95
- let tmpFY = (tmpDY / tmpDist) * tmpForce;
96
- tmpForceX[i] += tmpFX;
97
- tmpForceY[i] += tmpFY;
98
- tmpForceX[j] -= tmpFX;
99
- tmpForceY[j] -= tmpFY;
100
- }
101
- }
102
-
103
- // Spring forces along connections
104
- for (let i = 0; i < tmpConnections.length; i++)
105
- {
106
- let tmpConn = tmpConnections[i];
107
- let tmpSource = tmpNodeMap[tmpConn.SourceNodeHash];
108
- let tmpTarget = tmpNodeMap[tmpConn.TargetNodeHash];
109
- if (!tmpSource || !tmpTarget) continue;
110
-
111
- let tmpSourceIdx = pNodes.indexOf(tmpSource);
112
- let tmpTargetIdx = pNodes.indexOf(tmpTarget);
113
- if (tmpSourceIdx < 0 || tmpTargetIdx < 0) continue;
114
-
115
- let tmpDX = tmpTarget.X - tmpSource.X;
116
- let tmpDY = tmpTarget.Y - tmpSource.Y;
117
- let tmpDist = Math.sqrt(tmpDX * tmpDX + tmpDY * tmpDY);
118
- if (tmpDist < 0.0001) tmpDist = 0.0001;
119
-
120
- let tmpDelta = tmpDist - tmpSpringLength;
121
- let tmpForce = tmpSpringStiffness * tmpDelta;
122
- let tmpFX = (tmpDX / tmpDist) * tmpForce;
123
- let tmpFY = (tmpDY / tmpDist) * tmpForce;
124
-
125
- tmpForceX[tmpSourceIdx] += tmpFX;
126
- tmpForceY[tmpSourceIdx] += tmpFY;
127
- tmpForceX[tmpTargetIdx] -= tmpFX;
128
- tmpForceY[tmpTargetIdx] -= tmpFY;
129
- }
130
-
131
- // Center attraction
132
- for (let i = 0; i < pNodes.length; i++)
133
- {
134
- let tmpNode = pNodes[i];
135
- tmpForceX[i] += (tmpCenterX - tmpNode.X) * tmpCenterAttraction;
136
- tmpForceY[i] += (tmpCenterY - tmpNode.Y) * tmpCenterAttraction;
137
- }
138
-
139
- // Apply forces with temperature clamp
140
- for (let i = 0; i < pNodes.length; i++)
141
- {
142
- let tmpNode = pNodes[i];
143
- let tmpFX = tmpForceX[i];
144
- let tmpFY = tmpForceY[i];
145
- let tmpMag = Math.sqrt(tmpFX * tmpFX + tmpFY * tmpFY);
146
- if (tmpMag > tmpTemperature)
147
- {
148
- tmpFX = (tmpFX / tmpMag) * tmpTemperature;
149
- tmpFY = (tmpFY / tmpMag) * tmpTemperature;
150
- }
151
- tmpNode.X += tmpFX;
152
- tmpNode.Y += tmpFY;
153
- }
154
-
155
- tmpTemperature *= tmpCoolingFactor;
156
- }
157
-
158
- // Round to whole pixels for stable rendering and predictable tests
159
- for (let i = 0; i < pNodes.length; i++)
160
- {
161
- pNodes[i].X = Math.round(pNodes[i].X);
162
- pNodes[i].Y = Math.round(pNodes[i].Y);
163
- }
164
- },
165
-
166
- DefaultParameters:
167
- {
168
- Spacing: 1.0,
169
- Iterations: 200,
170
- CenterX: 1000,
171
- CenterY: 750,
172
- SpringLength: 200,
173
- SpringStiffness: 0.05,
174
- Repulsion: 8000,
175
- CenterAttraction: 0.01,
176
- CoolingFactor: 0.95,
177
- InitialTemperature: 100,
178
- Seed: 42,
179
- PreservePositions: false,
180
- InitialSpread: 400
181
- },
182
-
183
- ParameterSchema:
184
- {
185
- Spacing: { Type: 'PreciseNumber', Label: 'Spacing (multiplier)', Default: 1.0, Min: 0.1, Max: 5 },
186
- Iterations: { Type: 'Number', Label: 'Iterations', Default: 200, Min: 1, Max: 2000 },
187
- CenterX: { Type: 'PreciseNumber', Label: 'Center X', Default: 1000, Min: -10000, Max: 10000 },
188
- CenterY: { Type: 'PreciseNumber', Label: 'Center Y', Default: 750, Min: -10000, Max: 10000 },
189
- SpringLength: { Type: 'PreciseNumber', Label: 'Spring length', Default: 200, Min: 1, Max: 2000 },
190
- SpringStiffness: { Type: 'PreciseNumber', Label: 'Spring stiffness', Default: 0.05, Min: 0, Max: 1 },
191
- Repulsion: { Type: 'PreciseNumber', Label: 'Repulsion', Default: 8000, Min: 0, Max: 100000 },
192
- CenterAttraction: { Type: 'PreciseNumber', Label: 'Center attraction', Default: 0.01, Min: 0, Max: 1 },
193
- CoolingFactor: { Type: 'PreciseNumber', Label: 'Cooling factor', Default: 0.95, Min: 0.5, Max: 1 },
194
- InitialTemperature: { Type: 'PreciseNumber', Label: 'Initial temperature', Default: 100, Min: 1, Max: 1000 },
195
- Seed: { Type: 'Number', Label: 'Random seed', Default: 42, Min: 0, Max: 2147483647 },
196
- PreservePositions: { Type: 'boolean', Label: 'Preserve positions', Default: false },
197
- InitialSpread: { Type: 'PreciseNumber', Label: 'Initial spread', Default: 400, Min: 0, Max: 5000 }
198
- },
199
-
200
- ParameterManifest:
201
- {
202
- Scope: 'PictFlowLayout-ForcedFromCenter',
203
- Sections:
204
- [
205
- { Name: 'Center', Hash: 'PFLCenterSection', Groups: [{ Name: 'Defaults', Hash: 'PFLCenterGroup' }] },
206
- { Name: 'Forces', Hash: 'PFLForcesSection', Groups: [{ Name: 'Defaults', Hash: 'PFLForcesGroup' }] },
207
- { Name: 'Simulation', Hash: 'PFLSimSection', Groups: [{ Name: 'Defaults', Hash: 'PFLSimGroup' }] },
208
- { Name: 'Initialization', Hash: 'PFLInitSection', Groups: [{ Name: 'Defaults', Hash: 'PFLInitGroup' }] }
209
- ],
210
- Descriptors:
211
- {
212
- 'PictFlowLayoutEditor.Parameters.Spacing':
213
- { Name: 'Spacing (multiplier)', Hash: 'Spacing', DataType: 'PreciseNumber', Default: 1.0, PictForm: { Section: 'PFLCenterSection', Group: 'PFLCenterGroup', Row: 0, Width: 12, Min: 0.1, Max: 5 } },
214
- 'PictFlowLayoutEditor.Parameters.CenterX':
215
- { Name: 'Center X', Hash: 'CenterX', DataType: 'PreciseNumber', Default: 1000, PictForm: { Section: 'PFLCenterSection', Group: 'PFLCenterGroup', Row: 1, Width: 6, Min: -10000, Max: 10000 } },
216
- 'PictFlowLayoutEditor.Parameters.CenterY':
217
- { Name: 'Center Y', Hash: 'CenterY', DataType: 'PreciseNumber', Default: 750, PictForm: { Section: 'PFLCenterSection', Group: 'PFLCenterGroup', Row: 1, Width: 6, Min: -10000, Max: 10000 } },
218
- 'PictFlowLayoutEditor.Parameters.CenterAttraction':
219
- { Name: 'Center attraction', Hash: 'CenterAttraction', DataType: 'PreciseNumber', Default: 0.01, PictForm: { Section: 'PFLCenterSection', Group: 'PFLCenterGroup', Row: 2, Width: 12, Min: 0, Max: 1 } },
220
-
221
- 'PictFlowLayoutEditor.Parameters.SpringLength':
222
- { Name: 'Spring length', Hash: 'SpringLength', DataType: 'PreciseNumber', Default: 200, PictForm: { Section: 'PFLForcesSection', Group: 'PFLForcesGroup', Row: 1, Width: 6, Min: 1, Max: 2000 } },
223
- 'PictFlowLayoutEditor.Parameters.SpringStiffness':
224
- { Name: 'Spring stiffness', Hash: 'SpringStiffness', DataType: 'PreciseNumber', Default: 0.05, PictForm: { Section: 'PFLForcesSection', Group: 'PFLForcesGroup', Row: 1, Width: 6, Min: 0, Max: 1 } },
225
- 'PictFlowLayoutEditor.Parameters.Repulsion':
226
- { Name: 'Repulsion', Hash: 'Repulsion', DataType: 'PreciseNumber', Default: 8000, PictForm: { Section: 'PFLForcesSection', Group: 'PFLForcesGroup', Row: 2, Width: 12, Min: 0, Max: 100000 } },
227
-
228
- 'PictFlowLayoutEditor.Parameters.Iterations':
229
- { Name: 'Iterations', Hash: 'Iterations', DataType: 'Number', Default: 200, PictForm: { Section: 'PFLSimSection', Group: 'PFLSimGroup', Row: 1, Width: 6, Min: 1, Max: 2000 } },
230
- 'PictFlowLayoutEditor.Parameters.CoolingFactor':
231
- { Name: 'Cooling factor', Hash: 'CoolingFactor', DataType: 'PreciseNumber', Default: 0.95, PictForm: { Section: 'PFLSimSection', Group: 'PFLSimGroup', Row: 1, Width: 6, Min: 0.5, Max: 1 } },
232
- 'PictFlowLayoutEditor.Parameters.InitialTemperature':
233
- { Name: 'Initial temperature', Hash: 'InitialTemperature', DataType: 'PreciseNumber', Default: 100, PictForm: { Section: 'PFLSimSection', Group: 'PFLSimGroup', Row: 2, Width: 12, Min: 1, Max: 1000 } },
234
-
235
- 'PictFlowLayoutEditor.Parameters.Seed':
236
- { Name: 'Random seed', Hash: 'Seed', DataType: 'Number', Default: 42, PictForm: { Section: 'PFLInitSection', Group: 'PFLInitGroup', Row: 1, Width: 6, Min: 0, Max: 2147483647 } },
237
- 'PictFlowLayoutEditor.Parameters.InitialSpread':
238
- { Name: 'Initial spread', Hash: 'InitialSpread', DataType: 'PreciseNumber', Default: 400, PictForm: { Section: 'PFLInitSection', Group: 'PFLInitGroup', Row: 1, Width: 6, Min: 0, Max: 5000 } },
239
- 'PictFlowLayoutEditor.Parameters.PreservePositions':
240
- { Name: 'Preserve existing positions', Hash: 'PreservePositions', DataType: 'Boolean', Default: false, PictForm: { Section: 'PFLInitSection', Group: 'PFLInitGroup', Row: 2, Width: 12, InputType: 'Boolean' } }
241
- }
242
- }
243
- };
244
-
245
- function _makeMulberry32(pSeed)
246
- {
247
- let tmpState = pSeed >>> 0;
248
- return function ()
249
- {
250
- tmpState = (tmpState + 0x6D2B79F5) >>> 0;
251
- let tmpT = tmpState;
252
- tmpT = Math.imul(tmpT ^ (tmpT >>> 15), tmpT | 1);
253
- tmpT ^= tmpT + Math.imul(tmpT ^ (tmpT >>> 7), tmpT | 61);
254
- return ((tmpT ^ (tmpT >>> 14)) >>> 0) / 4294967296;
255
- };
256
- }