Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions web/src/pages/Graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ import {
NODE_TYPE_SQUARE,
selectedNode,
sigmaOptions,
withTopLayer,
drawLast,
softMutedNode,
} from "./graphCanvas";
import { EntityHistory } from "./EntityHistory";
Expand Down Expand Up @@ -873,15 +875,11 @@ export function Graph() {
/* 平行边扇成弧(见 `layOutParallelEdges`)。直线那一版把同一对节点
之间的每条边画在同一条线段上,于是几个标签逐字符叠成乱码——实测
一对节点之间最多压着六条 */
/* **同一个类注册两遍**,后注册的那两个专给高亮的边用(见 `boost()`)。
sigma 一个程序一批绘制,批次先后就是这里的键序,`zIndex` 只在批内
排——所以一条压暗的直边照样会盖在高亮的弧边上,放大看就是白线被
一条条黑细条切断。让高亮的边整批走最后画的程序,才真的在最上层。 */
edgeProgramClasses: {
// 每个程序配一份「最上层」的(见 `withTopLayer`):高亮的边整批最后画
edgeProgramClasses: withTopLayer({
line: EdgeRectangleProgram,
curved: EdgeCurveProgram,
lineTop: EdgeRectangleProgram,
curvedTop: EdgeCurveProgram,
},
}),
// 边上写的是谓词,近距离下每条画得出来的边都写(见 updateEdgeLabels)
renderEdgeLabels: true,
// 上千个节点,得缩得比本体页更远才看得见全貌
Expand Down Expand Up @@ -1073,7 +1071,7 @@ export function Graph() {
res.size = Math.max((attrs.size as number) * 1.42, 1.85);
res.zIndex = 5;
// 换到最后画的那批(见 `edgeProgramClasses`):光有 zIndex 压不住别的程序
res.type = res.type === "curved" ? "curvedTop" : "lineTop";
res.type = drawLast(String(res.type ?? "line"));
};
/* **时间轴停在某一刻时,这条边此刻存不存在**。
悬停的两条分支都会提前 return,绕过下面那道时间过滤——
Expand Down
10 changes: 8 additions & 2 deletions web/src/pages/OntologySchemaGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,12 @@ import {
neighborNode,
nodeInView,
NODE_TYPE_SHELL,
drawLast,
NODE_TYPE_SQUARE,
ownColorOf,
selectedNode,
sigmaOptions,
withTopLayer,
} from "./graphCanvas";
import { Maximize2, ZoomIn, ZoomOut } from "lucide-react";
import type { BusinessRule, EntityTypeView, RelationTypeView } from "../api";
Expand Down Expand Up @@ -119,6 +121,7 @@ const EDGE_TYPE_CURVED_ARROW = "curvedArrow"; // 弧线 + 箭头(EdgeCurvedArr
const EDGE_TYPE_LINE = "line"; // 直线,无箭头(EdgeLineProgram)——互斥专用
const EDGE_TYPE_CURVED_LINE = "curvedLine"; // 弧线,无箭头(EdgeCurveProgram)——互斥与别的边共用一对时


/** 结构边细、关系边粗一档——「语义关系比结构性信息更显眼」不能只靠颜色说,
* 粗细上也要有一档差。这个粗细同时也是点选判定的命中带宽——sigma 的边拾取
* 用的就是渲染出来的这条几何体(WebGL 拾取缓冲,不是另一套「点击容差」),
Expand Down Expand Up @@ -790,12 +793,13 @@ export function OntologySchemaGraph({
const sigma = new Sigma(g, containerRef.current, {
...sigmaOptions({
defaultEdgeType: EDGE_TYPE_ARROW,
edgeProgramClasses: {
// 每个程序配一份「最上层」的(见 `withTopLayer`):高亮的边整批最后画
edgeProgramClasses: withTopLayer({
[EDGE_TYPE_ARROW]: EdgeArrowProgram,
[EDGE_TYPE_LINE]: EdgeLineProgram,
[EDGE_TYPE_CURVED_ARROW]: EdgeCurvedArrowProgram,
[EDGE_TYPE_CURVED_LINE]: EdgeCurveProgram,
},
}),
minEdgeThickness: MIN_EDGE_THICKNESS,
// 几十上百个类,缩到 0.05 就看得见全貌;实例图动辄上千,那边缩得更远
minCameraRatio: 0.05,
Expand Down Expand Up @@ -885,6 +889,8 @@ export function OntologySchemaGraph({
res.color = focus;
res.size = Math.max((attrs.size as number) ?? 1, 1) * 1.5;
res.zIndex = 3;
// 换到最后画的那一批:光有 zIndex 压不住别的程序里的边
res.type = drawLast(String(res.type ?? EDGE_TYPE_ARROW));
return res;
}
if (
Expand Down
30 changes: 30 additions & 0 deletions web/src/pages/graphCanvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,36 @@ import {
export const NODE_TYPE_SHELL = "shell";
export const NODE_TYPE_SQUARE = "square";

/* ---------- 高亮的边画在最上层 ---------- */

/** 「最上层」那一批的类型名后缀 */
const TOP = "Top";

/** 一条边高亮时该用的类型名。
*
* **`zIndex` 在这里不管用。**sigma 一个程序一批绘制,`zIndex` 只在批内排序;
* 跨程序谁先谁后,看的是 `edgeProgramClasses` 里键的顺序。于是选中一个节点
* 之后,它那几条亮线照样会被别的程序里压暗的边切断——放大看就是白线被一条条
* 细黑条切开。
*
* 解法是把同一个程序**注册两遍**(见 `withTopLayer`),高亮的边整批走后注册的
* 那一份,最后画。两页都踩过这个坑:图谱页先解的,本体页后来又踩了一遍——
* 所以它收在这里,不在任何一页里。 */
export function drawLast(type: string): string {
return type.endsWith(TOP) ? type : type + TOP;
}

/** 给一份边程序表配上「最上层」的那一份:同样的程序,键排在后面。
* 高亮时把 `res.type` 换成 `drawLast(res.type)` 就落进这一批。 */
export function withTopLayer<T>(programs: Record<string, T>): Record<string, T> {
return {
...programs,
...Object.fromEntries(
Object.entries(programs).map(([name, program]) => [drawLast(name), program]),
),
};
}

export const NODE_PROGRAMS = {
[NODE_TYPE_SHELL]: createNodeBorderProgram({
borders: [
Expand Down