layerchart 0.41.6 → 0.42.0
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.
|
@@ -115,7 +115,7 @@ onMount(() => {
|
|
|
115
115
|
>
|
|
116
116
|
<!-- Apply `fitGeojson` using TransformContext instead of GeoContext if `applyTransform` is used -->
|
|
117
117
|
{@const initialTransform =
|
|
118
|
-
geo?.applyTransform && geo?.fitGeojson && geo?.projection
|
|
118
|
+
geo?.applyTransform?.includes('translate') && geo?.fitGeojson && geo?.projection
|
|
119
119
|
? geoFitObjectTransform(geo.projection(), [width, height], geo.fitGeojson)
|
|
120
120
|
: undefined}
|
|
121
121
|
|
|
@@ -1,49 +1,205 @@
|
|
|
1
|
-
<script>import { getContext } from 'svelte';
|
|
1
|
+
<script>import { createEventDispatcher, getContext } from 'svelte';
|
|
2
2
|
import { forceSimulation } from 'd3-force';
|
|
3
3
|
const { data } = getContext('LayerCake');
|
|
4
|
+
const dispatch = createEventDispatcher();
|
|
4
5
|
export let forces;
|
|
5
6
|
export let alpha = 1;
|
|
6
7
|
export let alphaTarget = 0;
|
|
7
8
|
export let alphaDecay = 1 - Math.pow(0.001, 1 / 300);
|
|
8
9
|
export let alphaMin = 0.001;
|
|
9
10
|
export let velocityDecay = 0.4;
|
|
10
|
-
|
|
11
|
-
export let
|
|
11
|
+
export let alphaResponse = undefined;
|
|
12
|
+
export let stopped = false;
|
|
12
13
|
let _static = false;
|
|
13
14
|
/** If true, will only update nodes after simulation has completed */
|
|
14
15
|
export { _static as static };
|
|
15
|
-
|
|
16
|
+
/** Clone data since simulation mutates original */
|
|
17
|
+
export const cloneData = false;
|
|
18
|
+
// MARK: Private Props
|
|
19
|
+
let nodes = [];
|
|
20
|
+
const simulation = forceSimulation().stop();
|
|
21
|
+
// d3.Simulation does not provide a `.forces()` getter, so we need to
|
|
22
|
+
// keep track of previous forces ourselves, for diffing against `forces`.
|
|
23
|
+
let previousForces = {};
|
|
24
|
+
let paused = true;
|
|
25
|
+
// MARK: Reactivity Effects
|
|
26
|
+
$: {
|
|
27
|
+
// Any time the `stopped` prop gets toggled we
|
|
28
|
+
// update the running state of the simulation:
|
|
29
|
+
if (stopped) {
|
|
30
|
+
pauseDynamicSimulation();
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
runOrResumeSimulation();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
$: {
|
|
37
|
+
// Any time the `static` prop gets toggled we
|
|
38
|
+
// either attach or detach our internal event listeners:
|
|
39
|
+
if (_static) {
|
|
40
|
+
simulation.on('tick', null).on('end', null);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
simulation.on('tick', onTick).on('end', onEnd);
|
|
44
|
+
}
|
|
45
|
+
runOrResumeSimulation();
|
|
46
|
+
}
|
|
47
|
+
$: {
|
|
48
|
+
// Any time the `$data` store gets changed we
|
|
49
|
+
// pass them to the internal d3 simulation object:
|
|
50
|
+
pushNodesToSimulation($data);
|
|
51
|
+
runOrResumeSimulation();
|
|
52
|
+
}
|
|
16
53
|
$: {
|
|
54
|
+
// Any time the `forces` prop gets changed we
|
|
55
|
+
// pass them to the internal d3 simulation object:
|
|
56
|
+
pushForcesToSimulation(forces);
|
|
57
|
+
runOrResumeSimulation();
|
|
58
|
+
}
|
|
59
|
+
$: {
|
|
60
|
+
// Any time the `alpha` prop gets changed we
|
|
61
|
+
// pass it to the internal d3 simulation object:
|
|
62
|
+
pushAlphaToSimulation(alpha);
|
|
63
|
+
// Only resume the simulation as long as `alpha`
|
|
64
|
+
// is above the cut-off threshold of `alphaMin`,
|
|
65
|
+
// otherwise our simulation will never terminate:
|
|
66
|
+
if (simulation.alpha() >= simulation.alphaMin()) {
|
|
67
|
+
runOrResumeSimulation();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
$: {
|
|
71
|
+
// Any time any of the the alpha props get changed we
|
|
72
|
+
// pass them all to the internal d3 simulation object
|
|
73
|
+
// (they are cheap, so passing them as a batch is fine!):
|
|
74
|
+
// We read `simulation.alpha()` instead of `alpha` here, so
|
|
75
|
+
// Svelte does not trigger this block on any change to `alpha`:
|
|
76
|
+
let alphaValue = simulation.alpha();
|
|
77
|
+
if (alphaTarget > alphaValue && alphaValue < alphaMin) {
|
|
78
|
+
// Lift `alpha` from below `alphaMin` in order to give the simulation
|
|
79
|
+
// a chance to get revived if an `alphaTarget > alpha` is provided:
|
|
80
|
+
alphaValue = alphaMin;
|
|
81
|
+
}
|
|
17
82
|
simulation
|
|
18
|
-
.alpha(
|
|
83
|
+
.alpha(alphaValue)
|
|
19
84
|
.alphaTarget(alphaTarget)
|
|
20
85
|
.alphaMin(alphaMin)
|
|
21
86
|
.alphaDecay(alphaDecay)
|
|
22
|
-
.velocityDecay(velocityDecay)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
87
|
+
.velocityDecay(velocityDecay);
|
|
88
|
+
runOrResumeSimulation();
|
|
89
|
+
}
|
|
90
|
+
// MARK: Push State
|
|
91
|
+
function pushAlphaToSimulation(alpha) {
|
|
92
|
+
simulation.alpha(alpha);
|
|
93
|
+
}
|
|
94
|
+
function pushNodesToSimulation(nodes) {
|
|
95
|
+
simulation.nodes(cloneData ? structuredClone(nodes) : nodes);
|
|
96
|
+
}
|
|
97
|
+
function pushForcesToSimulation(forces) {
|
|
98
|
+
// Evict obsolete forces:
|
|
99
|
+
Object.keys(previousForces).forEach((name) => {
|
|
100
|
+
if (!(name in forces)) {
|
|
101
|
+
simulation.force(name, null);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
// Add new or overwrite existing forces:
|
|
105
|
+
Object.entries(forces).forEach(([name, force]) => {
|
|
106
|
+
if (!(name in previousForces) || force !== previousForces[name]) {
|
|
29
107
|
simulation.force(name, force);
|
|
30
|
-
});
|
|
31
|
-
for (let i = 0, n = Math.ceil(Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay())); i < n; ++i) {
|
|
32
|
-
simulation.tick();
|
|
33
108
|
}
|
|
34
|
-
|
|
109
|
+
});
|
|
110
|
+
previousForces = forces;
|
|
111
|
+
}
|
|
112
|
+
// MARK: Pull State
|
|
113
|
+
function pullNodesFromSimulation() {
|
|
114
|
+
nodes = simulation.nodes();
|
|
115
|
+
}
|
|
116
|
+
function pullAlphaFromSimulation() {
|
|
117
|
+
alpha = simulation.alpha();
|
|
118
|
+
}
|
|
119
|
+
// MARK: Resume / Pause
|
|
120
|
+
function runOrResumeSimulation() {
|
|
121
|
+
if (_static) {
|
|
122
|
+
runStaticSimulationToCompletion();
|
|
35
123
|
}
|
|
36
124
|
else {
|
|
37
|
-
|
|
38
|
-
Object.entries(forces).forEach(([name, force]) => {
|
|
39
|
-
simulation.force(name, force);
|
|
40
|
-
});
|
|
125
|
+
resumeDynamicSimulation();
|
|
41
126
|
}
|
|
42
127
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
128
|
+
function runStaticSimulationToCompletion() {
|
|
129
|
+
if (stopped) {
|
|
130
|
+
// If a simulation is marked as stopped, then it should not get started.
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (!_static) {
|
|
134
|
+
// Only static simulations are run to completion.
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (!paused) {
|
|
138
|
+
// Pause any possibly still running dynamic simulation:
|
|
139
|
+
pauseDynamicSimulation();
|
|
140
|
+
}
|
|
141
|
+
const ticks = Math.ceil(Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay()));
|
|
142
|
+
pushAlphaToSimulation(1.0);
|
|
143
|
+
onStart();
|
|
144
|
+
for (let i = 0; i < ticks; ++i) {
|
|
145
|
+
simulation.tick();
|
|
146
|
+
}
|
|
147
|
+
pullNodesFromSimulation();
|
|
148
|
+
pullAlphaFromSimulation();
|
|
149
|
+
onEnd();
|
|
150
|
+
}
|
|
151
|
+
function resumeDynamicSimulation() {
|
|
152
|
+
if (!paused) {
|
|
153
|
+
// No need to restart an already running simulation.
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (stopped) {
|
|
157
|
+
// If a simulation is marked as stopped, then it should not get resumed.
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (_static) {
|
|
161
|
+
// Only dynamic simulations can be resumed.
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
onStart();
|
|
165
|
+
simulation.restart();
|
|
166
|
+
// No need to call `onEnd();` for dynamic simulations
|
|
167
|
+
// as the simulation itself takes care of firing `on:end`,
|
|
168
|
+
// which then gets calls `onEnd();` for us.
|
|
169
|
+
}
|
|
170
|
+
function pauseDynamicSimulation() {
|
|
171
|
+
if (paused) {
|
|
172
|
+
// No need to pause an already paused simulation.
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
simulation.stop();
|
|
176
|
+
onEnd();
|
|
177
|
+
}
|
|
178
|
+
// MARK: Event Listeners
|
|
179
|
+
function onStart() {
|
|
180
|
+
if (!paused) {
|
|
181
|
+
// Avoid double-emissions of `start` event due to race conditions.
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
paused = false;
|
|
185
|
+
dispatch('start');
|
|
186
|
+
}
|
|
187
|
+
function onTick() {
|
|
188
|
+
pullNodesFromSimulation();
|
|
189
|
+
pullAlphaFromSimulation();
|
|
190
|
+
dispatch('tick', {
|
|
191
|
+
alpha,
|
|
192
|
+
alphaTarget,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
function onEnd() {
|
|
196
|
+
if (paused) {
|
|
197
|
+
// Avoid double-emissions of `end` event due to race conditions.
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
paused = true;
|
|
201
|
+
dispatch('end');
|
|
202
|
+
}
|
|
47
203
|
</script>
|
|
48
204
|
|
|
49
205
|
<slot {nodes} {simulation} />
|
|
@@ -8,10 +8,20 @@ declare const __propDef: {
|
|
|
8
8
|
alphaDecay?: number;
|
|
9
9
|
alphaMin?: number;
|
|
10
10
|
velocityDecay?: number;
|
|
11
|
-
|
|
11
|
+
alphaResponse?: ((alpha: number, alphaTarget: number) => number) | undefined;
|
|
12
|
+
stopped?: boolean;
|
|
12
13
|
/** If true, will only update nodes after simulation has completed */ static?: boolean;
|
|
14
|
+
/** Clone data since simulation mutates original */ cloneData?: boolean;
|
|
13
15
|
};
|
|
14
16
|
events: {
|
|
17
|
+
start: CustomEvent<null>;
|
|
18
|
+
tick: CustomEvent<{
|
|
19
|
+
alpha: number;
|
|
20
|
+
alphaTarget: number;
|
|
21
|
+
}>;
|
|
22
|
+
change: CustomEvent<null>;
|
|
23
|
+
end: CustomEvent<null>;
|
|
24
|
+
} & {
|
|
15
25
|
[evt: string]: CustomEvent<any>;
|
|
16
26
|
};
|
|
17
27
|
slots: {
|
|
@@ -25,5 +35,6 @@ export type ForceSimulationProps = typeof __propDef.props;
|
|
|
25
35
|
export type ForceSimulationEvents = typeof __propDef.events;
|
|
26
36
|
export type ForceSimulationSlots = typeof __propDef.slots;
|
|
27
37
|
export default class ForceSimulation extends SvelteComponentTyped<ForceSimulationProps, ForceSimulationEvents, ForceSimulationSlots> {
|
|
38
|
+
get cloneData(): NonNullable<boolean | undefined>;
|
|
28
39
|
}
|
|
29
40
|
export {};
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"author": "Sean Lynch <techniq35@gmail.com>",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "techniq/layerchart",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.42.0",
|
|
8
8
|
"devDependencies": {
|
|
9
9
|
"@changesets/cli": "^2.27.5",
|
|
10
10
|
"@mdi/js": "^7.4.47",
|
|
@@ -85,9 +85,9 @@
|
|
|
85
85
|
"date-fns": "^3.6.0",
|
|
86
86
|
"layercake": "^8.3.0",
|
|
87
87
|
"lodash-es": "^4.17.21",
|
|
88
|
-
"posthog-js": "^1.
|
|
88
|
+
"posthog-js": "^1.140.1",
|
|
89
89
|
"shapefile": "^0.6.6",
|
|
90
|
-
"svelte-ux": "^0.
|
|
90
|
+
"svelte-ux": "^0.71.0",
|
|
91
91
|
"topojson-client": "^3.1.0"
|
|
92
92
|
},
|
|
93
93
|
"peerDependencies": {
|