@tellegen/svelte 0.4.2 → 0.4.4
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/README.md +20 -0
- package/dist/TellegenMap.svelte +63 -22
- package/dist/TellegenProvider.svelte +7 -4
- package/dist/TellegenShell.svelte +10 -1
- package/dist/TellegenViewer.svelte +2 -0
- package/dist/basemap.d.ts +14 -0
- package/dist/basemap.js +27 -0
- package/dist/colors.d.ts +6 -1
- package/dist/colors.js +11 -4
- package/dist/components/DemandSlider.svelte +0 -2
- package/dist/components/FormulationSelector.svelte +1 -1
- package/dist/components/LocalCaseDetails.svelte +18 -3
- package/dist/components/McResults.svelte +18 -3
- package/dist/components/McWorkbench.svelte +688 -0
- package/dist/components/McWorkbench.svelte.d.ts +38 -0
- package/dist/components/MultiWire.svelte +136 -0
- package/dist/components/MultiWire.svelte.d.ts +7 -0
- package/dist/components/MulticonductorDetails.svelte +7 -0
- package/dist/components/OmniSearch.svelte +163 -0
- package/dist/components/OmniSearch.svelte.d.ts +14 -0
- package/dist/components/ResultsTable.svelte +259 -0
- package/dist/components/ResultsTable.svelte.d.ts +13 -0
- package/dist/components/SchematicCanvas.svelte +1896 -0
- package/dist/components/SchematicCanvas.svelte.d.ts +13 -0
- package/dist/components/SolveCard.svelte +6 -0
- package/dist/components/schematic/PickCard.svelte +126 -0
- package/dist/components/schematic/PickCard.svelte.d.ts +56 -0
- package/dist/components/schematic/export.d.ts +21 -0
- package/dist/components/schematic/export.js +110 -0
- package/dist/components/schematic/layers.d.ts +161 -0
- package/dist/components/schematic/layers.js +756 -0
- package/dist/components/schematic/paint.d.ts +28 -0
- package/dist/components/schematic/paint.js +69 -0
- package/dist/components/schematic/palette.d.ts +34 -0
- package/dist/components/schematic/palette.js +54 -0
- package/dist/components/schematic/text.d.ts +83 -0
- package/dist/components/schematic/text.js +190 -0
- package/dist/components/schematic/view-prefs.svelte.d.ts +30 -0
- package/dist/components/schematic/view-prefs.svelte.js +77 -0
- package/dist/components.d.ts +1 -0
- package/dist/components.js +1 -0
- package/dist/context.svelte.d.ts +2 -0
- package/dist/context.svelte.js +2 -0
- package/dist/controller.svelte.d.ts +33 -5
- package/dist/controller.svelte.js +173 -20
- package/dist/diagram/basemap.d.ts +28 -0
- package/dist/diagram/basemap.js +39 -0
- package/dist/diagram/bus-stacks.d.ts +19 -0
- package/dist/diagram/bus-stacks.js +99 -0
- package/dist/diagram/colour-modes.d.ts +18 -0
- package/dist/diagram/colour-modes.js +33 -0
- package/dist/diagram/connections.d.ts +30 -0
- package/dist/diagram/connections.js +129 -0
- package/dist/diagram/display-tree.d.ts +21 -0
- package/dist/diagram/display-tree.js +65 -0
- package/dist/diagram/energisation.d.ts +10 -0
- package/dist/diagram/energisation.js +31 -0
- package/dist/diagram/feeders.d.ts +30 -0
- package/dist/diagram/feeders.js +57 -0
- package/dist/diagram/galvanic-zones.d.ts +60 -0
- package/dist/diagram/galvanic-zones.js +210 -0
- package/dist/diagram/graph-inputs.d.ts +18 -0
- package/dist/diagram/graph-inputs.js +103 -0
- package/dist/diagram/index.d.ts +28 -0
- package/dist/diagram/index.js +28 -0
- package/dist/diagram/landmarks.d.ts +50 -0
- package/dist/diagram/landmarks.js +153 -0
- package/dist/diagram/layout-morph.d.ts +15 -0
- package/dist/diagram/layout-morph.js +32 -0
- package/dist/diagram/multi-wire.d.ts +44 -0
- package/dist/diagram/multi-wire.js +65 -0
- package/dist/diagram/phase-bars.d.ts +35 -0
- package/dist/diagram/phase-bars.js +44 -0
- package/dist/diagram/results-table.d.ts +47 -0
- package/dist/diagram/results-table.js +173 -0
- package/dist/diagram/schematic-equipment.d.ts +133 -0
- package/dist/diagram/schematic-equipment.js +645 -0
- package/dist/diagram/schematic-layout.d.ts +51 -0
- package/dist/diagram/schematic-layout.js +240 -0
- package/dist/diagram/search.d.ts +36 -0
- package/dist/diagram/search.js +48 -0
- package/dist/diagram/shared-routes.d.ts +21 -0
- package/dist/diagram/shared-routes.js +40 -0
- package/dist/diagram/short-names.d.ts +11 -0
- package/dist/diagram/short-names.js +54 -0
- package/dist/diagram/snapshot.d.ts +115 -0
- package/dist/diagram/snapshot.js +242 -0
- package/dist/diagram/summary.d.ts +65 -0
- package/dist/diagram/summary.js +144 -0
- package/dist/diagram/svg-export.d.ts +80 -0
- package/dist/diagram/svg-export.js +108 -0
- package/dist/diagram/tree-walk.d.ts +13 -0
- package/dist/diagram/tree-walk.js +83 -0
- package/dist/diagram/value-labels.d.ts +24 -0
- package/dist/diagram/value-labels.js +69 -0
- package/dist/diagram/view-state.d.ts +12 -0
- package/dist/diagram/view-state.js +22 -0
- package/dist/diagram/voltage-levels.d.ts +14 -0
- package/dist/diagram/voltage-levels.js +20 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/state.svelte.d.ts +37 -1
- package/dist/state.svelte.js +35 -0
- package/dist/transformer-icon.d.ts +22 -13
- package/dist/transformer-icon.js +23 -13
- package/package.json +2 -2
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { ATTACHMENT_NAMES, ATTACHMENT_ORDER, neutralEarths } from './schematic-equipment.js';
|
|
2
|
+
export function treeWalk(tree, positions, stride = 3) {
|
|
3
|
+
const n = tree.parent.length;
|
|
4
|
+
const y = (i) => positions[i * stride + 1];
|
|
5
|
+
const x = (i) => positions[i * stride];
|
|
6
|
+
const order = (a, b) => y(a) - y(b) || x(a) - x(b) || a - b;
|
|
7
|
+
const children = Array.from({ length: n }, () => []);
|
|
8
|
+
for (let i = 0; i < n; i++)
|
|
9
|
+
if (tree.parent[i] !== i)
|
|
10
|
+
children[tree.parent[i]].push(i);
|
|
11
|
+
for (const list of children)
|
|
12
|
+
list.sort(order);
|
|
13
|
+
const roots = [...tree.roots].sort(order);
|
|
14
|
+
const siblings = (i) => (tree.parent[i] === i ? roots : children[tree.parent[i]]);
|
|
15
|
+
return {
|
|
16
|
+
children,
|
|
17
|
+
step(from, key) {
|
|
18
|
+
if (from < 0 || from >= n)
|
|
19
|
+
return roots[0] ?? -1;
|
|
20
|
+
switch (key) {
|
|
21
|
+
case 'parent':
|
|
22
|
+
return tree.parent[from];
|
|
23
|
+
case 'child': {
|
|
24
|
+
// Straight ahead: the child drawn nearest this bus's row.
|
|
25
|
+
const kids = children[from];
|
|
26
|
+
if (!kids.length)
|
|
27
|
+
return from;
|
|
28
|
+
return kids.reduce((best, k) => Math.abs(y(k) - y(from)) < Math.abs(y(best) - y(from)) ? k : best);
|
|
29
|
+
}
|
|
30
|
+
case 'previous':
|
|
31
|
+
case 'next': {
|
|
32
|
+
const list = siblings(from);
|
|
33
|
+
const at = list.indexOf(from);
|
|
34
|
+
const to = at + (key === 'next' ? 1 : -1);
|
|
35
|
+
return to >= 0 && to < list.length ? list[to] : from;
|
|
36
|
+
}
|
|
37
|
+
case 'root': {
|
|
38
|
+
let i = from;
|
|
39
|
+
while (tree.parent[i] !== i)
|
|
40
|
+
i = tree.parent[i];
|
|
41
|
+
return i;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** A spoken description of bus `i` for the keyboard cursor: where it sits in the tree, what hangs
|
|
48
|
+
* off it, and an optional result note (for example the worst voltage against its band). */
|
|
49
|
+
export function describeBus(graph, tree, walk, i, note) {
|
|
50
|
+
const bus = graph.buses[i];
|
|
51
|
+
let root = i;
|
|
52
|
+
while (tree.parent[root] !== root)
|
|
53
|
+
root = tree.parent[root];
|
|
54
|
+
const depth = tree.depth[i];
|
|
55
|
+
const where = depth === 0
|
|
56
|
+
? bus.has_source
|
|
57
|
+
? 'source bus'
|
|
58
|
+
: 'not reached from a source'
|
|
59
|
+
: `${depth} step${depth === 1 ? '' : 's'} from ${graph.buses[root].id}`;
|
|
60
|
+
const via = tree.parentEdge[i] >= 0
|
|
61
|
+
? `, fed by ${graph.edges[tree.parentEdge[i]].kind} ${graph.edges[tree.parentEdge[i]].id}`
|
|
62
|
+
: '';
|
|
63
|
+
const onward = walk.children[i].length;
|
|
64
|
+
const counts = new Map();
|
|
65
|
+
const earths = neutralEarths(bus);
|
|
66
|
+
for (const list of Object.values(bus.terminal_attachments ?? {}))
|
|
67
|
+
for (const a of list) {
|
|
68
|
+
const kind = a.kind === 'shunt' && earths.has(a.id) ? 'earth' : a.kind;
|
|
69
|
+
counts.set(kind, (counts.get(kind) ?? new Set()).add(a.id));
|
|
70
|
+
}
|
|
71
|
+
if (bus.grounded?.length)
|
|
72
|
+
counts.set('ground', new Set(bus.grounded));
|
|
73
|
+
const attached = ATTACHMENT_ORDER.filter((k) => counts.has(k)).map((k) => {
|
|
74
|
+
const c = counts.get(k).size;
|
|
75
|
+
return `${c} ${ATTACHMENT_NAMES[k]}${c === 1 ? '' : 's'}`;
|
|
76
|
+
});
|
|
77
|
+
return [
|
|
78
|
+
`${bus.id}: ${where}${via}`,
|
|
79
|
+
`${onward} branch${onward === 1 ? '' : 'es'} onward`,
|
|
80
|
+
...(attached.length ? [attached.join(', ')] : []),
|
|
81
|
+
...(note ? [note] : [])
|
|
82
|
+
].join('. ');
|
|
83
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ResultSnapshot } from './snapshot.js';
|
|
2
|
+
export declare const VALUE_LABEL_BUDGET = 60;
|
|
3
|
+
export declare const MIN_LABEL_ZOOM = -0.2;
|
|
4
|
+
export type LabelState = 'violation' | 'warning' | 'normal';
|
|
5
|
+
export interface LabelCandidate {
|
|
6
|
+
text: string;
|
|
7
|
+
state: LabelState;
|
|
8
|
+
priority: number;
|
|
9
|
+
/** Screen position in CSS pixels. */
|
|
10
|
+
screen: [number, number];
|
|
11
|
+
selected?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Greedy placement by (selected, severity, priority) on a coarse collision grid. The selection is
|
|
14
|
+
* always placed; others must fit inside the canvas margins and the budget. */
|
|
15
|
+
export declare function placeLabels<T extends LabelCandidate>(candidates: T[], { width, height, budget, box, margin }: {
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
budget?: number;
|
|
19
|
+
box?: [number, number];
|
|
20
|
+
margin?: [number, number, number, number];
|
|
21
|
+
}): T[];
|
|
22
|
+
/** The pair nearest a limit, shown in volts. Buses without declared limits rank last. */
|
|
23
|
+
export declare function busLabel(result: ResultSnapshot, bus: number): Omit<LabelCandidate, 'screen'> | null;
|
|
24
|
+
export declare function branchLabel(result: ResultSnapshot, branch: number, family: 'loading' | 'power'): Omit<LabelCandidate, 'screen'> | null;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { bandPosition } from './colour-modes.js';
|
|
2
|
+
import { loadingState } from './summary.js';
|
|
3
|
+
export const VALUE_LABEL_BUDGET = 60;
|
|
4
|
+
export const MIN_LABEL_ZOOM = -0.2;
|
|
5
|
+
const SEVERITY = { violation: 2, warning: 1, normal: 0 };
|
|
6
|
+
/** Greedy placement by (selected, severity, priority) on a coarse collision grid. The selection is
|
|
7
|
+
* always placed; others must fit inside the canvas margins and the budget. */
|
|
8
|
+
export function placeLabels(candidates, { width, height, budget = VALUE_LABEL_BUDGET, box = [64, 16], margin = [0, 40, 0, 24] }) {
|
|
9
|
+
const sorted = [...candidates].sort((a, b) => Number(!!b.selected) - Number(!!a.selected) ||
|
|
10
|
+
SEVERITY[b.state] - SEVERITY[a.state] ||
|
|
11
|
+
b.priority - a.priority);
|
|
12
|
+
const [cw, ch] = box;
|
|
13
|
+
const occupied = new Set();
|
|
14
|
+
const placed = [];
|
|
15
|
+
for (const c of sorted) {
|
|
16
|
+
if (placed.length >= budget)
|
|
17
|
+
break;
|
|
18
|
+
const [sx, sy] = c.screen;
|
|
19
|
+
const outside = sx < margin[3] ||
|
|
20
|
+
sx + cw > width - margin[1] ||
|
|
21
|
+
sy - ch < margin[0] ||
|
|
22
|
+
sy > height - margin[2];
|
|
23
|
+
if (!c.selected && outside)
|
|
24
|
+
continue;
|
|
25
|
+
const keys = [];
|
|
26
|
+
for (let gy = Math.floor((sy - ch) / ch); gy <= Math.floor(sy / ch); gy++)
|
|
27
|
+
for (let gx = Math.floor(sx / cw); gx <= Math.floor((sx + cw) / cw); gx++)
|
|
28
|
+
keys.push(`${gx},${gy}`);
|
|
29
|
+
if (!c.selected && keys.some((k) => occupied.has(k)))
|
|
30
|
+
continue;
|
|
31
|
+
for (const k of keys)
|
|
32
|
+
occupied.add(k);
|
|
33
|
+
placed.push(c);
|
|
34
|
+
}
|
|
35
|
+
return placed;
|
|
36
|
+
}
|
|
37
|
+
const short = (x) => Number(x.toPrecision(Math.abs(x) >= 1000 ? 5 : 4)).toLocaleString();
|
|
38
|
+
/** The pair nearest a limit, shown in volts. Buses without declared limits rank last. */
|
|
39
|
+
export function busLabel(result, bus) {
|
|
40
|
+
const pairs = result.buses[bus].pairs;
|
|
41
|
+
if (!pairs.length)
|
|
42
|
+
return null;
|
|
43
|
+
// Distance from mid-band; −1 without declared limits, so those pairs and buses rank last.
|
|
44
|
+
const rank = (p) => {
|
|
45
|
+
const band = bandPosition(p);
|
|
46
|
+
return band === null ? -1 : Math.abs(band);
|
|
47
|
+
};
|
|
48
|
+
let pick = pairs[0];
|
|
49
|
+
let t = rank(pick);
|
|
50
|
+
for (const p of pairs) {
|
|
51
|
+
const pt = rank(p);
|
|
52
|
+
if (pt > t)
|
|
53
|
+
[pick, t] = [p, pt];
|
|
54
|
+
}
|
|
55
|
+
return { text: `${short(pick.volts)} V`, state: t > 1 ? 'violation' : 'normal', priority: t };
|
|
56
|
+
}
|
|
57
|
+
export function branchLabel(result, branch, family) {
|
|
58
|
+
const row = result.branches[branch];
|
|
59
|
+
if (family === 'loading') {
|
|
60
|
+
const state = loadingState(row.loading);
|
|
61
|
+
return state === 'missing'
|
|
62
|
+
? null
|
|
63
|
+
: { text: `${row.loading.toFixed(1)} %`, state, priority: row.loading };
|
|
64
|
+
}
|
|
65
|
+
const end = row.ends[0] ?? row.ends[1];
|
|
66
|
+
return end
|
|
67
|
+
? { text: `${short(Math.abs(end.p))} kW`, state: 'normal', priority: Math.abs(end.p) }
|
|
68
|
+
: null;
|
|
69
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Shareable view state. Selections travel as entity keys (source identities), never array offsets. */
|
|
2
|
+
export interface CameraState {
|
|
3
|
+
target: [number, number, number];
|
|
4
|
+
zoom: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function encodeCamera(c: {
|
|
7
|
+
target: ArrayLike<number>;
|
|
8
|
+
zoom: number;
|
|
9
|
+
}): string;
|
|
10
|
+
export declare function decodeCamera(text: string | null | undefined): CameraState | null;
|
|
11
|
+
/** Merge state into a URL without dropping unrelated parameters; `null` or `''` removes a key. */
|
|
12
|
+
export declare function withView(href: string | URL, state: Record<string, string | number | null | undefined>): URL;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function encodeCamera(c) {
|
|
2
|
+
return [c.target[0].toFixed(1), c.target[1].toFixed(1), c.zoom.toFixed(2)].join(',');
|
|
3
|
+
}
|
|
4
|
+
export function decodeCamera(text) {
|
|
5
|
+
const parts = String(text ?? '')
|
|
6
|
+
.split(',')
|
|
7
|
+
.map(Number);
|
|
8
|
+
return parts.length === 3 && parts.every(Number.isFinite) && parts[2] >= -24 && parts[2] <= 5
|
|
9
|
+
? { target: [parts[0], parts[1], 0], zoom: parts[2] }
|
|
10
|
+
: null;
|
|
11
|
+
}
|
|
12
|
+
/** Merge state into a URL without dropping unrelated parameters; `null` or `''` removes a key. */
|
|
13
|
+
export function withView(href, state) {
|
|
14
|
+
const url = new URL(href);
|
|
15
|
+
for (const [k, v] of Object.entries(state)) {
|
|
16
|
+
if (v === null || v === undefined || v === '')
|
|
17
|
+
url.searchParams.delete(k);
|
|
18
|
+
else
|
|
19
|
+
url.searchParams.set(k, String(v));
|
|
20
|
+
}
|
|
21
|
+
return url;
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GalvanicZone } from './galvanic-zones.js';
|
|
2
|
+
/** Voltage classes for drawing, by a zone's level in volts. Wide bands, so a single-phase winding's
|
|
3
|
+
* own rating (7.2 kV of a 12.47 kV system, 19.1 kV SWER) lands where its system does. */
|
|
4
|
+
export type VoltageClass = 'hv' | 'sub' | 'mv' | 'lv';
|
|
5
|
+
export declare const VOLTAGE_CLASSES: {
|
|
6
|
+
cls: VoltageClass;
|
|
7
|
+
name: string;
|
|
8
|
+
from: number;
|
|
9
|
+
}[];
|
|
10
|
+
/** The class of a zone: by its level when known; otherwise the zone holding a source counts as MV
|
|
11
|
+
* and the others as LV, as for line weights. */
|
|
12
|
+
export declare function voltageClass(zone: GalvanicZone, volts: number | null): VoltageClass;
|
|
13
|
+
/** A level as engineers name it: "11 kV", "400 V", "19.1 kV". */
|
|
14
|
+
export declare function levelLabel(volts: number): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const VOLTAGE_CLASSES = [
|
|
2
|
+
{ cls: 'hv', name: 'transmission', from: 100_000 },
|
|
3
|
+
{ cls: 'sub', name: 'subtransmission', from: 25_000 },
|
|
4
|
+
{ cls: 'mv', name: 'MV', from: 1_000 },
|
|
5
|
+
{ cls: 'lv', name: 'LV', from: 0 }
|
|
6
|
+
];
|
|
7
|
+
/** The class of a zone: by its level when known; otherwise the zone holding a source counts as MV
|
|
8
|
+
* and the others as LV, as for line weights. */
|
|
9
|
+
export function voltageClass(zone, volts) {
|
|
10
|
+
if (volts === null)
|
|
11
|
+
return zone.hasSource ? 'mv' : 'lv';
|
|
12
|
+
return VOLTAGE_CLASSES.find((c) => volts >= c.from).cls;
|
|
13
|
+
}
|
|
14
|
+
/** A level as engineers name it: "11 kV", "400 V", "19.1 kV". */
|
|
15
|
+
export function levelLabel(volts) {
|
|
16
|
+
const kv = volts / 1000;
|
|
17
|
+
if (kv < 1)
|
|
18
|
+
return `${Math.round(volts)} V`;
|
|
19
|
+
return `${Number(kv.toPrecision(4))} kV`;
|
|
20
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -27,3 +27,4 @@ export type { BusSizing, CameraSnapshot, DiagramCameraSnapshot, DemandRangeMode,
|
|
|
27
27
|
export type { MultiView, PlacedMultiBus, PlacedMultiEdge } from './multiconductor.js';
|
|
28
28
|
export type { CaseFileSummary, DistAttachmentKind, DistEdgeKind, DistGraph, DistGraphAttachment, DistGraphBus, DistGraphEdge, DisplayPreview, Formulation, IngestedCase, IngestedDistCase, IngestedJsonDrop, JsonDropClassification, JsonDropKind, CapacityPlanBusWeightJson, CapacityPlanIterationJson, CapacityPlanOutcomeJson, CapacityPlanRatingChangeJson, CapacityPlanResultSummaryJson, CapacityPlanSpecJson, ImplicitObjectiveJson, SensTarget, Topology, TopologyBranch, TopologyBus } from '@tellegen/engine';
|
|
29
29
|
export { default as McResults } from './components/McResults.svelte';
|
|
30
|
+
export { default as McWorkbench } from './components/McWorkbench.svelte';
|
package/dist/index.js
CHANGED
|
@@ -17,3 +17,4 @@ export { distExtensionFormat } from './drop-classify.js';
|
|
|
17
17
|
export { caseDeltas, caseRatings, displayMetaFor, displaySeriesFor } from './display.js';
|
|
18
18
|
export { DEFAULT_FORMULATION, FORMULATIONS, BrowserStudy, Study, capabilities, createStudy, errorText, formatOf, classifyJson, ingestJsonDrop, ingestCase, isDisplayFile, isPermanentEngineFailure, parseDisplay, solveModule } from '@tellegen/engine';
|
|
19
19
|
export { default as McResults } from './components/McResults.svelte';
|
|
20
|
+
export { default as McWorkbench } from './components/McWorkbench.svelte';
|
package/dist/state.svelte.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { CaseSummary, DemandDeltas, Network, NetworkBranch, NetworkBus, SensitivityColumn, Solution, SolveIteration } from './api.js';
|
|
2
2
|
import { type StudyView, type BranchRatingDeltas, type BrowserMcPfSession, type CaseFileSummary, type DistGraph, type Formulation, type IngestedDistCase, type McPfCallTiming, type McPfResult, type McPfSummary, type McLoadBranchState, type McLoadPowerEdit, type McStudySnapshot, type SensTarget, type Topology } from '@tellegen/engine';
|
|
3
3
|
import type { MultiView } from './multiconductor.js';
|
|
4
|
-
|
|
4
|
+
import { type GraphInputs } from './diagram/graph-inputs.js';
|
|
5
|
+
import { type SearchIndex } from './diagram/search.js';
|
|
6
|
+
import { type ResultSnapshot } from './diagram/snapshot.js';
|
|
7
|
+
export type SolveBackend = 'clarabel-wasm' | 'clarabel-wasm-server-sensitivity' | 'pounce-wasi' | 'rust-server';
|
|
5
8
|
/** A map framing request: a case id, 'all', or one branch to center. */
|
|
6
9
|
export type FrameTarget = string | 'all' | {
|
|
7
10
|
caseId: string;
|
|
@@ -117,6 +120,8 @@ export declare class LocalCase {
|
|
|
117
120
|
solving: boolean;
|
|
118
121
|
solveMs: number | null;
|
|
119
122
|
solveBackend: SolveBackend | null;
|
|
123
|
+
/** Short solver-specific diagnostic shown beside the elapsed time. */
|
|
124
|
+
solveDetail: string | null;
|
|
120
125
|
solveFallbackReason: string | null;
|
|
121
126
|
/** Monotone token: only the latest solve may write this case. */
|
|
122
127
|
solveSeq: number;
|
|
@@ -155,6 +160,8 @@ export declare class CaseState {
|
|
|
155
160
|
solving: boolean;
|
|
156
161
|
solveMs: number | null;
|
|
157
162
|
solveBackend: SolveBackend | null;
|
|
163
|
+
/** Short solver-specific diagnostic shown beside the elapsed time. */
|
|
164
|
+
solveDetail: string | null;
|
|
158
165
|
solveFallbackReason: string | null;
|
|
159
166
|
/** Monotone token: only the latest solve may write this case. */
|
|
160
167
|
solveSeq: number;
|
|
@@ -231,6 +238,32 @@ export declare class MulticonductorCase {
|
|
|
231
238
|
/** The selected edge id, whose conductor pairing expands in the panel;
|
|
232
239
|
* mutually exclusive with selectedBusId. */
|
|
233
240
|
selectedEdgeId: string | null;
|
|
241
|
+
/** What the pointer is over in any linked view (schematic, profile, tables); highlighted in
|
|
242
|
+
* the others. Transient: never saved or put in links. */
|
|
243
|
+
hovered: {
|
|
244
|
+
kind: "bus" | "branch";
|
|
245
|
+
id: string;
|
|
246
|
+
} | null;
|
|
247
|
+
/** Bumped to ask the views to bring the current selection into view again (an issue clicked
|
|
248
|
+
* that is already selected). */
|
|
249
|
+
revealSeq: number;
|
|
250
|
+
/** The full operating point read for whole-network views (overview, schematic); null until read,
|
|
251
|
+
* and cleared when a large case's operating point changes. */
|
|
252
|
+
overviewResult: McPfResult | null;
|
|
253
|
+
overviewLoading: boolean;
|
|
254
|
+
/** The graph's buses and branches as the overview and the diagram read them: rebuilt when the
|
|
255
|
+
* graph is replaced, never after a solve. Both views read this one copy. */
|
|
256
|
+
overviewInputs: GraphInputs | null;
|
|
257
|
+
/** Search over the graph's buses and branches, rebuilt with `overviewInputs`. */
|
|
258
|
+
overviewSearch: SearchIndex | null;
|
|
259
|
+
/** `overviewResult` projected onto the graph, built once per operating point and shared by the
|
|
260
|
+
* overview and the diagram; null while there is none. */
|
|
261
|
+
overviewSnapshot: ResultSnapshot | null;
|
|
262
|
+
/** Show this case as a schematic instead of the map. */
|
|
263
|
+
schematic: boolean;
|
|
264
|
+
/** The diagram's last geographic view as a map camera, for the map to continue from when the
|
|
265
|
+
* diagram closes. Plain, not reactive: it is read once, on close. */
|
|
266
|
+
lastGeoView: CameraSnapshot | null;
|
|
234
267
|
constructor(init: {
|
|
235
268
|
id: string;
|
|
236
269
|
label: string;
|
|
@@ -264,6 +297,9 @@ export interface StudyDisplaySnapshot {
|
|
|
264
297
|
export declare class AppState {
|
|
265
298
|
#private;
|
|
266
299
|
camera: CameraSnapshot | null;
|
|
300
|
+
/** Set when a view hands its camera to the map it returns to (the diagram closing from
|
|
301
|
+
* geographic positions): the re-created map starts at `camera` instead of framing the case. */
|
|
302
|
+
cameraHandoff: boolean;
|
|
267
303
|
cameraRequest: CameraSnapshot | null;
|
|
268
304
|
cameraSeq: number;
|
|
269
305
|
diagramCamera: DiagramCameraSnapshot | null;
|
package/dist/state.svelte.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { DEFAULT_FORMULATION } from '@tellegen/engine';
|
|
2
|
+
import { graphInputs } from './diagram/graph-inputs.js';
|
|
3
|
+
import { buildSearchIndex } from './diagram/search.js';
|
|
4
|
+
import { snapshotFromMcPf } from './diagram/snapshot.js';
|
|
2
5
|
/** A case is perturbed when any committed demand or rating delta is nonzero. Shared
|
|
3
6
|
* by both solvable case classes so the "perturbed" rule stays single-sourced. */
|
|
4
7
|
const hasPerturbation = (edits) => Object.values(edits).some((mw) => mw !== 0);
|
|
@@ -40,6 +43,8 @@ export class LocalCase {
|
|
|
40
43
|
solving = $state(false);
|
|
41
44
|
solveMs = $state(null);
|
|
42
45
|
solveBackend = $state(null);
|
|
46
|
+
/** Short solver-specific diagnostic shown beside the elapsed time. */
|
|
47
|
+
solveDetail = $state(null);
|
|
43
48
|
solveFallbackReason = $state(null);
|
|
44
49
|
/** Monotone token: only the latest solve may write this case. */
|
|
45
50
|
solveSeq = 0;
|
|
@@ -92,6 +97,8 @@ export class CaseState {
|
|
|
92
97
|
solving = $state(false);
|
|
93
98
|
solveMs = $state(null);
|
|
94
99
|
solveBackend = $state(null);
|
|
100
|
+
/** Short solver-specific diagnostic shown beside the elapsed time. */
|
|
101
|
+
solveDetail = $state(null);
|
|
95
102
|
solveFallbackReason = $state(null);
|
|
96
103
|
/** Monotone token: only the latest solve may write this case. */
|
|
97
104
|
solveSeq = 0;
|
|
@@ -158,6 +165,31 @@ export class MulticonductorCase {
|
|
|
158
165
|
/** The selected edge id, whose conductor pairing expands in the panel;
|
|
159
166
|
* mutually exclusive with selectedBusId. */
|
|
160
167
|
selectedEdgeId = $state(null);
|
|
168
|
+
/** What the pointer is over in any linked view (schematic, profile, tables); highlighted in
|
|
169
|
+
* the others. Transient: never saved or put in links. */
|
|
170
|
+
hovered = $state(null);
|
|
171
|
+
/** Bumped to ask the views to bring the current selection into view again (an issue clicked
|
|
172
|
+
* that is already selected). */
|
|
173
|
+
revealSeq = $state(0);
|
|
174
|
+
/** The full operating point read for whole-network views (overview, schematic); null until read,
|
|
175
|
+
* and cleared when a large case's operating point changes. */
|
|
176
|
+
overviewResult = $state.raw(null);
|
|
177
|
+
overviewLoading = $state(false);
|
|
178
|
+
/** The graph's buses and branches as the overview and the diagram read them: rebuilt when the
|
|
179
|
+
* graph is replaced, never after a solve. Both views read this one copy. */
|
|
180
|
+
overviewInputs = $derived(this.graph ? graphInputs(this.graph) : null);
|
|
181
|
+
/** Search over the graph's buses and branches, rebuilt with `overviewInputs`. */
|
|
182
|
+
overviewSearch = $derived(this.overviewInputs ? buildSearchIndex(this.overviewInputs.entities) : null);
|
|
183
|
+
/** `overviewResult` projected onto the graph, built once per operating point and shared by the
|
|
184
|
+
* overview and the diagram; null while there is none. */
|
|
185
|
+
overviewSnapshot = $derived(this.overviewResult && this.overviewInputs
|
|
186
|
+
? snapshotFromMcPf(this.overviewResult, this.overviewInputs.buses, this.overviewInputs.branches)
|
|
187
|
+
: null);
|
|
188
|
+
/** Show this case as a schematic instead of the map. */
|
|
189
|
+
schematic = $state(false);
|
|
190
|
+
/** The diagram's last geographic view as a map camera, for the map to continue from when the
|
|
191
|
+
* diagram closes. Plain, not reactive: it is read once, on close. */
|
|
192
|
+
lastGeoView = null;
|
|
161
193
|
constructor(init) {
|
|
162
194
|
this.id = init.id;
|
|
163
195
|
this.label = init.label;
|
|
@@ -200,6 +232,9 @@ export class MulticonductorCase {
|
|
|
200
232
|
}
|
|
201
233
|
export class AppState {
|
|
202
234
|
camera = $state.raw(null);
|
|
235
|
+
/** Set when a view hands its camera to the map it returns to (the diagram closing from
|
|
236
|
+
* geographic positions): the re-created map starts at `camera` instead of framing the case. */
|
|
237
|
+
cameraHandoff = false;
|
|
203
238
|
cameraRequest = $state.raw(null);
|
|
204
239
|
cameraSeq = $state(0);
|
|
205
240
|
diagramCamera = $state.raw(null);
|
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
/** The IEC two-winding transformer symbol as a deck.gl icon:
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
/** The IEC two-winding transformer symbol as a prepacked deck.gl icon atlas:
|
|
2
|
+
* two overlapping circles on a canvas that deck.gl uploads as a texture.
|
|
3
|
+
* Nothing is fetched, so the symbol needs no `data:` in a CSP's connect-src.
|
|
4
|
+
* Drawn white on transparent with `mask: true`, so the layer's getColor tints
|
|
5
|
+
* it. */
|
|
6
|
+
/** The icon name for the layer's getIcon. */
|
|
7
|
+
export declare const TRANSFORMER_ICON = "transformer";
|
|
8
|
+
/** The atlas holds the one symbol, anchored at its center. */
|
|
9
|
+
export declare const transformerIconMapping: {
|
|
10
|
+
transformer: {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
anchorX: number;
|
|
16
|
+
anchorY: number;
|
|
17
|
+
mask: boolean;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
12
20
|
/** Rasterize the glyph lazily; layers only build client-side, after the map
|
|
13
21
|
* modules load, so document is always available here. The 128 px backing keeps
|
|
14
|
-
* a ~16 px on-screen symbol crisp on 2x/3x displays.
|
|
15
|
-
|
|
22
|
+
* a ~16 px on-screen symbol crisp on 2x/3x displays. Every call returns the
|
|
23
|
+
* same canvas, so deck.gl's prop diff keeps the texture it made from it. */
|
|
24
|
+
export declare function transformerIconAtlas(): HTMLCanvasElement;
|
package/dist/transformer-icon.js
CHANGED
|
@@ -1,15 +1,32 @@
|
|
|
1
|
-
/** The IEC two-winding transformer symbol as a deck.gl icon:
|
|
2
|
-
*
|
|
3
|
-
*
|
|
1
|
+
/** The IEC two-winding transformer symbol as a prepacked deck.gl icon atlas:
|
|
2
|
+
* two overlapping circles on a canvas that deck.gl uploads as a texture.
|
|
3
|
+
* Nothing is fetched, so the symbol needs no `data:` in a CSP's connect-src.
|
|
4
|
+
* Drawn white on transparent with `mask: true`, so the layer's getColor tints
|
|
5
|
+
* it. */
|
|
4
6
|
const SIZE = 128;
|
|
5
7
|
const RADIUS = 34;
|
|
6
8
|
const OFFSET = 17;
|
|
7
9
|
const STROKE = 9;
|
|
10
|
+
/** The icon name for the layer's getIcon. */
|
|
11
|
+
export const TRANSFORMER_ICON = 'transformer';
|
|
12
|
+
/** The atlas holds the one symbol, anchored at its center. */
|
|
13
|
+
export const transformerIconMapping = {
|
|
14
|
+
[TRANSFORMER_ICON]: {
|
|
15
|
+
x: 0,
|
|
16
|
+
y: 0,
|
|
17
|
+
width: SIZE,
|
|
18
|
+
height: SIZE,
|
|
19
|
+
anchorX: SIZE / 2,
|
|
20
|
+
anchorY: SIZE / 2,
|
|
21
|
+
mask: true
|
|
22
|
+
}
|
|
23
|
+
};
|
|
8
24
|
let cached = null;
|
|
9
25
|
/** Rasterize the glyph lazily; layers only build client-side, after the map
|
|
10
26
|
* modules load, so document is always available here. The 128 px backing keeps
|
|
11
|
-
* a ~16 px on-screen symbol crisp on 2x/3x displays.
|
|
12
|
-
|
|
27
|
+
* a ~16 px on-screen symbol crisp on 2x/3x displays. Every call returns the
|
|
28
|
+
* same canvas, so deck.gl's prop diff keeps the texture it made from it. */
|
|
29
|
+
export function transformerIconAtlas() {
|
|
13
30
|
if (cached)
|
|
14
31
|
return cached;
|
|
15
32
|
const canvas = document.createElement('canvas');
|
|
@@ -23,13 +40,6 @@ export function transformerIcon() {
|
|
|
23
40
|
ctx.arc(SIZE / 2 + dx, SIZE / 2, RADIUS, 0, 2 * Math.PI);
|
|
24
41
|
ctx.stroke();
|
|
25
42
|
}
|
|
26
|
-
cached =
|
|
27
|
-
url: canvas.toDataURL(),
|
|
28
|
-
width: SIZE,
|
|
29
|
-
height: SIZE,
|
|
30
|
-
anchorX: SIZE / 2,
|
|
31
|
-
anchorY: SIZE / 2,
|
|
32
|
-
mask: true
|
|
33
|
-
};
|
|
43
|
+
cached = canvas;
|
|
34
44
|
return cached;
|
|
35
45
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tellegen/svelte",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Svelte components for tellegen power system maps, local case files, and browser solves.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"pack:dry-run": "npm pack --dry-run --cache ../../node_modules/.cache/npm-pack"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@tellegen/engine": "^0.4.
|
|
70
|
+
"@tellegen/engine": "^0.4.3"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
73
|
"@deck.gl/core": "^9.4.0",
|