@kgdiff/renderer 0.0.0-stage → 0.1.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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -2
  3. package/dist/atlas.d.ts +55 -0
  4. package/dist/atlas.d.ts.map +1 -0
  5. package/dist/atlas.js +132 -0
  6. package/dist/atlas.js.map +1 -0
  7. package/dist/camera.d.ts +116 -0
  8. package/dist/camera.d.ts.map +1 -0
  9. package/dist/camera.js +368 -0
  10. package/dist/camera.js.map +1 -0
  11. package/dist/color.d.ts +12 -0
  12. package/dist/color.d.ts.map +1 -0
  13. package/dist/color.js +51 -0
  14. package/dist/color.js.map +1 -0
  15. package/dist/ctx.d.ts +57 -0
  16. package/dist/ctx.d.ts.map +1 -0
  17. package/dist/ctx.js +28 -0
  18. package/dist/ctx.js.map +1 -0
  19. package/dist/draw/background.d.ts +33 -0
  20. package/dist/draw/background.d.ts.map +1 -0
  21. package/dist/draw/background.js +105 -0
  22. package/dist/draw/background.js.map +1 -0
  23. package/dist/draw/badges.d.ts +26 -0
  24. package/dist/draw/badges.d.ts.map +1 -0
  25. package/dist/draw/badges.js +82 -0
  26. package/dist/draw/badges.js.map +1 -0
  27. package/dist/draw/collapsed.d.ts +15 -0
  28. package/dist/draw/collapsed.d.ts.map +1 -0
  29. package/dist/draw/collapsed.js +67 -0
  30. package/dist/draw/collapsed.js.map +1 -0
  31. package/dist/draw/edges.d.ts +14 -0
  32. package/dist/draw/edges.d.ts.map +1 -0
  33. package/dist/draw/edges.js +115 -0
  34. package/dist/draw/edges.js.map +1 -0
  35. package/dist/draw/glyphs.d.ts +31 -0
  36. package/dist/draw/glyphs.d.ts.map +1 -0
  37. package/dist/draw/glyphs.js +114 -0
  38. package/dist/draw/glyphs.js.map +1 -0
  39. package/dist/draw/halos.d.ts +19 -0
  40. package/dist/draw/halos.d.ts.map +1 -0
  41. package/dist/draw/halos.js +91 -0
  42. package/dist/draw/halos.js.map +1 -0
  43. package/dist/draw/labels.d.ts +55 -0
  44. package/dist/draw/labels.d.ts.map +1 -0
  45. package/dist/draw/labels.js +300 -0
  46. package/dist/draw/labels.js.map +1 -0
  47. package/dist/draw/link.d.ts +25 -0
  48. package/dist/draw/link.d.ts.map +1 -0
  49. package/dist/draw/link.js +122 -0
  50. package/dist/draw/link.js.map +1 -0
  51. package/dist/draw/nodes.d.ts +13 -0
  52. package/dist/draw/nodes.d.ts.map +1 -0
  53. package/dist/draw/nodes.js +165 -0
  54. package/dist/draw/nodes.js.map +1 -0
  55. package/dist/draw/shapes.d.ts +7 -0
  56. package/dist/draw/shapes.d.ts.map +1 -0
  57. package/dist/draw/shapes.js +26 -0
  58. package/dist/draw/shapes.js.map +1 -0
  59. package/dist/draw/view.d.ts +36 -0
  60. package/dist/draw/view.d.ts.map +1 -0
  61. package/dist/draw/view.js +16 -0
  62. package/dist/draw/view.js.map +1 -0
  63. package/dist/frame-cache.d.ts +103 -0
  64. package/dist/frame-cache.d.ts.map +1 -0
  65. package/dist/frame-cache.js +349 -0
  66. package/dist/frame-cache.js.map +1 -0
  67. package/dist/index.d.ts +162 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +19 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/input.d.ts +81 -0
  72. package/dist/input.d.ts.map +1 -0
  73. package/dist/input.js +329 -0
  74. package/dist/input.js.map +1 -0
  75. package/dist/labels.d.ts +103 -0
  76. package/dist/labels.d.ts.map +1 -0
  77. package/dist/labels.js +203 -0
  78. package/dist/labels.js.map +1 -0
  79. package/dist/math.d.ts +33 -0
  80. package/dist/math.d.ts.map +1 -0
  81. package/dist/math.js +138 -0
  82. package/dist/math.js.map +1 -0
  83. package/dist/minimap.d.ts +56 -0
  84. package/dist/minimap.d.ts.map +1 -0
  85. package/dist/minimap.js +501 -0
  86. package/dist/minimap.js.map +1 -0
  87. package/dist/paint-stats.d.ts +17 -0
  88. package/dist/paint-stats.d.ts.map +1 -0
  89. package/dist/paint-stats.js +23 -0
  90. package/dist/paint-stats.js.map +1 -0
  91. package/dist/paint.d.ts +64 -0
  92. package/dist/paint.d.ts.map +1 -0
  93. package/dist/paint.js +197 -0
  94. package/dist/paint.js.map +1 -0
  95. package/dist/pick.d.ts +26 -0
  96. package/dist/pick.d.ts.map +1 -0
  97. package/dist/pick.js +125 -0
  98. package/dist/pick.js.map +1 -0
  99. package/dist/renderer.d.ts +28 -0
  100. package/dist/renderer.d.ts.map +1 -0
  101. package/dist/renderer.js +470 -0
  102. package/dist/renderer.js.map +1 -0
  103. package/dist/smooth-zoom.d.ts +21 -0
  104. package/dist/smooth-zoom.d.ts.map +1 -0
  105. package/dist/smooth-zoom.js +52 -0
  106. package/dist/smooth-zoom.js.map +1 -0
  107. package/dist/spatial.d.ts +26 -0
  108. package/dist/spatial.d.ts.map +1 -0
  109. package/dist/spatial.js +103 -0
  110. package/dist/spatial.js.map +1 -0
  111. package/dist/text.d.ts +36 -0
  112. package/dist/text.d.ts.map +1 -0
  113. package/dist/text.js +123 -0
  114. package/dist/text.js.map +1 -0
  115. package/package.json +47 -3
  116. package/src/atlas.ts +171 -0
  117. package/src/camera.ts +482 -0
  118. package/src/color.ts +58 -0
  119. package/src/ctx.ts +102 -0
  120. package/src/draw/background.ts +139 -0
  121. package/src/draw/badges.ts +93 -0
  122. package/src/draw/collapsed.ts +86 -0
  123. package/src/draw/edges.ts +135 -0
  124. package/src/draw/glyphs.ts +168 -0
  125. package/src/draw/halos.ts +101 -0
  126. package/src/draw/labels.ts +399 -0
  127. package/src/draw/link.ts +157 -0
  128. package/src/draw/nodes.ts +184 -0
  129. package/src/draw/shapes.ts +36 -0
  130. package/src/draw/view.ts +37 -0
  131. package/src/frame-cache.ts +382 -0
  132. package/src/index.ts +161 -0
  133. package/src/input.ts +370 -0
  134. package/src/labels.ts +269 -0
  135. package/src/math.ts +185 -0
  136. package/src/minimap.ts +545 -0
  137. package/src/paint-stats.ts +38 -0
  138. package/src/paint.ts +235 -0
  139. package/src/pick.ts +173 -0
  140. package/src/renderer.ts +539 -0
  141. package/src/smooth-zoom.ts +65 -0
  142. package/src/spatial.ts +94 -0
  143. package/src/text.ts +130 -0
package/src/text.ts ADDED
@@ -0,0 +1,130 @@
1
+ /** Fonts, cached text measurement and ellipsis fitting for screen-space labels. */
2
+ import type { Theme } from '@kgdiff/scene';
3
+ import type { Ctx2D } from './ctx.js';
4
+
5
+ /** Labels longer than this (css px) are truncated with an ellipsis. */
6
+ export const LABEL_MAX_WIDTH = 240;
7
+ const MAX_CACHED = 20_000;
8
+
9
+ /** Line box height for a font size (px). */
10
+ export function lineHeight(size: number): number {
11
+ return Math.round(size * 1.3);
12
+ }
13
+
14
+ /** Interned CSS font strings (the theme's stacks are long; never rebuilt per frame). */
15
+ export class Fonts {
16
+ private readonly cache = new Map<number, string>();
17
+ private family: string;
18
+ private mono: string;
19
+
20
+ constructor(theme: Theme) {
21
+ this.family = theme.font.family;
22
+ this.mono = theme.font.mono;
23
+ }
24
+
25
+ setTheme(theme: Theme): void {
26
+ if (theme.font.family === this.family && theme.font.mono === this.mono) return;
27
+ this.family = theme.font.family;
28
+ this.mono = theme.font.mono;
29
+ this.cache.clear();
30
+ }
31
+
32
+ /** `weight size family` for the label (or mono) stack. */
33
+ get(size: number, weight: number, mono = false): string {
34
+ const key = (mono ? -1 : 1) * (Math.round(weight) * 10_000 + Math.round(size * 100));
35
+ let font = this.cache.get(key);
36
+ if (font === undefined) {
37
+ font = `${Math.round(weight)} ${Math.round(size * 100) / 100}px ${mono ? this.mono : this.family}`;
38
+ this.cache.set(key, font);
39
+ }
40
+ return font;
41
+ }
42
+ }
43
+
44
+ const CJK = /[ᄀ-ᇿ⺀-鿿가-힯豈-﫿＀-￯]/;
45
+
46
+ /** Width estimate when no 2D context exists (tests, workers without OffscreenCanvas). */
47
+ export function estimateWidth(font: string, text: string): number {
48
+ const m = /(\d+(?:\.\d+)?)px/.exec(font);
49
+ const size = m ? Number(m[1]) : 12;
50
+ let w = 0;
51
+ for (const ch of text) w += CJK.test(ch) ? size : size * 0.58;
52
+ return w;
53
+ }
54
+
55
+ /** Measures text with a 2D context, caching widths per font + text. */
56
+ export class TextMeasurer {
57
+ private readonly widths = new Map<string, Map<string, number>>();
58
+ private readonly fitted = new Map<string, Map<string, string>>();
59
+ private count = 0;
60
+ private font = '';
61
+
62
+ constructor(private ctx: Ctx2D | null) {}
63
+
64
+ setContext(ctx: Ctx2D | null): void {
65
+ this.ctx = ctx;
66
+ this.clear();
67
+ }
68
+
69
+ /** Forget everything (fonts finished loading, theme fonts changed). */
70
+ clear(): void {
71
+ this.widths.clear();
72
+ this.fitted.clear();
73
+ this.count = 0;
74
+ this.font = '';
75
+ }
76
+
77
+ width(font: string, text: string): number {
78
+ let byText = this.widths.get(font);
79
+ if (!byText) {
80
+ byText = new Map();
81
+ this.widths.set(font, byText);
82
+ }
83
+ let w = byText.get(text);
84
+ if (w === undefined) {
85
+ w = this.measure(font, text);
86
+ if (++this.count > MAX_CACHED) {
87
+ this.widths.clear();
88
+ this.fitted.clear();
89
+ this.count = 0;
90
+ byText = new Map();
91
+ this.widths.set(font, byText);
92
+ }
93
+ byText.set(text, w);
94
+ }
95
+ return w;
96
+ }
97
+
98
+ /** `text`, truncated with “…” so it is at most `maxWidth` px wide (code-point safe). */
99
+ fit(font: string, text: string, maxWidth: number = LABEL_MAX_WIDTH): string {
100
+ if (this.width(font, text) <= maxWidth) return text;
101
+ let byText = this.fitted.get(font);
102
+ if (!byText) {
103
+ byText = new Map();
104
+ this.fitted.set(font, byText);
105
+ }
106
+ const hit = byText.get(text);
107
+ if (hit !== undefined) return hit;
108
+ const chars = Array.from(text);
109
+ let lo = 0;
110
+ let hi = chars.length;
111
+ while (lo < hi) {
112
+ const mid = (lo + hi + 1) >> 1;
113
+ if (this.measure(font, `${chars.slice(0, mid).join('').trimEnd()}…`) <= maxWidth) lo = mid;
114
+ else hi = mid - 1;
115
+ }
116
+ const out = `${chars.slice(0, lo).join('').trimEnd()}…`;
117
+ byText.set(text, out);
118
+ return out;
119
+ }
120
+
121
+ private measure(font: string, text: string): number {
122
+ const ctx = this.ctx;
123
+ if (!ctx) return estimateWidth(font, text);
124
+ if (this.font !== font) {
125
+ ctx.font = font;
126
+ this.font = font;
127
+ }
128
+ return ctx.measureText(text).width;
129
+ }
130
+ }