topo-engine 0.1.9 → 0.1.10

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.
@@ -6658,11 +6658,11 @@ var parseGradient$1 = /* @__PURE__ */ function() {
6658
6658
  }
6659
6659
  }
6660
6660
  function matchPositioning() {
6661
- var location = matchCoordinates();
6662
- if (location.x || location.y) {
6661
+ var location2 = matchCoordinates();
6662
+ if (location2.x || location2.y) {
6663
6663
  return {
6664
6664
  type: "position",
6665
- value: location
6665
+ value: location2
6666
6666
  };
6667
6667
  }
6668
6668
  }
@@ -26311,8 +26311,10 @@ class TopoApi {
26311
26311
  }
26312
26312
  // ------------------------------------------------------------
26313
26313
  // 光晕 / 脉动 动画 —— 节点 & 箱内电表户
26314
- // setNodeGlow(ref, { color, spread/radius, duration, opacity })
26314
+ // setNodeGlow(ref, { color, spread/radius, duration, opacity, width, intensity, blink })
26315
26315
  // ref = 节点 id/psrId → 图元光环;assetNo → 该户表位光环
26316
+ // 强度栈:blur 柔光底衬 + 实心光块 + 外扩散环×2 + 霓虹主环(shadowBlur) + 亮芯环
26317
+ // intensity = 强度倍率(默认 1);blink = true/0~1 → 图元「整体闪烁」
26316
26318
  // setNodePulse(ref, opts) → 节点透明度呼吸;assetNo → 该户电表格淡入淡出
26317
26319
  // ------------------------------------------------------------
26318
26320
  /** 是否有需要逐帧推进的动画(节点光晕 / 户光晕 / 户脉动) */
@@ -26410,6 +26412,9 @@ class TopoApi {
26410
26412
  _glowSpread: void 0,
26411
26413
  _glowOpacity: void 0,
26412
26414
  _glowDuration: void 0,
26415
+ _glowWidth: void 0,
26416
+ _glowLevel: void 0,
26417
+ _glowBlink: void 0,
26413
26418
  _glowT: void 0
26414
26419
  }
26415
26420
  }]);
@@ -26417,9 +26422,32 @@ class TopoApi {
26417
26422
  }
26418
26423
  }
26419
26424
  /**
26420
- * 节点 / 箱内电表户光晕(发光环)动画。
26425
+ * 归一化「整体闪烁」参数:false / 未给 → 0(只画光环);true → 0.85;数字 → clamp(0,1)。
26426
+ * 数值含义 = 图元最暗时被压掉的透明度幅度(1 → 图元完全隐去,0.5 → 最暗降到 50%)。
26427
+ */
26428
+ _normBlink(v) {
26429
+ if (v === true) return 0.85;
26430
+ if (v === false || v == null) return 0;
26431
+ const n = Number(v);
26432
+ return Number.isFinite(n) ? Math.max(0, Math.min(1, n)) : 0;
26433
+ }
26434
+ /** 归一化强度倍率(intensity/level/strength):默认 1,范围 0.2~3 */
26435
+ _normLevel(v) {
26436
+ const n = Number(v);
26437
+ return Number.isFinite(n) && n > 0 ? Math.max(0.2, Math.min(3, n)) : 1;
26438
+ }
26439
+ /**
26440
+ * 节点 / 箱内电表户光晕(发光环 + 霓虹辉光)动画。
26421
26441
  * @param {string} ref 节点 id / psrId(图元光环)或资产编号 assetNo(该户表位光环)
26422
- * @param {object} [opts] - { color, spread/radius, duration, opacity }
26442
+ * @param {object} [opts] - { color, spread/radius, duration, opacity, width, intensity/level/strength, blink/flash }
26443
+ * 渲染栈:blur 柔光底衬 + 贴合图元的实心光块(面光)→ 图元 → 外扩散环 ×2 →
26444
+ * 霓虹主环(粗线 + canvas shadowBlur 辉光)→ 偏白亮芯环(刺眼高光)。
26445
+ * intensity:强度倍率,默认 1(线宽/辉光/底光同时放大);想“更夸张”给 1.5~2.5,想收敛给 0.5~0.7。
26446
+ * spread:光环外扩半径,默认按图元大小自适应(短边 ×0.7,16~46px)。
26447
+ * blink:图元「整体闪烁」开关/强度(默认 false 只画光环;true = 0.85;数字 0~1)。
26448
+ * 开启后图元本体透明度随相位「亮 ↔ 暗」交替(与光环相位互补,始终有强视觉元素),
26449
+ * 图元变暗的瞬间底衬光块最亮 —— 整块明暗交替比细线醒目得多。
26450
+ * width:主环初始线宽 (px),默认 5.6(节点)/ 3.6(户表位),随相位收细。
26423
26451
  * @returns {boolean} 是否命中并启动
26424
26452
  */
26425
26453
  setNodeGlow(ref2, opts = {}) {
@@ -26429,11 +26457,17 @@ class TopoApi {
26429
26457
  console.warn(`[TopoApi] setNodeGlow: 无法解析目标 ${ref2}(节点用 id/psrId,电表户用 assetNo)`);
26430
26458
  return false;
26431
26459
  }
26460
+ const width = parseFloat(opts.width);
26461
+ const spread = opts.spread != null ? opts.spread : opts.radius;
26432
26462
  const cfg = {
26433
26463
  color: opts.color || "#00C8FF",
26434
- spread: opts.spread != null ? opts.spread : opts.radius != null ? opts.radius : 14,
26435
- opacity: opts.opacity != null ? opts.opacity : 0.9,
26464
+ spread: Number.isFinite(parseFloat(spread)) ? parseFloat(spread) : null,
26465
+ // null 按图元大小自适应
26466
+ opacity: opts.opacity != null ? opts.opacity : 0.95,
26436
26467
  duration: opts.duration || 1600,
26468
+ width: Number.isFinite(width) && width > 0 ? width : null,
26469
+ level: this._normLevel(opts.intensity != null ? opts.intensity : opts.level != null ? opts.level : opts.strength),
26470
+ blink: this._normBlink(opts.blink != null ? opts.blink : opts.flash),
26437
26471
  _start: performance.now(),
26438
26472
  t: 0
26439
26473
  };
@@ -26443,9 +26477,12 @@ class TopoApi {
26443
26477
  id: t.id,
26444
26478
  style: {
26445
26479
  _glowColor: cfg.color,
26446
- _glowSpread: cfg.spread,
26480
+ _glowSpread: cfg.spread == null ? void 0 : cfg.spread,
26447
26481
  _glowOpacity: cfg.opacity,
26448
26482
  _glowDuration: cfg.duration,
26483
+ _glowWidth: cfg.width == null ? void 0 : cfg.width,
26484
+ _glowLevel: cfg.level,
26485
+ _glowBlink: cfg.blink || void 0,
26449
26486
  _glowT: 0
26450
26487
  }
26451
26488
  }]);
@@ -26497,6 +26534,9 @@ class TopoApi {
26497
26534
  _glowSpread: void 0,
26498
26535
  _glowOpacity: void 0,
26499
26536
  _glowDuration: void 0,
26537
+ _glowWidth: void 0,
26538
+ _glowLevel: void 0,
26539
+ _glowBlink: void 0,
26500
26540
  _glowT: void 0
26501
26541
  }
26502
26542
  });
@@ -28699,6 +28739,8 @@ const THEMES = {
28699
28739
  cyan: "#00C8FF",
28700
28740
  gold: "#FFD700",
28701
28741
  gray: "#8B8B8B",
28742
+ // 开关/熔丝(Kxx)标签文字:画在红色箱体(red)里的文字色,随主题切换
28743
+ switchText: "#FFFFFF",
28702
28744
  // 计量箱(JX):深色主题黑底白字;浅色主题白底深框深字(随画布配套)
28703
28745
  meterFill: "#000000",
28704
28746
  meterText: "#FFFFFF",
@@ -28727,6 +28769,9 @@ const THEMES = {
28727
28769
  cyan: "#0087B8",
28728
28770
  gold: "#B8860B",
28729
28771
  gray: "#9C9C9C",
28772
+ // 开关标签:浅色主题下箱体仍是深红 red(#C62828),此处保持浅色字
28773
+ // (白字对比度 ≈5.6:1;若改成深字,请连 red 一起调浅,否则只剩 ≈2.3:1 会发糊)
28774
+ switchText: "#FFFFFF",
28730
28775
  meterFill: "#FFFFFF",
28731
28776
  // 浅色主题:白底 + 深框深字(随白画布配套)
28732
28777
  meterText: "#3A3A3A",
@@ -28930,6 +28975,19 @@ function withAlpha(color2, a2) {
28930
28975
  }
28931
28976
  const LW = 1.8;
28932
28977
  const LW_HI = 2.2;
28978
+ function mixWhite(color2, k) {
28979
+ if (typeof color2 !== "string") return color2;
28980
+ const m = color2.match(/^#([0-9a-f]{6}|[0-9a-f]{3})$/i);
28981
+ if (!m) return color2;
28982
+ let hex2 = m[1];
28983
+ if (hex2.length === 3) hex2 = hex2.split("").map((h) => h + h).join("");
28984
+ const n = parseInt(hex2, 16);
28985
+ const mix = (v) => Math.round(v + (255 - v) * Math.max(0, Math.min(1, k)));
28986
+ const r = mix(n >> 16 & 255), g = mix(n >> 8 & 255), b = mix(n & 255);
28987
+ return `#${(r << 16 | g << 8 | b).toString(16).padStart(6, "0")}`;
28988
+ }
28989
+ const clampNum = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
28990
+ const NO_HIT = { pointerEvents: "none" };
28933
28991
  function drawHighlight(attributes, group) {
28934
28992
  if (!attributes.highlight) return;
28935
28993
  const [w, h] = attributes.size;
@@ -29023,7 +29081,7 @@ function _wrapLinesFixed(str2, chars2) {
29023
29081
  if (line) lines.push(line);
29024
29082
  return lines;
29025
29083
  }
29026
- function _drawWrappedTextFixed(group, str2, x, y, fontSize2, fill, chars2, lineGap = 1.3, weight = 400, align = "center") {
29084
+ function _drawWrappedTextFixed(group, str2, x, y, fontSize2, fill, chars2, lineGap = 1.3, weight = 400, align = "center", extra = null) {
29027
29085
  if (!str2) return;
29028
29086
  const lines = _wrapLinesFixed(str2, chars2);
29029
29087
  if (!lines.length) return;
@@ -29032,6 +29090,7 @@ function _drawWrappedTextFixed(group, str2, x, y, fontSize2, fill, chars2, lineG
29032
29090
  group.appendChild(
29033
29091
  new Text({
29034
29092
  style: {
29093
+ ...extra || {},
29035
29094
  text: lines[i],
29036
29095
  x,
29037
29096
  y: y + lineHeight2 * i,
@@ -29105,7 +29164,7 @@ function drawBreaker(attributes, group) {
29105
29164
  }
29106
29165
  group.appendChild(new Rect({ style: { x: bx, y: by, width: bw, height: bh, radius: r, fill: P.red, stroke: withAlpha(C2.white, 0.22), lineWidth: 1 } }));
29107
29166
  if (attributes.labelText) {
29108
- _drawWrappedText(group, attributes.labelText, 0, 0.6, 11.5, "#FFFFFF", bw - 8, 1.15, 700);
29167
+ _drawWrappedText(group, attributes.labelText, 0, 0.6, 11.5, C2.switchText, bw - 8, 1.15, 700);
29109
29168
  }
29110
29169
  }
29111
29170
  function drawCableTerminal(attributes, group) {
@@ -29152,20 +29211,33 @@ function cellFxAt(attributes, i) {
29152
29211
  return fx && typeof fx === "object" ? fx[i] || null : null;
29153
29212
  }
29154
29213
  function drawCellGlowRings(group, cx, cy, baseR, g, t) {
29155
- const color2 = g.color || "#00C8FF";
29156
- const spread = g.spread || 10;
29157
- const maxOp = g.opacity == null ? 0.8 : g.opacity;
29158
- const ease2 = 1 - (1 - t) * (1 - t);
29159
- const alpha = maxOp * Math.pow(1 - t, 1.4);
29160
- if (alpha <= 0.02) return;
29161
- const r = baseR + spread * ease2;
29162
- const lw = Math.max(1, 3.2 * (1 - t) + 0.6);
29163
- group.appendChild(new Circle({ style: { cx, cy, r, stroke: color2, lineWidth: lw, strokeOpacity: alpha, fill: "none", lineCap: "round" } }));
29164
- group.appendChild(new Circle({ style: { cx, cy, r: r + spread * 0.6, stroke: color2, lineWidth: Math.max(0.6, lw * 0.55), strokeOpacity: alpha * 0.35, fill: "none", lineCap: "round" } }));
29214
+ glowStack(group, cx, cy, baseR, {
29215
+ color: g.color,
29216
+ t: t == null ? g.t : t,
29217
+ spread: g.spread,
29218
+ opacity: g.opacity,
29219
+ width: g.width,
29220
+ level: g.level
29221
+ });
29222
+ }
29223
+ function cellBlinkOpacity(g) {
29224
+ if (!g || !g.blink) return 1;
29225
+ return 1 - g.blink * glowBlinkK(g.t == null ? 0 : g.t);
29226
+ }
29227
+ function drawCellGlowBackdrop(group, cx, cy, meterW, meterH, g) {
29228
+ if (!g) return;
29229
+ glowBackdrop(group, cx, cy, meterW, meterH, {
29230
+ color: g.color,
29231
+ t: g.t,
29232
+ spread: g.spread != null ? g.spread : 14,
29233
+ level: g.level,
29234
+ blink: g.blink
29235
+ });
29165
29236
  }
29166
29237
  function drawCellHighlight(group, cx, cy, baseR, hl) {
29167
29238
  group.appendChild(new Circle({
29168
29239
  style: {
29240
+ ...NO_HIT,
29169
29241
  cx,
29170
29242
  cy,
29171
29243
  r: baseR + 3,
@@ -29181,7 +29253,7 @@ function drawCellNote(group, cx, cy, baseR, note) {
29181
29253
  const color2 = note.color || C2.gold;
29182
29254
  const top = !note.position || note.position === "top";
29183
29255
  const y = top ? cy - baseR - 5 - size / 2 : cy + baseR + 6 + size / 2;
29184
- _drawWrappedTextFixed(group, note.text, cx, y, size, color2, 6, 1.15, 600, "center");
29256
+ _drawWrappedTextFixed(group, note.text, cx, y, size, color2, 6, 1.15, 600, "center", NO_HIT);
29185
29257
  }
29186
29258
  function drawMeterBox(attributes, group) {
29187
29259
  const [w, h] = attributes.size;
@@ -29203,6 +29275,8 @@ function drawMeterBox(attributes, group) {
29203
29275
  const meterH = 26;
29204
29276
  const cx = 0;
29205
29277
  const cy = -1;
29278
+ const baseR = Math.max(12, meterH / 2 + 2);
29279
+ if (fx && fx.glow) drawCellGlowBackdrop(cellGroup, cx, cy, meterW, meterH, fx.glow);
29206
29280
  if (sel || hover) {
29207
29281
  const ring = new Rect({
29208
29282
  style: {
@@ -29219,9 +29293,10 @@ function drawMeterBox(attributes, group) {
29219
29293
  markCell(ring);
29220
29294
  cellGroup.appendChild(ring);
29221
29295
  }
29296
+ const faceStart = cellGroup.children.length;
29222
29297
  drawMeterFace(cellGroup, cx, cy, meterW, meterH, lineColor, markCell, 0);
29298
+ const faceEnd = cellGroup.children.length;
29223
29299
  if (fx) {
29224
- const baseR = Math.max(12, meterH / 2 + 2);
29225
29300
  if (fx.hl) drawCellHighlight(cellGroup, cx, cy, baseR, fx.hl);
29226
29301
  if (fx.note && fx.note.text) drawCellNote(cellGroup, cx, cy, baseR, fx.note);
29227
29302
  if (fx.glow) drawCellGlowRings(cellGroup, cx, cy, baseR, fx.glow, fx.glow.t || 0);
@@ -29233,6 +29308,13 @@ function drawMeterBox(attributes, group) {
29233
29308
  if (fx && fx.pulse && fx.pulse.opacity != null && fx.pulse.opacity < 1) {
29234
29309
  cellGroup.style.opacity = fx.pulse.opacity;
29235
29310
  }
29311
+ const blinkOp0 = fx && fx.glow ? cellBlinkOpacity(fx.glow) : 1;
29312
+ if (blinkOp0 < 1) {
29313
+ for (let k = faceStart; k < faceEnd && k < cellGroup.children.length; k++) {
29314
+ const s2 = cellGroup.children[k].style;
29315
+ s2.opacity = (s2.opacity == null ? 1 : s2.opacity) * blinkOp0;
29316
+ }
29317
+ }
29236
29318
  group.appendChild(cellGroup);
29237
29319
  } else if (attributes._meterBoxMode === "grid" && attributes._customers) {
29238
29320
  const frameColor = boxColor || C2.meterText;
@@ -29258,6 +29340,7 @@ function drawMeterBox(attributes, group) {
29258
29340
  shape.__custBox = nodeId;
29259
29341
  shape.__custIndex = i;
29260
29342
  };
29343
+ const hitLayer = new Group({});
29261
29344
  for (let i = 0; i < customers.length; i++) {
29262
29345
  const row2 = Math.floor(i / cols);
29263
29346
  const col = i % cols;
@@ -29269,6 +29352,7 @@ function drawMeterBox(attributes, group) {
29269
29352
  const lineColor = sel ? C2.gold : hover ? C2.cyan : baseColor;
29270
29353
  const fx = cellFxAt(attributes, i);
29271
29354
  const cellGroup = new Group({});
29355
+ if (fx && fx.glow) drawCellGlowBackdrop(cellGroup, cx, cy, meterW, meterH, fx.glow);
29272
29356
  if (sel || hover) {
29273
29357
  cellGroup.appendChild(new Circle({
29274
29358
  style: {
@@ -29282,7 +29366,9 @@ function drawMeterBox(attributes, group) {
29282
29366
  }
29283
29367
  }));
29284
29368
  }
29369
+ const faceStart = cellGroup.children.length;
29285
29370
  drawMeterFace(cellGroup, cx, cy, meterW, meterH, lineColor, markCell, i);
29371
+ const faceEnd = cellGroup.children.length;
29286
29372
  if (fx) {
29287
29373
  if (fx.hl) drawCellHighlight(cellGroup, cx, cy, hitR, fx.hl);
29288
29374
  if (fx.note && fx.note.text) drawCellNote(cellGroup, cx, cy, hitR, fx.note);
@@ -29294,9 +29380,19 @@ function drawMeterBox(attributes, group) {
29294
29380
  _drawWrappedTextFixed(cellGroup, name, cx, cy + meterH / 2 + 6.5, 6.5, lineColor, 4, 1.15, 500);
29295
29381
  }
29296
29382
  }
29297
- const hit = new Circle({ style: { cx, cy, r: hitR, fill: "rgba(255,255,255,0.01)", stroke: "none", cursor: "pointer" } });
29298
- markCell(hit, i);
29299
- cellGroup.appendChild(hit);
29383
+ const hitCell = new Rect({
29384
+ style: {
29385
+ x: cx - cellW / 2 - METER_GAP / 2,
29386
+ y: cy - cellW / 2 - METER_GAP / 2,
29387
+ width: cellW + METER_GAP,
29388
+ height: cellH + METER_GAP,
29389
+ fill: "rgba(255,255,255,0.01)",
29390
+ stroke: "none",
29391
+ cursor: "pointer"
29392
+ }
29393
+ });
29394
+ markCell(hitCell, i);
29395
+ hitLayer.appendChild(hitCell);
29300
29396
  if (showName) {
29301
29397
  const name = customers[i].realConsName || customers[i].consName || "";
29302
29398
  if (name) {
@@ -29311,14 +29407,22 @@ function drawMeterBox(attributes, group) {
29311
29407
  }
29312
29408
  });
29313
29409
  markCell(hitName, i);
29314
- cellGroup.appendChild(hitName);
29410
+ hitLayer.appendChild(hitName);
29315
29411
  }
29316
29412
  }
29317
29413
  if (fx && fx.pulse && fx.pulse.opacity != null && fx.pulse.opacity < 1) {
29318
29414
  cellGroup.style.opacity = fx.pulse.opacity;
29319
29415
  }
29416
+ const blinkOp = fx && fx.glow ? cellBlinkOpacity(fx.glow) : 1;
29417
+ if (blinkOp < 1) {
29418
+ for (let k = faceStart; k < faceEnd && k < cellGroup.children.length; k++) {
29419
+ const s2 = cellGroup.children[k].style;
29420
+ s2.opacity = (s2.opacity == null ? 1 : s2.opacity) * blinkOp;
29421
+ }
29422
+ }
29320
29423
  group.appendChild(cellGroup);
29321
29424
  }
29425
+ group.appendChild(hitLayer);
29322
29426
  } else {
29323
29427
  const s2 = Math.min(w, h) || 21;
29324
29428
  const ink = boxColor || C2.meterText;
@@ -29406,35 +29510,134 @@ const DRAWERS = {
29406
29510
  other: drawUnknown
29407
29511
  };
29408
29512
  const _pulseState = /* @__PURE__ */ new Map();
29409
- function drawGlow(attributes, group) {
29410
- const color2 = attributes._glowColor;
29411
- if (!color2) return;
29412
- const t = attributes._glowT == null ? 0 : attributes._glowT;
29413
- const [w, h] = attributes.size || [40, 40];
29414
- const baseR = Math.max(w, h) / 2 + 3;
29415
- const spread = attributes._glowSpread || 14;
29416
- const maxOp = attributes._glowOpacity == null ? 0.9 : attributes._glowOpacity;
29417
- const ease2 = 1 - (1 - t) * (1 - t);
29418
- const alpha = maxOp * Math.pow(1 - t, 1.4);
29513
+ function pushGlowRing(group, cx, cy, r, lineWidth, strokeOpacity, color2, blur) {
29514
+ if (strokeOpacity <= 0.015 || lineWidth <= 0) return;
29515
+ const style = {
29516
+ ...NO_HIT,
29517
+ cx,
29518
+ cy,
29519
+ r,
29520
+ stroke: color2,
29521
+ lineWidth,
29522
+ strokeOpacity,
29523
+ fill: "none",
29524
+ lineCap: "round"
29525
+ };
29526
+ if (blur > 0.5) {
29527
+ style.shadowColor = color2;
29528
+ style.shadowBlur = blur;
29529
+ }
29530
+ group.appendChild(new Circle({ style }));
29531
+ }
29532
+ function glowStack(group, cx, cy, baseR, g) {
29533
+ const color2 = g.color || "#00C8FF";
29534
+ const level = g.level > 0 ? g.level : 1;
29535
+ const tt = clampNum(g.t == null ? 0 : g.t, 0, 1);
29536
+ const spread = g.spread != null ? g.spread : 14;
29537
+ const maxOp = g.opacity == null ? 0.95 : g.opacity;
29538
+ const alpha = Math.min(1, maxOp * Math.pow(1 - tt, 1.1) * Math.min(1.5, level));
29419
29539
  if (alpha <= 0.02) return;
29420
- const r = baseR + spread * ease2;
29421
- const lw = Math.max(1.2, 4.4 * (1 - t) + 0.8);
29422
- group.appendChild(new Circle({
29423
- style: { cx: 0, cy: 0, r, stroke: color2, lineWidth: lw, strokeOpacity: alpha, fill: "none", lineCap: "round" }
29540
+ const r = baseR + spread * (1 - (1 - tt) * (1 - tt));
29541
+ const lw = Math.max(1.3, (g.width || 3.6) * level * (0.34 + 0.66 * (1 - tt)));
29542
+ pushGlowRing(group, cx, cy, r + spread * 1.1, lw * 0.4, alpha * 0.26, color2, 0);
29543
+ pushGlowRing(group, cx, cy, r + spread * 0.55, lw * 0.62, alpha * 0.5, color2, lw * 2.5);
29544
+ pushGlowRing(group, cx, cy, r, lw, alpha, color2, lw * 6);
29545
+ pushGlowRing(group, cx, cy, r, Math.max(1.1, lw * 0.34), Math.min(1, alpha * 1.05), mixWhite(color2, 0.62), lw * 2);
29546
+ }
29547
+ function glowBackdrop(group, x, y, w, h, g) {
29548
+ const color2 = g.color || "#00C8FF";
29549
+ const level = g.level > 0 ? g.level : 1;
29550
+ const tt = clampNum(g.t == null ? 0 : g.t, 0, 1);
29551
+ const spread = g.spread != null ? g.spread : Math.max(14, Math.min(w, h) * 0.7);
29552
+ const blink = g.blink || 0;
29553
+ const fade = Math.pow(1 - tt, 1.1);
29554
+ const bk = glowBlinkK(tt);
29555
+ const base = Math.min(0.6, level * (0.26 * fade + 0.5 * blink * bk));
29556
+ const aura = Math.min(0.45, level * (0.2 * fade + 0.42 * blink * bk));
29557
+ if (base <= 0.02 && aura <= 0.02) return;
29558
+ const rad2 = Math.max(3, Math.min(w, h) * 0.16);
29559
+ const blurPx = clampNum(Math.min(w, h) * 0.28, 4, 18) * level;
29560
+ group.appendChild(new Rect({
29561
+ style: {
29562
+ ...NO_HIT,
29563
+ x: x - w / 2 - spread * 0.85,
29564
+ y: y - h / 2 - spread * 0.85,
29565
+ width: w + spread * 1.7,
29566
+ height: h + spread * 1.7,
29567
+ radius: rad2 + 4,
29568
+ fill: color2,
29569
+ fillOpacity: aura,
29570
+ stroke: "none",
29571
+ filter: `blur(${blurPx.toFixed(1)}px)`
29572
+ }
29424
29573
  }));
29425
- group.appendChild(new Circle({
29574
+ group.appendChild(new Rect({
29426
29575
  style: {
29427
- cx: 0,
29428
- cy: 0,
29429
- r: r + spread * 0.6,
29430
- stroke: color2,
29431
- lineWidth: Math.max(0.8, lw * 0.55),
29432
- strokeOpacity: alpha * 0.35,
29433
- fill: "none",
29434
- lineCap: "round"
29576
+ ...NO_HIT,
29577
+ x: x - w / 2 - 2.5,
29578
+ y: y - h / 2 - 2.5,
29579
+ width: w + 5,
29580
+ height: h + 5,
29581
+ radius: rad2,
29582
+ fill: color2,
29583
+ fillOpacity: base,
29584
+ stroke: "none"
29435
29585
  }
29436
29586
  }));
29437
29587
  }
29588
+ function glowBlinkK(t) {
29589
+ return 0.5 - 0.5 * Math.cos((t || 0) * Math.PI * 2);
29590
+ }
29591
+ function glowParams(attributes) {
29592
+ const color2 = attributes._glowColor;
29593
+ if (!color2) return null;
29594
+ const [w, h] = attributes.size || [40, 40];
29595
+ const t = clampNum(attributes._glowT == null ? 0 : attributes._glowT, 0, 1);
29596
+ const level = attributes._glowLevel > 0 ? attributes._glowLevel : 1;
29597
+ const spread = attributes._glowSpread != null ? attributes._glowSpread : clampNum(Math.min(w, h) * 0.7, 16, 46);
29598
+ return {
29599
+ color: color2,
29600
+ t,
29601
+ w,
29602
+ h,
29603
+ level,
29604
+ baseR: Math.max(w, h) / 2 + 3,
29605
+ spread,
29606
+ maxOp: attributes._glowOpacity == null ? 0.95 : attributes._glowOpacity,
29607
+ lw0: attributes._glowWidth || 5.6,
29608
+ // 主环初始线宽(可调,默认 5.6)
29609
+ blink: attributes._glowBlink || 0
29610
+ // 整体闪烁强度(0=关;setNodeGlow blink 参数)
29611
+ };
29612
+ }
29613
+ function drawGlowBackdrop(attributes, group) {
29614
+ const p = glowParams(attributes);
29615
+ if (!p) return;
29616
+ glowBackdrop(group, 0, 0, p.w, p.h, {
29617
+ color: p.color,
29618
+ t: p.t,
29619
+ spread: p.spread,
29620
+ level: p.level,
29621
+ blink: p.blink
29622
+ });
29623
+ }
29624
+ function nodeBlinkOpacity(attributes) {
29625
+ const blink = attributes._glowBlink;
29626
+ if (!blink) return 1;
29627
+ return 1 - blink * glowBlinkK(attributes._glowT == null ? 0 : attributes._glowT);
29628
+ }
29629
+ function drawGlow(attributes, group) {
29630
+ const p = glowParams(attributes);
29631
+ if (!p) return;
29632
+ glowStack(group, 0, 0, p.baseR, {
29633
+ color: p.color,
29634
+ t: p.t,
29635
+ spread: p.spread,
29636
+ opacity: p.maxOp,
29637
+ width: p.lw0,
29638
+ level: p.level
29639
+ });
29640
+ }
29438
29641
  class SvgSymbolNode extends BaseNode {
29439
29642
  drawKeyShape(attributes, container) {
29440
29643
  const [w, h] = attributes.size;
@@ -29464,7 +29667,10 @@ class SvgSymbolNode extends BaseNode {
29464
29667
  const target = rot ? new Group({ style: { transform: `rotate(${rot}deg)` } }) : key;
29465
29668
  if (rot) key.appendChild(target);
29466
29669
  const draw = DRAWERS[attributes.cat] || drawUnknown;
29670
+ drawGlowBackdrop(attributes, target);
29671
+ const symStart = (target.children || []).length;
29467
29672
  draw(attributes, target);
29673
+ const symEnd = (target.children || []).length;
29468
29674
  drawGlow(attributes, target);
29469
29675
  drawHighlight(attributes, target);
29470
29676
  drawAnnotation(attributes, target);
@@ -29482,6 +29688,15 @@ class SvgSymbolNode extends BaseNode {
29482
29688
  for (const child of items) child.style.opacity = op;
29483
29689
  }
29484
29690
  }
29691
+ const blinkOp = nodeBlinkOpacity(attributes);
29692
+ if (blinkOp < 1) {
29693
+ const kids = target.children || [];
29694
+ for (let i = symStart; i < symEnd && i < kids.length; i++) {
29695
+ const s2 = kids[i].style;
29696
+ const base = s2.opacity == null ? 1 : s2.opacity;
29697
+ s2.opacity = base * blinkOp;
29698
+ }
29699
+ }
29485
29700
  return key;
29486
29701
  }
29487
29702
  }
@@ -29579,6 +29794,56 @@ const _sfc_main$2 = {
29579
29794
  function cellFromShape(shape) {
29580
29795
  return shape && shape.__custBox != null && shape.__custIndex != null ? { nodeId: shape.__custBox, index: shape.__custIndex } : null;
29581
29796
  }
29797
+ function cellIndexAtPoint(nodeId, evt) {
29798
+ const st = styleOf(nodeId);
29799
+ if (!st || st._meterBoxMode !== "grid" || !Array.isArray(st._customers) || !st._customers.length) return null;
29800
+ const c = evt && (evt.canvas || evt.viewport) || {};
29801
+ if (c.x == null || c.y == null || st.x == null || st.y == null) return null;
29802
+ let lx = c.x - st.x;
29803
+ let ly = c.y - st.y;
29804
+ const rot = (st.rotate || 0) * Math.PI / 180;
29805
+ if (rot) {
29806
+ const cos = Math.cos(-rot);
29807
+ const sin = Math.sin(-rot);
29808
+ const nx = lx * cos - ly * sin;
29809
+ const ny = lx * sin + ly * cos;
29810
+ lx = nx;
29811
+ ly = ny;
29812
+ }
29813
+ const cols = st._meterBoxCols || Math.min(st._customers.length, MAX_COLS);
29814
+ const cellW = METER_CELL_W;
29815
+ const cellH = st._meterBoxShowName ? METER_CELL_H : METER_CELL_W;
29816
+ const rows = st._meterBoxRows || Math.ceil(st._customers.length / cols);
29817
+ const gridW = cols * cellW + (cols - 1) * METER_GAP;
29818
+ const gridH = rows * cellH + (rows - 1) * METER_GAP;
29819
+ const col = Math.floor((lx + gridW / 2) / (cellW + METER_GAP));
29820
+ const row2 = Math.floor((ly + gridH / 2) / (cellH + METER_GAP));
29821
+ if (col < 0 || col >= cols || row2 < 0 || row2 >= rows) return null;
29822
+ const i = row2 * cols + col;
29823
+ return i < st._customers.length ? i : null;
29824
+ }
29825
+ const DEBUG_CELL = typeof location !== "undefined" && /[?&]debugCell\b/.test(location.search);
29826
+ function dbgCell(tag, detail) {
29827
+ if (DEBUG_CELL) console.log("[cell-debug]", tag, detail);
29828
+ }
29829
+ function nodeIdOfShape(shape) {
29830
+ if (!shape || !nodeByIdMap) return null;
29831
+ let el = shape;
29832
+ for (let hops = 0; el && hops < 16; hops++) {
29833
+ if (el.id && nodeByIdMap.has(el.id)) return el.id;
29834
+ el = el.parentElement;
29835
+ }
29836
+ return null;
29837
+ }
29838
+ function resolveCell(evt, shape, nodeIdHint, allowBboxFallback = true) {
29839
+ const direct = cellFromShape(shape);
29840
+ if (direct) return direct;
29841
+ let nid = nodeIdHint || nodeIdOfShape(shape);
29842
+ if (!nid && allowBboxFallback) nid = hitTestNodeAtCanvas(evt);
29843
+ if (!nid) return null;
29844
+ const idx = cellIndexAtPoint(nid, evt);
29845
+ return idx == null ? null : { nodeId: nid, index: idx };
29846
+ }
29582
29847
  function neighborsOf(id2) {
29583
29848
  const upstream = [];
29584
29849
  const downstream = [];
@@ -29816,14 +30081,21 @@ const _sfc_main$2 = {
29816
30081
  graph.on("node:click", (evt) => {
29817
30082
  const id2 = evt.target.id;
29818
30083
  if (!id2) return;
29819
- const cell = cellFromShape(evt.originalTarget);
29820
- if (cell && customersOf(id2)) {
30084
+ const cell = resolveCell(evt, evt.originalTarget, id2);
30085
+ dbgCell("node:click", {
30086
+ id: id2,
30087
+ shape: evt.originalTarget && evt.originalTarget.nodeName,
30088
+ mark: cellFromShape(evt.originalTarget),
30089
+ cell,
30090
+ canvas: evt.canvas
30091
+ });
30092
+ if (cell && customersOf(id2) && cell.nodeId === id2) {
29821
30093
  selectCustomerWithInfo(id2, cell.index);
29822
30094
  return;
29823
30095
  }
29824
30096
  routeNodeTap(id2);
29825
30097
  });
29826
- function hitTestNodeAtCanvas(evt) {
30098
+ function hitTestNodeAtCanvas2(evt) {
29827
30099
  const c = evt && (evt.canvas || evt.viewport) || {};
29828
30100
  const cx = c.x, cy = c.y;
29829
30101
  if (cx == null || cy == null) return null;
@@ -29873,8 +30145,14 @@ const _sfc_main$2 = {
29873
30145
  api._emit("edge:click", { edgeId: id2, source: src, target: tgt });
29874
30146
  });
29875
30147
  graph.on("canvas:click", (evt) => {
29876
- const hitId = hitTestNodeAtCanvas(evt);
30148
+ const hitId = hitTestNodeAtCanvas2(evt);
29877
30149
  if (hitId) {
30150
+ const cell = resolveCell(evt, null, hitId);
30151
+ dbgCell("canvas:click", { hitId, cell, canvas: evt.canvas });
30152
+ if (cell && cell.nodeId === hitId && customersOf(hitId)) {
30153
+ selectCustomerWithInfo(hitId, cell.index);
30154
+ return;
30155
+ }
29878
30156
  routeNodeTap(hitId);
29879
30157
  return;
29880
30158
  }
@@ -29896,7 +30174,7 @@ const _sfc_main$2 = {
29896
30174
  const gCanvas = graph.getCanvas && graph.getCanvas();
29897
30175
  const doc = gCanvas && gCanvas.document;
29898
30176
  if (doc) {
29899
- doc.addEventListener("pointermove", (e2) => setCellHover(cellFromShape(e2 && e2.target)));
30177
+ doc.addEventListener("pointermove", (e2) => setCellHover(resolveCell(e2, e2 && e2.target, null, false)));
29900
30178
  }
29901
30179
  hoverLeaveHandler = () => setCellHover(null);
29902
30180
  container.value.addEventListener("pointerleave", hoverLeaveHandler);
@@ -30036,7 +30314,7 @@ const _sfc_main$2 = {
30036
30314
  };
30037
30315
  }
30038
30316
  };
30039
- const TopoGraph = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-d3774ed5"]]);
30317
+ const TopoGraph = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-a4a4c398"]]);
30040
30318
  const _hoisted_1$1 = ["data-theme"];
30041
30319
  const _hoisted_2$1 = { class: "panel-head" };
30042
30320
  const _hoisted_3$1 = {
@@ -30409,7 +30687,7 @@ const _sfc_main = {
30409
30687
  }
30410
30688
  };
30411
30689
  const DistanceTop10 = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-438b6017"]]);
30412
- const VERSION = "0.1.9";
30690
+ const VERSION = "0.1.10";
30413
30691
  const DESCRIPTION = "配电台区单线图拓扑成图引擎";
30414
30692
  export {
30415
30693
  DESCRIPTION,