Skip to content

Commit 6feb4f4

Browse files
committed
labels
1 parent 65a5c3d commit 6feb4f4

File tree

7 files changed

+125
-91
lines changed

7 files changed

+125
-91
lines changed

src/components/template_dialog.vue

+3-3
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,9 @@ const tempN = ref("");
150150
151151
const tempV = computed({
152152
get: () =>
153-
tempN.value in store.templates ? store.templates[tempN.value] : "",
153+
tempN.value in store.optTemplates ? store.optTemplates[tempN.value] : "",
154154
set: (v) => {
155-
store.templates[tempN.value] = v;
155+
store.optTemplates[tempN.value] = v;
156156
},
157157
});
158158
@@ -196,7 +196,7 @@ watch(visible, (value) => {
196196
});
197197
198198
const tempOptions = computed(() =>
199-
Object.keys(store.templates).map((v) => {
199+
Object.keys(store.optTemplates).map((v) => {
200200
return {
201201
label: v,
202202
value: v,

src/stores/mainStore.ts

+17-9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
WsMessage,
1313
Options,
1414
WsMsgCodes,
15+
TemplateFiles,
1516
} from "@/utils/types";
1617
import { fFarEndNode } from "@/utils/helpers";
1718
import { wsSend } from "@/utils/eventbus";
@@ -26,19 +27,23 @@ export function message(): MessageApi {
2627
export const useMainStore = defineStore("main", {
2728
// a function that returns a fresh state
2829
state: () => ({
30+
/** topo file */
2931
topo: {
3032
name: "",
3133
nodes: {} as Nodes,
3234
links: {} as Links,
3335
/** compiled vars per NE */
3436
vars: {} as NodeVars,
3537
},
36-
layout: "grid",
37-
zoom: 1.5,
38-
templates: {} as Record<string, string>,
39-
layouts: {
38+
/** *.tmpl files from the template path */
39+
templateFiles: {} as TemplateFiles,
40+
// option file
41+
optLayouts: {
4042
nodes: {},
4143
} as vngLayout,
44+
optTemplates: {} as Record<string, string>,
45+
optLayout: "grid",
46+
optZoom: 1.5,
4247
/** used while loading (?) */
4348
loading: 0,
4449
/** split vars or show the merge value! */
@@ -52,9 +57,9 @@ export const useMainStore = defineStore("main", {
5257
return {
5358
code: WsMsgCodes.save,
5459
data: {
55-
options: { layout: state.layout, zoom: state.zoom } as Options,
56-
layouts: state.layouts,
57-
templates: state.templates,
60+
options: { layout: state.optLayout, zoom: state.optZoom } as Options,
61+
layouts: state.optLayouts,
62+
templates: state.optTemplates,
5863
} as UiData,
5964
} as WsMessage;
6065
},
@@ -118,6 +123,9 @@ export const useMainStore = defineStore("main", {
118123
json_fetch("/labctl/vars").then((resp) => {
119124
Object.assign(this.topo.vars, resp.data);
120125
});
126+
json_fetch("/labctl/templates").then((resp) => {
127+
Object.assign(this.templateFiles, resp.data);
128+
});
121129
}
122130
},
123131

@@ -128,8 +136,8 @@ export const useMainStore = defineStore("main", {
128136
}
129137
console.log("load layouts+", data.options);
130138
Object.assign(this, data.options);
131-
this.layouts = data.layouts;
132-
this.templates = data.templates;
139+
this.optLayouts = data.layouts;
140+
this.optTemplates = data.templates;
133141
},
134142

135143
save() {

src/utils/eventbus.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { useEventBus } from "@vueuse/core";
2-
import { WebSocketTemplate, WsMessage } from "@/utils/types";
2+
import { WsTemplate, WsMessage } from "@/utils/types";
33

4-
// export const templateKey: EventBusKey<WebSocketTemplate> =
5-
// Symbol("ws-template-key");
6-
export const wsTemplateBus = useEventBus<WebSocketTemplate>("ws-template-bus");
4+
export const wsTemplateBus = useEventBus<WsTemplate>("ws-template-bus");
75
export const wsRxBus = useEventBus<WsMessage>("ws-bus");
86
export const wsTxBus = useEventBus<WsMessage>("ws-tx-bus");
97

src/utils/helpers.ts

+44-29
Original file line numberDiff line numberDiff line change
@@ -17,42 +17,57 @@ export function fFarEndNode(far: string) {
1717
};
1818
}
1919

20-
interface rr {
21-
points: Point[];
22-
n: number;
23-
s: number;
24-
pos: string;
25-
}
26-
2720
function diff(p1: Point, p2: Point): Point {
2821
return { x: p1.x - p2.x, y: p1.y - p2.y } as Point;
2922
}
3023

31-
export function labelPosition(nodes: Record<string, Point>, links: Links) {
32-
// filter all links with no endpoint
33-
const res: Record<string, rr> = {};
24+
export function labelDirection(
25+
nodes: Record<string, Point>,
26+
links: Links,
27+
debug?: boolean
28+
) {
29+
const res: Record<string, string> = {};
3430
Object.keys(nodes).forEach((n) => {
35-
res[n] = Object.values(links).reduce(
36-
(acc: rr, l1: Link) => {
37-
if (l1.source === n && l1.source in nodes && l1.target in nodes) {
38-
acc.points.push(diff(nodes[l1.target], nodes[n]));
39-
}
40-
if (l1.target === n && l1.source in nodes && l1.target in nodes) {
41-
acc.points.push(diff(nodes[l1.target], nodes[n]));
42-
}
43-
return acc;
44-
},
45-
{ points: [] as Point[], n: 0, s: 0 } as rr
46-
);
47-
48-
res[n].points.forEach((p) => {
49-
const ratio = Math.abs(p.y / p.x);
50-
if (p.y < 0) {
51-
res[n].n += ratio;
52-
} else {
53-
res[n].s += ratio;
31+
const node = {
32+
d: "south",
33+
n: 0,
34+
s: 0,
35+
e: 0,
36+
w: 0,
37+
pts: [] as Point[],
38+
};
39+
40+
// collect all far end points
41+
Object.values(links).forEach((l1: Link) => {
42+
if (l1.source === n && l1.source in nodes && l1.target in nodes) {
43+
node.pts.push(diff(nodes[l1.target], nodes[n]));
5444
}
45+
if (l1.target === n && l1.source in nodes && l1.target in nodes) {
46+
node.pts.push(diff(nodes[l1.source], nodes[n]));
47+
}
48+
});
49+
50+
node.pts.forEach((p) => {
51+
node[p.y < 0 ? "n" : "s"] += Math.atan(Math.abs(p.y / p.x));
52+
node[p.x < 0 ? "w" : "e"] += Math.atan(Math.abs(p.x / p.y));
5553
});
54+
55+
// Pick the best direction (lowest, preference: s,n,e,w)
56+
let low = node.s;
57+
if (node.n < low) {
58+
low = node.n;
59+
node.d = "north";
60+
}
61+
if (node.e < low) {
62+
low = node.e;
63+
node.d = "east";
64+
}
65+
if (node.w < low) {
66+
node.d = "west";
67+
}
68+
69+
if (debug) console.debug(node);
70+
res[n] = node.d;
5671
});
5772

5873
return res;

src/utils/types.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export interface UiData {
4949
templates: Record<string, string>;
5050
}
5151

52-
export interface WebSocketTemplate {
52+
export interface WsTemplate {
5353
id: string;
5454
/** the template name */
5555
name: string;
@@ -70,7 +70,7 @@ export interface WsMessage {
7070
code: WsMsgCodes;
7171
data?: UiData;
7272
msg?: string;
73-
template?: WebSocketTemplate;
73+
template?: WsTemplate;
7474
}
7575

7676
interface pLinkVar {
@@ -129,3 +129,11 @@ export interface JsonResponse {
129129
msg?: string;
130130
data: Dictionary;
131131
}
132+
133+
/** labctl Template struct from the server */
134+
export interface TemplateFiles {
135+
name: string;
136+
p: string;
137+
shadow: string[];
138+
value: string;
139+
}

src/views/graphView.vue

+23-30
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
style="width: 160px"
1515
/> -->
1616
<n-switch
17-
:value="layout === 'force'"
18-
@update:value="(v) => (layout = v ? 'force' : 'grid')"
17+
:value="optLayout === 'force'"
18+
@update:value="(v) => (optLayout = v ? 'force' : 'grid')"
1919
>
2020
<template #checked> force </template>
21-
<template #unchecked> {{ layout }} </template>
21+
<template #unchecked> {{ optLayout }} </template>
2222
</n-switch>
2323
<n-switch v-model:value="show_logs">
2424
<template #checked> logs </template>
@@ -30,13 +30,13 @@
3030
<div :style="{ height: `${ttheight}px` }">
3131
<v-network-graph
3232
ref="graph"
33-
v-model:zoom-level="zoom"
33+
v-model:zoom-level="optZoom"
3434
v-model:selected-nodes="selectedNodes"
3535
v-model:selected-edges="selectedLinks"
3636
:nodes="store.topo.nodes"
3737
:edges="store.topo.links"
3838
:configs="configs"
39-
:layouts="layouts"
39+
:layouts="optLayouts"
4040
:event-handlers="eventHandlers"
4141
>
4242
<template
@@ -121,7 +121,7 @@
121121
</template>
122122
<!-- <template #badge="{ scale }">
123123
<circle
124-
v-for="(pos, node) in layouts.nodes"
124+
v-for="(pos, node) in optLayouts.nodes"
125125
:key="node"
126126
:cx="pos.x + 9 * scale + pan.x"
127127
:cy="pos.y - 9 * scale + pan.y"
@@ -200,26 +200,12 @@ import VarsView from "@/components/vars_view.vue";
200200
import { wsTemplateBus, wsTxBus, wsRxBus, wsSend } from "@/utils/eventbus";
201201
import { WsMsgCodes } from "@/utils/types";
202202
import { useLocalStorage } from "@vueuse/core";
203+
import { labelDirection } from "@/utils/helpers";
203204
204205
const store = useMainStore();
205206
const selectedNodes = ref<string[]>([]);
206207
const selectedLinks = ref<string[]>([]);
207-
const { layout, zoom, layouts } = storeToRefs(store);
208-
209-
// const layoutOptions = [
210-
// {
211-
// label: "Force layout",
212-
// value: "force",
213-
// },
214-
// {
215-
// label: "Grid layout",
216-
// value: "grid",
217-
// },
218-
// {
219-
// label: "Free layout",
220-
// value: "free",
221-
// },
222-
// ];
208+
const { optLayout, optZoom, optLayouts } = storeToRefs(store);
223209
224210
interface lblLinkT {
225211
source_above?: string;
@@ -278,15 +264,15 @@ const eventHandlers: vNG.EventHandlers = {
278264
// watch(layout, store.save);
279265
280266
function getLayoutHandler() {
281-
if (layout.value === "force") {
267+
if (optLayout.value === "force") {
282268
return new ForceLayout();
283-
} else if (layout.value === "grid") {
269+
} else if (optLayout.value === "grid") {
284270
return new vNG.GridLayout({ grid: 15 });
285271
}
286272
return new vNG.SimpleLayout();
287273
}
288274
289-
watch(layout, () => {
275+
watch(optLayout, () => {
290276
store.save();
291277
if (configs.view) {
292278
configs.view.layoutHandler = getLayoutHandler();
@@ -295,6 +281,10 @@ watch(layout, () => {
295281
296282
const nodeSize = 30;
297283
284+
const directions = computed(() => {
285+
return labelDirection(optLayouts.value.nodes, store.topo.links);
286+
});
287+
298288
const configs = reactive(
299289
vNG.defineConfigs({
300290
view: {
@@ -313,7 +303,10 @@ const configs = reactive(
313303
lineHeight: 1.1,
314304
color: "#000000",
315305
margin: 4,
316-
direction: "south",
306+
direction: (n) =>
307+
n.name && n.name in directions.value
308+
? directions.value[n.name]
309+
: "south",
317310
text: "name",
318311
},
319312
selectable: true,
@@ -332,7 +325,7 @@ const tooltipPos = computed(() => {
332325
if (!graph.value || !tooltip.value) return { x: 0, y: 0 };
333326
if (!tooltipTNode.value) return { x: 0, y: 0 };
334327
335-
const nodePos = store.layouts.nodes[tooltipTNode.value];
328+
const nodePos = store.optLayouts.nodes[tooltipTNode.value];
336329
// translate coordinates: SVG -> DOM
337330
const domPoint = graph.value.translateFromSvgToDomCoordinates(nodePos);
338331
// calculates top-left position of the tooltip.
@@ -381,7 +374,7 @@ wsTemplateBus.on((t) => {
381374
}
382375
});
383376
384-
watch(store.templates, () => {
377+
watch(store.optTemplates, () => {
385378
// If the templates changes, update the labels
386379
updatelabels();
387380
});
@@ -393,7 +386,7 @@ function updatelabels() {
393386
template: {
394387
id: lid,
395388
name: "link",
396-
template: store.templates["link"],
389+
template: store.optTemplates["link"],
397390
vars: store.linkVars(lid),
398391
result: "",
399392
},
@@ -405,7 +398,7 @@ function updatelabels() {
405398
template: {
406399
id: nid,
407400
name: "node",
408-
template: store.templates["node"],
401+
template: store.optTemplates["node"],
409402
vars: store.topo.vars[nid],
410403
result: "",
411404
},

0 commit comments

Comments
 (0)