@knowvah/dot-engine 2.6.12 → 2.7.0
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.
- package/README.md +1 -1
- package/dist/api.js +18 -4
- package/dist/api.js.map +3 -3
- package/dist/common/htmltable-emit-fill.d.ts +20 -2
- package/dist/common/htmltable-emit-fill.d.ts.map +1 -1
- package/dist/common/htmltable-emit.d.ts.map +1 -1
- package/dist/common/htmltable-lex-expat.d.ts.map +1 -1
- package/dist/common/htmltable-pos-runs.d.ts +7 -4
- package/dist/common/htmltable-pos-runs.d.ts.map +1 -1
- package/dist/common/htmltable.d.ts.map +1 -1
- package/dist/common/libm-trig.d.ts +5 -0
- package/dist/common/libm-trig.d.ts.map +1 -0
- package/dist/common/multicolor.d.ts +21 -13
- package/dist/common/multicolor.d.ts.map +1 -1
- package/dist/common/poly-gencode.d.ts +1 -2
- package/dist/common/poly-gencode.d.ts.map +1 -1
- package/dist/common/poly-init.d.ts.map +1 -1
- package/dist/common/splines-clip.d.ts +7 -0
- package/dist/common/splines-clip.d.ts.map +1 -1
- package/dist/gvc/context.d.ts +6 -0
- package/dist/gvc/context.d.ts.map +1 -1
- package/dist/gvc/device-cluster.d.ts +15 -0
- package/dist/gvc/device-cluster.d.ts.map +1 -1
- package/dist/gvc/device.d.ts.map +1 -1
- package/dist/gvc/first-layer.d.ts +9 -0
- package/dist/gvc/first-layer.d.ts.map +1 -0
- package/dist/gvc/gvrender-set-style.d.ts +28 -0
- package/dist/gvc/gvrender-set-style.d.ts.map +1 -0
- package/dist/gvc/usershape.d.ts +18 -0
- package/dist/gvc/usershape.d.ts.map +1 -1
- package/dist/index.js +2460 -901
- package/dist/index.js.map +4 -4
- package/dist/layout/dot/compound.d.ts.map +1 -1
- package/dist/layout/dot/edge-route-clip.d.ts +14 -4
- package/dist/layout/dot/edge-route-clip.d.ts.map +1 -1
- package/dist/layout/dot/edge-route-routing.d.ts.map +1 -1
- package/dist/layout/neato/__fixtures__/dijkstra-f.d.ts +11 -0
- package/dist/layout/neato/__fixtures__/dijkstra-f.d.ts.map +1 -0
- package/dist/layout/neato/__fixtures__/hier-imds.d.ts +36 -0
- package/dist/layout/neato/__fixtures__/hier-imds.d.ts.map +1 -0
- package/dist/layout/neato/__fixtures__/hier-majorization.d.ts +32 -0
- package/dist/layout/neato/__fixtures__/hier-majorization.d.ts.map +1 -0
- package/dist/layout/neato/cdt-surface.d.ts.map +1 -1
- package/dist/layout/neato/compute-hierarchy.d.ts +24 -0
- package/dist/layout/neato/compute-hierarchy.d.ts.map +1 -0
- package/dist/layout/neato/conjgrad-mkernel.d.ts +20 -0
- package/dist/layout/neato/conjgrad-mkernel.d.ts.map +1 -0
- package/dist/layout/neato/conjgrad.d.ts +19 -0
- package/dist/layout/neato/conjgrad.d.ts.map +1 -1
- package/dist/layout/neato/constrained-majorization.d.ts +26 -0
- package/dist/layout/neato/constrained-majorization.d.ts.map +1 -0
- package/dist/layout/neato/dijkstra.d.ts +33 -16
- package/dist/layout/neato/dijkstra.d.ts.map +1 -1
- package/dist/layout/neato/gts-triangle.d.ts +14 -0
- package/dist/layout/neato/gts-triangle.d.ts.map +1 -0
- package/dist/layout/neato/init.d.ts +2 -12
- package/dist/layout/neato/init.d.ts.map +1 -1
- package/dist/layout/neato/kkutils.d.ts +20 -0
- package/dist/layout/neato/kkutils.d.ts.map +1 -0
- package/dist/layout/neato/make-graph-data.d.ts +43 -0
- package/dist/layout/neato/make-graph-data.d.ts.map +1 -0
- package/dist/layout/neato/matrix-ops.d.ts +46 -0
- package/dist/layout/neato/matrix-ops.d.ts.map +1 -1
- package/dist/layout/neato/opt-arrangement.d.ts +20 -0
- package/dist/layout/neato/opt-arrangement.d.ts.map +1 -0
- package/dist/layout/neato/quad-prog-solve.d.ts +44 -0
- package/dist/layout/neato/quad-prog-solve.d.ts.map +1 -0
- package/dist/layout/neato/smart-ini-x.d.ts +9 -0
- package/dist/layout/neato/smart-ini-x.d.ts.map +1 -0
- package/dist/layout/neato/splines.d.ts +1 -1
- package/dist/layout/neato/splines.d.ts.map +1 -1
- package/dist/layout/neato/start.d.ts +4 -1
- package/dist/layout/neato/start.d.ts.map +1 -1
- package/dist/layout/neato/stress.d.ts +8 -0
- package/dist/layout/neato/stress.d.ts.map +1 -1
- package/dist/model/edge.d.ts +1 -1
- package/dist/model/edge.d.ts.map +1 -1
- package/dist/model/lazy-attrs.d.ts +10 -0
- package/dist/model/lazy-attrs.d.ts.map +1 -0
- package/dist/model/node.d.ts +1 -1
- package/dist/model/node.d.ts.map +1 -1
- package/dist/ortho/index.d.ts.map +1 -1
- package/dist/render/dot/edge-draw.d.ts +1 -1
- package/dist/render/dot/edge-draw.d.ts.map +1 -1
- package/dist/render/svg-multicolor.d.ts +2 -2
- package/dist/render/svg-multicolor.d.ts.map +1 -1
- package/dist/render.js +2340 -781
- package/dist/render.js.map +4 -4
- package/package.json +1 -1
- package/src/common/graph-init.ts +1 -1
- package/src/common/htmltable-emit-fill.ts +56 -11
- package/src/common/htmltable-emit.ts +4 -3
- package/src/common/htmltable-lex-expat.ts +21 -3
- package/src/common/htmltable-pos-runs.ts +11 -6
- package/src/common/htmltable.ts +28 -116
- package/src/common/libm-trig.ts +43 -0
- package/src/common/multicolor.ts +88 -24
- package/src/common/poly-gencode.ts +16 -8
- package/src/common/poly-init.ts +19 -1
- package/src/common/postproc.ts +2 -2
- package/src/common/splines-clip.ts +15 -12
- package/src/common/splines-routespl.ts +2 -2
- package/src/common/textmeasure-lookup.ts +1 -1
- package/src/gvc/context.ts +8 -0
- package/src/gvc/device-cluster.ts +63 -8
- package/src/gvc/device.ts +16 -2
- package/src/gvc/first-layer.ts +26 -0
- package/src/gvc/gvrender-set-style.ts +114 -0
- package/src/gvc/usershape.ts +26 -0
- package/src/layout/dot/cluster.ts +1 -1
- package/src/layout/dot/compound.ts +3 -4
- package/src/layout/dot/conc.ts +1 -1
- package/src/layout/dot/edge-route-clip.ts +19 -7
- package/src/layout/dot/edge-route-routing.ts +11 -3
- package/src/layout/dot/fastgr.ts +2 -2
- package/src/layout/neato/__fixtures__/dijkstra-f.ts +17 -0
- package/src/layout/neato/__fixtures__/hier/c1554.gv +33 -0
- package/src/layout/neato/__fixtures__/hier/c1554.plain +26 -0
- package/src/layout/neato/__fixtures__/hier/c1554.xdot +161 -0
- package/src/layout/neato/__fixtures__/hier/cases/cycle.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/cycle.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/cycle.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/dirnone.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/dirnone.plain +11 -0
- package/src/layout/neato/__fixtures__/hier/cases/dirnone.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/epsilon.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/epsilon.plain +13 -0
- package/src/layout/neato/__fixtures__/hier/cases/epsilon.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/levelsgap.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/levelsgap.plain +8 -0
- package/src/layout/neato/__fixtures__/hier/cases/levelsgap.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/mds.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/mds.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/mds.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/multiedge.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/multiedge.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/multiedge.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.plain +8 -0
- package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/startrandom.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/startrandom.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/startrandom.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/startregular.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/startregular.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/startregular.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/tdist.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/tdist.plain +13 -0
- package/src/layout/neato/__fixtures__/hier/cases/tdist.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/undirected.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/undirected.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/undirected.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/hier-probe.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/hier-probe.plain +16 -0
- package/src/layout/neato/__fixtures__/hier/hier-probe.xdot +62 -0
- package/src/layout/neato/__fixtures__/hier-imds.ts +151 -0
- package/src/layout/neato/__fixtures__/hier-majorization.ts +145 -0
- package/src/layout/neato/cdt-surface.ts +6 -16
- package/src/layout/neato/compute-hierarchy.ts +90 -0
- package/src/layout/neato/conjgrad-mkernel.ts +141 -0
- package/src/layout/neato/conjgrad.ts +208 -0
- package/src/layout/neato/constrained-majorization.ts +479 -0
- package/src/layout/neato/dijkstra.ts +218 -92
- package/src/layout/neato/edge-len.ts +2 -2
- package/src/layout/neato/fdp-adjust.ts +3 -3
- package/src/layout/neato/gts-triangle.ts +42 -0
- package/src/layout/neato/init.ts +57 -57
- package/src/layout/neato/kk.ts +2 -2
- package/src/layout/neato/kkutils.ts +96 -0
- package/src/layout/neato/make-graph-data.ts +166 -0
- package/src/layout/neato/matrix-ops.ts +138 -0
- package/src/layout/neato/opt-arrangement.ts +86 -0
- package/src/layout/neato/quad-prog-solve.ts +489 -0
- package/src/layout/neato/smart-ini-x.ts +503 -0
- package/src/layout/neato/splines.ts +4 -4
- package/src/layout/neato/start.ts +7 -6
- package/src/layout/neato/stress.ts +28 -0
- package/src/model/edge.ts +3 -2
- package/src/model/lazy-attrs.ts +36 -0
- package/src/model/node.ts +3 -2
- package/src/ortho/index.ts +2 -3
- package/src/parser/dot.js +1 -1
- package/src/parser/dot.pegjs +1 -1
- package/src/parser/index.ts +1 -1
- package/src/render/color-resolve.ts +2 -2
- package/src/render/dot/edge-draw.ts +6 -3
- package/src/render/dot.ts +1 -1
- package/src/render/svg-edge-split.ts +2 -2
- package/src/render/svg-multicolor.ts +4 -3
- package/src/render/svg-parallel-edge.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knowvah/dot-engine",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "Faithful TypeScript port of Graphviz (dot, neato, fdp, sfdp, circo, twopi, osage, patchwork) — no C: no native binary, no WASM. Oracle-verified against the C implementation, runs in the browser.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"graphviz",
|
package/src/common/graph-init.ts
CHANGED
|
@@ -89,7 +89,7 @@ export function findCharset(g: Graph): number {
|
|
|
89
89
|
}
|
|
90
90
|
if (p === 'big-5' || p === 'big5') return CHAR_BIG5;
|
|
91
91
|
if (p === 'utf-8' || p === 'utf8') return CHAR_UTF8;
|
|
92
|
-
agwarningf(`Unsupported charset "${raw}" - assuming utf-8
|
|
92
|
+
agwarningf(`Unsupported charset "${raw}" - assuming utf-8`);
|
|
93
93
|
return CHAR_UTF8;
|
|
94
94
|
}
|
|
95
95
|
|
|
@@ -16,10 +16,11 @@ import { ObjType, EmitState, MapShape } from '../gvc/job.js';
|
|
|
16
16
|
import type { RendererPlugin } from '../gvc/context.js';
|
|
17
17
|
import { FillType, PenType } from '../gvc/context.js';
|
|
18
18
|
import { transformPoint } from '../gvc/device.js';
|
|
19
|
-
import { resolveRenderColor } from '../render/color-resolve.js';
|
|
19
|
+
import { firstColor, resolveRenderColor } from '../render/color-resolve.js';
|
|
20
20
|
import { emitRoundedBezier } from './poly-shapes.js';
|
|
21
21
|
import type { PlacedHtml, PlacedCell } from './htmltable-pos.js';
|
|
22
22
|
import { htmlStyle } from './htmltable-style.js';
|
|
23
|
+
import { gvrenderSetStyle } from '../gvc/gvrender-set-style.js';
|
|
23
24
|
import {
|
|
24
25
|
BORDER_LEFT,
|
|
25
26
|
BORDER_TOP,
|
|
@@ -27,25 +28,54 @@ import {
|
|
|
27
28
|
BORDER_BOTTOM,
|
|
28
29
|
BORDER_MASK,
|
|
29
30
|
} from './htmltable-types.js';
|
|
31
|
+
import { parseSegs, parseSegsSilent } from './multicolor.js';
|
|
32
|
+
import { agwarningf } from './agerr.js';
|
|
30
33
|
import { registerRenderState } from './render-state.js';
|
|
31
34
|
|
|
32
35
|
// ---------------------------------------------------------------------------
|
|
33
36
|
// Gradient spec parser
|
|
34
37
|
// ---------------------------------------------------------------------------
|
|
35
38
|
|
|
39
|
+
/** The colours and fraction findStopColor yields; `stop` null → DEFAULT_COLOR. */
|
|
40
|
+
export interface HtmlStopColor {
|
|
41
|
+
fill: string;
|
|
42
|
+
stop: string | null;
|
|
43
|
+
frac: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** @see lib/common/emit.c:findStopColor */
|
|
47
|
+
const MSG_MANY_COLORS = 'More than 2 colors specified for a gradient - ignoring remaining';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Port of C's findStopColor over parseSegs: null when parseSegs returns
|
|
51
|
+
* non-zero (including the rv 3 over-full warning), has < 2 segments or has no
|
|
52
|
+
* first colour. `silent` skips every diagnostic (port-internal pre-passes);
|
|
53
|
+
* otherwise parseSegs' once-per-render messages and the more-than-2-colours
|
|
54
|
+
* warning print as C does (no AGPREV context).
|
|
55
|
+
*
|
|
56
|
+
* @see lib/common/emit.c:findStopColor
|
|
57
|
+
*/
|
|
58
|
+
export function findHtmlStopColor(color: string, silent = false): HtmlStopColor | null {
|
|
59
|
+
const { segs, error } = silent ? parseSegsSilent(color) : parseSegs(color);
|
|
60
|
+
const first = segs[0];
|
|
61
|
+
const second = segs[1];
|
|
62
|
+
if (error !== 0 || first === undefined || second === undefined || first.color === null) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
if (segs.length > 2 && !silent) agwarningf(MSG_MANY_COLORS);
|
|
66
|
+
const frac = first.hasFraction ? first.t : second.hasFraction ? 1 - second.t : 0;
|
|
67
|
+
return { fill: first.color, stop: second.color, frac };
|
|
68
|
+
}
|
|
69
|
+
|
|
36
70
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
71
|
+
* [fill, stop] of a colour-list gradient spec, or null for a solid colour.
|
|
72
|
+
* Silent {@link findHtmlStopColor} for call sites that only need the colours.
|
|
39
73
|
*
|
|
40
74
|
* @see lib/common/emit.c:findStopColor
|
|
41
75
|
*/
|
|
42
76
|
export function parseGradientSpec(color: string): [string, string] | null {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
const c0 = color.slice(0, idx).trim();
|
|
46
|
-
const c1 = color.slice(idx + 1).trim();
|
|
47
|
-
if (c0.length === 0) return null;
|
|
48
|
-
return [c0, c1.length > 0 ? c1 : 'black'];
|
|
77
|
+
const spec = findHtmlStopColor(color, true);
|
|
78
|
+
return spec === null ? null : [spec.fill, spec.stop ?? 'black'];
|
|
49
79
|
}
|
|
50
80
|
|
|
51
81
|
// ---------------------------------------------------------------------------
|
|
@@ -104,6 +134,8 @@ export interface HtmlPaint {
|
|
|
104
134
|
* C setFill's findStopColor hit. @see lib/common/htmltable.c:setFill
|
|
105
135
|
*/
|
|
106
136
|
stop?: string;
|
|
137
|
+
/** findStopColor's fraction; defaults to 0. Ignored without `stop`. */
|
|
138
|
+
gradientFrac?: number;
|
|
107
139
|
/** Gradient angle in degrees; defaults to 0. Ignored without `stop`. */
|
|
108
140
|
gradientAngle?: number;
|
|
109
141
|
/** True → radial gradient (RGRADIENT); else linear. Ignored without `stop`. */
|
|
@@ -141,6 +173,16 @@ export function resetHtmlFillPenWidth(): void {
|
|
|
141
173
|
registerRenderState(resetHtmlFillPenWidth);
|
|
142
174
|
|
|
143
175
|
/** Map an HTML STYLE to the C pen type: dashed wins over dotted. @see lib/common/htmltable.c:doBorder */
|
|
176
|
+
/** gvc->defaultlinestyle. @see lib/common/emit.c:105 */
|
|
177
|
+
const DEFAULT_LINE_STYLE: readonly string[] = ['solid', 'setlinewidth(1)'];
|
|
178
|
+
|
|
179
|
+
/** The style list doBorder hands to gvrender_set_style for a pen type. */
|
|
180
|
+
function htmlBorderStyle(pen: PenType): readonly string[] {
|
|
181
|
+
if (pen === PenType.Dashed) return ['dashed'];
|
|
182
|
+
if (pen === PenType.Dotted) return ['dotted'];
|
|
183
|
+
return DEFAULT_LINE_STYLE;
|
|
184
|
+
}
|
|
185
|
+
|
|
144
186
|
function penTypeOf(style: string | undefined): PenType {
|
|
145
187
|
const st = htmlStyle(style);
|
|
146
188
|
if (st.dashed) return PenType.Dashed;
|
|
@@ -156,7 +198,7 @@ function penTypeOf(style: string | undefined): PenType {
|
|
|
156
198
|
*/
|
|
157
199
|
export function withHtmlPaint(paint: HtmlPaint, job: RenderJob, drawFn: () => void): void {
|
|
158
200
|
paintObj.fillColor = paint.fill !== undefined
|
|
159
|
-
? resolveRenderColor(paint.fill) : { type: 'none' };
|
|
201
|
+
? resolveRenderColor(firstColor(paint.fill)) : { type: 'none' };
|
|
160
202
|
paintObj.penColor = paint.pen !== undefined
|
|
161
203
|
? resolveRenderColor(paint.pen) : { type: 'string', s: 'transparent' };
|
|
162
204
|
applyHtmlFill(paint);
|
|
@@ -166,6 +208,9 @@ export function withHtmlPaint(paint: HtmlPaint, job: RenderJob, drawFn: () => vo
|
|
|
166
208
|
// so xdot emits `S 5 -solid` per border/fill. rawStyle carries it (SVG's
|
|
167
209
|
// style emit treats "solid" as the default → no visible change).
|
|
168
210
|
paintObj.rawStyle = paintObj.pen === PenType.Solid ? ['solid'] : [];
|
|
211
|
+
// emit_html_tbl/cell border: dashed or dotted by name, else defaultlinestyle.
|
|
212
|
+
// @see lib/common/htmltable.c:267 doBorder
|
|
213
|
+
gvrenderSetStyle(htmlBorderStyle(paintObj.pen));
|
|
169
214
|
// Inherit the enclosing object's emit-state (the label state during an HTML
|
|
170
215
|
// label emit) so the fill/border ops route to the same xbuf as the label
|
|
171
216
|
// (_ldraw_), not the shape draw buffer. SVG ignores emit_state, so unchanged.
|
|
@@ -186,7 +231,7 @@ function applyHtmlFill(paint: HtmlPaint): void {
|
|
|
186
231
|
paintObj.fill = paint.radial === true ? FillType.Radial : FillType.Linear;
|
|
187
232
|
paintObj.stopColor = resolveRenderColor(paint.stop);
|
|
188
233
|
paintObj.gradientAngle = paint.gradientAngle ?? 0;
|
|
189
|
-
paintObj.gradientFrac = 0;
|
|
234
|
+
paintObj.gradientFrac = paint.gradientFrac ?? 0;
|
|
190
235
|
return;
|
|
191
236
|
}
|
|
192
237
|
paintObj.fill = paint.fill !== undefined ? FillType.Solid : FillType.None;
|
|
@@ -31,7 +31,7 @@ import type { PlacedHtml, PlacedCell, PlacedLine, PlacedImage } from './htmltabl
|
|
|
31
31
|
import { transformPoint } from '../gvc/device.js';
|
|
32
32
|
import { gvrenderTextspan } from '../gvc/textspan-emit.js';
|
|
33
33
|
import {
|
|
34
|
-
withHtmlPaint,
|
|
34
|
+
withHtmlPaint, findHtmlStopColor, doBorder, mkPts,
|
|
35
35
|
htmlFillPenWidth, resetHtmlFillPenWidth, type HtmlPaint,
|
|
36
36
|
} from './htmltable-emit-fill.js';
|
|
37
37
|
import { emitRoundedBezier } from './poly-shapes.js';
|
|
@@ -121,10 +121,11 @@ function bgFillPaint(ctx: BgFillCtx): HtmlPaint {
|
|
|
121
121
|
// penWidth = the leaked gvrender pen width (prior doBorder); a bordered cell's
|
|
122
122
|
// fill carries it as stroke-width even with stroke="none". @see htmltable.c:setFill
|
|
123
123
|
const penWidth = htmlFillPenWidth();
|
|
124
|
-
const spec =
|
|
124
|
+
const spec = findHtmlStopColor(ctx.bgcolor);
|
|
125
125
|
if (spec === null) return { fill: ctx.bgcolor, penWidth };
|
|
126
126
|
return {
|
|
127
|
-
fill: spec
|
|
127
|
+
fill: spec.fill, stop: spec.stop ?? 'black', gradientFrac: spec.frac,
|
|
128
|
+
gradientAngle: ctx.gradientangle ?? 0,
|
|
128
129
|
radial: htmlStyle(ctx.style).radial, penWidth,
|
|
129
130
|
};
|
|
130
131
|
}
|
|
@@ -270,15 +270,32 @@ function scanCdata(text: string, p: number, base: number, dataOf: CdataText, tok
|
|
|
270
270
|
return close < 0 ? -1 : close + CDATA_CLOSE.length;
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
/**
|
|
274
|
+
* A character expat's tokenizer rejects in character data (BT_NONXML in the
|
|
275
|
+
* UTF-8 table): controls other than tab, LF and CR, U+FFFE and U+FFFF, and a
|
|
276
|
+
* lone surrogate (the UTF-8 form of one is invalid). DEL is BT_OTHER, valid.
|
|
277
|
+
* @see expat lib/asciitab.h
|
|
278
|
+
* @see expat lib/xmltok_impl.c:PREFIX(contentTok) (BT_NONXML)
|
|
279
|
+
*/
|
|
280
|
+
const CONTENT_NON_XML = /[\x00-\x08\x0b\x0c\x0e-\x1f\ufffe\uffff]|[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/;
|
|
281
|
+
|
|
273
282
|
/**
|
|
274
283
|
* Character data at `p` up to the next `<`: one `text` token, then the index it
|
|
275
284
|
* ends at. A reference cut off by the end of the data is not part of the token
|
|
276
|
-
* (`held`): expat waits for the characters that settle it.
|
|
277
|
-
*
|
|
285
|
+
* (`held`): expat waits for the characters that settle it. A BT_NONXML character
|
|
286
|
+
* ends the data token; the next call finds it an invalid token (`invalid`).
|
|
287
|
+
* @see expat lib/xmltok_impl.c:PREFIX(contentTok) (XML_TOK_PARTIAL, BT_NONXML)
|
|
278
288
|
*/
|
|
279
|
-
function scanText(
|
|
289
|
+
function scanText(
|
|
290
|
+
text: string, p: number, base: number, tokens: ExpatToken[],
|
|
291
|
+
): { at: number; held: boolean; invalid?: number } {
|
|
280
292
|
const e = text.indexOf('<', p);
|
|
281
293
|
const slice = text.slice(p, e < 0 ? text.length : e);
|
|
294
|
+
const bad = CONTENT_NON_XML.exec(slice)?.index;
|
|
295
|
+
if (bad !== undefined) {
|
|
296
|
+
if (bad > 0) tokens.push({ kind: 'text', start: base + p, end: base + p + bad });
|
|
297
|
+
return { at: p + bad, held: false, invalid: p + bad };
|
|
298
|
+
}
|
|
282
299
|
const from = e < 0 ? heldRefStart(slice) : undefined;
|
|
283
300
|
const end = from === undefined ? p + slice.length : p + from;
|
|
284
301
|
if (end > p) tokens.push({ kind: 'text', start: base + p, end: base + end });
|
|
@@ -303,6 +320,7 @@ function scanContent(text: string, base: number, inCdata: boolean, dataOf: Cdata
|
|
|
303
320
|
}
|
|
304
321
|
if (text[p] !== '<') {
|
|
305
322
|
const scanned = scanText(text, p, base, tokens);
|
|
323
|
+
if (scanned.invalid !== undefined) return { tokens, consumed: scanned.at, invalid: base + scanned.invalid, inCdata: false };
|
|
306
324
|
if (scanned.held) return { tokens, consumed: scanned.at, inCdata: false };
|
|
307
325
|
p = scanned.at;
|
|
308
326
|
continue;
|
|
@@ -48,7 +48,10 @@ export interface HtmlFontInfo {
|
|
|
48
48
|
fontcolor: string;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
/** The label font defaults the line builder needs (sizing env or placement finfo). */
|
|
52
|
+
export type LabelFont = Pick<HtmlFontInfo, 'fontname' | 'fontsize'>;
|
|
53
|
+
|
|
54
|
+
export interface LineRun {
|
|
52
55
|
items: HtmlTextItem[];
|
|
53
56
|
width: number;
|
|
54
57
|
/** Max raw font size over items (C mxfsize). */
|
|
@@ -84,7 +87,7 @@ export function itemFontFlags(item: HtmlTextItem): number {
|
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
/** Resolved face/size for an item, defaulting to the inherited finfo. */
|
|
87
|
-
function itemFace(item: HtmlTextItem, finfo:
|
|
90
|
+
function itemFace(item: HtmlTextItem, finfo: LabelFont): { fs: number; face: string } {
|
|
88
91
|
return {
|
|
89
92
|
fs: item.fontSize !== undefined ? item.fontSize : finfo.fontsize,
|
|
90
93
|
face: item.fontFace !== undefined ? item.fontFace : finfo.fontname,
|
|
@@ -95,7 +98,7 @@ function itemFace(item: HtmlTextItem, finfo: HtmlFontInfo): { fs: number; face:
|
|
|
95
98
|
interface ItemMetric { w: number; h: number; fs: number; yoffsetCenterline?: number; yoffsetLayout?: number; }
|
|
96
99
|
|
|
97
100
|
/** Measure one text item; null when the item carries no text. */
|
|
98
|
-
function measureItem(item: HtmlTextItem, finfo:
|
|
101
|
+
function measureItem(item: HtmlTextItem, finfo: LabelFont, measurer: TextMeasurer): ItemMetric | null {
|
|
99
102
|
if (item.text === undefined) return null;
|
|
100
103
|
const { fs, face } = itemFace(item, finfo);
|
|
101
104
|
const flags = itemFontFlags(item);
|
|
@@ -142,11 +145,13 @@ function accumulateItem(cur: LineRun, item: HtmlTextItem, m: ItemMetric): void {
|
|
|
142
145
|
* in the current font (consecutive BRs = a real blank line with height);
|
|
143
146
|
* - at end of text a line is added ONLY if items remain — a trailing BR does
|
|
144
147
|
* NOT create a phantom empty line (this over-sized e.g. html2's node c).
|
|
148
|
+
* The single builder: size_html_txt (sizing) and emit_htextspans (placement)
|
|
149
|
+
* both read these runs, as C reads one htextspan_t list.
|
|
145
150
|
* @see lib/common/htmlparse.y:appendFLineList / mkText
|
|
146
151
|
*/
|
|
147
152
|
export function buildLineRuns(
|
|
148
153
|
texts: HtmlText[],
|
|
149
|
-
finfo:
|
|
154
|
+
finfo: LabelFont,
|
|
150
155
|
measurer: TextMeasurer,
|
|
151
156
|
): LineRun[] {
|
|
152
157
|
const runs: LineRun[] = [];
|
|
@@ -181,7 +186,7 @@ export function buildLineRuns(
|
|
|
181
186
|
*/
|
|
182
187
|
function runSimpleKey(
|
|
183
188
|
run: LineRun,
|
|
184
|
-
finfo:
|
|
189
|
+
finfo: LabelFont,
|
|
185
190
|
): { fs: number; face: string } | null | undefined {
|
|
186
191
|
if (run.items.length > 1) return null;
|
|
187
192
|
const item = run.items[0];
|
|
@@ -195,7 +200,7 @@ function runSimpleKey(
|
|
|
195
200
|
* uniform resolved face/size across lines.
|
|
196
201
|
* @see lib/common/htmltable.c:size_html_txt (lines 946-986)
|
|
197
202
|
*/
|
|
198
|
-
export function runsAreSimple(runs: LineRun[], finfo:
|
|
203
|
+
export function runsAreSimple(runs: LineRun[], finfo: LabelFont): boolean {
|
|
199
204
|
let prevFs = -1;
|
|
200
205
|
let prevFace: string | null = null;
|
|
201
206
|
for (const run of runs) {
|
package/src/common/htmltable.ts
CHANGED
|
@@ -20,11 +20,13 @@ export type { TextMeasurer } from './textmeasure.js';
|
|
|
20
20
|
|
|
21
21
|
import type {
|
|
22
22
|
HtmlCell, HtmlCellContent, HtmlImage, HtmlLabel, HtmlTable, HtmlText,
|
|
23
|
-
|
|
23
|
+
ImageSizer,
|
|
24
24
|
} from './htmltable-types.js';
|
|
25
|
-
import { findImageSize } from '../gvc/usershape.js';
|
|
25
|
+
import { findImageSize, imageIsSupplied, imageOpenWarning } from '../gvc/usershape.js';
|
|
26
|
+
import { registerRenderState } from './render-state.js';
|
|
26
27
|
import { PointSet } from './pointset.js';
|
|
27
28
|
import type { TextMeasurer } from './textmeasure.js';
|
|
29
|
+
import { buildLineRuns, runsAreSimple } from './htmltable-pos-runs.js';
|
|
28
30
|
import { withTableFont } from './htmltable-fontinfo.js';
|
|
29
31
|
import { agerrLine, type StderrLines } from './htmltable-attr-warn.js';
|
|
30
32
|
|
|
@@ -90,107 +92,6 @@ export interface HtmlSizeEnv {
|
|
|
90
92
|
|
|
91
93
|
const defaultSizeEnv = (): HtmlSizeEnv => ({ fontsize: DEFAULT_FONTSIZE, fontname: '' });
|
|
92
94
|
|
|
93
|
-
/** One measured text line (a C "span"). @see size_html_txt */
|
|
94
|
-
interface SizedLine {
|
|
95
|
-
items: HtmlTextItem[];
|
|
96
|
-
width: number;
|
|
97
|
-
/** Max metric line height over items (C mxysize). */
|
|
98
|
-
mxysize: number;
|
|
99
|
-
/** Max raw font size over items (C mxfsize). */
|
|
100
|
-
mxfsize: number;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const itemFont = (item: HtmlTextItem, env: HtmlSizeEnv): { fs: number; face: string } => ({
|
|
104
|
-
fs: item.fontSize !== undefined ? item.fontSize : env.fontsize,
|
|
105
|
-
face: item.fontFace !== undefined ? item.fontFace : env.fontname,
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
const measureItem = (
|
|
109
|
-
item: HtmlTextItem,
|
|
110
|
-
measurer: TextMeasurer,
|
|
111
|
-
env: HtmlSizeEnv,
|
|
112
|
-
): { w: number; h: number } => {
|
|
113
|
-
const { fs, face } = itemFont(item, env);
|
|
114
|
-
// Variant widths per run flags. @see lib/common/htmltable.c:size_html_txt
|
|
115
|
-
const flags = (item.bold === true || item.italic === true)
|
|
116
|
-
? { bold: item.bold === true, italic: item.italic === true }
|
|
117
|
-
: undefined;
|
|
118
|
-
return measurer.measure(item.text ?? '', face, fs, flags);
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Split items into measured lines at <BR/>, mirroring C's parser exactly
|
|
123
|
-
* (htmlparse.y appendFLineList/mkText): a BR with no accumulated items yields
|
|
124
|
-
* a line with one empty-string item in the current font (consecutive BRs = a
|
|
125
|
-
* real blank line with height); at end of text a line is added ONLY if items
|
|
126
|
-
* remain — a trailing BR does NOT create a phantom empty line. Must stay in
|
|
127
|
-
* lockstep with buildLineRuns (htmltable-pos-runs.ts), the placement twin.
|
|
128
|
-
* @see lib/common/htmlparse.y:appendFLineList / mkText
|
|
129
|
-
* @see lib/common/htmltable.c:size_html_txt
|
|
130
|
-
*/
|
|
131
|
-
const splitSizedLines = (
|
|
132
|
-
texts: HtmlText[],
|
|
133
|
-
measurer: TextMeasurer,
|
|
134
|
-
env: HtmlSizeEnv,
|
|
135
|
-
): SizedLine[] => {
|
|
136
|
-
const lines: SizedLine[] = [];
|
|
137
|
-
let cur: SizedLine = { items: [], width: 0, mxysize: 0, mxfsize: 0 };
|
|
138
|
-
const accumulate = (item: HtmlTextItem): void => {
|
|
139
|
-
const sz = measureItem(item, measurer, env);
|
|
140
|
-
cur.items.push(item);
|
|
141
|
-
cur.width += sz.w;
|
|
142
|
-
cur.mxysize = Math.max(cur.mxysize, sz.h);
|
|
143
|
-
cur.mxfsize = Math.max(cur.mxfsize, itemFont(item, env).fs);
|
|
144
|
-
};
|
|
145
|
-
const flush = (br?: HtmlTextItem): void => {
|
|
146
|
-
if (cur.items.length === 0 && br !== undefined) {
|
|
147
|
-
// C: empty fitemList on a BR → one ""-item span in the current font.
|
|
148
|
-
const { br: _b, brAlign: _a, ...fontProps } = br;
|
|
149
|
-
accumulate({ text: '', ...fontProps });
|
|
150
|
-
}
|
|
151
|
-
lines.push(cur);
|
|
152
|
-
cur = { items: [], width: 0, mxysize: 0, mxfsize: 0 };
|
|
153
|
-
};
|
|
154
|
-
for (const txt of texts) {
|
|
155
|
-
for (const item of txt.items) {
|
|
156
|
-
if (item.br) { flush(item); continue; }
|
|
157
|
-
if (item.text === undefined) continue;
|
|
158
|
-
accumulate(item);
|
|
159
|
-
}
|
|
160
|
-
if (cur.items.length > 0) flush(); // C mkText: only when items remain
|
|
161
|
-
}
|
|
162
|
-
return lines;
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
/** Does an item carry any font flag (C textfont_t.flags)? */
|
|
166
|
-
const itemHasFlags = (i: HtmlTextItem): boolean =>
|
|
167
|
-
i.bold === true || i.italic === true || i.underline === true ||
|
|
168
|
-
i.strikethrough === true || i.overline === true ||
|
|
169
|
-
i.subscript === true || i.superscript === true;
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* C's "simple" test over the line list (C spans): one item per line, no font
|
|
173
|
-
* flags, uniform resolved face and size across all lines. It reads each
|
|
174
|
-
* line's first item, so the "" item appendFLineList gives an empty <BR/> line
|
|
175
|
-
* counts with its font: `k<U><BR/><BR/></U>` is not simple.
|
|
176
|
-
* @see lib/common/htmltable.c:size_html_txt (lines 946-986)
|
|
177
|
-
*/
|
|
178
|
-
const htmlTextIsSimple = (lines: SizedLine[], env: HtmlSizeEnv): boolean => {
|
|
179
|
-
let prevFs = -1;
|
|
180
|
-
let prevFace: string | null = null;
|
|
181
|
-
for (const ln of lines) {
|
|
182
|
-
// splitSizedLines never yields an itemless line (C: nitems >= 1).
|
|
183
|
-
const item = ln.items[0]!;
|
|
184
|
-
if (ln.items.length > 1 || itemHasFlags(item)) return false;
|
|
185
|
-
const { fs, face } = itemFont(item, env);
|
|
186
|
-
if (prevFs < 0) prevFs = fs;
|
|
187
|
-
else if (fs !== prevFs) return false;
|
|
188
|
-
if (prevFace === null) prevFace = face;
|
|
189
|
-
else if (face !== prevFace) return false;
|
|
190
|
-
}
|
|
191
|
-
return true;
|
|
192
|
-
};
|
|
193
|
-
|
|
194
95
|
/**
|
|
195
96
|
* Size a text block. C: per line lsize = mxysize when simple, else the
|
|
196
97
|
* raw max font size (mxfsize); a single-line block always uses mxysize.
|
|
@@ -201,16 +102,17 @@ const sizeTextContent = (
|
|
|
201
102
|
measurer: TextMeasurer,
|
|
202
103
|
env: HtmlSizeEnv = defaultSizeEnv(),
|
|
203
104
|
): { w: number; h: number } => {
|
|
204
|
-
|
|
205
|
-
const
|
|
105
|
+
// The one line builder, shared with placement (htmltable-pos-runs.ts).
|
|
106
|
+
const lines = buildLineRuns(texts, env, measurer);
|
|
107
|
+
const simple = runsAreSimple(lines, env);
|
|
206
108
|
let w = 0;
|
|
207
109
|
let h = 0;
|
|
208
110
|
for (const ln of lines) {
|
|
209
111
|
w = Math.max(w, ln.width);
|
|
210
|
-
const lsize = simple ? ln.
|
|
112
|
+
const lsize = simple ? ln.height : ln.fontSize;
|
|
211
113
|
h += ln.items.length > 0 ? lsize : DEFAULT_FONTSIZE;
|
|
212
114
|
}
|
|
213
|
-
if (lines.length === 1) h = lines[0]!.items.length > 0 ? lines[0]!.
|
|
115
|
+
if (lines.length === 1) h = lines[0]!.items.length > 0 ? lines[0]!.height : DEFAULT_FONTSIZE;
|
|
214
116
|
return { w, h };
|
|
215
117
|
};
|
|
216
118
|
|
|
@@ -228,25 +130,28 @@ const sizeTextContent = (
|
|
|
228
130
|
* @see lib/common/htmltable.c:size_html_img (line 1080)
|
|
229
131
|
*/
|
|
230
132
|
/** Images whose sizing failed: size_html_img's rv = 1 on every later call. */
|
|
231
|
-
const missingImages = new WeakSet<HtmlImage>();
|
|
133
|
+
const missingImages = { set: new WeakSet<HtmlImage>() };
|
|
232
134
|
|
|
233
135
|
const sizeHtmlImg = (img: HtmlImage, { imageSizer, stderr }: HtmlSizeEnv): number => {
|
|
234
136
|
// C sizes each image once (processTbl); the port's size and pos passes
|
|
235
137
|
// share layoutHtmlTable, so guard against re-sizing (and re-warning). The
|
|
236
138
|
// returned rv still follows C on every call.
|
|
237
|
-
if (img.width !== undefined && img.height !== undefined) return missingImages.has(img) ? 1 : 0;
|
|
139
|
+
if (img.width !== undefined && img.height !== undefined) return missingImages.set.has(img) ? 1 : 0;
|
|
238
140
|
const sz = imageSizer !== undefined ? imageSizer(img.src) : findImageSize(img.src);
|
|
239
141
|
if (sz !== null) {
|
|
240
142
|
img.width = sz.w; img.height = sz.h;
|
|
241
143
|
return 0;
|
|
242
144
|
}
|
|
243
|
-
// Missing-image:
|
|
145
|
+
// Missing-image: gvusershape_open's warning (unless bytes are supplied),
|
|
146
|
+
// then zero size + the Error.
|
|
147
|
+
// @see lib/gvc/gvusershape.c:gvusershape_file_access
|
|
148
|
+
if (!imageIsSupplied(img.src, imageSizer)) agerrLine(stderr, `Warning: ${imageOpenWarning(img.src)}`);
|
|
244
149
|
// C: agerrorf("No or improper image file=\"%s\"\n", img->src) — agerr
|
|
245
150
|
// prefixes "Error: " on stderr.
|
|
246
151
|
agerrLine(stderr, `Error: No or improper image file="${img.src}"`);
|
|
247
152
|
img.width = 0;
|
|
248
153
|
img.height = 0;
|
|
249
|
-
missingImages.add(img);
|
|
154
|
+
missingImages.set.add(img);
|
|
250
155
|
return 1;
|
|
251
156
|
};
|
|
252
157
|
|
|
@@ -317,7 +222,14 @@ const WARN_TABLE_UNSPECIFIED = 'Warning: fixed table size with unspecified width
|
|
|
317
222
|
* C sizes each cell/table exactly once; the port's size and pos passes both
|
|
318
223
|
* run layoutHtmlTable, so the size warnings are emitted on first sight only.
|
|
319
224
|
*/
|
|
320
|
-
const sizeWarned = new WeakSet<object>();
|
|
225
|
+
const sizeWarned = { set: new WeakSet<object>() };
|
|
226
|
+
|
|
227
|
+
// Native re-parses per run, so each render starts with both sets empty (AD-7);
|
|
228
|
+
// a WeakSet cannot be cleared, so the holder's field is swapped.
|
|
229
|
+
registerRenderState(() => {
|
|
230
|
+
missingImages.set = new WeakSet<HtmlImage>();
|
|
231
|
+
sizeWarned.set = new WeakSet<object>();
|
|
232
|
+
});
|
|
321
233
|
|
|
322
234
|
/**
|
|
323
235
|
* FIXED_FLAG branch of size_html_cell: C's rv (set at the warning sites; the
|
|
@@ -357,8 +269,8 @@ const getCellSize = (
|
|
|
357
269
|
// @see lib/common/htmltable.c:size_html_cell (FIXED_FLAG branch + box.UR MAX)
|
|
358
270
|
let szx = cw + margin;
|
|
359
271
|
let szy = ch + margin;
|
|
360
|
-
const warn = !sizeWarned.has(cell);
|
|
361
|
-
sizeWarned.add(cell);
|
|
272
|
+
const warn = !sizeWarned.set.has(cell);
|
|
273
|
+
sizeWarned.set.add(cell);
|
|
362
274
|
const fixed = fixedCellCheck(cell, { w: szx, h: szy }, warn, env.stderr);
|
|
363
275
|
rv |= fixed.rv;
|
|
364
276
|
if (fixed.zero) { szx = 0; szy = 0; }
|
|
@@ -504,8 +416,8 @@ export const layoutHtmlTable = (
|
|
|
504
416
|
* @see lib/common/htmltable.c:size_html_tbl (:1683, :1689)
|
|
505
417
|
*/
|
|
506
418
|
const warnFixedTable = (tbl: HtmlTable, dims: { wd: number; ht: number }, out: StderrLines | undefined): number => {
|
|
507
|
-
const warn = !sizeWarned.has(tbl);
|
|
508
|
-
sizeWarned.add(tbl);
|
|
419
|
+
const warn = !sizeWarned.set.has(tbl);
|
|
420
|
+
sizeWarned.set.add(tbl);
|
|
509
421
|
if (tbl.fixedsize !== true) return 0;
|
|
510
422
|
if (tbl.width && tbl.height) {
|
|
511
423
|
if (tbl.width >= dims.wd && tbl.height >= dims.ht) return 0;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EPL-2.0
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `cos`/`sin` as the native build host's libm computes them, at the only
|
|
5
|
+
* inputs Graphviz feeds with constant arguments: the corner angles
|
|
6
|
+
* 2π·(i ∓ 0.5)/8 of neatosplines.c's circumscribed ellipse 8-gon.
|
|
7
|
+
*
|
|
8
|
+
* V8's Math.sin/Math.cos are not correctly rounded everywhere, and the
|
|
9
|
+
* result depends on the CPU: at 9π/8 Node 22-26 on arm64 return
|
|
10
|
+
* -0.3826834323650896 (x64 and Node 20 return the correctly rounded value)
|
|
11
|
+
* where the reference libm (macOS arm64 libSystem) returns -0.38268343236508967.
|
|
12
|
+
* The ulp moves an obstacle corner off its node's centre line, which flips
|
|
13
|
+
* knife-edge triangulations in the multispline router. The table holds the
|
|
14
|
+
* host values for those nine angles (printed with `%a` by a clang -O2
|
|
15
|
+
* harness of the same expression, which also reproduces the oracle's
|
|
16
|
+
* makeObstacle vertices); any other input falls back to Math.cos/Math.sin.
|
|
17
|
+
*
|
|
18
|
+
* @see lib/neatogen/neatosplines.c:circumscribed_polygon_corner_about_ellipse
|
|
19
|
+
* @see docs/known-divergences.md (host libm)
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** angle → [cos, sin] from the host libm, angles 2π·(k + 0.5)/8, k = -1..7. */
|
|
23
|
+
const OCTAGON_TRIG: ReadonlyMap<number, readonly [number, number]> = new Map([
|
|
24
|
+
[-0.39269908169872414, [0.9238795325112867, -0.3826834323650898]],
|
|
25
|
+
[0.39269908169872414, [0.9238795325112867, 0.3826834323650898]],
|
|
26
|
+
[1.1780972450961724, [0.38268343236508984, 0.9238795325112867]],
|
|
27
|
+
[1.9634954084936207, [-0.3826834323650897, 0.9238795325112867]],
|
|
28
|
+
[2.748893571891069, [-0.9238795325112867, 0.3826834323650899]],
|
|
29
|
+
[3.5342917352885173, [-0.9238795325112868, -0.38268343236508967]],
|
|
30
|
+
[4.319689898685965, [-0.38268343236509034, -0.9238795325112865]],
|
|
31
|
+
[5.105088062083414, [0.38268343236509, -0.9238795325112866]],
|
|
32
|
+
[5.890486225480862, [0.9238795325112865, -0.3826834323650904]],
|
|
33
|
+
] as const);
|
|
34
|
+
|
|
35
|
+
/** libm `cos(x)` of the native build host (table above, else Math.cos). */
|
|
36
|
+
export function libmCos(x: number): number {
|
|
37
|
+
return OCTAGON_TRIG.get(x)?.[0] ?? Math.cos(x);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** libm `sin(x)` of the native build host (table above, else Math.sin). */
|
|
41
|
+
export function libmSin(x: number): number {
|
|
42
|
+
return OCTAGON_TRIG.get(x)?.[1] ?? Math.sin(x);
|
|
43
|
+
}
|