@knowvah/dot-engine 2.6.1 → 2.6.3

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 (48) 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/pointset.d.ts +35 -0
  13. package/dist/common/pointset.d.ts.map +1 -0
  14. package/dist/common/spline-midpoint.d.ts +8 -0
  15. package/dist/common/spline-midpoint.d.ts.map +1 -1
  16. package/dist/index.js +639 -315
  17. package/dist/index.js.map +4 -4
  18. package/dist/label/node.d.ts +7 -1
  19. package/dist/label/node.d.ts.map +1 -1
  20. package/dist/layout/circo/position-helpers.d.ts +15 -3
  21. package/dist/layout/circo/position-helpers.d.ts.map +1 -1
  22. package/dist/layout/circo/position.d.ts +17 -1
  23. package/dist/layout/circo/position.d.ts.map +1 -1
  24. package/dist/layout/dot/ortho-adapter.d.ts.map +1 -1
  25. package/dist/layout/neato/multispline.d.ts.map +1 -1
  26. package/dist/layout/pack/poly-pack.d.ts +2 -18
  27. package/dist/layout/pack/poly-pack.d.ts.map +1 -1
  28. package/dist/layout/pack/poly-place.d.ts.map +1 -1
  29. package/dist/pathplan/shortest.d.ts.map +1 -1
  30. package/dist/render.js +792 -468
  31. package/dist/render.js.map +4 -4
  32. package/package.json +2 -2
  33. package/src/common/htmltable-lex.ts +265 -5
  34. package/src/common/htmltable-parse-attrs.ts +166 -0
  35. package/src/common/htmltable-parse.ts +262 -242
  36. package/src/common/htmltable-pos.ts +37 -38
  37. package/src/common/htmltable.ts +139 -45
  38. package/src/common/make-label.ts +19 -3
  39. package/src/common/pointset.ts +73 -0
  40. package/src/common/spline-midpoint.ts +30 -9
  41. package/src/label/node.ts +8 -2
  42. package/src/layout/circo/position-helpers.ts +28 -17
  43. package/src/layout/circo/position.ts +28 -7
  44. package/src/layout/dot/ortho-adapter.ts +20 -9
  45. package/src/layout/neato/multispline.ts +17 -5
  46. package/src/layout/pack/poly-pack.ts +5 -31
  47. package/src/layout/pack/poly-place.ts +42 -47
  48. package/src/pathplan/shortest.ts +15 -2
package/dist/render.js CHANGED
@@ -5854,16 +5854,152 @@ function parseOpenToken(inner) {
5854
5854
  checkTag(tag);
5855
5855
  return { type: "open", tag: normalizeTag(tag), attrs: parseAttrs(inner.slice(sp + 1)) };
5856
5856
  }
5857
- function tokenize(src) {
5857
+ var XML_ERROR_UNDEFINED_ENTITY = "undefined entity";
5858
+ var XML_ERROR_TAG_MISMATCH = "mismatched tag";
5859
+ var XML_ERROR_INVALID_TOKEN = "not well-formed (invalid token)";
5860
+ var XML_ERROR_DUPLICATE_ATTRIBUTE = "duplicate attribute";
5861
+ var XML_PREDEFINED_ENTITIES = /* @__PURE__ */ new Set(["lt", "gt", "amp", "quot", "apos"]);
5862
+ var XML_NAME = /^[A-Za-z_:][\w.:-]*$/;
5863
+ var HtmlLexError = class extends HtmlParseError {
5864
+ diagnostics;
5865
+ constructor(tag, message, diagnostics) {
5866
+ super(tag);
5867
+ this.message = message;
5868
+ this.diagnostics = diagnostics;
5869
+ }
5870
+ };
5871
+ function htmlErrorInLine(message, line, context) {
5872
+ const text = `${message} in line ${line} `;
5873
+ return new HtmlLexError(message, text, [`Error: ${text}`, `... ${context} ...`]);
5874
+ }
5875
+ var chunkInfo = /* @__PURE__ */ new WeakMap();
5876
+ function lineAt(src, pos) {
5877
+ let line = 1;
5878
+ for (let k = 0; k < pos; k++) {
5879
+ if (src[k] === "\n" || src[k] === "\r" && src[k + 1] !== "\n") line++;
5880
+ }
5881
+ return line;
5882
+ }
5883
+ function chunkLine(info) {
5884
+ if (info.src[info.pos] === "<") return lineAt(info.src, info.pos);
5885
+ const last = info.src.lastIndexOf("\n", info.end - 1);
5886
+ if (last < info.pos) return lineAt(info.src, info.pos);
5887
+ return lineAt(info.src, last === info.end - 1 ? last : last + 1);
5888
+ }
5889
+ function htmlErrorAtToken(message, tok) {
5890
+ const info = tok === void 0 ? void 0 : chunkInfo.get(tok);
5891
+ if (info === void 0) return void 0;
5892
+ return htmlErrorInLine(message, chunkLine(info), info.context);
5893
+ }
5894
+ function chunkEnd(src, i) {
5895
+ const j = src[i] === "<" ? src.indexOf(">", i + 1) : src.indexOf("<", i);
5896
+ if (j < 0) return src.length;
5897
+ return src[i] === "<" ? j + 1 : j;
5898
+ }
5899
+ function findUndefinedEntity(text) {
5900
+ for (let t = text.indexOf("&"); t >= 0; t = text.indexOf("&", t + 1)) {
5901
+ if (text[t + 1] === "#") continue;
5902
+ const semi = text.indexOf(";", t + 1);
5903
+ if (semi < 0) continue;
5904
+ const key = text.slice(t + 1, semi);
5905
+ if (XML_NAME.test(key) && !Object.hasOwn(HTML_ENTITIES, key) && !XML_PREDEFINED_ENTITIES.has(key)) {
5906
+ return t;
5907
+ }
5908
+ }
5909
+ return -1;
5910
+ }
5911
+ function parseTagChunk(chunk) {
5912
+ if (!chunk.endsWith(">")) return void 0;
5913
+ const body = chunk.slice(1, -1);
5914
+ if (body.startsWith("!")) return void 0;
5915
+ const close = body.startsWith("/");
5916
+ const name = /^[^\s/>]*/.exec(close ? body.slice(1) : body)?.[0] ?? "";
5917
+ return { close, selfClose: !close && body.endsWith("/"), name };
5918
+ }
5919
+ var ATTR_NAME = /^[A-Za-z_:][\w.:-]*/;
5920
+ var isSpace = (c) => c !== void 0 && /\s/.test(c);
5921
+ var invalidToken = (offset) => ({ message: XML_ERROR_INVALID_TOKEN, offset });
5922
+ function skipSpace(chunk, i) {
5923
+ let j = i;
5924
+ while (isSpace(chunk[j])) j++;
5925
+ return j;
5926
+ }
5927
+ function readAttrPair(chunk, i) {
5928
+ const name = ATTR_NAME.exec(chunk.slice(i))?.[0];
5929
+ if (name === void 0) return invalidToken(i);
5930
+ let j = skipSpace(chunk, i + name.length);
5931
+ if (chunk[j] !== "=") return invalidToken(j);
5932
+ j = skipSpace(chunk, j + 1);
5933
+ const q = chunk[j];
5934
+ if (q !== '"' && q !== "'") return invalidToken(j);
5935
+ const end = chunk.indexOf(q, j + 1);
5936
+ return end < 0 ? void 0 : { name, next: end + 1 };
5937
+ }
5938
+ function findAttrError(chunk, from) {
5939
+ const seen = /* @__PURE__ */ new Set();
5940
+ let dup;
5941
+ let i = skipSpace(chunk, from);
5942
+ while (chunk[i] !== ">" && !(chunk[i] === "/" && chunk[i + 1] === ">")) {
5943
+ const pair = readAttrPair(chunk, i);
5944
+ if (pair === void 0) return void 0;
5945
+ if ("message" in pair) return pair;
5946
+ if (seen.has(pair.name) && dup === void 0) dup = { message: XML_ERROR_DUPLICATE_ATTRIBUTE, offset: i };
5947
+ seen.add(pair.name);
5948
+ i = skipSpace(chunk, pair.next);
5949
+ }
5950
+ return dup;
5951
+ }
5952
+ function checkTagChunk(src, start, st) {
5953
+ const tag = parseTagChunk(st.curr);
5954
+ if (tag === void 0) return;
5955
+ if (tag.close) {
5956
+ if (st.stack.length > 0 && st.stack.pop() !== tag.name) {
5957
+ throw htmlErrorInLine(XML_ERROR_TAG_MISMATCH, lineAt(src, start), st.prev + st.curr);
5958
+ }
5959
+ } else {
5960
+ const bad = findAttrError(st.curr, 1 + tag.name.length);
5961
+ if (bad !== void 0) {
5962
+ throw htmlErrorInLine(bad.message, lineAt(src, start + bad.offset), st.prev + st.curr);
5963
+ }
5964
+ if (!tag.selfClose) st.stack.push(tag.name);
5965
+ }
5966
+ if (KNOWN_TAGS.has(tag.name.toUpperCase())) return;
5967
+ const text = `Unknown HTML element <${tag.name}> on line ${lineAt(src, start)} `;
5968
+ throw new HtmlLexError(tag.name.toUpperCase(), text, [`Error: ${text}`]);
5969
+ }
5970
+ function checkChunk(src, start, st) {
5971
+ if (st.curr.startsWith("<")) {
5972
+ checkTagChunk(src, start, st);
5973
+ return;
5974
+ }
5975
+ const off = findUndefinedEntity(st.curr);
5976
+ if (off >= 0) {
5977
+ throw htmlErrorInLine(XML_ERROR_UNDEFINED_ENTITY, lineAt(src, start + off), st.prev + st.curr);
5978
+ }
5979
+ }
5980
+ function tokenizeDeferred(src) {
5858
5981
  const tokens = [];
5982
+ const st = { stack: [], prev: "", curr: "" };
5859
5983
  let i = 0;
5860
- while (i < src.length) {
5861
- i = src[i] === "<" ? scanTag(src, i, tokens) : scanText(src, i, tokens);
5984
+ try {
5985
+ while (i < src.length) {
5986
+ const first = tokens.length;
5987
+ const start = i;
5988
+ st.prev = st.curr;
5989
+ st.curr = src.slice(i, chunkEnd(src, i));
5990
+ checkChunk(src, i, st);
5991
+ i = src[i] === "<" ? scanTag(src, i, tokens) : scanText(src, i, tokens);
5992
+ const info = { src, pos: start, end: i, context: st.prev + st.curr };
5993
+ for (let k = first; k < tokens.length; k++) chunkInfo.set(tokens[k], info);
5994
+ }
5995
+ } catch (e) {
5996
+ if (e instanceof HtmlParseError) return { tokens, error: e };
5997
+ throw e;
5862
5998
  }
5863
- return tokens;
5999
+ return { tokens };
5864
6000
  }
5865
6001
 
5866
- // src/common/htmltable-parse.ts
6002
+ // src/common/htmltable-parse-attrs.ts
5867
6003
  function anchorAttr(v) {
5868
6004
  return v === void 0 ? void 0 : htmlEntityUTF8(v);
5869
6005
  }
@@ -5886,6 +6022,81 @@ var num = (s) => {
5886
6022
  const n = parseInt(s, 10);
5887
6023
  return isNaN(n) ? void 0 : n;
5888
6024
  };
6025
+ var parseFixedSize = (a) => a["fixedsize"]?.toUpperCase() === "TRUE" ? true : void 0;
6026
+ var parseTitle = (a) => a["title"] !== void 0 ? a["title"] : a["tooltip"];
6027
+ var parseSides = (v) => {
6028
+ if (v === void 0) return void 0;
6029
+ let flags = 0;
6030
+ for (const c of v.toLowerCase()) {
6031
+ if (c === "l") flags |= BORDER_LEFT;
6032
+ else if (c === "t") flags |= BORDER_TOP;
6033
+ else if (c === "r") flags |= BORDER_RIGHT;
6034
+ else if (c === "b") flags |= BORDER_BOTTOM;
6035
+ }
6036
+ if (flags === 0 || flags === BORDER_MASK) return void 0;
6037
+ return flags;
6038
+ };
6039
+ var parseGradientAngle = (v) => {
6040
+ if (v === void 0) return void 0;
6041
+ const n = parseInt(v, 10);
6042
+ if (isNaN(n) || String(n) !== v.trim()) return void 0;
6043
+ if (n < 0 || n > 360) return void 0;
6044
+ return n;
6045
+ };
6046
+ var parseColumns = (v) => v === "*" ? true : void 0;
6047
+ var parseRows = (v) => v === "*" ? true : void 0;
6048
+ var buildCell = (content, a) => ({
6049
+ kind: "cell",
6050
+ content,
6051
+ port: a["port"],
6052
+ align: parseAlign(a["align"] !== void 0 ? a["align"] : ""),
6053
+ valign: parseVAlign(a["valign"] !== void 0 ? a["valign"] : ""),
6054
+ balign: parseAlign(a["balign"] !== void 0 ? a["balign"] : ""),
6055
+ bgcolor: a["bgcolor"],
6056
+ color: a["color"],
6057
+ border: num(a["border"]),
6058
+ cellpadding: num(a["cellpadding"]),
6059
+ cellspacing: num(a["cellspacing"]),
6060
+ sides: parseSides(a["sides"]),
6061
+ gradientangle: parseGradientAngle(a["gradientangle"]),
6062
+ style: a["style"],
6063
+ width: num(a["width"]),
6064
+ height: num(a["height"]),
6065
+ fixedsize: parseFixedSize(a),
6066
+ colspan: num(a["colspan"]),
6067
+ rowspan: num(a["rowspan"]),
6068
+ href: anchorAttr(a["href"]),
6069
+ title: parseTitle(a),
6070
+ target: anchorAttr(a["target"]),
6071
+ id: a["id"]
6072
+ });
6073
+ var buildTable = (rows, a) => ({
6074
+ kind: "table",
6075
+ rows,
6076
+ border: num(a["border"]),
6077
+ cellborder: num(a["cellborder"]),
6078
+ cellspacing: num(a["cellspacing"]),
6079
+ cellpadding: num(a["cellpadding"]),
6080
+ bgcolor: a["bgcolor"],
6081
+ color: a["color"],
6082
+ style: a["style"],
6083
+ align: parseAlign(a["align"] !== void 0 ? a["align"] : ""),
6084
+ valign: parseVAlign(a["valign"] !== void 0 ? a["valign"] : ""),
6085
+ fixedsize: parseFixedSize(a),
6086
+ width: num(a["width"]),
6087
+ height: num(a["height"]),
6088
+ href: anchorAttr(a["href"]),
6089
+ title: parseTitle(a),
6090
+ target: anchorAttr(a["target"]),
6091
+ id: a["id"],
6092
+ port: a["port"],
6093
+ sides: parseSides(a["sides"]),
6094
+ gradientangle: parseGradientAngle(a["gradientangle"]),
6095
+ vrule: parseColumns(a["columns"]),
6096
+ hrule: parseRows(a["rows"])
6097
+ });
6098
+
6099
+ // src/common/htmltable-parse.ts
5889
6100
  var definedOf = (s) => Object.fromEntries(
5890
6101
  Object.entries(s).filter(([, v]) => v !== void 0)
5891
6102
  );
@@ -5909,6 +6120,11 @@ var fontItemProps = (f) => {
5909
6120
  if (f.fontSize !== void 0) out.fontSize = f.fontSize;
5910
6121
  return out;
5911
6122
  };
6123
+ var MSG_SYNTAX_ERROR = "syntax error";
6124
+ var MSG_TEXT_BEFORE_TABLE = "Syntax error: non-space string used before <TABLE>";
6125
+ var MSG_TEXT_AFTER_TABLE = "Syntax error: non-space string used after </TABLE>";
6126
+ var nonSpace = (text) => /[^ \t\n\r]/.test(text);
6127
+ var parseError = (s, tok, message) => (tok === void 0 ? s.pending : htmlErrorAtToken(message, tok)) ?? new HtmlParseError(message);
5912
6128
  var peek = (s) => s.tokens[s.pos];
5913
6129
  var consume = (s) => {
5914
6130
  const t = s.tokens[s.pos];
@@ -5927,12 +6143,20 @@ var inlineFontState = (tag, _attrs) => {
5927
6143
  if (tag === "SUP") fs.superscript = true;
5928
6144
  return fs;
5929
6145
  };
6146
+ var consumeClose = (s, tag) => {
6147
+ const nxt = peek(s);
6148
+ if (nxt === void 0) return;
6149
+ if (nxt.type === "close" && nxt.tag === tag) {
6150
+ consume(s);
6151
+ return;
6152
+ }
6153
+ throw parseError(s, nxt, MSG_SYNTAX_ERROR);
6154
+ };
5930
6155
  var applyBrTag = (s, t, fontStack, items) => {
5931
6156
  consume(s);
5932
6157
  const font = fontStack[fontStack.length - 1] ?? {};
5933
6158
  items.push({ br: true, brAlign: parseAlign(t.attrs["align"] ?? ""), ...fontItemProps(font) });
5934
- const nxt = peek(s);
5935
- if (nxt?.type === "close" && nxt.tag === "BR") consume(s);
6159
+ consumeClose(s, "BR");
5936
6160
  };
5937
6161
  var applyFontTag = (s, t, fontStack, items) => {
5938
6162
  consume(s);
@@ -5989,12 +6213,6 @@ var parseText = (s, fontStack) => {
5989
6213
  }
5990
6214
  return { kind: "text", items };
5991
6215
  };
5992
- var parseHrContent = (s) => {
5993
- consume(s);
5994
- const nxt = peek(s);
5995
- if (nxt?.type === "close" && nxt.tag === "HR") consume(s);
5996
- return { kind: "hr" };
5997
- };
5998
6216
  var parseImgContent = (s, t, cellAttrs) => {
5999
6217
  consume(s);
6000
6218
  const img = {
@@ -6002,24 +6220,54 @@ var parseImgContent = (s, t, cellAttrs) => {
6002
6220
  src: cellAttrs["src"] ?? t.attrs["src"] ?? "",
6003
6221
  scale: t.attrs["scale"]
6004
6222
  };
6005
- const nxt = peek(s);
6006
- if (nxt?.type === "close" && nxt.tag === "IMG") consume(s);
6223
+ consumeClose(s, "IMG");
6007
6224
  return img;
6008
6225
  };
6009
- var parseCellItem = (s, t, cellAttrs, fontStack) => {
6010
- if (t.type === "open" && t.tag === "TABLE") return [parseTable(s, fontStack)];
6011
- if (t.type === "open" && t.tag === "IMG") return [parseImgContent(s, t, cellAttrs)];
6012
- if (t.type === "open" && t.tag === "HR") return [parseHrContent(s)];
6226
+ var scanCellTable = (s, t, fontStack, st) => {
6227
+ if (st.kind === "image" || st.kind === "table") throw parseError(s, t, MSG_SYNTAX_ERROR);
6228
+ if (st.textNonSpace) throw parseError(s, t, MSG_TEXT_BEFORE_TABLE);
6229
+ const tbl = parseTable(s, fontStack);
6230
+ st.kind = "table";
6231
+ return [tbl];
6232
+ };
6233
+ var scanCellText = (s, t, fontStack, st) => {
6234
+ const start = s.pos;
6013
6235
  const txt = parseText(s, fontStack);
6236
+ if (s.pos === start) throw parseError(s, t, MSG_SYNTAX_ERROR);
6237
+ if (txt.items.some((i) => i.text !== void 0 && nonSpace(i.text))) st.textNonSpace = true;
6238
+ st.kind = "text";
6014
6239
  return txt.items.length > 0 ? [txt] : [];
6015
6240
  };
6241
+ var scanAfterTable = (s, t, st) => {
6242
+ if (t.type !== "text") throw parseError(s, t, st.afterNonSpace ? MSG_TEXT_AFTER_TABLE : MSG_SYNTAX_ERROR);
6243
+ consume(s);
6244
+ if (nonSpace(t.value)) st.afterNonSpace = true;
6245
+ return [];
6246
+ };
6247
+ var scanCellImage = (s, t, cellAttrs, st) => {
6248
+ if (st.kind !== "none") throw parseError(s, t, MSG_SYNTAX_ERROR);
6249
+ st.kind = "image";
6250
+ return [parseImgContent(s, t, cellAttrs)];
6251
+ };
6252
+ var scanCellItem = (s, t, cellAttrs, fontStack, st) => {
6253
+ if (st.kind === "table") return scanAfterTable(s, t, st);
6254
+ if (t.type === "open" && t.tag === "TABLE") return scanCellTable(s, t, fontStack, st);
6255
+ if (t.type === "open" && t.tag === "IMG") return scanCellImage(s, t, cellAttrs, st);
6256
+ if (st.kind === "image") throw parseError(s, t, MSG_SYNTAX_ERROR);
6257
+ return scanCellText(s, t, fontStack, st);
6258
+ };
6259
+ var endsCell = (s, t, st) => {
6260
+ if (t.type !== "close") return false;
6261
+ const afterText = st.kind === "table" && st.afterNonSpace;
6262
+ if (t.tag === "TD" && !afterText) return true;
6263
+ throw parseError(s, t, afterText ? MSG_TEXT_AFTER_TABLE : MSG_SYNTAX_ERROR);
6264
+ };
6016
6265
  var parseCellContent = (s, cellAttrs, fontStack) => {
6017
6266
  const content = [];
6018
- while (s.pos < s.tokens.length) {
6019
- const t = peek(s);
6020
- if (!t) break;
6021
- if (t.type === "close" && t.tag === "TD") break;
6022
- content.push(...parseCellItem(s, t, cellAttrs, fontStack));
6267
+ const st = { kind: "none", textNonSpace: false, afterNonSpace: false };
6268
+ for (let t = peek(s); t !== void 0; t = peek(s)) {
6269
+ if (endsCell(s, t, st)) break;
6270
+ content.push(...scanCellItem(s, t, cellAttrs, fontStack, st));
6023
6271
  }
6024
6272
  const nxt = peek(s);
6025
6273
  if (nxt?.type === "close" && nxt.tag === "TD") consume(s);
@@ -6027,134 +6275,85 @@ var parseCellContent = (s, cellAttrs, fontStack) => {
6027
6275
  if (tbl !== void 0) return [tbl];
6028
6276
  return content;
6029
6277
  };
6030
- var parseFixedSize = (a) => a["fixedsize"]?.toUpperCase() === "TRUE" ? true : void 0;
6031
- var parseTitle = (a) => a["title"] !== void 0 ? a["title"] : a["tooltip"];
6032
- var parseSides = (v) => {
6033
- if (v === void 0) return void 0;
6034
- let flags = 0;
6035
- for (const c of v.toLowerCase()) {
6036
- if (c === "l") flags |= BORDER_LEFT;
6037
- else if (c === "t") flags |= BORDER_TOP;
6038
- else if (c === "r") flags |= BORDER_RIGHT;
6039
- else if (c === "b") flags |= BORDER_BOTTOM;
6040
- }
6041
- if (flags === 0 || flags === BORDER_MASK) return void 0;
6042
- return flags;
6043
- };
6044
- var parseGradientAngle = (v) => {
6045
- if (v === void 0) return void 0;
6046
- const n = parseInt(v, 10);
6047
- if (isNaN(n) || String(n) !== v.trim()) return void 0;
6048
- if (n < 0 || n > 360) return void 0;
6049
- return n;
6050
- };
6051
- var parseColumns = (v) => v === "*" ? true : void 0;
6052
- var parseRows = (v) => v === "*" ? true : void 0;
6053
- var buildCell = (content, a) => ({
6054
- kind: "cell",
6055
- content,
6056
- port: a["port"],
6057
- align: parseAlign(a["align"] !== void 0 ? a["align"] : ""),
6058
- valign: parseVAlign(a["valign"] !== void 0 ? a["valign"] : ""),
6059
- balign: parseAlign(a["balign"] !== void 0 ? a["balign"] : ""),
6060
- bgcolor: a["bgcolor"],
6061
- color: a["color"],
6062
- border: num(a["border"]),
6063
- cellpadding: num(a["cellpadding"]),
6064
- cellspacing: num(a["cellspacing"]),
6065
- sides: parseSides(a["sides"]),
6066
- gradientangle: parseGradientAngle(a["gradientangle"]),
6067
- style: a["style"],
6068
- width: num(a["width"]),
6069
- height: num(a["height"]),
6070
- fixedsize: parseFixedSize(a),
6071
- colspan: num(a["colspan"]),
6072
- rowspan: num(a["rowspan"]),
6073
- href: anchorAttr(a["href"]),
6074
- title: parseTitle(a),
6075
- target: anchorAttr(a["target"]),
6076
- id: a["id"]
6077
- });
6078
6278
  var parseCell = (s, attrs, fontStack) => buildCell(parseCellContent(s, attrs, fontStack), attrs);
6079
- var processRowToken = (s, t, cells, fontStack) => {
6279
+ var parseRowVr = (s, t, cells) => {
6280
+ const prev = cells[cells.length - 1];
6281
+ if (prev === void 0) throw parseError(s, t, MSG_SYNTAX_ERROR);
6282
+ consume(s);
6283
+ consumeClose(s, "VR");
6284
+ prev.vruled = true;
6285
+ };
6286
+ var scanRowCell = (s, t, fontStack, rs) => {
6080
6287
  if (t.type === "open" && t.tag === "TD") {
6081
6288
  consume(s);
6082
- cells.push(parseCell(s, t.attrs, fontStack));
6083
- return;
6289
+ rs.cells.push(parseCell(s, t.attrs, fontStack));
6290
+ rs.needCell = false;
6291
+ } else if (t.type === "open" && t.tag === "VR" && !rs.needCell) {
6292
+ parseRowVr(s, t, rs.cells);
6293
+ rs.needCell = true;
6294
+ } else {
6295
+ throw parseError(s, t, MSG_SYNTAX_ERROR);
6084
6296
  }
6085
- if (t.type === "open" && t.tag === "VR") {
6297
+ };
6298
+ var scanRowToken = (s, t, fontStack, rs) => {
6299
+ if (t.type === "text") {
6086
6300
  consume(s);
6087
- const nxt = peek(s);
6088
- if (nxt?.type === "close" && nxt.tag === "VR") consume(s);
6089
- const prev = cells[cells.length - 1];
6090
- if (prev === void 0) throw new HtmlParseError("VR");
6091
- prev.vruled = true;
6092
- return;
6301
+ return false;
6093
6302
  }
6094
- consume(s);
6303
+ if (t.type !== "close" || t.tag !== "TR") {
6304
+ scanRowCell(s, t, fontStack, rs);
6305
+ return false;
6306
+ }
6307
+ if (rs.cells.length === 0 || rs.needCell) throw parseError(s, t, MSG_SYNTAX_ERROR);
6308
+ return true;
6095
6309
  };
6096
6310
  var parseRow = (s, fontStack) => {
6097
- const cells = [];
6098
- while (s.pos < s.tokens.length) {
6099
- const t = peek(s);
6100
- if (!t) break;
6101
- if (t.type === "close" && t.tag === "TR") break;
6102
- processRowToken(s, t, cells, fontStack);
6311
+ const rs = { cells: [], needCell: false };
6312
+ for (let t = peek(s); t !== void 0; t = peek(s)) {
6313
+ if (scanRowToken(s, t, fontStack, rs)) break;
6103
6314
  }
6104
6315
  const nxt = peek(s);
6105
6316
  if (nxt?.type === "close" && nxt.tag === "TR") consume(s);
6106
- return { cells };
6317
+ return { cells: rs.cells };
6318
+ };
6319
+ var scanTableRow = (s, t, fontStack, ts) => {
6320
+ if (t.type === "open" && t.tag === "TR") {
6321
+ consume(s);
6322
+ ts.rows.push(parseTableRow(s, fontStack, ts));
6323
+ ts.needRow = false;
6324
+ } else if (t.type === "open" && t.tag === "HR" && ts.rows.length > 0 && !ts.needRow) {
6325
+ consumeRowHr(s, ts.rows);
6326
+ ts.needRow = true;
6327
+ } else {
6328
+ throw parseError(s, t, MSG_SYNTAX_ERROR);
6329
+ }
6330
+ };
6331
+ var scanTableToken = (s, t, fontStack, ts) => {
6332
+ if (t.type === "text") {
6333
+ consume(s);
6334
+ return false;
6335
+ }
6336
+ if (t.type !== "close" || t.tag !== "TABLE") {
6337
+ scanTableRow(s, t, fontStack, ts);
6338
+ return false;
6339
+ }
6340
+ if (ts.rows.length === 0 || ts.needRow) throw parseError(s, t, MSG_SYNTAX_ERROR);
6341
+ consume(s);
6342
+ return true;
6107
6343
  };
6108
- var buildTable = (rows, a) => ({
6109
- kind: "table",
6110
- rows,
6111
- border: num(a["border"]),
6112
- cellborder: num(a["cellborder"]),
6113
- cellspacing: num(a["cellspacing"]),
6114
- cellpadding: num(a["cellpadding"]),
6115
- bgcolor: a["bgcolor"],
6116
- color: a["color"],
6117
- style: a["style"],
6118
- align: parseAlign(a["align"] !== void 0 ? a["align"] : ""),
6119
- valign: parseVAlign(a["valign"] !== void 0 ? a["valign"] : ""),
6120
- fixedsize: parseFixedSize(a),
6121
- width: num(a["width"]),
6122
- height: num(a["height"]),
6123
- href: anchorAttr(a["href"]),
6124
- title: parseTitle(a),
6125
- target: anchorAttr(a["target"]),
6126
- id: a["id"],
6127
- port: a["port"],
6128
- sides: parseSides(a["sides"]),
6129
- gradientangle: parseGradientAngle(a["gradientangle"]),
6130
- vrule: parseColumns(a["columns"]),
6131
- hrule: parseRows(a["rows"])
6132
- });
6133
6344
  var parseTable = (s, fontStack) => {
6134
6345
  const open = consume(s);
6135
6346
  if (open.type !== "open" || open.tag !== "TABLE") throw new HtmlParseError("TABLE");
6136
- const hrule = parseRows(open.attrs["rows"]) === true;
6137
- const vrule = parseColumns(open.attrs["columns"]) === true;
6138
- const rows = [];
6139
- while (s.pos < s.tokens.length) {
6140
- const t = peek(s);
6141
- if (!t) break;
6142
- if (t.type === "close" && t.tag === "TABLE") {
6143
- consume(s);
6144
- break;
6145
- }
6146
- if (t.type === "open" && t.tag === "TR") {
6147
- consume(s);
6148
- rows.push(parseTableRow(s, fontStack, { hrule, vrule }));
6149
- continue;
6150
- }
6151
- if (t.type === "open" && t.tag === "HR") {
6152
- consumeRowHr(s, rows);
6153
- continue;
6154
- }
6155
- consume(s);
6347
+ const ts = {
6348
+ rows: [],
6349
+ needRow: false,
6350
+ hrule: parseRows(open.attrs["rows"]) === true,
6351
+ vrule: parseColumns(open.attrs["columns"]) === true
6352
+ };
6353
+ for (let t = peek(s); t !== void 0; t = peek(s)) {
6354
+ if (scanTableToken(s, t, fontStack, ts)) break;
6156
6355
  }
6157
- return buildTable(rows, open.attrs);
6356
+ return buildTable(ts.rows, open.attrs);
6158
6357
  };
6159
6358
  var parseTableRow = (s, fontStack, opts) => {
6160
6359
  const row = parseRow(s, fontStack);
@@ -6164,11 +6363,9 @@ var parseTableRow = (s, fontStack, opts) => {
6164
6363
  };
6165
6364
  var consumeRowHr = (s, rows) => {
6166
6365
  consume(s);
6167
- const nxt = peek(s);
6168
- if (nxt?.type === "close" && nxt.tag === "HR") consume(s);
6366
+ consumeClose(s, "HR");
6169
6367
  const prev = rows[rows.length - 1];
6170
- if (prev === void 0) throw new HtmlParseError("HR");
6171
- prev.ruled = true;
6368
+ if (prev !== void 0) prev.ruled = true;
6172
6369
  };
6173
6370
  var findFirstRealToken = (tokens, start) => {
6174
6371
  for (let i = start; i < tokens.length; i++) {
@@ -6184,34 +6381,61 @@ var skipLeadingWs = (s) => {
6184
6381
  consume(s);
6185
6382
  }
6186
6383
  };
6384
+ var finishTableLabel = (s) => {
6385
+ let afterNonSpace = false;
6386
+ for (let t = peek(s); t?.type === "text"; t = peek(s)) {
6387
+ consume(s);
6388
+ if (nonSpace(t.value)) afterNonSpace = true;
6389
+ }
6390
+ const nxt = peek(s);
6391
+ if (nxt === void 0) return;
6392
+ if (afterNonSpace) throw parseError(s, nxt, MSG_TEXT_AFTER_TABLE);
6393
+ if (nxt.type !== "close" || nxt.tag !== "HTML") throw parseError(s, nxt, MSG_SYNTAX_ERROR);
6394
+ consume(s);
6395
+ };
6187
6396
  var parseTableLabel = (s, fontStack) => {
6188
6397
  skipLeadingWs(s);
6189
6398
  const table = parseTable(s, fontStack);
6190
- const nxt = peek(s);
6191
- if (nxt?.type === "close" && nxt.tag === "HTML") consume(s);
6399
+ finishTableLabel(s);
6192
6400
  return { kind: "table", table };
6193
6401
  };
6194
- var parseHtmlLabel = (src) => {
6195
- const wrapped = `<HTML>${src}</HTML>`;
6196
- const tokens = tokenize(wrapped);
6197
- const s = { tokens, pos: 0 };
6198
- const fontStack = [{}];
6199
- const htmlOpen = consume(s);
6200
- if (htmlOpen.type !== "open" || htmlOpen.tag !== "HTML") throw new HtmlParseError("HTML");
6201
- const firstReal = findFirstRealToken(tokens, s.pos);
6202
- if (firstReal?.type === "open" && firstReal.tag === "TABLE") {
6203
- return parseTableLabel(s, fontStack);
6204
- }
6402
+ var parseTextLabel = (s, fontStack) => {
6205
6403
  const texts = [];
6206
6404
  const txt = parseText(s, fontStack);
6207
6405
  if (txt.items.length > 0) texts.push(txt);
6208
6406
  const nxt = peek(s);
6209
- if (nxt?.type === "open" && nxt.tag === "TABLE") {
6210
- throw new HtmlParseError("non-space string used before <TABLE>");
6407
+ if (nxt === void 0) return { kind: "text", texts };
6408
+ if (nxt.type === "open" && nxt.tag === "TABLE") {
6409
+ if (txt.items.some((i) => i.text !== void 0 && nonSpace(i.text))) {
6410
+ throw parseError(s, nxt, MSG_TEXT_BEFORE_TABLE);
6411
+ }
6412
+ throw new HtmlParseError("TABLE");
6211
6413
  }
6212
- if (nxt?.type === "close" && nxt.tag === "HTML") consume(s);
6414
+ if (nxt.type !== "close" || nxt.tag !== "HTML") throw parseError(s, nxt, MSG_SYNTAX_ERROR);
6415
+ consume(s);
6213
6416
  return { kind: "text", texts };
6214
6417
  };
6418
+ var parseTokens = (s) => {
6419
+ const fontStack = [{}];
6420
+ const htmlOpen = consume(s);
6421
+ if (htmlOpen.type !== "open" || htmlOpen.tag !== "HTML") throw new HtmlParseError("HTML");
6422
+ const firstReal = findFirstRealToken(s.tokens, s.pos);
6423
+ if (firstReal?.type === "open" && firstReal.tag === "TABLE") return parseTableLabel(s, fontStack);
6424
+ return parseTextLabel(s, fontStack);
6425
+ };
6426
+ var parseHtmlLabel = (src) => {
6427
+ const { tokens, error } = tokenizeDeferred(`<HTML>${src}</HTML>`);
6428
+ const s = { tokens, pos: 0, pending: error };
6429
+ let label;
6430
+ try {
6431
+ label = parseTokens(s);
6432
+ } catch (e) {
6433
+ if (error !== void 0 && s.pos >= tokens.length) throw error;
6434
+ throw e;
6435
+ }
6436
+ if (error !== void 0) throw error;
6437
+ return label;
6438
+ };
6215
6439
 
6216
6440
  // src/gvc/usershape.ts
6217
6441
  var activeSizer = null;
@@ -6219,28 +6443,80 @@ function findImageSize(src) {
6219
6443
  return activeSizer !== null ? activeSizer(src) : null;
6220
6444
  }
6221
6445
 
6446
+ // src/common/pointset.ts
6447
+ function cmpPair(a, b) {
6448
+ if (a.x > b.x) return 1;
6449
+ if (a.x < b.x) return -1;
6450
+ if (a.y > b.y) return 1;
6451
+ if (a.y < b.y) return -1;
6452
+ return 0;
6453
+ }
6454
+ var PointSet = class {
6455
+ byX = /* @__PURE__ */ new Map();
6456
+ n = 0;
6457
+ /**
6458
+ * Insert (x, y). A duplicate keeps the point inserted first, as `dtinsert`
6459
+ * returns the existing object and the new one is freed.
6460
+ * @see lib/common/pointset.c:addPS
6461
+ */
6462
+ addPS(x, y) {
6463
+ let col = this.byX.get(x);
6464
+ if (col === void 0) {
6465
+ col = /* @__PURE__ */ new Map();
6466
+ this.byX.set(x, col);
6467
+ }
6468
+ if (col.has(y)) return;
6469
+ col.set(y, { x, y });
6470
+ this.n++;
6471
+ }
6472
+ /** @see lib/common/pointset.c:inPS */
6473
+ inPS(pt) {
6474
+ return this.byX.get(pt.x)?.has(pt.y) ?? false;
6475
+ }
6476
+ /** @see lib/common/pointset.c:isInPS */
6477
+ isInPS(x, y) {
6478
+ return this.byX.get(x)?.has(y) ?? false;
6479
+ }
6480
+ /** @see lib/common/pointset.c:sizeOf */
6481
+ sizeOf() {
6482
+ return this.n;
6483
+ }
6484
+ /**
6485
+ * The points in dictionary order (`dtflatten` walks the ordered set).
6486
+ * @see lib/common/pointset.c:pointsOf
6487
+ */
6488
+ pointsOf() {
6489
+ const pts = [];
6490
+ for (const col of this.byX.values()) {
6491
+ for (const p of col.values()) pts.push({ x: p.x, y: p.y });
6492
+ }
6493
+ return pts.sort(cmpPair);
6494
+ }
6495
+ };
6496
+
6222
6497
  // src/common/htmltable.ts
6223
6498
  var DEFAULT_BORDER = 1;
6224
6499
  var DEFAULT_CELLPADDING = 2;
6225
6500
  var DEFAULT_CELLSPACING = 2;
6226
6501
  var DEFAULT_FONTSIZE = 14;
6227
- var findCol = (used, row, startCol, colspan, rowspan) => {
6502
+ var findCol = (ps, row, startCol, colspan, rowspan) => {
6228
6503
  let col = startCol;
6229
- for (; ; ) {
6230
- let conflict = false;
6231
- for (let c = col; c < col + colspan && !conflict; c++) {
6232
- for (let r = row; r < row + rowspan && !conflict; r++) {
6233
- if (used.has(`${c},${r}`)) {
6234
- conflict = true;
6235
- col = c + 1;
6236
- }
6237
- }
6504
+ let notFound = true;
6505
+ const end = colspan - 1;
6506
+ while (notFound) {
6507
+ const lastc = col + end;
6508
+ let c;
6509
+ for (c = lastc; c >= col; c--) {
6510
+ if (ps.isInPS(c, row)) break;
6511
+ }
6512
+ if (c >= col) col = c + 1;
6513
+ else notFound = false;
6514
+ }
6515
+ for (let j = col; j < col + colspan; j++) {
6516
+ for (let i = row; i < row + rowspan; i++) {
6517
+ ps.addPS(j, i);
6238
6518
  }
6239
- if (!conflict) break;
6240
6519
  }
6241
- for (let c = col; c < col + colspan; c++)
6242
- for (let r = row; r < row + rowspan; r++)
6243
- used.add(`${c},${r}`);
6244
6520
  return col;
6245
6521
  };
6246
6522
  var DEFAULT_SIZE_ENV = { fontsize: DEFAULT_FONTSIZE, fontname: "" };
@@ -6319,29 +6595,32 @@ var sizeTextContent = (texts, measurer, env = DEFAULT_SIZE_ENV) => {
6319
6595
  if (lines.length === 1) h = lines[0].items.length > 0 ? lines[0].mxysize : DEFAULT_FONTSIZE;
6320
6596
  return { w, h };
6321
6597
  };
6598
+ var missingImages = /* @__PURE__ */ new WeakSet();
6322
6599
  var sizeHtmlImg = (img, imageSizer) => {
6323
- if (img.width !== void 0 && img.height !== void 0) return;
6600
+ if (img.width !== void 0 && img.height !== void 0) return missingImages.has(img) ? 1 : 0;
6324
6601
  const sz = imageSizer !== void 0 ? imageSizer(img.src) : findImageSize(img.src);
6325
6602
  if (sz !== null) {
6326
6603
  img.width = sz.w;
6327
6604
  img.height = sz.h;
6328
- return;
6605
+ return 0;
6329
6606
  }
6330
6607
  console.error(`Error: No or improper image file="${img.src}"`);
6331
6608
  img.width = 0;
6332
6609
  img.height = 0;
6610
+ missingImages.add(img);
6611
+ return 1;
6333
6612
  };
6334
6613
  var sizeContentItem = (item, measurer, env) => {
6335
- if (item.kind === "text") return sizeTextContent([item], measurer, env);
6614
+ if (item.kind === "text") return { ...sizeTextContent([item], measurer, env), rv: 0 };
6336
6615
  if (item.kind === "table") {
6337
- sizeTableInner(item, measurer, env);
6338
- return item.dimen !== void 0 ? item.dimen : { w: 0, h: 0 };
6616
+ const rv = sizeTableInner(item, measurer, env);
6617
+ return { ...item.dimen !== void 0 ? item.dimen : { w: 0, h: 0 }, rv };
6339
6618
  }
6340
6619
  if (item.kind === "image") {
6341
- sizeHtmlImg(item, env.imageSizer);
6342
- return { w: item.width ?? 0, h: item.height ?? 0 };
6620
+ const rv = sizeHtmlImg(item, env.imageSizer);
6621
+ return { w: item.width ?? 0, h: item.height ?? 0, rv };
6343
6622
  }
6344
- return { w: 0, h: 0 };
6623
+ return { w: 0, h: 0, rv: 0 };
6345
6624
  };
6346
6625
  var cellPad = (cell2, tbl) => cell2.cellpadding !== void 0 ? cell2.cellpadding : tbl.cellpadding !== void 0 ? tbl.cellpadding : DEFAULT_CELLPADDING;
6347
6626
  var cellBorder = (cell2, tbl) => {
@@ -6351,27 +6630,60 @@ var cellBorder = (cell2, tbl) => {
6351
6630
  if (tbl.border !== void 0) return tbl.border;
6352
6631
  return DEFAULT_BORDER;
6353
6632
  };
6633
+ function inheritTablePenColor(tbl, parentColor) {
6634
+ if (tbl.color === void 0) tbl.color = parentColor;
6635
+ const tblColor = tbl.color;
6636
+ for (const row of tbl.rows) {
6637
+ for (const cell2 of row.cells) {
6638
+ if (cell2.color === void 0) cell2.color = tblColor;
6639
+ for (const item of cell2.content) {
6640
+ if (item.kind === "table") inheritTablePenColor(item, cell2.color);
6641
+ }
6642
+ }
6643
+ }
6644
+ }
6645
+ var WARN_CELL_TOO_SMALL = "Warning: cell size too small for content";
6646
+ var WARN_CELL_UNSPECIFIED = "Warning: fixed cell size with unspecified width or height";
6647
+ var WARN_TABLE_TOO_SMALL = "Warning: table size too small for content";
6648
+ var WARN_TABLE_UNSPECIFIED = "Warning: fixed table size with unspecified width or height";
6649
+ var sizeWarned = /* @__PURE__ */ new WeakSet();
6650
+ var fixedCellCheck = (cell2, sz, warn) => {
6651
+ if (!cell2.fixedsize) return { rv: 0, zero: false };
6652
+ if (cell2.width && cell2.height) {
6653
+ const small = (cell2.width < sz.w || cell2.height < sz.h) && cell2.content[0]?.kind !== "image";
6654
+ if (small && warn) console.warn(WARN_CELL_TOO_SMALL);
6655
+ return { rv: small ? 1 : 0, zero: true };
6656
+ }
6657
+ if (warn) console.warn(WARN_CELL_UNSPECIFIED);
6658
+ return { rv: 1, zero: false };
6659
+ };
6354
6660
  var getCellSize = (cell2, tbl, measurer, env) => {
6355
6661
  const margin = 2 * (cellPad(cell2, tbl) + cellBorder(cell2, tbl));
6356
- let cw = 0, ch = 0;
6662
+ let cw = 0, ch = 0, rv = 0;
6357
6663
  for (const item of cell2.content) {
6358
6664
  const s = sizeContentItem(item, measurer, env);
6359
6665
  cw = Math.max(cw, s.w);
6360
6666
  ch += s.h;
6667
+ rv |= s.rv;
6361
6668
  }
6362
6669
  let szx = cw + margin;
6363
6670
  let szy = ch + margin;
6364
- if (cell2.fixedsize && cell2.width && cell2.height) {
6671
+ const warn = !sizeWarned.has(cell2);
6672
+ sizeWarned.add(cell2);
6673
+ const fixed = fixedCellCheck(cell2, { w: szx, h: szy }, warn);
6674
+ rv |= fixed.rv;
6675
+ if (fixed.zero) {
6365
6676
  szx = 0;
6366
6677
  szy = 0;
6367
6678
  }
6368
6679
  const bw = cell2.width !== void 0 ? cell2.width : 0;
6369
6680
  const bh = cell2.height !== void 0 ? cell2.height : 0;
6370
- return { w: Math.max(szx, bw), h: Math.max(szy, bh) };
6681
+ return { w: Math.max(szx, bw), h: Math.max(szy, bh), rv };
6371
6682
  };
6372
6683
  var buildLayouts = (tbl, measurer, env) => {
6373
6684
  const entries = [];
6374
- const used = /* @__PURE__ */ new Set();
6685
+ let rv = 0;
6686
+ const ps = new PointSet();
6375
6687
  let rowIdx = 0;
6376
6688
  for (const row of tbl.rows) {
6377
6689
  let col = 0;
@@ -6381,14 +6693,15 @@ var buildLayouts = (tbl, measurer, env) => {
6381
6693
  }
6382
6694
  const colspan = c.colspan !== void 0 ? c.colspan : 1;
6383
6695
  const rowspan = c.rowspan !== void 0 ? c.rowspan : 1;
6384
- const { w, h } = getCellSize(c, tbl, measurer, env);
6385
- col = findCol(used, rowIdx, col, colspan, rowspan);
6696
+ const { w, h, rv: cellRv } = getCellSize(c, tbl, measurer, env);
6697
+ rv |= cellRv;
6698
+ col = findCol(ps, rowIdx, col, colspan, rowspan);
6386
6699
  entries.push({ cell: c, row: rowIdx, col, colspan, rowspan, w, h });
6387
6700
  col += colspan;
6388
6701
  }
6389
6702
  rowIdx++;
6390
6703
  }
6391
- return entries;
6704
+ return { entries, rv };
6392
6705
  };
6393
6706
  var setColWidths = (entries, ncols, spacing) => {
6394
6707
  const widths = new Array(ncols + 1).fill(0);
@@ -6441,21 +6754,34 @@ var sumTableDims = (a) => {
6441
6754
  var layoutHtmlTable = (tbl, measurer, env = DEFAULT_SIZE_ENV) => {
6442
6755
  const spacing = getSpacing(tbl);
6443
6756
  const border = getBorder(tbl);
6444
- const entries = buildLayouts(tbl, measurer, env);
6757
+ const { entries, rv } = buildLayouts(tbl, measurer, env);
6445
6758
  if (entries.length === 0) {
6446
- return { entries, widths: [], heights: [], spacing, border };
6759
+ return { entries, widths: [], heights: [], spacing, border, rv };
6447
6760
  }
6448
6761
  const ncols = Math.max(...entries.map((e) => e.col + e.colspan));
6449
6762
  const nrows = Math.max(...entries.map((e) => e.row + e.rowspan));
6450
6763
  const widths = setColWidths(entries, ncols, spacing);
6451
6764
  const heights = setRowHeights(entries, nrows, spacing);
6452
- return { entries, widths, heights, spacing, border };
6765
+ return { entries, widths, heights, spacing, border, rv };
6766
+ };
6767
+ var warnFixedTable = (tbl, dims) => {
6768
+ const warn = !sizeWarned.has(tbl);
6769
+ sizeWarned.add(tbl);
6770
+ if (tbl.fixedsize !== true) return 0;
6771
+ if (tbl.width && tbl.height) {
6772
+ if (tbl.width >= dims.wd && tbl.height >= dims.ht) return 0;
6773
+ if (warn) console.warn(WARN_TABLE_TOO_SMALL);
6774
+ return 1;
6775
+ }
6776
+ if (warn) console.warn(WARN_TABLE_UNSPECIFIED);
6777
+ return 1;
6453
6778
  };
6454
6779
  var sizeTableInner = (tbl, measurer, env) => {
6455
- const { entries, widths, heights, spacing, border } = layoutHtmlTable(tbl, measurer, env);
6780
+ const { entries, widths, heights, spacing, border, rv } = layoutHtmlTable(tbl, measurer, env);
6456
6781
  if (entries.length === 0) {
6782
+ const fixed2 = warnFixedTable(tbl, sumTableDims({ widths, heights, ncols: 0, nrows: 0, spacing, border }));
6457
6783
  tbl.dimen = { w: 0, h: 0 };
6458
- return;
6784
+ return rv | fixed2;
6459
6785
  }
6460
6786
  const ncols = Math.max(...entries.map((e) => e.col + e.colspan));
6461
6787
  const nrows = Math.max(...entries.map((e) => e.row + e.rowspan));
@@ -6463,19 +6789,22 @@ var sizeTableInner = (tbl, measurer, env) => {
6463
6789
  const { wd, ht } = sumTableDims({ widths, heights, ncols, nrows, spacing, border });
6464
6790
  const baseWd = tbl.width !== void 0 ? tbl.width : 0;
6465
6791
  const baseHt = tbl.height !== void 0 ? tbl.height : 0;
6792
+ const fixed = warnFixedTable(tbl, { wd, ht });
6466
6793
  const zeroContent = isTblFixed(tbl) && baseWd !== 0 && baseHt !== 0;
6467
6794
  tbl.dimen = {
6468
6795
  w: Math.max(zeroContent ? 0 : wd, baseWd),
6469
6796
  h: Math.max(zeroContent ? 0 : ht, baseHt)
6470
6797
  };
6798
+ return rv | fixed;
6471
6799
  };
6472
6800
  var sizeHtmlLabel = (label, measurer, env = DEFAULT_SIZE_ENV) => {
6473
6801
  if (label.kind === "table") {
6474
- sizeTableInner(label.table, measurer, env);
6802
+ const rv = sizeTableInner(label.table, measurer, env);
6475
6803
  label.dimen = label.table.dimen;
6476
- } else {
6477
- label.dimen = sizeTextContent(label.texts, measurer, env);
6804
+ return rv;
6478
6805
  }
6806
+ label.dimen = sizeTextContent(label.texts, measurer, env);
6807
+ return 0;
6479
6808
  };
6480
6809
 
6481
6810
  // src/common/splines-constants.ts
@@ -6918,28 +7247,18 @@ function posHtmlLabel(label, finfo, measurer) {
6918
7247
  if (label.kind === "table") return posHtmlTable(label.table, finfo, measurer);
6919
7248
  return posTextLabel(label, finfo, measurer);
6920
7249
  }
6921
- function inheritTablePenColor(tbl, parentColor) {
6922
- if (tbl.color === void 0) tbl.color = parentColor;
6923
- const tblColor = tbl.color;
6924
- for (const row of tbl.rows) {
6925
- for (const cell2 of row.cells) {
6926
- if (cell2.color === void 0) cell2.color = tblColor;
6927
- for (const item of cell2.content) {
6928
- if (item.kind === "table") inheritTablePenColor(item, cell2.color);
6929
- }
6930
- }
6931
- }
6932
- }
6933
- function makeHtmlLabel(content, finfo, measurer) {
7250
+ var HTML_PARSE_FATAL = 3;
7251
+ function makeHtmlLabelRv(content, finfo, measurer) {
6934
7252
  const { fontname, fontsize, fontcolor, imageSizer, pencolor } = finfo;
6935
7253
  let lbl;
6936
7254
  try {
6937
7255
  lbl = parseHtmlLabel(content);
6938
- } catch {
6939
- return makeLabel("", fontname, fontsize, fontcolor, measurer);
7256
+ } catch (e) {
7257
+ if (e instanceof HtmlLexError) for (const line of e.diagnostics) console.error(line);
7258
+ return { label: makeLabel("", fontname, fontsize, fontcolor, measurer), rv: HTML_PARSE_FATAL };
6940
7259
  }
6941
7260
  if (lbl.kind === "table") inheritTablePenColor(lbl.table, pencolor);
6942
- sizeHtmlLabel(lbl, measurer, {
7261
+ const rv = sizeHtmlLabel(lbl, measurer, {
6943
7262
  fontsize,
6944
7263
  fontname,
6945
7264
  imageSizer
@@ -6947,7 +7266,7 @@ function makeHtmlLabel(content, finfo, measurer) {
6947
7266
  const placed = posHtmlLabel(lbl, { fontname, fontsize, fontcolor }, measurer);
6948
7267
  const dim = lbl.dimen ?? { w: 0, h: 0 };
6949
7268
  const text = lbl.kind === "table" ? "<TABLE>" : content;
6950
- return htmlTextLabel(text, { fontname, fontsize, fontcolor }, placed, dim);
7269
+ return { label: htmlTextLabel(text, { fontname, fontsize, fontcolor }, placed, dim), rv };
6951
7270
  }
6952
7271
  function htmlTextLabel(text, finfo, placed, dim) {
6953
7272
  return {
@@ -7336,6 +7655,159 @@ function interpretCRNL(str) {
7336
7655
  return out;
7337
7656
  }
7338
7657
 
7658
+ // src/model/graphInfo.ts
7659
+ function makeGraphInfo() {
7660
+ return {
7661
+ bb: { ll: { x: 0, y: 0 }, ur: { x: 0, y: 0 } },
7662
+ rankdir: 0,
7663
+ flags: 0,
7664
+ charset: 0,
7665
+ gui_state: 0,
7666
+ has_labels: 0,
7667
+ has_images: false
7668
+ };
7669
+ }
7670
+
7671
+ // src/model/graph.ts
7672
+ var Graph = class {
7673
+ /** Graph name; agnameof equivalent. @see lib/cgraph/cgraph.h:agnameof */
7674
+ name;
7675
+ /**
7676
+ * True when the graph was declared with NO name (`digraph {`), as opposed to
7677
+ * an explicit empty name (`digraph "" {`). Both store `name === ''`, but
7678
+ * cgraph gives the truly-anonymous root the internal id `%1` (agnameof) while
7679
+ * the empty-named one keeps `""`. Only the imagemap `<map id/name>` reads this
7680
+ * (map.ts:mapGraphName); dot/xdot re-serialization keeps `name` so an
7681
+ * anonymous root round-trips as `digraph {`, not `digraph %1 {`.
7682
+ * @see lib/cgraph/id.c:idmap (anon → `%1`) ; lib/cgraph/agraph.c:agnameof
7683
+ */
7684
+ anonymous;
7685
+ /**
7686
+ * Directed/strict classification; mirrors Agdesc_t.directed and
7687
+ * Agdesc_t.strict. @see lib/cgraph/cgraph.h:Agdesc_s
7688
+ */
7689
+ kind;
7690
+ /**
7691
+ * Node set in insertion order, keyed by name.
7692
+ * Mirrors Agraph_t.n_seq (sequence dict) merged with n_id access.
7693
+ * @see lib/cgraph/cgraph.h:Agraph_s
7694
+ */
7695
+ nodes;
7696
+ /**
7697
+ * All edges owned by this graph (root graph only for ownership; subgraphs
7698
+ * reference a subset). Mirrors Agraph_t.e_seq.
7699
+ * @see lib/cgraph/cgraph.h:Agraph_s
7700
+ */
7701
+ edges;
7702
+ /**
7703
+ * Named subgraphs, keyed by name. Mirrors Agraph_t.g_seq/g_id.
7704
+ * @see lib/cgraph/cgraph.h:Agraph_s
7705
+ */
7706
+ subgraphs;
7707
+ /**
7708
+ * String attributes (agget/agset equivalents).
7709
+ * Replaces the Agattr_t / Agsym_t machinery for simple key-value access.
7710
+ * @see lib/cgraph/cgraph.h:Agattr_s
7711
+ */
7712
+ attrs;
7713
+ /** Default attributes applied to all nodes in this graph scope. */
7714
+ nodeDefaults;
7715
+ /** Default attributes applied to all edges in this graph scope. */
7716
+ edgeDefaults;
7717
+ /**
7718
+ * Snapshot of the enclosing graphs' graph-attribute defaults (this graph ->
7719
+ * root, inner wins) captured when this subgraph was OPENED during parsing.
7720
+ * Graph attributes set on an ancestor AFTER this subgraph was created are not
7721
+ * included — mirroring cgraph's parse-time agsubg defval copy. Used for
7722
+ * order-correct label/font inheritance (e.g. a root `label=` declared after a
7723
+ * cluster must NOT apply to that cluster). Undefined on the root graph.
7724
+ * @see lib/cgraph/graph.c:agsubg (attr defval copy)
7725
+ */
7726
+ graphDefaultsSnapshot;
7727
+ /**
7728
+ * Keys in `attrs` the BUILDER seeded from `graphDefaultsSnapshot` rather than
7729
+ * the input declaring them in this scope (see builder.ts GRAPH_LABEL_INHERIT_
7730
+ * KEYS). cgraph distinguishes these structurally — a seeded value is inherited
7731
+ * through the dict view and has no local symbol, while a local declaration
7732
+ * creates one even when its value equals the inherited value — so the
7733
+ * serializer needs the provenance to decide what write_dict would print.
7734
+ * Undefined on the root graph and on any scope that seeded nothing.
7735
+ * @see lib/cgraph/write.c:262 write_dict
7736
+ */
7737
+ seededAttrs;
7738
+ /**
7739
+ * Graph-attribute keys whose declaration in THIS scope was the first anywhere
7740
+ * in the graph. cgraph's `setattr` splits on exactly this: a key with no
7741
+ * existing symbol takes the global-declaration branch, which ends up seeding
7742
+ * every direct subgraph that already exists with a local empty value (see
7743
+ * `eagerEmptyParts`); a key some earlier scope already declared takes the
7744
+ * "new local definition" branch and touches no subgraph at all. So an
7745
+ * identical `graph [fill=x]` statement makes a child print `fill=""` or print
7746
+ * nothing purely according to whether a SIBLING declared `fill` first.
7747
+ * Undefined on any scope that declared nothing new.
7748
+ * @see lib/cgraph/attr.c:257 setattr (lsym / psym / global branches)
7749
+ */
7750
+ firstGraphDecl;
7751
+ /**
7752
+ * Layout-engine info; replaces GD_* macros via agbindrec (AD-1).
7753
+ * @see lib/cgraph/cgraph.h:agbindrec
7754
+ */
7755
+ info;
7756
+ /**
7757
+ * Immediate parent graph. null for root graphs.
7758
+ * @see lib/cgraph/cgraph.h:Agraph_s
7759
+ */
7760
+ parent;
7761
+ /**
7762
+ * Root (main) graph. Self-referential for root graphs;
7763
+ * points up for subgraphs.
7764
+ * @see lib/cgraph/cgraph.h:Agraph_s
7765
+ */
7766
+ root;
7767
+ /**
7768
+ * Subgraph sequence number (AGSEQ). A global counter on the root, assigned
7769
+ * at creation in source order, counting anonymous subgraphs. The root keeps
7770
+ * the default 0 (par == NULL never calls agnextseq). Consumed by getObjId to
7771
+ * emit cluster ids `clust<seq>`.
7772
+ * @see lib/cgraph/cgraph.h:AGSEQ
7773
+ * @see lib/cgraph/graph.c:agopen (AGSEQ(g) = agnextseq(par, AGRAPH))
7774
+ */
7775
+ seq = 0;
7776
+ /**
7777
+ * Root-only counter mirroring `clos->seq[AGRAPH]`: the running maximum AGSEQ
7778
+ * handed out to subgraphs. Meaningful only on the root graph; `assignSubgSeq`
7779
+ * pre-increments it. Other graphs leave it at 0.
7780
+ * @see lib/cgraph/graph.c:agnextseq (++clos->seq[AGRAPH])
7781
+ */
7782
+ subgSeqCounter = 0;
7783
+ /**
7784
+ * Names of graph attributes (AGRAPH) declared anywhere in this hierarchy —
7785
+ * the port's stand-in for cgraph's `Agsym_t` symbol table. Populated on the
7786
+ * ROOT only, at parse time, whenever a graph attribute is assigned on any
7787
+ * (sub)graph: setting `ordering=out` on a subgraph declares "ordering"
7788
+ * graph-wide with an empty default, so `agGraphAttr(root, "ordering")` returns
7789
+ * `""` (not undefined) — mirroring `agfindgraphattr`/`late_string`. Read via
7790
+ * `agGraphAttr` (model/cgraph-ops.ts); empty on non-root graphs.
7791
+ * @see lib/cgraph/attr.c:agattr (graph-wide declaration, "" default)
7792
+ */
7793
+ declaredGraphAttrs = /* @__PURE__ */ new Set();
7794
+ /** @see lib/cgraph/graph.c:agopen */
7795
+ constructor(name, kind, anonymous = false) {
7796
+ this.name = name;
7797
+ this.anonymous = anonymous;
7798
+ this.kind = kind;
7799
+ this.nodes = /* @__PURE__ */ new Map();
7800
+ this.edges = [];
7801
+ this.subgraphs = /* @__PURE__ */ new Map();
7802
+ this.attrs = /* @__PURE__ */ new Map();
7803
+ this.nodeDefaults = /* @__PURE__ */ new Map();
7804
+ this.edgeDefaults = /* @__PURE__ */ new Map();
7805
+ this.info = makeGraphInfo();
7806
+ this.parent = null;
7807
+ this.root = this;
7808
+ }
7809
+ };
7810
+
7339
7811
  // src/common/make-label.ts
7340
7812
  var DEFAULT_FONTSIZE2 = 14;
7341
7813
  var DEFAULT_FONTNAME = "Times-Roman";
@@ -7351,6 +7823,14 @@ function getPenColor(obj) {
7351
7823
  function rootOf(obj) {
7352
7824
  return obj instanceof Edge ? obj.tail.root : obj.root;
7353
7825
  }
7826
+ function inLabelOf(obj) {
7827
+ if (obj instanceof Edge) {
7828
+ const sep = obj.head.root.kind.endsWith("undirected") ? "--" : "->";
7829
+ return `in label of edge ${obj.tail.name} ${sep} ${obj.head.name}`;
7830
+ }
7831
+ if (obj instanceof Graph) return `in label of graph ${obj.name}`;
7832
+ return `in label of node ${obj.name}`;
7833
+ }
7354
7834
  function buildSpan(text, font, measured) {
7355
7835
  return {
7356
7836
  str: text,
@@ -7418,7 +7898,9 @@ function makeLabel(text, fontname, fontsize, fontcolor, measurer) {
7418
7898
  function makeAnyLabel(content, isHtml, font, measurer, obj) {
7419
7899
  if (isHtml && content !== "") {
7420
7900
  const imageSizer = obj !== void 0 ? rootOf(obj).info.gvc?.imageSizer : void 0;
7421
- return makeHtmlLabel(content, { ...font, pencolor: getPenColor(obj), imageSizer }, measurer);
7901
+ const { label, rv } = makeHtmlLabelRv(content, { ...font, pencolor: getPenColor(obj), imageSizer }, measurer);
7902
+ if (rv !== 0 && obj !== void 0) console.error(inLabelOf(obj));
7903
+ return label;
7422
7904
  }
7423
7905
  const subst2 = obj !== void 0 ? substObj(content, obj, false) : content;
7424
7906
  const text = htmlEntityUTF8(subst2);
@@ -8887,19 +9369,6 @@ var RenderJob = class {
8887
9369
  }
8888
9370
  };
8889
9371
 
8890
- // src/model/graphInfo.ts
8891
- function makeGraphInfo() {
8892
- return {
8893
- bb: { ll: { x: 0, y: 0 }, ur: { x: 0, y: 0 } },
8894
- rankdir: 0,
8895
- flags: 0,
8896
- charset: 0,
8897
- gui_state: 0,
8898
- has_labels: 0,
8899
- has_images: false
8900
- };
8901
- }
8902
-
8903
9372
  // src/common/shapeData.ts
8904
9373
  var DOGEAR = 1;
8905
9374
  var TAB = 2;
@@ -15489,146 +15958,6 @@ function ncross(ctx) {
15489
15958
  return total;
15490
15959
  }
15491
15960
 
15492
- // src/model/graph.ts
15493
- var Graph = class {
15494
- /** Graph name; agnameof equivalent. @see lib/cgraph/cgraph.h:agnameof */
15495
- name;
15496
- /**
15497
- * True when the graph was declared with NO name (`digraph {`), as opposed to
15498
- * an explicit empty name (`digraph "" {`). Both store `name === ''`, but
15499
- * cgraph gives the truly-anonymous root the internal id `%1` (agnameof) while
15500
- * the empty-named one keeps `""`. Only the imagemap `<map id/name>` reads this
15501
- * (map.ts:mapGraphName); dot/xdot re-serialization keeps `name` so an
15502
- * anonymous root round-trips as `digraph {`, not `digraph %1 {`.
15503
- * @see lib/cgraph/id.c:idmap (anon → `%1`) ; lib/cgraph/agraph.c:agnameof
15504
- */
15505
- anonymous;
15506
- /**
15507
- * Directed/strict classification; mirrors Agdesc_t.directed and
15508
- * Agdesc_t.strict. @see lib/cgraph/cgraph.h:Agdesc_s
15509
- */
15510
- kind;
15511
- /**
15512
- * Node set in insertion order, keyed by name.
15513
- * Mirrors Agraph_t.n_seq (sequence dict) merged with n_id access.
15514
- * @see lib/cgraph/cgraph.h:Agraph_s
15515
- */
15516
- nodes;
15517
- /**
15518
- * All edges owned by this graph (root graph only for ownership; subgraphs
15519
- * reference a subset). Mirrors Agraph_t.e_seq.
15520
- * @see lib/cgraph/cgraph.h:Agraph_s
15521
- */
15522
- edges;
15523
- /**
15524
- * Named subgraphs, keyed by name. Mirrors Agraph_t.g_seq/g_id.
15525
- * @see lib/cgraph/cgraph.h:Agraph_s
15526
- */
15527
- subgraphs;
15528
- /**
15529
- * String attributes (agget/agset equivalents).
15530
- * Replaces the Agattr_t / Agsym_t machinery for simple key-value access.
15531
- * @see lib/cgraph/cgraph.h:Agattr_s
15532
- */
15533
- attrs;
15534
- /** Default attributes applied to all nodes in this graph scope. */
15535
- nodeDefaults;
15536
- /** Default attributes applied to all edges in this graph scope. */
15537
- edgeDefaults;
15538
- /**
15539
- * Snapshot of the enclosing graphs' graph-attribute defaults (this graph ->
15540
- * root, inner wins) captured when this subgraph was OPENED during parsing.
15541
- * Graph attributes set on an ancestor AFTER this subgraph was created are not
15542
- * included — mirroring cgraph's parse-time agsubg defval copy. Used for
15543
- * order-correct label/font inheritance (e.g. a root `label=` declared after a
15544
- * cluster must NOT apply to that cluster). Undefined on the root graph.
15545
- * @see lib/cgraph/graph.c:agsubg (attr defval copy)
15546
- */
15547
- graphDefaultsSnapshot;
15548
- /**
15549
- * Keys in `attrs` the BUILDER seeded from `graphDefaultsSnapshot` rather than
15550
- * the input declaring them in this scope (see builder.ts GRAPH_LABEL_INHERIT_
15551
- * KEYS). cgraph distinguishes these structurally — a seeded value is inherited
15552
- * through the dict view and has no local symbol, while a local declaration
15553
- * creates one even when its value equals the inherited value — so the
15554
- * serializer needs the provenance to decide what write_dict would print.
15555
- * Undefined on the root graph and on any scope that seeded nothing.
15556
- * @see lib/cgraph/write.c:262 write_dict
15557
- */
15558
- seededAttrs;
15559
- /**
15560
- * Graph-attribute keys whose declaration in THIS scope was the first anywhere
15561
- * in the graph. cgraph's `setattr` splits on exactly this: a key with no
15562
- * existing symbol takes the global-declaration branch, which ends up seeding
15563
- * every direct subgraph that already exists with a local empty value (see
15564
- * `eagerEmptyParts`); a key some earlier scope already declared takes the
15565
- * "new local definition" branch and touches no subgraph at all. So an
15566
- * identical `graph [fill=x]` statement makes a child print `fill=""` or print
15567
- * nothing purely according to whether a SIBLING declared `fill` first.
15568
- * Undefined on any scope that declared nothing new.
15569
- * @see lib/cgraph/attr.c:257 setattr (lsym / psym / global branches)
15570
- */
15571
- firstGraphDecl;
15572
- /**
15573
- * Layout-engine info; replaces GD_* macros via agbindrec (AD-1).
15574
- * @see lib/cgraph/cgraph.h:agbindrec
15575
- */
15576
- info;
15577
- /**
15578
- * Immediate parent graph. null for root graphs.
15579
- * @see lib/cgraph/cgraph.h:Agraph_s
15580
- */
15581
- parent;
15582
- /**
15583
- * Root (main) graph. Self-referential for root graphs;
15584
- * points up for subgraphs.
15585
- * @see lib/cgraph/cgraph.h:Agraph_s
15586
- */
15587
- root;
15588
- /**
15589
- * Subgraph sequence number (AGSEQ). A global counter on the root, assigned
15590
- * at creation in source order, counting anonymous subgraphs. The root keeps
15591
- * the default 0 (par == NULL never calls agnextseq). Consumed by getObjId to
15592
- * emit cluster ids `clust<seq>`.
15593
- * @see lib/cgraph/cgraph.h:AGSEQ
15594
- * @see lib/cgraph/graph.c:agopen (AGSEQ(g) = agnextseq(par, AGRAPH))
15595
- */
15596
- seq = 0;
15597
- /**
15598
- * Root-only counter mirroring `clos->seq[AGRAPH]`: the running maximum AGSEQ
15599
- * handed out to subgraphs. Meaningful only on the root graph; `assignSubgSeq`
15600
- * pre-increments it. Other graphs leave it at 0.
15601
- * @see lib/cgraph/graph.c:agnextseq (++clos->seq[AGRAPH])
15602
- */
15603
- subgSeqCounter = 0;
15604
- /**
15605
- * Names of graph attributes (AGRAPH) declared anywhere in this hierarchy —
15606
- * the port's stand-in for cgraph's `Agsym_t` symbol table. Populated on the
15607
- * ROOT only, at parse time, whenever a graph attribute is assigned on any
15608
- * (sub)graph: setting `ordering=out` on a subgraph declares "ordering"
15609
- * graph-wide with an empty default, so `agGraphAttr(root, "ordering")` returns
15610
- * `""` (not undefined) — mirroring `agfindgraphattr`/`late_string`. Read via
15611
- * `agGraphAttr` (model/cgraph-ops.ts); empty on non-root graphs.
15612
- * @see lib/cgraph/attr.c:agattr (graph-wide declaration, "" default)
15613
- */
15614
- declaredGraphAttrs = /* @__PURE__ */ new Set();
15615
- /** @see lib/cgraph/graph.c:agopen */
15616
- constructor(name, kind, anonymous = false) {
15617
- this.name = name;
15618
- this.anonymous = anonymous;
15619
- this.kind = kind;
15620
- this.nodes = /* @__PURE__ */ new Map();
15621
- this.edges = [];
15622
- this.subgraphs = /* @__PURE__ */ new Map();
15623
- this.attrs = /* @__PURE__ */ new Map();
15624
- this.nodeDefaults = /* @__PURE__ */ new Map();
15625
- this.edgeDefaults = /* @__PURE__ */ new Map();
15626
- this.info = makeGraphInfo();
15627
- this.parent = null;
15628
- this.root = this;
15629
- }
15630
- };
15631
-
15632
15961
  // src/model/cgraph-ops.ts
15633
15962
  var LOCALNAMEPREFIX = "%";
15634
15963
  function freshNodeId(root) {
@@ -25057,18 +25386,22 @@ function dotSameports(g) {
25057
25386
  function dist2(p, q) {
25058
25387
  const dx = p.x - q.x;
25059
25388
  const dy = p.y - q.y;
25060
- return dx * dx + dy * dy;
25389
+ return fma(dx, dx, dy * dy);
25061
25390
  }
25062
25391
  function distPt(p, q) {
25063
25392
  return Math.sqrt(dist2(p, q));
25064
25393
  }
25065
- function evalBez4(pts, t) {
25394
+ function bezier(v, t) {
25066
25395
  const u = 1 - t;
25067
- const [p0, p1, p2, p3] = pts;
25068
- return {
25069
- x: u * u * u * p0.x + 3 * u * u * t * p1.x + 3 * u * t * t * p2.x + t * t * t * p3.x,
25070
- y: u * u * u * p0.y + 3 * u * u * t * p1.y + 3 * u * t * t * p2.y + t * t * t * p3.y
25071
- };
25396
+ const x = [v[0].x, v[1].x, v[2].x, v[3].x];
25397
+ const y = [v[0].y, v[1].y, v[2].y, v[3].y];
25398
+ for (let i = 1; i <= 3; i++) {
25399
+ for (let j = 0; j <= 3 - i; j++) {
25400
+ x[j] = fma(x[j], u, t * x[j + 1]);
25401
+ y[j] = fma(y[j], u, t * y[j + 1]);
25402
+ }
25403
+ }
25404
+ return { x: x[0], y: y[0] };
25072
25405
  }
25073
25406
  function dotneatoClosest(spl, pt) {
25074
25407
  let besti = 0;
@@ -25098,7 +25431,7 @@ function dotneatoClosest(spl, pt) {
25098
25431
  let pt2;
25099
25432
  for (; ; ) {
25100
25433
  const t = (lo + hi) / 2;
25101
- pt2 = evalBez4(c, t);
25434
+ pt2 = bezier(c, t);
25102
25435
  if (Math.abs(dlo2 - dhi2) < 1 || Math.abs(hi - lo) < 1e-5) break;
25103
25436
  if (dlo2 < dhi2) {
25104
25437
  hi = t;
@@ -26010,9 +26343,19 @@ var ShortestHelper = class _ShortestHelper {
26010
26343
  _ShortestHelper.connectAll(tris);
26011
26344
  const ftrii = _ShortestHelper.findTri(tris, eps[0]);
26012
26345
  const ltrii = _ShortestHelper.findTri(tris, eps[1]);
26013
- if (ftrii === NO_TRI || ltrii === NO_TRI) return null;
26346
+ if (ftrii === NO_TRI) {
26347
+ console.warn("source point not in any triangle");
26348
+ return null;
26349
+ }
26350
+ if (ltrii === NO_TRI) {
26351
+ console.warn("destination point not in any triangle");
26352
+ return null;
26353
+ }
26014
26354
  if (ftrii === ltrii) return [eps[0], eps[1]];
26015
- if (!_ShortestHelper.markPath(tris, ftrii, ltrii)) return [eps[0], eps[1]];
26355
+ if (!_ShortestHelper.markPath(tris, ftrii, ltrii)) {
26356
+ console.warn("cannot find triangle path");
26357
+ return [eps[0], eps[1]];
26358
+ }
26016
26359
  const epnls = [{ pp: eps[0], link: null }, { pp: eps[1], link: null }];
26017
26360
  const dq = _ShortestHelper.initDeque(pnlps.length);
26018
26361
  _ShortestHelper.add2dq(dq, DQ_FRONT, epnls[0]);
@@ -27994,7 +28337,7 @@ function nodeCover(n) {
27994
28337
  for (let i = 0; i < NODECARD; i++) {
27995
28338
  if (n.branch[i].child !== null) {
27996
28339
  if (flag) {
27997
- r = n.branch[i].rect;
28340
+ r = { boundary: [...n.branch[i].rect.boundary] };
27998
28341
  flag = false;
27999
28342
  } else {
28000
28343
  r = combineRect(r, n.branch[i].rect);
@@ -30260,8 +30603,8 @@ function nodeInsideFn2(box) {
30260
30603
  const pw = box.penwidth ?? DEFAULT_NODEPENWIDTH;
30261
30604
  return box.isEllipse ? makeEllipseInsideFn(halfW, halfH, pw) : makeBoxInsideFn(halfW, halfH, pw);
30262
30605
  }
30263
- function clipToNodes(bezier, tailBox, headBox, penwidth = DEFAULT_NODEPENWIDTH, clip = CLIP_BOTH) {
30264
- const step1 = clip.tail ? bezierClipNode(bezier, tailBox.center.x, tailBox.center.y, nodeInsideFn2(tailBox), true) : bezier;
30606
+ function clipToNodes(bezier2, tailBox, headBox, penwidth = DEFAULT_NODEPENWIDTH, clip = CLIP_BOTH) {
30607
+ const step1 = clip.tail ? bezierClipNode(bezier2, tailBox.center.x, tailBox.center.y, nodeInsideFn2(tailBox), true) : bezier2;
30265
30608
  const step2 = clip.head ? bezierClipNode(step1, headBox.center.x, headBox.center.y, nodeInsideFn2(headBox), false) : step1;
30266
30609
  const arrowTip = step2[3];
30267
30610
  const elen = normalArrowLen(penwidth);
@@ -30275,9 +30618,9 @@ function clipToNodes(bezier, tailBox, headBox, penwidth = DEFAULT_NODEPENWIDTH,
30275
30618
  function routeWithRank(tailBox, headBox, rank3, penwidth = DEFAULT_NODEPENWIDTH, port) {
30276
30619
  const corridor = port ? { tailP: port.tailP, headP: port.headP } : void 0;
30277
30620
  const { startPt, endPt, boxes } = buildRankCorridor(tailBox, headBox, rank3, corridor);
30278
- const bezier = computeSpline(boxes, startPt, endPt);
30621
+ const bezier2 = computeSpline(boxes, startPt, endPt);
30279
30622
  const clip = port ? { tail: port.clipTail, head: port.clipHead } : CLIP_BOTH;
30280
- const { clipped, arrowTip, arrowDir } = clipToNodes(bezier, tailBox, headBox, penwidth, clip);
30623
+ const { clipped, arrowTip, arrowDir } = clipToNodes(bezier2, tailBox, headBox, penwidth, clip);
30281
30624
  return { bezierPts: clipped, arrowTip, arrowDir };
30282
30625
  }
30283
30626
  function routeSimple(tailBox, headBox, routeBezierFn) {
@@ -32870,24 +33213,27 @@ function buildNodes(nodeArr) {
32870
33213
  ysize: n.info.ht
32871
33214
  }));
32872
33215
  }
33216
+ function isConcentrateDup(ps, e) {
33217
+ const ti = e.tail.id;
33218
+ const hi = e.head.id;
33219
+ const lo = ti <= hi ? ti : hi;
33220
+ const up = ti <= hi ? hi : ti;
33221
+ if (ps.isInPS(lo, up)) return true;
33222
+ ps.addPS(lo, up);
33223
+ return false;
33224
+ }
32873
33225
  function buildEdges(g, nodeArr, orthoNodes) {
32874
33226
  const concentrate = dotRoot(g).info.concentrate ?? false;
32875
33227
  const nodeIndex = /* @__PURE__ */ new Map();
32876
33228
  nodeArr.forEach((n, i) => nodeIndex.set(n, i));
32877
33229
  const outIndex = buildOutEdgeIndex(g);
32878
- const seen = /* @__PURE__ */ new Set();
33230
+ const ps = concentrate ? new PointSet() : null;
32879
33231
  const result = [];
32880
33232
  for (const n of nodeArr) {
32881
33233
  const oes = outIndex.get(n);
32882
33234
  if (oes === void 0) continue;
32883
33235
  for (const e of oes) {
32884
- if (concentrate) {
32885
- const ti = e.tail.id;
32886
- const hi = e.head.id;
32887
- const key = ti <= hi ? `${ti},${hi}` : `${hi},${ti}`;
32888
- if (seen.has(key)) continue;
32889
- seen.add(key);
32890
- }
33236
+ if (ps !== null && isConcentrateDup(ps, e)) continue;
32891
33237
  result.push({
32892
33238
  tail: orthoNodes[nodeIndex.get(e.tail)],
32893
33239
  head: orthoNodes[nodeIndex.get(e.head)],
@@ -33975,15 +34321,6 @@ function spiralSearch(info, ctx, wide) {
33975
34321
 
33976
34322
  // src/layout/pack/poly-pack.ts
33977
34323
  var C2 = 100;
33978
- function psKey(x, y) {
33979
- return `${cround(x)},${cround(y)}`;
33980
- }
33981
- function inPS(ps, p) {
33982
- return ps.has(psKey(p.x, p.y));
33983
- }
33984
- function insertPS(ps, p) {
33985
- ps.add(psKey(p.x, p.y));
33986
- }
33987
34324
  function computeStep(ng, bbs, margin) {
33988
34325
  let b = 0;
33989
34326
  let c = 0;
@@ -34030,14 +34367,14 @@ function genBox2(p) {
34030
34367
  function hasCollision(cells, dx, dy, ps) {
34031
34368
  for (let i = 0; i < cells.length; i++) {
34032
34369
  const cell2 = cells[i];
34033
- if (cell2 !== void 0 && inPS(ps, { x: cell2.x + dx, y: cell2.y + dy })) return true;
34370
+ if (cell2 !== void 0 && ps.inPS({ x: cell2.x + dx, y: cell2.y + dy })) return true;
34034
34371
  }
34035
34372
  return false;
34036
34373
  }
34037
34374
  function markCells(cells, dx, dy, ps) {
34038
34375
  for (let i = 0; i < cells.length; i++) {
34039
34376
  const cell2 = cells[i];
34040
- if (cell2 !== void 0) insertPS(ps, { x: cell2.x + dx, y: cell2.y + dy });
34377
+ if (cell2 !== void 0) ps.addPS(cell2.x + dx, cell2.y + dy);
34041
34378
  }
34042
34379
  }
34043
34380
  function fits(x, y, info, ctx) {
@@ -34082,7 +34419,7 @@ function polyRects(ng, bbs, pinfo) {
34082
34419
  infos.push(genBox2({ bb, ssize: step, margin: pinfo.margin, idx: i }));
34083
34420
  }
34084
34421
  const sorted = gvQsort([...infos], cmpByGPerimeter);
34085
- const ctx = { ps: /* @__PURE__ */ new Set(), step, bbs };
34422
+ const ctx = { ps: new PointSet(), step, bbs };
34086
34423
  const places = new Array(ng).fill({ x: 0, y: 0 });
34087
34424
  for (let i = 0; i < sorted.length; i++) {
34088
34425
  const info = sorted[i];
@@ -34103,21 +34440,6 @@ function cval2(v, s) {
34103
34440
  function cell(p, s) {
34104
34441
  return { x: cval2(p.x, s), y: cval2(p.y, s) };
34105
34442
  }
34106
- var PointSet = class {
34107
- m = /* @__PURE__ */ new Map();
34108
- add(x, y) {
34109
- this.m.set(`${x},${y}`, { x, y });
34110
- }
34111
- has(p) {
34112
- return this.m.has(`${p.x},${p.y}`);
34113
- }
34114
- points() {
34115
- return [...this.m.values()];
34116
- }
34117
- get size() {
34118
- return this.m.size;
34119
- }
34120
- };
34121
34443
  function computeStep2(bbs, margin) {
34122
34444
  const a = C_AVG * bbs.length - 1;
34123
34445
  let b = 0;
@@ -34135,7 +34457,7 @@ function computeStep2(bbs, margin) {
34135
34457
  function lineXDominant(st, ps) {
34136
34458
  let d = st.ay - (st.ax >> 1);
34137
34459
  for (; ; ) {
34138
- ps.add(st.x, st.y);
34460
+ ps.addPS(st.x, st.y);
34139
34461
  if (st.x === st.x2) return;
34140
34462
  if (d >= 0) {
34141
34463
  st.y += st.sy;
@@ -34148,7 +34470,7 @@ function lineXDominant(st, ps) {
34148
34470
  function lineYDominant(st, ps) {
34149
34471
  let d = st.ax - (st.ay >> 1);
34150
34472
  for (; ; ) {
34151
- ps.add(st.x, st.y);
34473
+ ps.addPS(st.x, st.y);
34152
34474
  if (st.y === st.y2) return;
34153
34475
  if (d >= 0) {
34154
34476
  st.x += st.sx;
@@ -34180,7 +34502,7 @@ function fillLine(p, q, ps) {
34180
34502
  }
34181
34503
  function fillRect(LL, UR, ps) {
34182
34504
  for (let x = LL.x; x <= UR.x; x++) {
34183
- for (let y = LL.y; y <= UR.y; y++) ps.add(x, y);
34505
+ for (let y = LL.y; y <= UR.y; y++) ps.addPS(x, y);
34184
34506
  }
34185
34507
  }
34186
34508
  function roundedCell(p, ssize) {
@@ -34232,7 +34554,7 @@ function genBox3(bb0, ssize, margin, center) {
34232
34554
  const UR = roundedCell({ x: center.x + bbW + margin, y: center.y + bbH + margin }, ssize);
34233
34555
  fillRect(LL, UR, ps);
34234
34556
  return {
34235
- cells: ps.points(),
34557
+ cells: ps.pointsOf(),
34236
34558
  perim: grid(bb0.ur.x - bb0.ll.x + 2 * margin, ssize) + grid(bb0.ur.y - bb0.ll.y + 2 * margin, ssize),
34237
34559
  index: 0
34238
34560
  };
@@ -34264,42 +34586,38 @@ function genPoly(root, g, bb, ssize, pinfo, center) {
34264
34586
  };
34265
34587
  for (const n of g.nodes.values()) coverNode(n, ctx);
34266
34588
  return {
34267
- cells: ps.points(),
34589
+ cells: ps.pointsOf(),
34268
34590
  perim: grid(bb.ur.x - bb.ll.x + 2 * pinfo.margin, ssize) + grid(bb.ur.y - bb.ll.y + 2 * pinfo.margin, ssize),
34269
34591
  index: 0
34270
34592
  };
34271
34593
  }
34272
34594
  function fits2(x, y, info, ctx) {
34273
34595
  for (const c of info.cells) {
34274
- if (ctx.ps.has({ x: c.x + x, y: c.y + y })) return false;
34596
+ if (ctx.ps.isInPS(c.x + x, c.y + y)) return false;
34275
34597
  }
34276
34598
  const bb = ctx.bbs[info.index];
34277
34599
  const LL = { x: cround(bb.ll.x), y: cround(bb.ll.y) };
34278
34600
  ctx.places[info.index] = { x: Math.imul(ctx.step, x) - LL.x, y: Math.imul(ctx.step, y) - LL.y };
34279
- for (const c of info.cells) ctx.ps.add(c.x + x, c.y + y);
34601
+ for (const c of info.cells) ctx.ps.addPS(c.x + x, c.y + y);
34280
34602
  return true;
34281
34603
  }
34282
- function* ringWide(bnd) {
34283
- let x = 0;
34284
- let y = -bnd;
34285
- for (; x < bnd; x++) yield [x, y];
34286
- for (; y < bnd; y++) yield [x, y];
34287
- for (; x > -bnd; x--) yield [x, y];
34288
- for (; y > -bnd; y--) yield [x, y];
34289
- for (; x < 0; x++) yield [x, y];
34290
- }
34291
- function* ringTall(bnd) {
34292
- let y = 0;
34293
- let x = -bnd;
34294
- for (; y > -bnd; y--) yield [x, y];
34295
- for (; x < bnd; x++) yield [x, y];
34296
- for (; y < bnd; y++) yield [x, y];
34297
- for (; x > -bnd; x--) yield [x, y];
34298
- for (; y > 0; y--) yield [x, y];
34604
+ function leg(c, dx, dy, end, info, ctx) {
34605
+ for (; (dx !== 0 ? c.x : c.y) !== end; c.x += dx, c.y += dy) {
34606
+ if (fits2(c.x, c.y, info, ctx)) return true;
34607
+ }
34608
+ return false;
34609
+ }
34610
+ function ringWide(bnd, info, ctx) {
34611
+ const c = { x: 0, y: -bnd };
34612
+ return leg(c, 1, 0, bnd, info, ctx) || leg(c, 0, 1, bnd, info, ctx) || leg(c, -1, 0, -bnd, info, ctx) || leg(c, 0, -1, -bnd, info, ctx) || leg(c, 1, 0, 0, info, ctx);
34613
+ }
34614
+ function ringTall(bnd, info, ctx) {
34615
+ const c = { x: -bnd, y: 0 };
34616
+ return leg(c, 0, -1, -bnd, info, ctx) || leg(c, 1, 0, bnd, info, ctx) || leg(c, 0, 1, bnd, info, ctx) || leg(c, -1, 0, -bnd, info, ctx) || leg(c, 0, -1, 0, info, ctx);
34299
34617
  }
34300
34618
  function placeFixed(info, ctx, center) {
34301
34619
  ctx.places[info.index] = { x: -center.x, y: -center.y };
34302
- for (const c of info.cells) ctx.ps.add(c.x, c.y);
34620
+ for (const c of info.cells) ctx.ps.addPS(c.x, c.y);
34303
34621
  }
34304
34622
  function placeGraph2(i, info, ctx) {
34305
34623
  const bb = ctx.bbs[info.index];
@@ -34311,10 +34629,7 @@ function placeGraph2(i, info, ctx) {
34311
34629
  if (fits2(0, 0, info, ctx)) return;
34312
34630
  const wide = Math.ceil(bb.ur.x - bb.ll.x) >= Math.ceil(bb.ur.y - bb.ll.y);
34313
34631
  for (let bnd = 1; ; bnd++) {
34314
- const ring = wide ? ringWide(bnd) : ringTall(bnd);
34315
- for (const [x, y] of ring) {
34316
- if (fits2(x, y, info, ctx)) return;
34317
- }
34632
+ if (wide ? ringWide(bnd, info, ctx) : ringTall(bnd, info, ctx)) return;
34318
34633
  }
34319
34634
  }
34320
34635
  function polyGraphs(gs, root, pinfo, bbs) {
@@ -37954,6 +38269,12 @@ function chainOf(e) {
37954
38269
  }
37955
38270
  return list;
37956
38271
  }
38272
+ function warnNoControlPoints(e) {
38273
+ console.warn(
38274
+ `Warning: Could not create control points for multiple spline for edge (${e.tail.name},${e.head.name})`
38275
+ );
38276
+ return 1;
38277
+ }
37957
38278
  function genroute(g, trip, t, e, doPolyline) {
37958
38279
  const eps = [
37959
38280
  { x: trip.ps[0].x, y: trip.ps[0].y },
@@ -37963,7 +38284,7 @@ function genroute(g, trip, t, e, doPolyline) {
37963
38284
  const mult = e.info.count ?? 1;
37964
38285
  const head = e.head;
37965
38286
  const pl = shortestPath({ ps: trip.ps }, eps);
37966
- if (pl === null) return 1;
38287
+ if (pl === null) return warnNoControlPoints(e);
37967
38288
  if (pl.length === 2) {
37968
38289
  const chain = chainOf(e);
37969
38290
  makeStraightEdges(g, chain, chain.length, doPolyline ? 1 : 0, MSINFO);
@@ -37971,7 +38292,7 @@ function genroute(g, trip, t, e, doPolyline) {
37971
38292
  }
37972
38293
  if (mult === 1 || concentrate) {
37973
38294
  const spl = routeMember(trip.ps, t, pl, doPolyline);
37974
- if (spl === null) return 1;
38295
+ if (spl === null) return warnNoControlPoints(e);
37975
38296
  finishEdge(e, spl, e.head !== head);
37976
38297
  return 0;
37977
38298
  }
@@ -37979,7 +38300,7 @@ function genroute(g, trip, t, e, doPolyline) {
37979
38300
  const cpts = [];
37980
38301
  for (let i = 0; i + 2 < pl.length; i++) {
37981
38302
  const cp = mkCtrlPts(t, mult + 1, pl[i], pl[i + 1], pl[i + 2], trip);
37982
- if (cp === null) return 1;
38303
+ if (cp === null) return warnNoControlPoints(e);
37983
38304
  cpts.push(cp);
37984
38305
  }
37985
38306
  let cur = e;
@@ -37994,9 +38315,9 @@ function genroute(g, trip, t, e, doPolyline) {
37994
38315
  poly[pn - j] = cpts[j - 1][i + 1];
37995
38316
  }
37996
38317
  const mmpl = shortestPath({ ps: poly }, eps);
37997
- if (mmpl === null) return 1;
38318
+ if (mmpl === null) return warnNoControlPoints(cur);
37998
38319
  const spl = routeMember(poly, pl.length - 1, mmpl, doPolyline);
37999
- if (spl === null) return 1;
38320
+ if (spl === null) return warnNoControlPoints(cur);
38000
38321
  finishEdge(cur, spl, cur.head !== head);
38001
38322
  cur = cur.info.to_virt;
38002
38323
  }
@@ -47220,22 +47541,22 @@ function freeBlocktree(bp) {
47220
47541
 
47221
47542
  // src/layout/circo/position-helpers.ts
47222
47543
  function rotatePoint(x, y, cosR, sinR) {
47223
- return [x * cosR - y * sinR, x * sinR + y * cosR];
47544
+ return [fma(x, cosR, -sinR * y), fma(x, sinR, cosR * y)];
47224
47545
  }
47225
47546
  function nodePsi(n) {
47226
47547
  return n.info.alg?.psi ?? 0;
47227
47548
  }
47228
47549
  function rotFromParentPos(sn, theta) {
47229
- let r = theta + Math.PI - sn.parentPos;
47550
+ let r = theta + (Math.PI - sn.parentPos);
47230
47551
  if (r < 0) r += 2 * Math.PI;
47231
47552
  return r;
47232
47553
  }
47233
47554
  function closestNode(sn, x, y, skip) {
47234
47555
  let best = skip;
47235
- let bestDist = Math.hypot(skip.info.pos[0] + x, skip.info.pos[1] + y);
47556
+ let bestDist = libmHypot(skip.info.pos[0] + x, skip.info.pos[1] + y);
47236
47557
  for (const dn of sn.subGraph.nodes) {
47237
47558
  if (dn.orig === skip) continue;
47238
- const d = Math.hypot(dn.orig.info.pos[0] + x, dn.orig.info.pos[1] + y);
47559
+ const d = libmHypot(dn.orig.info.pos[0] + x, dn.orig.info.pos[1] + y);
47239
47560
  if (d < bestDist) {
47240
47561
  bestDist = d;
47241
47562
  best = dn.orig;
@@ -47248,22 +47569,19 @@ function rotCoalesced(sn, x, y, nbr, theta) {
47248
47569
  const r = sn.radius - rho;
47249
47570
  const nx = nbr.info.pos[0];
47250
47571
  const ny = nbr.info.pos[1];
47251
- if (-r >= nx) {
47252
- const phi2 = Math.atan2(ny, nx);
47253
- return theta + Math.PI - phi2 - nodePsi(nbr);
47254
- }
47255
- const R2 = Math.hypot(x, y);
47572
+ const R2 = libmHypot(x, y);
47256
47573
  const phi = Math.atan2(ny, nx + r);
47257
47574
  const l = r - rho / Math.cos(phi);
47258
- return theta + Math.PI / 2 - phi - Math.asin(l / R2 * Math.cos(phi));
47575
+ return theta + (Math.PI / 2 - phi - Math.asin(l / R2 * Math.cos(phi)));
47259
47576
  }
47260
47577
  function rotGeneral(sn, x, y, nbr, theta) {
47261
47578
  if (closestNode(sn, x, y, nbr) === nbr) return 0;
47262
- if (isCoalesced(sn)) return rotCoalesced(sn, x, y, nbr, theta);
47579
+ const r = sn.radius - sn.rad0;
47580
+ if (isCoalesced(sn) && -r < nbr.info.pos[0]) return rotCoalesced(sn, x, y, nbr, theta);
47263
47581
  const phi = Math.atan2(nbr.info.pos[1], nbr.info.pos[0]);
47264
- let r = theta + Math.PI - phi - nodePsi(nbr);
47265
- if (r > 2 * Math.PI) r -= 2 * Math.PI;
47266
- return r;
47582
+ let t = theta + (Math.PI - phi - nodePsi(nbr));
47583
+ if (t > 2 * Math.PI) t -= 2 * Math.PI;
47584
+ return t;
47267
47585
  }
47268
47586
  function applyDelta(sn, x, y, rotate) {
47269
47587
  const cosR = Math.cos(rotate);
@@ -47306,7 +47624,7 @@ function getInfo(pi, stp, minDist) {
47306
47624
  }
47307
47625
  function setInfo(p0, p1, delta2) {
47308
47626
  const t = Math.max(
47309
- (p0.diameter * p1.minRadius + p1.diameter * p0.minRadius) / (2 * delta2 * p0.minRadius * p1.minRadius),
47627
+ fma(p0.diameter, p1.minRadius, p1.diameter * p0.minRadius) / (2 * delta2 * p0.minRadius * p1.minRadius),
47310
47628
  1
47311
47629
  );
47312
47630
  p0.scale = Math.max(p0.scale, t);
@@ -47316,7 +47634,13 @@ function placeChild(child, ca, ctx) {
47316
47634
  const dx = ctx.childRadius * Math.cos(ca);
47317
47635
  const dy = ctx.childRadius * Math.sin(ca);
47318
47636
  applyDelta(child, dx, dy, getRotation(child, dx, dy, ca));
47319
- return ctx.length === 1 ? ca + ctx.incAngle + ctx.mindistAngle : ca + ctx.incAngle + ctx.mindistAngle / 2;
47637
+ return nextChildAngle(ca, ctx.incAngle, ctx.mindistAngle, ctx.length);
47638
+ }
47639
+ function nextChildAngle(ca, inc2, mda, length) {
47640
+ return length === 1 ? ca + (inc2 + mda) : ca + (inc2 + mda / 2);
47641
+ }
47642
+ function leftoverArc(childRadius, diameter) {
47643
+ return fma(2 * Math.PI, childRadius, -diameter);
47320
47644
  }
47321
47645
  function advanceAngleL1(ca, inc2, total, cnt, stp) {
47322
47646
  const next = cnt === 0 ? 0 : total === 2 ? Math.PI : ca + inc2;
@@ -47326,7 +47650,7 @@ function advanceAngleL1(ca, inc2, total, cnt, stp) {
47326
47650
  }
47327
47651
  function advanceAngleLN(ca, inc2, mda, total, theta) {
47328
47652
  if (total === 1) return theta;
47329
- return ca + inc2 + mda / 2;
47653
+ return ca + (inc2 + mda / 2);
47330
47654
  }
47331
47655
  function maybeWritePsi(n, stp, ca, isMid) {
47332
47656
  if (isMid && n === stp.neighbor) {
@@ -47341,7 +47665,7 @@ function positionChildren(pi, stp, length, minDist) {
47341
47665
  let md = minDist;
47342
47666
  if (length === 1) {
47343
47667
  cr = Math.max(cr, pi.diameter / (2 * Math.PI));
47344
- const d = 2 * Math.PI * cr - pi.diameter;
47668
+ const d = leftoverArc(cr, pi.diameter);
47345
47669
  if (d > 0) md += d / pi.childCount;
47346
47670
  }
47347
47671
  stp.subtreeR = Math.max(stp.subtreeR, cr + pi.maxRadius);
@@ -54370,11 +54694,11 @@ function lexToken(s, i) {
54370
54694
  function scanNumberAmbiguities(src, stripped) {
54371
54695
  const out = [];
54372
54696
  let line = 1;
54373
- let lineAt = 0;
54697
+ let lineAt2 = 0;
54374
54698
  for (let i = 0; i < stripped.length; ) {
54375
54699
  const { next, bad } = lexToken(stripped, i);
54376
54700
  if (bad !== null) {
54377
- for (; lineAt < i; lineAt++) if (src[lineAt] === "\n") line++;
54701
+ for (; lineAt2 < i; lineAt2++) if (src[lineAt2] === "\n") line++;
54378
54702
  out.push({ text: bad, line, offset: i });
54379
54703
  }
54380
54704
  i = next;