@knowvah/dot-engine 2.6.2 → 2.6.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.
Files changed (46) hide show
  1. package/dist/common/htmltable-lex.d.ts +38 -0
  2. package/dist/common/htmltable-lex.d.ts.map +1 -1
  3. package/dist/common/htmltable-parse-attrs.d.ts +41 -0
  4. package/dist/common/htmltable-parse-attrs.d.ts.map +1 -0
  5. package/dist/common/htmltable-parse.d.ts +3 -0
  6. package/dist/common/htmltable-parse.d.ts.map +1 -1
  7. package/dist/common/htmltable-pos.d.ts +17 -0
  8. package/dist/common/htmltable-pos.d.ts.map +1 -1
  9. package/dist/common/htmltable.d.ts +13 -1
  10. package/dist/common/htmltable.d.ts.map +1 -1
  11. package/dist/common/make-label.d.ts.map +1 -1
  12. package/dist/common/spline-midpoint.d.ts +8 -0
  13. package/dist/common/spline-midpoint.d.ts.map +1 -1
  14. package/dist/index.js +621 -323
  15. package/dist/index.js.map +4 -4
  16. package/dist/label/node.d.ts +7 -1
  17. package/dist/label/node.d.ts.map +1 -1
  18. package/dist/layout/circo/position-helpers.d.ts +15 -3
  19. package/dist/layout/circo/position-helpers.d.ts.map +1 -1
  20. package/dist/layout/circo/position.d.ts +17 -1
  21. package/dist/layout/circo/position.d.ts.map +1 -1
  22. package/dist/layout/dot/ortho-adapter.d.ts.map +1 -1
  23. package/dist/layout/dot/splines-flat-labeled.d.ts.map +1 -1
  24. package/dist/layout/neato/multispline.d.ts.map +1 -1
  25. package/dist/layout/pack/poly-pack.d.ts +2 -18
  26. package/dist/layout/pack/poly-pack.d.ts.map +1 -1
  27. package/dist/pathplan/shortest.d.ts.map +1 -1
  28. package/dist/render.js +774 -476
  29. package/dist/render.js.map +4 -4
  30. package/package.json +1 -1
  31. package/src/common/htmltable-lex.ts +265 -5
  32. package/src/common/htmltable-parse-attrs.ts +166 -0
  33. package/src/common/htmltable-parse.ts +262 -242
  34. package/src/common/htmltable-pos.ts +37 -38
  35. package/src/common/htmltable.ts +139 -45
  36. package/src/common/make-label.ts +19 -3
  37. package/src/common/spline-midpoint.ts +30 -9
  38. package/src/label/node.ts +8 -2
  39. package/src/layout/circo/position-helpers.ts +28 -17
  40. package/src/layout/circo/position.ts +28 -7
  41. package/src/layout/dot/__fixtures__/2796-flat-labeled-lost.dot +161 -0
  42. package/src/layout/dot/ortho-adapter.ts +20 -9
  43. package/src/layout/dot/splines-flat-labeled.ts +3 -1
  44. package/src/layout/neato/multispline.ts +17 -5
  45. package/src/layout/pack/poly-pack.ts +5 -31
  46. package/src/pathplan/shortest.ts +15 -2
package/dist/index.js CHANGED
@@ -4302,11 +4302,11 @@ function lexToken(s, i) {
4302
4302
  function scanNumberAmbiguities(src, stripped) {
4303
4303
  const out = [];
4304
4304
  let line = 1;
4305
- let lineAt = 0;
4305
+ let lineAt2 = 0;
4306
4306
  for (let i = 0; i < stripped.length; ) {
4307
4307
  const { next, bad } = lexToken(stripped, i);
4308
4308
  if (bad !== null) {
4309
- for (; lineAt < i; lineAt++) if (src[lineAt] === "\n") line++;
4309
+ for (; lineAt2 < i; lineAt2++) if (src[lineAt2] === "\n") line++;
4310
4310
  out.push({ text: bad, line, offset: i });
4311
4311
  }
4312
4312
  i = next;
@@ -10341,16 +10341,152 @@ function parseOpenToken(inner) {
10341
10341
  checkTag(tag);
10342
10342
  return { type: "open", tag: normalizeTag(tag), attrs: parseAttrs(inner.slice(sp + 1)) };
10343
10343
  }
10344
- function tokenize(src) {
10344
+ var XML_ERROR_UNDEFINED_ENTITY = "undefined entity";
10345
+ var XML_ERROR_TAG_MISMATCH = "mismatched tag";
10346
+ var XML_ERROR_INVALID_TOKEN = "not well-formed (invalid token)";
10347
+ var XML_ERROR_DUPLICATE_ATTRIBUTE = "duplicate attribute";
10348
+ var XML_PREDEFINED_ENTITIES = /* @__PURE__ */ new Set(["lt", "gt", "amp", "quot", "apos"]);
10349
+ var XML_NAME = /^[A-Za-z_:][\w.:-]*$/;
10350
+ var HtmlLexError = class extends HtmlParseError {
10351
+ diagnostics;
10352
+ constructor(tag, message, diagnostics) {
10353
+ super(tag);
10354
+ this.message = message;
10355
+ this.diagnostics = diagnostics;
10356
+ }
10357
+ };
10358
+ function htmlErrorInLine(message, line, context) {
10359
+ const text = `${message} in line ${line} `;
10360
+ return new HtmlLexError(message, text, [`Error: ${text}`, `... ${context} ...`]);
10361
+ }
10362
+ var chunkInfo = /* @__PURE__ */ new WeakMap();
10363
+ function lineAt(src, pos) {
10364
+ let line = 1;
10365
+ for (let k = 0; k < pos; k++) {
10366
+ if (src[k] === "\n" || src[k] === "\r" && src[k + 1] !== "\n") line++;
10367
+ }
10368
+ return line;
10369
+ }
10370
+ function chunkLine(info) {
10371
+ if (info.src[info.pos] === "<") return lineAt(info.src, info.pos);
10372
+ const last = info.src.lastIndexOf("\n", info.end - 1);
10373
+ if (last < info.pos) return lineAt(info.src, info.pos);
10374
+ return lineAt(info.src, last === info.end - 1 ? last : last + 1);
10375
+ }
10376
+ function htmlErrorAtToken(message, tok) {
10377
+ const info = tok === void 0 ? void 0 : chunkInfo.get(tok);
10378
+ if (info === void 0) return void 0;
10379
+ return htmlErrorInLine(message, chunkLine(info), info.context);
10380
+ }
10381
+ function chunkEnd(src, i) {
10382
+ const j = src[i] === "<" ? src.indexOf(">", i + 1) : src.indexOf("<", i);
10383
+ if (j < 0) return src.length;
10384
+ return src[i] === "<" ? j + 1 : j;
10385
+ }
10386
+ function findUndefinedEntity(text) {
10387
+ for (let t = text.indexOf("&"); t >= 0; t = text.indexOf("&", t + 1)) {
10388
+ if (text[t + 1] === "#") continue;
10389
+ const semi = text.indexOf(";", t + 1);
10390
+ if (semi < 0) continue;
10391
+ const key = text.slice(t + 1, semi);
10392
+ if (XML_NAME.test(key) && !Object.hasOwn(HTML_ENTITIES, key) && !XML_PREDEFINED_ENTITIES.has(key)) {
10393
+ return t;
10394
+ }
10395
+ }
10396
+ return -1;
10397
+ }
10398
+ function parseTagChunk(chunk) {
10399
+ if (!chunk.endsWith(">")) return void 0;
10400
+ const body = chunk.slice(1, -1);
10401
+ if (body.startsWith("!")) return void 0;
10402
+ const close = body.startsWith("/");
10403
+ const name = /^[^\s/>]*/.exec(close ? body.slice(1) : body)?.[0] ?? "";
10404
+ return { close, selfClose: !close && body.endsWith("/"), name };
10405
+ }
10406
+ var ATTR_NAME = /^[A-Za-z_:][\w.:-]*/;
10407
+ var isSpace = (c) => c !== void 0 && /\s/.test(c);
10408
+ var invalidToken = (offset) => ({ message: XML_ERROR_INVALID_TOKEN, offset });
10409
+ function skipSpace(chunk, i) {
10410
+ let j = i;
10411
+ while (isSpace(chunk[j])) j++;
10412
+ return j;
10413
+ }
10414
+ function readAttrPair(chunk, i) {
10415
+ const name = ATTR_NAME.exec(chunk.slice(i))?.[0];
10416
+ if (name === void 0) return invalidToken(i);
10417
+ let j = skipSpace(chunk, i + name.length);
10418
+ if (chunk[j] !== "=") return invalidToken(j);
10419
+ j = skipSpace(chunk, j + 1);
10420
+ const q = chunk[j];
10421
+ if (q !== '"' && q !== "'") return invalidToken(j);
10422
+ const end = chunk.indexOf(q, j + 1);
10423
+ return end < 0 ? void 0 : { name, next: end + 1 };
10424
+ }
10425
+ function findAttrError(chunk, from) {
10426
+ const seen = /* @__PURE__ */ new Set();
10427
+ let dup;
10428
+ let i = skipSpace(chunk, from);
10429
+ while (chunk[i] !== ">" && !(chunk[i] === "/" && chunk[i + 1] === ">")) {
10430
+ const pair = readAttrPair(chunk, i);
10431
+ if (pair === void 0) return void 0;
10432
+ if ("message" in pair) return pair;
10433
+ if (seen.has(pair.name) && dup === void 0) dup = { message: XML_ERROR_DUPLICATE_ATTRIBUTE, offset: i };
10434
+ seen.add(pair.name);
10435
+ i = skipSpace(chunk, pair.next);
10436
+ }
10437
+ return dup;
10438
+ }
10439
+ function checkTagChunk(src, start, st) {
10440
+ const tag = parseTagChunk(st.curr);
10441
+ if (tag === void 0) return;
10442
+ if (tag.close) {
10443
+ if (st.stack.length > 0 && st.stack.pop() !== tag.name) {
10444
+ throw htmlErrorInLine(XML_ERROR_TAG_MISMATCH, lineAt(src, start), st.prev + st.curr);
10445
+ }
10446
+ } else {
10447
+ const bad = findAttrError(st.curr, 1 + tag.name.length);
10448
+ if (bad !== void 0) {
10449
+ throw htmlErrorInLine(bad.message, lineAt(src, start + bad.offset), st.prev + st.curr);
10450
+ }
10451
+ if (!tag.selfClose) st.stack.push(tag.name);
10452
+ }
10453
+ if (KNOWN_TAGS.has(tag.name.toUpperCase())) return;
10454
+ const text = `Unknown HTML element <${tag.name}> on line ${lineAt(src, start)} `;
10455
+ throw new HtmlLexError(tag.name.toUpperCase(), text, [`Error: ${text}`]);
10456
+ }
10457
+ function checkChunk(src, start, st) {
10458
+ if (st.curr.startsWith("<")) {
10459
+ checkTagChunk(src, start, st);
10460
+ return;
10461
+ }
10462
+ const off = findUndefinedEntity(st.curr);
10463
+ if (off >= 0) {
10464
+ throw htmlErrorInLine(XML_ERROR_UNDEFINED_ENTITY, lineAt(src, start + off), st.prev + st.curr);
10465
+ }
10466
+ }
10467
+ function tokenizeDeferred(src) {
10345
10468
  const tokens = [];
10469
+ const st = { stack: [], prev: "", curr: "" };
10346
10470
  let i = 0;
10347
- while (i < src.length) {
10348
- i = src[i] === "<" ? scanTag(src, i, tokens) : scanText(src, i, tokens);
10471
+ try {
10472
+ while (i < src.length) {
10473
+ const first = tokens.length;
10474
+ const start = i;
10475
+ st.prev = st.curr;
10476
+ st.curr = src.slice(i, chunkEnd(src, i));
10477
+ checkChunk(src, i, st);
10478
+ i = src[i] === "<" ? scanTag(src, i, tokens) : scanText(src, i, tokens);
10479
+ const info = { src, pos: start, end: i, context: st.prev + st.curr };
10480
+ for (let k = first; k < tokens.length; k++) chunkInfo.set(tokens[k], info);
10481
+ }
10482
+ } catch (e) {
10483
+ if (e instanceof HtmlParseError) return { tokens, error: e };
10484
+ throw e;
10349
10485
  }
10350
- return tokens;
10486
+ return { tokens };
10351
10487
  }
10352
10488
 
10353
- // src/common/htmltable-parse.ts
10489
+ // src/common/htmltable-parse-attrs.ts
10354
10490
  function anchorAttr(v) {
10355
10491
  return v === void 0 ? void 0 : htmlEntityUTF8(v);
10356
10492
  }
@@ -10373,6 +10509,81 @@ var num = (s) => {
10373
10509
  const n = parseInt(s, 10);
10374
10510
  return isNaN(n) ? void 0 : n;
10375
10511
  };
10512
+ var parseFixedSize = (a) => a["fixedsize"]?.toUpperCase() === "TRUE" ? true : void 0;
10513
+ var parseTitle = (a) => a["title"] !== void 0 ? a["title"] : a["tooltip"];
10514
+ var parseSides = (v) => {
10515
+ if (v === void 0) return void 0;
10516
+ let flags = 0;
10517
+ for (const c of v.toLowerCase()) {
10518
+ if (c === "l") flags |= BORDER_LEFT;
10519
+ else if (c === "t") flags |= BORDER_TOP;
10520
+ else if (c === "r") flags |= BORDER_RIGHT;
10521
+ else if (c === "b") flags |= BORDER_BOTTOM;
10522
+ }
10523
+ if (flags === 0 || flags === BORDER_MASK) return void 0;
10524
+ return flags;
10525
+ };
10526
+ var parseGradientAngle = (v) => {
10527
+ if (v === void 0) return void 0;
10528
+ const n = parseInt(v, 10);
10529
+ if (isNaN(n) || String(n) !== v.trim()) return void 0;
10530
+ if (n < 0 || n > 360) return void 0;
10531
+ return n;
10532
+ };
10533
+ var parseColumns = (v) => v === "*" ? true : void 0;
10534
+ var parseRows = (v) => v === "*" ? true : void 0;
10535
+ var buildCell = (content, a) => ({
10536
+ kind: "cell",
10537
+ content,
10538
+ port: a["port"],
10539
+ align: parseAlign(a["align"] !== void 0 ? a["align"] : ""),
10540
+ valign: parseVAlign(a["valign"] !== void 0 ? a["valign"] : ""),
10541
+ balign: parseAlign(a["balign"] !== void 0 ? a["balign"] : ""),
10542
+ bgcolor: a["bgcolor"],
10543
+ color: a["color"],
10544
+ border: num(a["border"]),
10545
+ cellpadding: num(a["cellpadding"]),
10546
+ cellspacing: num(a["cellspacing"]),
10547
+ sides: parseSides(a["sides"]),
10548
+ gradientangle: parseGradientAngle(a["gradientangle"]),
10549
+ style: a["style"],
10550
+ width: num(a["width"]),
10551
+ height: num(a["height"]),
10552
+ fixedsize: parseFixedSize(a),
10553
+ colspan: num(a["colspan"]),
10554
+ rowspan: num(a["rowspan"]),
10555
+ href: anchorAttr(a["href"]),
10556
+ title: parseTitle(a),
10557
+ target: anchorAttr(a["target"]),
10558
+ id: a["id"]
10559
+ });
10560
+ var buildTable = (rows, a) => ({
10561
+ kind: "table",
10562
+ rows,
10563
+ border: num(a["border"]),
10564
+ cellborder: num(a["cellborder"]),
10565
+ cellspacing: num(a["cellspacing"]),
10566
+ cellpadding: num(a["cellpadding"]),
10567
+ bgcolor: a["bgcolor"],
10568
+ color: a["color"],
10569
+ style: a["style"],
10570
+ align: parseAlign(a["align"] !== void 0 ? a["align"] : ""),
10571
+ valign: parseVAlign(a["valign"] !== void 0 ? a["valign"] : ""),
10572
+ fixedsize: parseFixedSize(a),
10573
+ width: num(a["width"]),
10574
+ height: num(a["height"]),
10575
+ href: anchorAttr(a["href"]),
10576
+ title: parseTitle(a),
10577
+ target: anchorAttr(a["target"]),
10578
+ id: a["id"],
10579
+ port: a["port"],
10580
+ sides: parseSides(a["sides"]),
10581
+ gradientangle: parseGradientAngle(a["gradientangle"]),
10582
+ vrule: parseColumns(a["columns"]),
10583
+ hrule: parseRows(a["rows"])
10584
+ });
10585
+
10586
+ // src/common/htmltable-parse.ts
10376
10587
  var definedOf = (s) => Object.fromEntries(
10377
10588
  Object.entries(s).filter(([, v]) => v !== void 0)
10378
10589
  );
@@ -10396,6 +10607,11 @@ var fontItemProps = (f) => {
10396
10607
  if (f.fontSize !== void 0) out.fontSize = f.fontSize;
10397
10608
  return out;
10398
10609
  };
10610
+ var MSG_SYNTAX_ERROR = "syntax error";
10611
+ var MSG_TEXT_BEFORE_TABLE = "Syntax error: non-space string used before <TABLE>";
10612
+ var MSG_TEXT_AFTER_TABLE = "Syntax error: non-space string used after </TABLE>";
10613
+ var nonSpace = (text) => /[^ \t\n\r]/.test(text);
10614
+ var parseError = (s, tok, message) => (tok === void 0 ? s.pending : htmlErrorAtToken(message, tok)) ?? new HtmlParseError(message);
10399
10615
  var peek = (s) => s.tokens[s.pos];
10400
10616
  var consume = (s) => {
10401
10617
  const t = s.tokens[s.pos];
@@ -10414,12 +10630,20 @@ var inlineFontState = (tag, _attrs) => {
10414
10630
  if (tag === "SUP") fs.superscript = true;
10415
10631
  return fs;
10416
10632
  };
10633
+ var consumeClose = (s, tag) => {
10634
+ const nxt = peek(s);
10635
+ if (nxt === void 0) return;
10636
+ if (nxt.type === "close" && nxt.tag === tag) {
10637
+ consume(s);
10638
+ return;
10639
+ }
10640
+ throw parseError(s, nxt, MSG_SYNTAX_ERROR);
10641
+ };
10417
10642
  var applyBrTag = (s, t, fontStack, items) => {
10418
10643
  consume(s);
10419
10644
  const font = fontStack[fontStack.length - 1] ?? {};
10420
10645
  items.push({ br: true, brAlign: parseAlign(t.attrs["align"] ?? ""), ...fontItemProps(font) });
10421
- const nxt = peek(s);
10422
- if (nxt?.type === "close" && nxt.tag === "BR") consume(s);
10646
+ consumeClose(s, "BR");
10423
10647
  };
10424
10648
  var applyFontTag = (s, t, fontStack, items) => {
10425
10649
  consume(s);
@@ -10476,12 +10700,6 @@ var parseText = (s, fontStack) => {
10476
10700
  }
10477
10701
  return { kind: "text", items };
10478
10702
  };
10479
- var parseHrContent = (s) => {
10480
- consume(s);
10481
- const nxt = peek(s);
10482
- if (nxt?.type === "close" && nxt.tag === "HR") consume(s);
10483
- return { kind: "hr" };
10484
- };
10485
10703
  var parseImgContent = (s, t, cellAttrs) => {
10486
10704
  consume(s);
10487
10705
  const img = {
@@ -10489,24 +10707,54 @@ var parseImgContent = (s, t, cellAttrs) => {
10489
10707
  src: cellAttrs["src"] ?? t.attrs["src"] ?? "",
10490
10708
  scale: t.attrs["scale"]
10491
10709
  };
10492
- const nxt = peek(s);
10493
- if (nxt?.type === "close" && nxt.tag === "IMG") consume(s);
10710
+ consumeClose(s, "IMG");
10494
10711
  return img;
10495
10712
  };
10496
- var parseCellItem = (s, t, cellAttrs, fontStack) => {
10497
- if (t.type === "open" && t.tag === "TABLE") return [parseTable(s, fontStack)];
10498
- if (t.type === "open" && t.tag === "IMG") return [parseImgContent(s, t, cellAttrs)];
10499
- if (t.type === "open" && t.tag === "HR") return [parseHrContent(s)];
10713
+ var scanCellTable = (s, t, fontStack, st) => {
10714
+ if (st.kind === "image" || st.kind === "table") throw parseError(s, t, MSG_SYNTAX_ERROR);
10715
+ if (st.textNonSpace) throw parseError(s, t, MSG_TEXT_BEFORE_TABLE);
10716
+ const tbl = parseTable(s, fontStack);
10717
+ st.kind = "table";
10718
+ return [tbl];
10719
+ };
10720
+ var scanCellText = (s, t, fontStack, st) => {
10721
+ const start = s.pos;
10500
10722
  const txt = parseText(s, fontStack);
10723
+ if (s.pos === start) throw parseError(s, t, MSG_SYNTAX_ERROR);
10724
+ if (txt.items.some((i) => i.text !== void 0 && nonSpace(i.text))) st.textNonSpace = true;
10725
+ st.kind = "text";
10501
10726
  return txt.items.length > 0 ? [txt] : [];
10502
10727
  };
10728
+ var scanAfterTable = (s, t, st) => {
10729
+ if (t.type !== "text") throw parseError(s, t, st.afterNonSpace ? MSG_TEXT_AFTER_TABLE : MSG_SYNTAX_ERROR);
10730
+ consume(s);
10731
+ if (nonSpace(t.value)) st.afterNonSpace = true;
10732
+ return [];
10733
+ };
10734
+ var scanCellImage = (s, t, cellAttrs, st) => {
10735
+ if (st.kind !== "none") throw parseError(s, t, MSG_SYNTAX_ERROR);
10736
+ st.kind = "image";
10737
+ return [parseImgContent(s, t, cellAttrs)];
10738
+ };
10739
+ var scanCellItem = (s, t, cellAttrs, fontStack, st) => {
10740
+ if (st.kind === "table") return scanAfterTable(s, t, st);
10741
+ if (t.type === "open" && t.tag === "TABLE") return scanCellTable(s, t, fontStack, st);
10742
+ if (t.type === "open" && t.tag === "IMG") return scanCellImage(s, t, cellAttrs, st);
10743
+ if (st.kind === "image") throw parseError(s, t, MSG_SYNTAX_ERROR);
10744
+ return scanCellText(s, t, fontStack, st);
10745
+ };
10746
+ var endsCell = (s, t, st) => {
10747
+ if (t.type !== "close") return false;
10748
+ const afterText = st.kind === "table" && st.afterNonSpace;
10749
+ if (t.tag === "TD" && !afterText) return true;
10750
+ throw parseError(s, t, afterText ? MSG_TEXT_AFTER_TABLE : MSG_SYNTAX_ERROR);
10751
+ };
10503
10752
  var parseCellContent = (s, cellAttrs, fontStack) => {
10504
10753
  const content = [];
10505
- while (s.pos < s.tokens.length) {
10506
- const t = peek(s);
10507
- if (!t) break;
10508
- if (t.type === "close" && t.tag === "TD") break;
10509
- content.push(...parseCellItem(s, t, cellAttrs, fontStack));
10754
+ const st = { kind: "none", textNonSpace: false, afterNonSpace: false };
10755
+ for (let t = peek(s); t !== void 0; t = peek(s)) {
10756
+ if (endsCell(s, t, st)) break;
10757
+ content.push(...scanCellItem(s, t, cellAttrs, fontStack, st));
10510
10758
  }
10511
10759
  const nxt = peek(s);
10512
10760
  if (nxt?.type === "close" && nxt.tag === "TD") consume(s);
@@ -10514,134 +10762,85 @@ var parseCellContent = (s, cellAttrs, fontStack) => {
10514
10762
  if (tbl !== void 0) return [tbl];
10515
10763
  return content;
10516
10764
  };
10517
- var parseFixedSize = (a) => a["fixedsize"]?.toUpperCase() === "TRUE" ? true : void 0;
10518
- var parseTitle = (a) => a["title"] !== void 0 ? a["title"] : a["tooltip"];
10519
- var parseSides = (v) => {
10520
- if (v === void 0) return void 0;
10521
- let flags = 0;
10522
- for (const c of v.toLowerCase()) {
10523
- if (c === "l") flags |= BORDER_LEFT;
10524
- else if (c === "t") flags |= BORDER_TOP;
10525
- else if (c === "r") flags |= BORDER_RIGHT;
10526
- else if (c === "b") flags |= BORDER_BOTTOM;
10527
- }
10528
- if (flags === 0 || flags === BORDER_MASK) return void 0;
10529
- return flags;
10530
- };
10531
- var parseGradientAngle = (v) => {
10532
- if (v === void 0) return void 0;
10533
- const n = parseInt(v, 10);
10534
- if (isNaN(n) || String(n) !== v.trim()) return void 0;
10535
- if (n < 0 || n > 360) return void 0;
10536
- return n;
10537
- };
10538
- var parseColumns = (v) => v === "*" ? true : void 0;
10539
- var parseRows = (v) => v === "*" ? true : void 0;
10540
- var buildCell = (content, a) => ({
10541
- kind: "cell",
10542
- content,
10543
- port: a["port"],
10544
- align: parseAlign(a["align"] !== void 0 ? a["align"] : ""),
10545
- valign: parseVAlign(a["valign"] !== void 0 ? a["valign"] : ""),
10546
- balign: parseAlign(a["balign"] !== void 0 ? a["balign"] : ""),
10547
- bgcolor: a["bgcolor"],
10548
- color: a["color"],
10549
- border: num(a["border"]),
10550
- cellpadding: num(a["cellpadding"]),
10551
- cellspacing: num(a["cellspacing"]),
10552
- sides: parseSides(a["sides"]),
10553
- gradientangle: parseGradientAngle(a["gradientangle"]),
10554
- style: a["style"],
10555
- width: num(a["width"]),
10556
- height: num(a["height"]),
10557
- fixedsize: parseFixedSize(a),
10558
- colspan: num(a["colspan"]),
10559
- rowspan: num(a["rowspan"]),
10560
- href: anchorAttr(a["href"]),
10561
- title: parseTitle(a),
10562
- target: anchorAttr(a["target"]),
10563
- id: a["id"]
10564
- });
10565
10765
  var parseCell = (s, attrs, fontStack) => buildCell(parseCellContent(s, attrs, fontStack), attrs);
10566
- var processRowToken = (s, t, cells, fontStack) => {
10766
+ var parseRowVr = (s, t, cells) => {
10767
+ const prev = cells[cells.length - 1];
10768
+ if (prev === void 0) throw parseError(s, t, MSG_SYNTAX_ERROR);
10769
+ consume(s);
10770
+ consumeClose(s, "VR");
10771
+ prev.vruled = true;
10772
+ };
10773
+ var scanRowCell = (s, t, fontStack, rs) => {
10567
10774
  if (t.type === "open" && t.tag === "TD") {
10568
10775
  consume(s);
10569
- cells.push(parseCell(s, t.attrs, fontStack));
10570
- return;
10776
+ rs.cells.push(parseCell(s, t.attrs, fontStack));
10777
+ rs.needCell = false;
10778
+ } else if (t.type === "open" && t.tag === "VR" && !rs.needCell) {
10779
+ parseRowVr(s, t, rs.cells);
10780
+ rs.needCell = true;
10781
+ } else {
10782
+ throw parseError(s, t, MSG_SYNTAX_ERROR);
10571
10783
  }
10572
- if (t.type === "open" && t.tag === "VR") {
10784
+ };
10785
+ var scanRowToken = (s, t, fontStack, rs) => {
10786
+ if (t.type === "text") {
10573
10787
  consume(s);
10574
- const nxt = peek(s);
10575
- if (nxt?.type === "close" && nxt.tag === "VR") consume(s);
10576
- const prev = cells[cells.length - 1];
10577
- if (prev === void 0) throw new HtmlParseError("VR");
10578
- prev.vruled = true;
10579
- return;
10788
+ return false;
10580
10789
  }
10581
- consume(s);
10790
+ if (t.type !== "close" || t.tag !== "TR") {
10791
+ scanRowCell(s, t, fontStack, rs);
10792
+ return false;
10793
+ }
10794
+ if (rs.cells.length === 0 || rs.needCell) throw parseError(s, t, MSG_SYNTAX_ERROR);
10795
+ return true;
10582
10796
  };
10583
10797
  var parseRow = (s, fontStack) => {
10584
- const cells = [];
10585
- while (s.pos < s.tokens.length) {
10586
- const t = peek(s);
10587
- if (!t) break;
10588
- if (t.type === "close" && t.tag === "TR") break;
10589
- processRowToken(s, t, cells, fontStack);
10798
+ const rs = { cells: [], needCell: false };
10799
+ for (let t = peek(s); t !== void 0; t = peek(s)) {
10800
+ if (scanRowToken(s, t, fontStack, rs)) break;
10590
10801
  }
10591
10802
  const nxt = peek(s);
10592
10803
  if (nxt?.type === "close" && nxt.tag === "TR") consume(s);
10593
- return { cells };
10804
+ return { cells: rs.cells };
10805
+ };
10806
+ var scanTableRow = (s, t, fontStack, ts) => {
10807
+ if (t.type === "open" && t.tag === "TR") {
10808
+ consume(s);
10809
+ ts.rows.push(parseTableRow(s, fontStack, ts));
10810
+ ts.needRow = false;
10811
+ } else if (t.type === "open" && t.tag === "HR" && ts.rows.length > 0 && !ts.needRow) {
10812
+ consumeRowHr(s, ts.rows);
10813
+ ts.needRow = true;
10814
+ } else {
10815
+ throw parseError(s, t, MSG_SYNTAX_ERROR);
10816
+ }
10817
+ };
10818
+ var scanTableToken = (s, t, fontStack, ts) => {
10819
+ if (t.type === "text") {
10820
+ consume(s);
10821
+ return false;
10822
+ }
10823
+ if (t.type !== "close" || t.tag !== "TABLE") {
10824
+ scanTableRow(s, t, fontStack, ts);
10825
+ return false;
10826
+ }
10827
+ if (ts.rows.length === 0 || ts.needRow) throw parseError(s, t, MSG_SYNTAX_ERROR);
10828
+ consume(s);
10829
+ return true;
10594
10830
  };
10595
- var buildTable = (rows, a) => ({
10596
- kind: "table",
10597
- rows,
10598
- border: num(a["border"]),
10599
- cellborder: num(a["cellborder"]),
10600
- cellspacing: num(a["cellspacing"]),
10601
- cellpadding: num(a["cellpadding"]),
10602
- bgcolor: a["bgcolor"],
10603
- color: a["color"],
10604
- style: a["style"],
10605
- align: parseAlign(a["align"] !== void 0 ? a["align"] : ""),
10606
- valign: parseVAlign(a["valign"] !== void 0 ? a["valign"] : ""),
10607
- fixedsize: parseFixedSize(a),
10608
- width: num(a["width"]),
10609
- height: num(a["height"]),
10610
- href: anchorAttr(a["href"]),
10611
- title: parseTitle(a),
10612
- target: anchorAttr(a["target"]),
10613
- id: a["id"],
10614
- port: a["port"],
10615
- sides: parseSides(a["sides"]),
10616
- gradientangle: parseGradientAngle(a["gradientangle"]),
10617
- vrule: parseColumns(a["columns"]),
10618
- hrule: parseRows(a["rows"])
10619
- });
10620
10831
  var parseTable = (s, fontStack) => {
10621
10832
  const open = consume(s);
10622
10833
  if (open.type !== "open" || open.tag !== "TABLE") throw new HtmlParseError("TABLE");
10623
- const hrule = parseRows(open.attrs["rows"]) === true;
10624
- const vrule = parseColumns(open.attrs["columns"]) === true;
10625
- const rows = [];
10626
- while (s.pos < s.tokens.length) {
10627
- const t = peek(s);
10628
- if (!t) break;
10629
- if (t.type === "close" && t.tag === "TABLE") {
10630
- consume(s);
10631
- break;
10632
- }
10633
- if (t.type === "open" && t.tag === "TR") {
10634
- consume(s);
10635
- rows.push(parseTableRow(s, fontStack, { hrule, vrule }));
10636
- continue;
10637
- }
10638
- if (t.type === "open" && t.tag === "HR") {
10639
- consumeRowHr(s, rows);
10640
- continue;
10641
- }
10642
- consume(s);
10834
+ const ts = {
10835
+ rows: [],
10836
+ needRow: false,
10837
+ hrule: parseRows(open.attrs["rows"]) === true,
10838
+ vrule: parseColumns(open.attrs["columns"]) === true
10839
+ };
10840
+ for (let t = peek(s); t !== void 0; t = peek(s)) {
10841
+ if (scanTableToken(s, t, fontStack, ts)) break;
10643
10842
  }
10644
- return buildTable(rows, open.attrs);
10843
+ return buildTable(ts.rows, open.attrs);
10645
10844
  };
10646
10845
  var parseTableRow = (s, fontStack, opts) => {
10647
10846
  const row = parseRow(s, fontStack);
@@ -10651,11 +10850,9 @@ var parseTableRow = (s, fontStack, opts) => {
10651
10850
  };
10652
10851
  var consumeRowHr = (s, rows) => {
10653
10852
  consume(s);
10654
- const nxt = peek(s);
10655
- if (nxt?.type === "close" && nxt.tag === "HR") consume(s);
10853
+ consumeClose(s, "HR");
10656
10854
  const prev = rows[rows.length - 1];
10657
- if (prev === void 0) throw new HtmlParseError("HR");
10658
- prev.ruled = true;
10855
+ if (prev !== void 0) prev.ruled = true;
10659
10856
  };
10660
10857
  var findFirstRealToken = (tokens, start) => {
10661
10858
  for (let i = start; i < tokens.length; i++) {
@@ -10671,34 +10868,61 @@ var skipLeadingWs = (s) => {
10671
10868
  consume(s);
10672
10869
  }
10673
10870
  };
10871
+ var finishTableLabel = (s) => {
10872
+ let afterNonSpace = false;
10873
+ for (let t = peek(s); t?.type === "text"; t = peek(s)) {
10874
+ consume(s);
10875
+ if (nonSpace(t.value)) afterNonSpace = true;
10876
+ }
10877
+ const nxt = peek(s);
10878
+ if (nxt === void 0) return;
10879
+ if (afterNonSpace) throw parseError(s, nxt, MSG_TEXT_AFTER_TABLE);
10880
+ if (nxt.type !== "close" || nxt.tag !== "HTML") throw parseError(s, nxt, MSG_SYNTAX_ERROR);
10881
+ consume(s);
10882
+ };
10674
10883
  var parseTableLabel = (s, fontStack) => {
10675
10884
  skipLeadingWs(s);
10676
10885
  const table = parseTable(s, fontStack);
10677
- const nxt = peek(s);
10678
- if (nxt?.type === "close" && nxt.tag === "HTML") consume(s);
10886
+ finishTableLabel(s);
10679
10887
  return { kind: "table", table };
10680
10888
  };
10681
- var parseHtmlLabel = (src) => {
10682
- const wrapped = `<HTML>${src}</HTML>`;
10683
- const tokens = tokenize(wrapped);
10684
- const s = { tokens, pos: 0 };
10685
- const fontStack = [{}];
10686
- const htmlOpen = consume(s);
10687
- if (htmlOpen.type !== "open" || htmlOpen.tag !== "HTML") throw new HtmlParseError("HTML");
10688
- const firstReal = findFirstRealToken(tokens, s.pos);
10689
- if (firstReal?.type === "open" && firstReal.tag === "TABLE") {
10690
- return parseTableLabel(s, fontStack);
10691
- }
10889
+ var parseTextLabel = (s, fontStack) => {
10692
10890
  const texts = [];
10693
10891
  const txt = parseText(s, fontStack);
10694
10892
  if (txt.items.length > 0) texts.push(txt);
10695
10893
  const nxt = peek(s);
10696
- if (nxt?.type === "open" && nxt.tag === "TABLE") {
10697
- throw new HtmlParseError("non-space string used before <TABLE>");
10894
+ if (nxt === void 0) return { kind: "text", texts };
10895
+ if (nxt.type === "open" && nxt.tag === "TABLE") {
10896
+ if (txt.items.some((i) => i.text !== void 0 && nonSpace(i.text))) {
10897
+ throw parseError(s, nxt, MSG_TEXT_BEFORE_TABLE);
10898
+ }
10899
+ throw new HtmlParseError("TABLE");
10698
10900
  }
10699
- if (nxt?.type === "close" && nxt.tag === "HTML") consume(s);
10901
+ if (nxt.type !== "close" || nxt.tag !== "HTML") throw parseError(s, nxt, MSG_SYNTAX_ERROR);
10902
+ consume(s);
10700
10903
  return { kind: "text", texts };
10701
10904
  };
10905
+ var parseTokens = (s) => {
10906
+ const fontStack = [{}];
10907
+ const htmlOpen = consume(s);
10908
+ if (htmlOpen.type !== "open" || htmlOpen.tag !== "HTML") throw new HtmlParseError("HTML");
10909
+ const firstReal = findFirstRealToken(s.tokens, s.pos);
10910
+ if (firstReal?.type === "open" && firstReal.tag === "TABLE") return parseTableLabel(s, fontStack);
10911
+ return parseTextLabel(s, fontStack);
10912
+ };
10913
+ var parseHtmlLabel = (src) => {
10914
+ const { tokens, error } = tokenizeDeferred(`<HTML>${src}</HTML>`);
10915
+ const s = { tokens, pos: 0, pending: error };
10916
+ let label;
10917
+ try {
10918
+ label = parseTokens(s);
10919
+ } catch (e) {
10920
+ if (error !== void 0 && s.pos >= tokens.length) throw error;
10921
+ throw e;
10922
+ }
10923
+ if (error !== void 0) throw error;
10924
+ return label;
10925
+ };
10702
10926
 
10703
10927
  // src/gvc/usershape.ts
10704
10928
  var activeSizer = null;
@@ -10712,28 +10936,80 @@ function findImageSize(src) {
10712
10936
  return activeSizer !== null ? activeSizer(src) : null;
10713
10937
  }
10714
10938
 
10939
+ // src/common/pointset.ts
10940
+ function cmpPair(a, b) {
10941
+ if (a.x > b.x) return 1;
10942
+ if (a.x < b.x) return -1;
10943
+ if (a.y > b.y) return 1;
10944
+ if (a.y < b.y) return -1;
10945
+ return 0;
10946
+ }
10947
+ var PointSet = class {
10948
+ byX = /* @__PURE__ */ new Map();
10949
+ n = 0;
10950
+ /**
10951
+ * Insert (x, y). A duplicate keeps the point inserted first, as `dtinsert`
10952
+ * returns the existing object and the new one is freed.
10953
+ * @see lib/common/pointset.c:addPS
10954
+ */
10955
+ addPS(x, y) {
10956
+ let col = this.byX.get(x);
10957
+ if (col === void 0) {
10958
+ col = /* @__PURE__ */ new Map();
10959
+ this.byX.set(x, col);
10960
+ }
10961
+ if (col.has(y)) return;
10962
+ col.set(y, { x, y });
10963
+ this.n++;
10964
+ }
10965
+ /** @see lib/common/pointset.c:inPS */
10966
+ inPS(pt) {
10967
+ return this.byX.get(pt.x)?.has(pt.y) ?? false;
10968
+ }
10969
+ /** @see lib/common/pointset.c:isInPS */
10970
+ isInPS(x, y) {
10971
+ return this.byX.get(x)?.has(y) ?? false;
10972
+ }
10973
+ /** @see lib/common/pointset.c:sizeOf */
10974
+ sizeOf() {
10975
+ return this.n;
10976
+ }
10977
+ /**
10978
+ * The points in dictionary order (`dtflatten` walks the ordered set).
10979
+ * @see lib/common/pointset.c:pointsOf
10980
+ */
10981
+ pointsOf() {
10982
+ const pts = [];
10983
+ for (const col of this.byX.values()) {
10984
+ for (const p of col.values()) pts.push({ x: p.x, y: p.y });
10985
+ }
10986
+ return pts.sort(cmpPair);
10987
+ }
10988
+ };
10989
+
10715
10990
  // src/common/htmltable.ts
10716
10991
  var DEFAULT_BORDER = 1;
10717
10992
  var DEFAULT_CELLPADDING = 2;
10718
10993
  var DEFAULT_CELLSPACING = 2;
10719
10994
  var DEFAULT_FONTSIZE = 14;
10720
- var findCol = (used, row, startCol, colspan, rowspan) => {
10995
+ var findCol = (ps, row, startCol, colspan, rowspan) => {
10721
10996
  let col = startCol;
10722
- for (; ; ) {
10723
- let conflict = false;
10724
- for (let c = col; c < col + colspan && !conflict; c++) {
10725
- for (let r = row; r < row + rowspan && !conflict; r++) {
10726
- if (used.has(`${c},${r}`)) {
10727
- conflict = true;
10728
- col = c + 1;
10729
- }
10730
- }
10997
+ let notFound = true;
10998
+ const end = colspan - 1;
10999
+ while (notFound) {
11000
+ const lastc = col + end;
11001
+ let c;
11002
+ for (c = lastc; c >= col; c--) {
11003
+ if (ps.isInPS(c, row)) break;
11004
+ }
11005
+ if (c >= col) col = c + 1;
11006
+ else notFound = false;
11007
+ }
11008
+ for (let j = col; j < col + colspan; j++) {
11009
+ for (let i = row; i < row + rowspan; i++) {
11010
+ ps.addPS(j, i);
10731
11011
  }
10732
- if (!conflict) break;
10733
11012
  }
10734
- for (let c = col; c < col + colspan; c++)
10735
- for (let r = row; r < row + rowspan; r++)
10736
- used.add(`${c},${r}`);
10737
11013
  return col;
10738
11014
  };
10739
11015
  var DEFAULT_SIZE_ENV = { fontsize: DEFAULT_FONTSIZE, fontname: "" };
@@ -10812,29 +11088,32 @@ var sizeTextContent = (texts, measurer, env = DEFAULT_SIZE_ENV) => {
10812
11088
  if (lines.length === 1) h = lines[0].items.length > 0 ? lines[0].mxysize : DEFAULT_FONTSIZE;
10813
11089
  return { w, h };
10814
11090
  };
11091
+ var missingImages = /* @__PURE__ */ new WeakSet();
10815
11092
  var sizeHtmlImg = (img, imageSizer) => {
10816
- if (img.width !== void 0 && img.height !== void 0) return;
11093
+ if (img.width !== void 0 && img.height !== void 0) return missingImages.has(img) ? 1 : 0;
10817
11094
  const sz = imageSizer !== void 0 ? imageSizer(img.src) : findImageSize(img.src);
10818
11095
  if (sz !== null) {
10819
11096
  img.width = sz.w;
10820
11097
  img.height = sz.h;
10821
- return;
11098
+ return 0;
10822
11099
  }
10823
11100
  console.error(`Error: No or improper image file="${img.src}"`);
10824
11101
  img.width = 0;
10825
11102
  img.height = 0;
11103
+ missingImages.add(img);
11104
+ return 1;
10826
11105
  };
10827
11106
  var sizeContentItem = (item, measurer, env) => {
10828
- if (item.kind === "text") return sizeTextContent([item], measurer, env);
11107
+ if (item.kind === "text") return { ...sizeTextContent([item], measurer, env), rv: 0 };
10829
11108
  if (item.kind === "table") {
10830
- sizeTableInner(item, measurer, env);
10831
- return item.dimen !== void 0 ? item.dimen : { w: 0, h: 0 };
11109
+ const rv = sizeTableInner(item, measurer, env);
11110
+ return { ...item.dimen !== void 0 ? item.dimen : { w: 0, h: 0 }, rv };
10832
11111
  }
10833
11112
  if (item.kind === "image") {
10834
- sizeHtmlImg(item, env.imageSizer);
10835
- return { w: item.width ?? 0, h: item.height ?? 0 };
11113
+ const rv = sizeHtmlImg(item, env.imageSizer);
11114
+ return { w: item.width ?? 0, h: item.height ?? 0, rv };
10836
11115
  }
10837
- return { w: 0, h: 0 };
11116
+ return { w: 0, h: 0, rv: 0 };
10838
11117
  };
10839
11118
  var cellPad = (cell2, tbl) => cell2.cellpadding !== void 0 ? cell2.cellpadding : tbl.cellpadding !== void 0 ? tbl.cellpadding : DEFAULT_CELLPADDING;
10840
11119
  var cellBorder = (cell2, tbl) => {
@@ -10844,27 +11123,60 @@ var cellBorder = (cell2, tbl) => {
10844
11123
  if (tbl.border !== void 0) return tbl.border;
10845
11124
  return DEFAULT_BORDER;
10846
11125
  };
11126
+ function inheritTablePenColor(tbl, parentColor) {
11127
+ if (tbl.color === void 0) tbl.color = parentColor;
11128
+ const tblColor = tbl.color;
11129
+ for (const row of tbl.rows) {
11130
+ for (const cell2 of row.cells) {
11131
+ if (cell2.color === void 0) cell2.color = tblColor;
11132
+ for (const item of cell2.content) {
11133
+ if (item.kind === "table") inheritTablePenColor(item, cell2.color);
11134
+ }
11135
+ }
11136
+ }
11137
+ }
11138
+ var WARN_CELL_TOO_SMALL = "Warning: cell size too small for content";
11139
+ var WARN_CELL_UNSPECIFIED = "Warning: fixed cell size with unspecified width or height";
11140
+ var WARN_TABLE_TOO_SMALL = "Warning: table size too small for content";
11141
+ var WARN_TABLE_UNSPECIFIED = "Warning: fixed table size with unspecified width or height";
11142
+ var sizeWarned = /* @__PURE__ */ new WeakSet();
11143
+ var fixedCellCheck = (cell2, sz, warn) => {
11144
+ if (!cell2.fixedsize) return { rv: 0, zero: false };
11145
+ if (cell2.width && cell2.height) {
11146
+ const small = (cell2.width < sz.w || cell2.height < sz.h) && cell2.content[0]?.kind !== "image";
11147
+ if (small && warn) console.warn(WARN_CELL_TOO_SMALL);
11148
+ return { rv: small ? 1 : 0, zero: true };
11149
+ }
11150
+ if (warn) console.warn(WARN_CELL_UNSPECIFIED);
11151
+ return { rv: 1, zero: false };
11152
+ };
10847
11153
  var getCellSize = (cell2, tbl, measurer, env) => {
10848
11154
  const margin = 2 * (cellPad(cell2, tbl) + cellBorder(cell2, tbl));
10849
- let cw = 0, ch = 0;
11155
+ let cw = 0, ch = 0, rv = 0;
10850
11156
  for (const item of cell2.content) {
10851
11157
  const s = sizeContentItem(item, measurer, env);
10852
11158
  cw = Math.max(cw, s.w);
10853
11159
  ch += s.h;
11160
+ rv |= s.rv;
10854
11161
  }
10855
11162
  let szx = cw + margin;
10856
11163
  let szy = ch + margin;
10857
- if (cell2.fixedsize && cell2.width && cell2.height) {
11164
+ const warn = !sizeWarned.has(cell2);
11165
+ sizeWarned.add(cell2);
11166
+ const fixed = fixedCellCheck(cell2, { w: szx, h: szy }, warn);
11167
+ rv |= fixed.rv;
11168
+ if (fixed.zero) {
10858
11169
  szx = 0;
10859
11170
  szy = 0;
10860
11171
  }
10861
11172
  const bw = cell2.width !== void 0 ? cell2.width : 0;
10862
11173
  const bh = cell2.height !== void 0 ? cell2.height : 0;
10863
- return { w: Math.max(szx, bw), h: Math.max(szy, bh) };
11174
+ return { w: Math.max(szx, bw), h: Math.max(szy, bh), rv };
10864
11175
  };
10865
11176
  var buildLayouts = (tbl, measurer, env) => {
10866
11177
  const entries = [];
10867
- const used = /* @__PURE__ */ new Set();
11178
+ let rv = 0;
11179
+ const ps = new PointSet();
10868
11180
  let rowIdx = 0;
10869
11181
  for (const row of tbl.rows) {
10870
11182
  let col = 0;
@@ -10874,14 +11186,15 @@ var buildLayouts = (tbl, measurer, env) => {
10874
11186
  }
10875
11187
  const colspan = c.colspan !== void 0 ? c.colspan : 1;
10876
11188
  const rowspan = c.rowspan !== void 0 ? c.rowspan : 1;
10877
- const { w, h } = getCellSize(c, tbl, measurer, env);
10878
- col = findCol(used, rowIdx, col, colspan, rowspan);
11189
+ const { w, h, rv: cellRv } = getCellSize(c, tbl, measurer, env);
11190
+ rv |= cellRv;
11191
+ col = findCol(ps, rowIdx, col, colspan, rowspan);
10879
11192
  entries.push({ cell: c, row: rowIdx, col, colspan, rowspan, w, h });
10880
11193
  col += colspan;
10881
11194
  }
10882
11195
  rowIdx++;
10883
11196
  }
10884
- return entries;
11197
+ return { entries, rv };
10885
11198
  };
10886
11199
  var setColWidths = (entries, ncols, spacing) => {
10887
11200
  const widths = new Array(ncols + 1).fill(0);
@@ -10934,21 +11247,34 @@ var sumTableDims = (a) => {
10934
11247
  var layoutHtmlTable = (tbl, measurer, env = DEFAULT_SIZE_ENV) => {
10935
11248
  const spacing = getSpacing(tbl);
10936
11249
  const border = getBorder(tbl);
10937
- const entries = buildLayouts(tbl, measurer, env);
11250
+ const { entries, rv } = buildLayouts(tbl, measurer, env);
10938
11251
  if (entries.length === 0) {
10939
- return { entries, widths: [], heights: [], spacing, border };
11252
+ return { entries, widths: [], heights: [], spacing, border, rv };
10940
11253
  }
10941
11254
  const ncols = Math.max(...entries.map((e) => e.col + e.colspan));
10942
11255
  const nrows = Math.max(...entries.map((e) => e.row + e.rowspan));
10943
11256
  const widths = setColWidths(entries, ncols, spacing);
10944
11257
  const heights = setRowHeights(entries, nrows, spacing);
10945
- return { entries, widths, heights, spacing, border };
11258
+ return { entries, widths, heights, spacing, border, rv };
11259
+ };
11260
+ var warnFixedTable = (tbl, dims) => {
11261
+ const warn = !sizeWarned.has(tbl);
11262
+ sizeWarned.add(tbl);
11263
+ if (tbl.fixedsize !== true) return 0;
11264
+ if (tbl.width && tbl.height) {
11265
+ if (tbl.width >= dims.wd && tbl.height >= dims.ht) return 0;
11266
+ if (warn) console.warn(WARN_TABLE_TOO_SMALL);
11267
+ return 1;
11268
+ }
11269
+ if (warn) console.warn(WARN_TABLE_UNSPECIFIED);
11270
+ return 1;
10946
11271
  };
10947
11272
  var sizeTableInner = (tbl, measurer, env) => {
10948
- const { entries, widths, heights, spacing, border } = layoutHtmlTable(tbl, measurer, env);
11273
+ const { entries, widths, heights, spacing, border, rv } = layoutHtmlTable(tbl, measurer, env);
10949
11274
  if (entries.length === 0) {
11275
+ const fixed2 = warnFixedTable(tbl, sumTableDims({ widths, heights, ncols: 0, nrows: 0, spacing, border }));
10950
11276
  tbl.dimen = { w: 0, h: 0 };
10951
- return;
11277
+ return rv | fixed2;
10952
11278
  }
10953
11279
  const ncols = Math.max(...entries.map((e) => e.col + e.colspan));
10954
11280
  const nrows = Math.max(...entries.map((e) => e.row + e.rowspan));
@@ -10956,19 +11282,22 @@ var sizeTableInner = (tbl, measurer, env) => {
10956
11282
  const { wd, ht } = sumTableDims({ widths, heights, ncols, nrows, spacing, border });
10957
11283
  const baseWd = tbl.width !== void 0 ? tbl.width : 0;
10958
11284
  const baseHt = tbl.height !== void 0 ? tbl.height : 0;
11285
+ const fixed = warnFixedTable(tbl, { wd, ht });
10959
11286
  const zeroContent = isTblFixed(tbl) && baseWd !== 0 && baseHt !== 0;
10960
11287
  tbl.dimen = {
10961
11288
  w: Math.max(zeroContent ? 0 : wd, baseWd),
10962
11289
  h: Math.max(zeroContent ? 0 : ht, baseHt)
10963
11290
  };
11291
+ return rv | fixed;
10964
11292
  };
10965
11293
  var sizeHtmlLabel = (label, measurer, env = DEFAULT_SIZE_ENV) => {
10966
11294
  if (label.kind === "table") {
10967
- sizeTableInner(label.table, measurer, env);
11295
+ const rv = sizeTableInner(label.table, measurer, env);
10968
11296
  label.dimen = label.table.dimen;
10969
- } else {
10970
- label.dimen = sizeTextContent(label.texts, measurer, env);
11297
+ return rv;
10971
11298
  }
11299
+ label.dimen = sizeTextContent(label.texts, measurer, env);
11300
+ return 0;
10972
11301
  };
10973
11302
 
10974
11303
  // src/common/splines-constants.ts
@@ -16538,28 +16867,18 @@ function posHtmlLabel(label, finfo, measurer) {
16538
16867
  if (label.kind === "table") return posHtmlTable(label.table, finfo, measurer);
16539
16868
  return posTextLabel(label, finfo, measurer);
16540
16869
  }
16541
- function inheritTablePenColor(tbl, parentColor) {
16542
- if (tbl.color === void 0) tbl.color = parentColor;
16543
- const tblColor = tbl.color;
16544
- for (const row of tbl.rows) {
16545
- for (const cell2 of row.cells) {
16546
- if (cell2.color === void 0) cell2.color = tblColor;
16547
- for (const item of cell2.content) {
16548
- if (item.kind === "table") inheritTablePenColor(item, cell2.color);
16549
- }
16550
- }
16551
- }
16552
- }
16553
- function makeHtmlLabel(content, finfo, measurer) {
16870
+ var HTML_PARSE_FATAL = 3;
16871
+ function makeHtmlLabelRv(content, finfo, measurer) {
16554
16872
  const { fontname, fontsize, fontcolor, imageSizer, pencolor } = finfo;
16555
16873
  let lbl;
16556
16874
  try {
16557
16875
  lbl = parseHtmlLabel(content);
16558
- } catch {
16559
- return makeLabel("", fontname, fontsize, fontcolor, measurer);
16876
+ } catch (e) {
16877
+ if (e instanceof HtmlLexError) for (const line of e.diagnostics) console.error(line);
16878
+ return { label: makeLabel("", fontname, fontsize, fontcolor, measurer), rv: HTML_PARSE_FATAL };
16560
16879
  }
16561
16880
  if (lbl.kind === "table") inheritTablePenColor(lbl.table, pencolor);
16562
- sizeHtmlLabel(lbl, measurer, {
16881
+ const rv = sizeHtmlLabel(lbl, measurer, {
16563
16882
  fontsize,
16564
16883
  fontname,
16565
16884
  imageSizer
@@ -16567,7 +16886,7 @@ function makeHtmlLabel(content, finfo, measurer) {
16567
16886
  const placed = posHtmlLabel(lbl, { fontname, fontsize, fontcolor }, measurer);
16568
16887
  const dim = lbl.dimen ?? { w: 0, h: 0 };
16569
16888
  const text = lbl.kind === "table" ? "<TABLE>" : content;
16570
- return htmlTextLabel(text, { fontname, fontsize, fontcolor }, placed, dim);
16889
+ return { label: htmlTextLabel(text, { fontname, fontsize, fontcolor }, placed, dim), rv };
16571
16890
  }
16572
16891
  function htmlTextLabel(text, finfo, placed, dim) {
16573
16892
  return {
@@ -16686,6 +17005,14 @@ function getPenColor(obj) {
16686
17005
  function rootOf(obj) {
16687
17006
  return obj instanceof Edge ? obj.tail.root : obj.root;
16688
17007
  }
17008
+ function inLabelOf(obj) {
17009
+ if (obj instanceof Edge) {
17010
+ const sep = obj.head.root.kind.endsWith("undirected") ? "--" : "->";
17011
+ return `in label of edge ${obj.tail.name} ${sep} ${obj.head.name}`;
17012
+ }
17013
+ if (obj instanceof Graph) return `in label of graph ${obj.name}`;
17014
+ return `in label of node ${obj.name}`;
17015
+ }
16689
17016
  function buildSpan(text, font, measured) {
16690
17017
  return {
16691
17018
  str: text,
@@ -16753,7 +17080,9 @@ function makeLabel(text, fontname, fontsize, fontcolor, measurer) {
16753
17080
  function makeAnyLabel(content, isHtml, font, measurer, obj) {
16754
17081
  if (isHtml && content !== "") {
16755
17082
  const imageSizer = obj !== void 0 ? rootOf(obj).info.gvc?.imageSizer : void 0;
16756
- return makeHtmlLabel(content, { ...font, pencolor: getPenColor(obj), imageSizer }, measurer);
17083
+ const { label, rv } = makeHtmlLabelRv(content, { ...font, pencolor: getPenColor(obj), imageSizer }, measurer);
17084
+ if (rv !== 0 && obj !== void 0) console.error(inLabelOf(obj));
17085
+ return label;
16757
17086
  }
16758
17087
  const subst2 = obj !== void 0 ? substObj(content, obj, false) : content;
16759
17088
  const text = htmlEntityUTF8(subst2);
@@ -29053,18 +29382,22 @@ function dotSameports(g) {
29053
29382
  function dist2(p, q) {
29054
29383
  const dx = p.x - q.x;
29055
29384
  const dy = p.y - q.y;
29056
- return dx * dx + dy * dy;
29385
+ return fma(dx, dx, dy * dy);
29057
29386
  }
29058
29387
  function distPt(p, q) {
29059
29388
  return Math.sqrt(dist2(p, q));
29060
29389
  }
29061
- function evalBez4(pts, t) {
29390
+ function bezier(v, t) {
29062
29391
  const u = 1 - t;
29063
- const [p0, p1, p2, p3] = pts;
29064
- return {
29065
- x: u * u * u * p0.x + 3 * u * u * t * p1.x + 3 * u * t * t * p2.x + t * t * t * p3.x,
29066
- y: u * u * u * p0.y + 3 * u * u * t * p1.y + 3 * u * t * t * p2.y + t * t * t * p3.y
29067
- };
29392
+ const x = [v[0].x, v[1].x, v[2].x, v[3].x];
29393
+ const y = [v[0].y, v[1].y, v[2].y, v[3].y];
29394
+ for (let i = 1; i <= 3; i++) {
29395
+ for (let j = 0; j <= 3 - i; j++) {
29396
+ x[j] = fma(x[j], u, t * x[j + 1]);
29397
+ y[j] = fma(y[j], u, t * y[j + 1]);
29398
+ }
29399
+ }
29400
+ return { x: x[0], y: y[0] };
29068
29401
  }
29069
29402
  function dotneatoClosest(spl, pt) {
29070
29403
  let besti = 0;
@@ -29094,7 +29427,7 @@ function dotneatoClosest(spl, pt) {
29094
29427
  let pt2;
29095
29428
  for (; ; ) {
29096
29429
  const t = (lo + hi) / 2;
29097
- pt2 = evalBez4(c, t);
29430
+ pt2 = bezier(c, t);
29098
29431
  if (Math.abs(dlo2 - dhi2) < 1 || Math.abs(hi - lo) < 1e-5) break;
29099
29432
  if (dlo2 < dhi2) {
29100
29433
  hi = t;
@@ -30006,9 +30339,19 @@ var ShortestHelper = class _ShortestHelper {
30006
30339
  _ShortestHelper.connectAll(tris);
30007
30340
  const ftrii = _ShortestHelper.findTri(tris, eps[0]);
30008
30341
  const ltrii = _ShortestHelper.findTri(tris, eps[1]);
30009
- if (ftrii === NO_TRI || ltrii === NO_TRI) return null;
30342
+ if (ftrii === NO_TRI) {
30343
+ console.warn("source point not in any triangle");
30344
+ return null;
30345
+ }
30346
+ if (ltrii === NO_TRI) {
30347
+ console.warn("destination point not in any triangle");
30348
+ return null;
30349
+ }
30010
30350
  if (ftrii === ltrii) return [eps[0], eps[1]];
30011
- if (!_ShortestHelper.markPath(tris, ftrii, ltrii)) return [eps[0], eps[1]];
30351
+ if (!_ShortestHelper.markPath(tris, ftrii, ltrii)) {
30352
+ console.warn("cannot find triangle path");
30353
+ return [eps[0], eps[1]];
30354
+ }
30012
30355
  const epnls = [{ pp: eps[0], link: null }, { pp: eps[1], link: null }];
30013
30356
  const dq = _ShortestHelper.initDeque(pnlps.length);
30014
30357
  _ShortestHelper.add2dq(dq, DQ_FRONT, epnls[0]);
@@ -31990,7 +32333,7 @@ function nodeCover(n) {
31990
32333
  for (let i = 0; i < NODECARD; i++) {
31991
32334
  if (n.branch[i].child !== null) {
31992
32335
  if (flag) {
31993
- r = n.branch[i].rect;
32336
+ r = { boundary: [...n.branch[i].rect.boundary] };
31994
32337
  flag = false;
31995
32338
  } else {
31996
32339
  r = combineRect(r, n.branch[i].rect);
@@ -33943,7 +34286,10 @@ function makeFlatLabeledEdge(g, e) {
33943
34286
  info: { ...e.info, tail_port: e.info.head_port, head_port: e.info.tail_port }
33944
34287
  };
33945
34288
  const ps = routeFlatLabeledChannel(g, sample, ln, et);
33946
- if (ps === null || ps.length === 0) return true;
34289
+ if (ps === null || ps.length === 0) {
34290
+ e.info.lost = true;
34291
+ return true;
34292
+ }
33947
34293
  if (!tailIsLeft) ps.reverse();
33948
34294
  clipAndInstall(e, e.head, ps, ps.length, { ...buildDotSinfo(), ignoreSwap: true });
33949
34295
  return true;
@@ -34256,8 +34602,8 @@ function nodeInsideFn2(box) {
34256
34602
  const pw = box.penwidth ?? DEFAULT_NODEPENWIDTH;
34257
34603
  return box.isEllipse ? makeEllipseInsideFn(halfW, halfH, pw) : makeBoxInsideFn(halfW, halfH, pw);
34258
34604
  }
34259
- function clipToNodes(bezier, tailBox, headBox, penwidth = DEFAULT_NODEPENWIDTH, clip = CLIP_BOTH) {
34260
- const step1 = clip.tail ? bezierClipNode(bezier, tailBox.center.x, tailBox.center.y, nodeInsideFn2(tailBox), true) : bezier;
34605
+ function clipToNodes(bezier2, tailBox, headBox, penwidth = DEFAULT_NODEPENWIDTH, clip = CLIP_BOTH) {
34606
+ const step1 = clip.tail ? bezierClipNode(bezier2, tailBox.center.x, tailBox.center.y, nodeInsideFn2(tailBox), true) : bezier2;
34261
34607
  const step2 = clip.head ? bezierClipNode(step1, headBox.center.x, headBox.center.y, nodeInsideFn2(headBox), false) : step1;
34262
34608
  const arrowTip = step2[3];
34263
34609
  const elen = normalArrowLen(penwidth);
@@ -34271,9 +34617,9 @@ function clipToNodes(bezier, tailBox, headBox, penwidth = DEFAULT_NODEPENWIDTH,
34271
34617
  function routeWithRank(tailBox, headBox, rank3, penwidth = DEFAULT_NODEPENWIDTH, port) {
34272
34618
  const corridor = port ? { tailP: port.tailP, headP: port.headP } : void 0;
34273
34619
  const { startPt, endPt, boxes } = buildRankCorridor(tailBox, headBox, rank3, corridor);
34274
- const bezier = computeSpline(boxes, startPt, endPt);
34620
+ const bezier2 = computeSpline(boxes, startPt, endPt);
34275
34621
  const clip = port ? { tail: port.clipTail, head: port.clipHead } : CLIP_BOTH;
34276
- const { clipped, arrowTip, arrowDir } = clipToNodes(bezier, tailBox, headBox, penwidth, clip);
34622
+ const { clipped, arrowTip, arrowDir } = clipToNodes(bezier2, tailBox, headBox, penwidth, clip);
34277
34623
  return { bezierPts: clipped, arrowTip, arrowDir };
34278
34624
  }
34279
34625
  function routeSimple(tailBox, headBox, routeBezierFn) {
@@ -36866,24 +37212,27 @@ function buildNodes(nodeArr) {
36866
37212
  ysize: n.info.ht
36867
37213
  }));
36868
37214
  }
37215
+ function isConcentrateDup(ps, e) {
37216
+ const ti = e.tail.id;
37217
+ const hi = e.head.id;
37218
+ const lo = ti <= hi ? ti : hi;
37219
+ const up = ti <= hi ? hi : ti;
37220
+ if (ps.isInPS(lo, up)) return true;
37221
+ ps.addPS(lo, up);
37222
+ return false;
37223
+ }
36869
37224
  function buildEdges(g, nodeArr, orthoNodes) {
36870
37225
  const concentrate = dotRoot(g).info.concentrate ?? false;
36871
37226
  const nodeIndex = /* @__PURE__ */ new Map();
36872
37227
  nodeArr.forEach((n, i) => nodeIndex.set(n, i));
36873
37228
  const outIndex = buildOutEdgeIndex(g);
36874
- const seen = /* @__PURE__ */ new Set();
37229
+ const ps = concentrate ? new PointSet() : null;
36875
37230
  const result = [];
36876
37231
  for (const n of nodeArr) {
36877
37232
  const oes = outIndex.get(n);
36878
37233
  if (oes === void 0) continue;
36879
37234
  for (const e of oes) {
36880
- if (concentrate) {
36881
- const ti = e.tail.id;
36882
- const hi = e.head.id;
36883
- const key = ti <= hi ? `${ti},${hi}` : `${hi},${ti}`;
36884
- if (seen.has(key)) continue;
36885
- seen.add(key);
36886
- }
37235
+ if (ps !== null && isConcentrateDup(ps, e)) continue;
36887
37236
  result.push({
36888
37237
  tail: orthoNodes[nodeIndex.get(e.tail)],
36889
37238
  head: orthoNodes[nodeIndex.get(e.head)],
@@ -37971,15 +38320,6 @@ function spiralSearch(info, ctx, wide) {
37971
38320
 
37972
38321
  // src/layout/pack/poly-pack.ts
37973
38322
  var C2 = 100;
37974
- function psKey(x, y) {
37975
- return `${cround(x)},${cround(y)}`;
37976
- }
37977
- function inPS(ps, p) {
37978
- return ps.has(psKey(p.x, p.y));
37979
- }
37980
- function insertPS(ps, p) {
37981
- ps.add(psKey(p.x, p.y));
37982
- }
37983
38323
  function computeStep(ng, bbs, margin) {
37984
38324
  let b = 0;
37985
38325
  let c = 0;
@@ -38026,14 +38366,14 @@ function genBox2(p) {
38026
38366
  function hasCollision(cells, dx, dy, ps) {
38027
38367
  for (let i = 0; i < cells.length; i++) {
38028
38368
  const cell2 = cells[i];
38029
- if (cell2 !== void 0 && inPS(ps, { x: cell2.x + dx, y: cell2.y + dy })) return true;
38369
+ if (cell2 !== void 0 && ps.inPS({ x: cell2.x + dx, y: cell2.y + dy })) return true;
38030
38370
  }
38031
38371
  return false;
38032
38372
  }
38033
38373
  function markCells(cells, dx, dy, ps) {
38034
38374
  for (let i = 0; i < cells.length; i++) {
38035
38375
  const cell2 = cells[i];
38036
- if (cell2 !== void 0) insertPS(ps, { x: cell2.x + dx, y: cell2.y + dy });
38376
+ if (cell2 !== void 0) ps.addPS(cell2.x + dx, cell2.y + dy);
38037
38377
  }
38038
38378
  }
38039
38379
  function fits(x, y, info, ctx) {
@@ -38078,7 +38418,7 @@ function polyRects(ng, bbs, pinfo) {
38078
38418
  infos.push(genBox2({ bb, ssize: step, margin: pinfo.margin, idx: i }));
38079
38419
  }
38080
38420
  const sorted = gvQsort([...infos], cmpByGPerimeter);
38081
- const ctx = { ps: /* @__PURE__ */ new Set(), step, bbs };
38421
+ const ctx = { ps: new PointSet(), step, bbs };
38082
38422
  const places = new Array(ng).fill({ x: 0, y: 0 });
38083
38423
  for (let i = 0; i < sorted.length; i++) {
38084
38424
  const info = sorted[i];
@@ -38088,57 +38428,6 @@ function polyRects(ng, bbs, pinfo) {
38088
38428
  return places;
38089
38429
  }
38090
38430
 
38091
- // src/common/pointset.ts
38092
- function cmpPair(a, b) {
38093
- if (a.x > b.x) return 1;
38094
- if (a.x < b.x) return -1;
38095
- if (a.y > b.y) return 1;
38096
- if (a.y < b.y) return -1;
38097
- return 0;
38098
- }
38099
- var PointSet = class {
38100
- byX = /* @__PURE__ */ new Map();
38101
- n = 0;
38102
- /**
38103
- * Insert (x, y). A duplicate keeps the point inserted first, as `dtinsert`
38104
- * returns the existing object and the new one is freed.
38105
- * @see lib/common/pointset.c:addPS
38106
- */
38107
- addPS(x, y) {
38108
- let col = this.byX.get(x);
38109
- if (col === void 0) {
38110
- col = /* @__PURE__ */ new Map();
38111
- this.byX.set(x, col);
38112
- }
38113
- if (col.has(y)) return;
38114
- col.set(y, { x, y });
38115
- this.n++;
38116
- }
38117
- /** @see lib/common/pointset.c:inPS */
38118
- inPS(pt) {
38119
- return this.byX.get(pt.x)?.has(pt.y) ?? false;
38120
- }
38121
- /** @see lib/common/pointset.c:isInPS */
38122
- isInPS(x, y) {
38123
- return this.byX.get(x)?.has(y) ?? false;
38124
- }
38125
- /** @see lib/common/pointset.c:sizeOf */
38126
- sizeOf() {
38127
- return this.n;
38128
- }
38129
- /**
38130
- * The points in dictionary order (`dtflatten` walks the ordered set).
38131
- * @see lib/common/pointset.c:pointsOf
38132
- */
38133
- pointsOf() {
38134
- const pts = [];
38135
- for (const col of this.byX.values()) {
38136
- for (const p of col.values()) pts.push({ x: p.x, y: p.y });
38137
- }
38138
- return pts.sort(cmpPair);
38139
- }
38140
- };
38141
-
38142
38431
  // src/layout/pack/poly-place.ts
38143
38432
  var C_AVG = 100;
38144
38433
  function grid(x, s) {
@@ -41979,6 +42268,12 @@ function chainOf(e) {
41979
42268
  }
41980
42269
  return list;
41981
42270
  }
42271
+ function warnNoControlPoints(e) {
42272
+ console.warn(
42273
+ `Warning: Could not create control points for multiple spline for edge (${e.tail.name},${e.head.name})`
42274
+ );
42275
+ return 1;
42276
+ }
41982
42277
  function genroute(g, trip, t, e, doPolyline) {
41983
42278
  const eps = [
41984
42279
  { x: trip.ps[0].x, y: trip.ps[0].y },
@@ -41988,7 +42283,7 @@ function genroute(g, trip, t, e, doPolyline) {
41988
42283
  const mult = e.info.count ?? 1;
41989
42284
  const head = e.head;
41990
42285
  const pl = shortestPath({ ps: trip.ps }, eps);
41991
- if (pl === null) return 1;
42286
+ if (pl === null) return warnNoControlPoints(e);
41992
42287
  if (pl.length === 2) {
41993
42288
  const chain = chainOf(e);
41994
42289
  makeStraightEdges(g, chain, chain.length, doPolyline ? 1 : 0, MSINFO);
@@ -41996,7 +42291,7 @@ function genroute(g, trip, t, e, doPolyline) {
41996
42291
  }
41997
42292
  if (mult === 1 || concentrate) {
41998
42293
  const spl = routeMember(trip.ps, t, pl, doPolyline);
41999
- if (spl === null) return 1;
42294
+ if (spl === null) return warnNoControlPoints(e);
42000
42295
  finishEdge(e, spl, e.head !== head);
42001
42296
  return 0;
42002
42297
  }
@@ -42004,7 +42299,7 @@ function genroute(g, trip, t, e, doPolyline) {
42004
42299
  const cpts = [];
42005
42300
  for (let i = 0; i + 2 < pl.length; i++) {
42006
42301
  const cp = mkCtrlPts(t, mult + 1, pl[i], pl[i + 1], pl[i + 2], trip);
42007
- if (cp === null) return 1;
42302
+ if (cp === null) return warnNoControlPoints(e);
42008
42303
  cpts.push(cp);
42009
42304
  }
42010
42305
  let cur = e;
@@ -42019,9 +42314,9 @@ function genroute(g, trip, t, e, doPolyline) {
42019
42314
  poly[pn - j] = cpts[j - 1][i + 1];
42020
42315
  }
42021
42316
  const mmpl = shortestPath({ ps: poly }, eps);
42022
- if (mmpl === null) return 1;
42317
+ if (mmpl === null) return warnNoControlPoints(cur);
42023
42318
  const spl = routeMember(poly, pl.length - 1, mmpl, doPolyline);
42024
- if (spl === null) return 1;
42319
+ if (spl === null) return warnNoControlPoints(cur);
42025
42320
  finishEdge(cur, spl, cur.head !== head);
42026
42321
  cur = cur.info.to_virt;
42027
42322
  }
@@ -51245,22 +51540,22 @@ function freeBlocktree(bp) {
51245
51540
 
51246
51541
  // src/layout/circo/position-helpers.ts
51247
51542
  function rotatePoint(x, y, cosR, sinR) {
51248
- return [x * cosR - y * sinR, x * sinR + y * cosR];
51543
+ return [fma(x, cosR, -sinR * y), fma(x, sinR, cosR * y)];
51249
51544
  }
51250
51545
  function nodePsi(n) {
51251
51546
  return n.info.alg?.psi ?? 0;
51252
51547
  }
51253
51548
  function rotFromParentPos(sn, theta) {
51254
- let r = theta + Math.PI - sn.parentPos;
51549
+ let r = theta + (Math.PI - sn.parentPos);
51255
51550
  if (r < 0) r += 2 * Math.PI;
51256
51551
  return r;
51257
51552
  }
51258
51553
  function closestNode(sn, x, y, skip) {
51259
51554
  let best = skip;
51260
- let bestDist = Math.hypot(skip.info.pos[0] + x, skip.info.pos[1] + y);
51555
+ let bestDist = libmHypot(skip.info.pos[0] + x, skip.info.pos[1] + y);
51261
51556
  for (const dn of sn.subGraph.nodes) {
51262
51557
  if (dn.orig === skip) continue;
51263
- const d = Math.hypot(dn.orig.info.pos[0] + x, dn.orig.info.pos[1] + y);
51558
+ const d = libmHypot(dn.orig.info.pos[0] + x, dn.orig.info.pos[1] + y);
51264
51559
  if (d < bestDist) {
51265
51560
  bestDist = d;
51266
51561
  best = dn.orig;
@@ -51273,22 +51568,19 @@ function rotCoalesced(sn, x, y, nbr, theta) {
51273
51568
  const r = sn.radius - rho;
51274
51569
  const nx = nbr.info.pos[0];
51275
51570
  const ny = nbr.info.pos[1];
51276
- if (-r >= nx) {
51277
- const phi2 = Math.atan2(ny, nx);
51278
- return theta + Math.PI - phi2 - nodePsi(nbr);
51279
- }
51280
- const R2 = Math.hypot(x, y);
51571
+ const R2 = libmHypot(x, y);
51281
51572
  const phi = Math.atan2(ny, nx + r);
51282
51573
  const l = r - rho / Math.cos(phi);
51283
- return theta + Math.PI / 2 - phi - Math.asin(l / R2 * Math.cos(phi));
51574
+ return theta + (Math.PI / 2 - phi - Math.asin(l / R2 * Math.cos(phi)));
51284
51575
  }
51285
51576
  function rotGeneral(sn, x, y, nbr, theta) {
51286
51577
  if (closestNode(sn, x, y, nbr) === nbr) return 0;
51287
- if (isCoalesced(sn)) return rotCoalesced(sn, x, y, nbr, theta);
51578
+ const r = sn.radius - sn.rad0;
51579
+ if (isCoalesced(sn) && -r < nbr.info.pos[0]) return rotCoalesced(sn, x, y, nbr, theta);
51288
51580
  const phi = Math.atan2(nbr.info.pos[1], nbr.info.pos[0]);
51289
- let r = theta + Math.PI - phi - nodePsi(nbr);
51290
- if (r > 2 * Math.PI) r -= 2 * Math.PI;
51291
- return r;
51581
+ let t = theta + (Math.PI - phi - nodePsi(nbr));
51582
+ if (t > 2 * Math.PI) t -= 2 * Math.PI;
51583
+ return t;
51292
51584
  }
51293
51585
  function applyDelta(sn, x, y, rotate) {
51294
51586
  const cosR = Math.cos(rotate);
@@ -51331,7 +51623,7 @@ function getInfo(pi, stp, minDist) {
51331
51623
  }
51332
51624
  function setInfo(p0, p1, delta2) {
51333
51625
  const t = Math.max(
51334
- (p0.diameter * p1.minRadius + p1.diameter * p0.minRadius) / (2 * delta2 * p0.minRadius * p1.minRadius),
51626
+ fma(p0.diameter, p1.minRadius, p1.diameter * p0.minRadius) / (2 * delta2 * p0.minRadius * p1.minRadius),
51335
51627
  1
51336
51628
  );
51337
51629
  p0.scale = Math.max(p0.scale, t);
@@ -51341,7 +51633,13 @@ function placeChild(child, ca, ctx) {
51341
51633
  const dx = ctx.childRadius * Math.cos(ca);
51342
51634
  const dy = ctx.childRadius * Math.sin(ca);
51343
51635
  applyDelta(child, dx, dy, getRotation(child, dx, dy, ca));
51344
- return ctx.length === 1 ? ca + ctx.incAngle + ctx.mindistAngle : ca + ctx.incAngle + ctx.mindistAngle / 2;
51636
+ return nextChildAngle(ca, ctx.incAngle, ctx.mindistAngle, ctx.length);
51637
+ }
51638
+ function nextChildAngle(ca, inc2, mda, length) {
51639
+ return length === 1 ? ca + (inc2 + mda) : ca + (inc2 + mda / 2);
51640
+ }
51641
+ function leftoverArc(childRadius, diameter) {
51642
+ return fma(2 * Math.PI, childRadius, -diameter);
51345
51643
  }
51346
51644
  function advanceAngleL1(ca, inc2, total, cnt, stp) {
51347
51645
  const next = cnt === 0 ? 0 : total === 2 ? Math.PI : ca + inc2;
@@ -51351,7 +51649,7 @@ function advanceAngleL1(ca, inc2, total, cnt, stp) {
51351
51649
  }
51352
51650
  function advanceAngleLN(ca, inc2, mda, total, theta) {
51353
51651
  if (total === 1) return theta;
51354
- return ca + inc2 + mda / 2;
51652
+ return ca + (inc2 + mda / 2);
51355
51653
  }
51356
51654
  function maybeWritePsi(n, stp, ca, isMid) {
51357
51655
  if (isMid && n === stp.neighbor) {
@@ -51366,7 +51664,7 @@ function positionChildren(pi, stp, length, minDist) {
51366
51664
  let md = minDist;
51367
51665
  if (length === 1) {
51368
51666
  cr = Math.max(cr, pi.diameter / (2 * Math.PI));
51369
- const d = 2 * Math.PI * cr - pi.diameter;
51667
+ const d = leftoverArc(cr, pi.diameter);
51370
51668
  if (d > 0) md += d / pi.childCount;
51371
51669
  }
51372
51670
  stp.subtreeR = Math.max(stp.subtreeR, cr + pi.maxRadius);