novac 2.0.1 → 2.2.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 (161) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1574 -597
  3. package/bin/novac +468 -171
  4. package/bin/nvc +522 -0
  5. package/bin/nvml +78 -17
  6. package/demo.nv +0 -0
  7. package/demo_builtins.nv +0 -0
  8. package/demo_http.nv +0 -0
  9. package/examples/bf.nv +69 -0
  10. package/examples/math.nv +21 -0
  11. package/kits/birdAPI/kitdef.js +954 -0
  12. package/kits/kitRNG/kitdef.js +740 -0
  13. package/kits/kitSSH/kitdef.js +1272 -0
  14. package/kits/kitadb/kitdef.js +606 -0
  15. package/kits/kitai/kitdef.js +2185 -0
  16. package/kits/kitansi/kitdef.js +1402 -0
  17. package/kits/kitcanvas/kitdef.js +914 -0
  18. package/kits/kitclippy/kitdef.js +925 -0
  19. package/kits/kitformat/kitdef.js +1485 -0
  20. package/kits/kitgps/kitdef.js +1862 -0
  21. package/kits/kitlibproc/kitdef.js +3 -2
  22. package/kits/kitmatrix/ex.js +19 -0
  23. package/kits/kitmatrix/kitdef.js +960 -0
  24. package/kits/kitmorse/kitdef.js +229 -0
  25. package/kits/kitmpatch/kitdef.js +906 -0
  26. package/kits/kitnet/kitdef.js +1401 -0
  27. package/kits/kitnovacweb/README.md +1416 -143
  28. package/kits/kitnovacweb/kitdef.js +92 -2
  29. package/kits/kitnovacweb/nvml/executor.js +578 -176
  30. package/kits/kitnovacweb/nvml/index.js +2 -2
  31. package/kits/kitnovacweb/nvml/lexer.js +72 -69
  32. package/kits/kitnovacweb/nvml/parser.js +328 -159
  33. package/kits/kitnovacweb/nvml/renderer.js +770 -270
  34. package/kits/kitparse/kitdef.js +1688 -0
  35. package/kits/kitproto/kitdef.js +613 -0
  36. package/kits/kitqr/kitdef.js +637 -0
  37. package/kits/kitregex++/kitdef.js +1353 -0
  38. package/kits/kitrequire/kitdef.js +1599 -0
  39. package/kits/kitx11/kitdef.js +1 -0
  40. package/kits/kitx11/kitx11.js +2472 -0
  41. package/kits/kitx11/kitx11_conn.js +948 -0
  42. package/kits/kitx11/kitx11_worker.js +121 -0
  43. package/kits/libtea/kitdef.js +2691 -0
  44. package/kits/libterm/ex.js +285 -0
  45. package/kits/libterm/kitdef.js +1927 -0
  46. package/novac/LICENSE +21 -0
  47. package/novac/README.md +1823 -0
  48. package/novac/bin/novac +950 -0
  49. package/novac/bin/nvc +522 -0
  50. package/novac/bin/nvml +542 -0
  51. package/novac/demo.nv +245 -0
  52. package/novac/demo_builtins.nv +209 -0
  53. package/novac/demo_http.nv +62 -0
  54. package/novac/examples/bf.nv +69 -0
  55. package/novac/examples/math.nv +21 -0
  56. package/novac/kits/kitai/kitdef.js +2185 -0
  57. package/novac/kits/kitansi/kitdef.js +1402 -0
  58. package/novac/kits/kitformat/kitdef.js +1485 -0
  59. package/novac/kits/kitgps/kitdef.js +1862 -0
  60. package/novac/kits/kitlibfs/kitdef.js +231 -0
  61. package/{examples/example-project/nova_modules → novac/kits}/kitlibproc/kitdef.js +3 -2
  62. package/novac/kits/kitmatrix/ex.js +19 -0
  63. package/novac/kits/kitmatrix/kitdef.js +960 -0
  64. package/novac/kits/kitmpatch/kitdef.js +906 -0
  65. package/novac/kits/kitnovacweb/README.md +1572 -0
  66. package/novac/kits/kitnovacweb/demo.nv +12 -0
  67. package/novac/kits/kitnovacweb/demo.nvml +71 -0
  68. package/novac/kits/kitnovacweb/index.nova +12 -0
  69. package/novac/kits/kitnovacweb/kitdef.js +692 -0
  70. package/novac/kits/kitnovacweb/nova.kit.json +8 -0
  71. package/novac/kits/kitnovacweb/nvml/executor.js +739 -0
  72. package/novac/kits/kitnovacweb/nvml/index.js +67 -0
  73. package/novac/kits/kitnovacweb/nvml/lexer.js +263 -0
  74. package/novac/kits/kitnovacweb/nvml/parser.js +508 -0
  75. package/novac/kits/kitnovacweb/nvml/renderer.js +924 -0
  76. package/novac/kits/kitparse/kitdef.js +1688 -0
  77. package/novac/kits/kitregex++/kitdef.js +1353 -0
  78. package/novac/kits/kitrequire/kitdef.js +1599 -0
  79. package/novac/kits/kitx11/kitdef.js +1 -0
  80. package/novac/kits/kitx11/kitx11.js +2472 -0
  81. package/novac/kits/kitx11/kitx11_conn.js +948 -0
  82. package/novac/kits/kitx11/kitx11_worker.js +121 -0
  83. package/novac/kits/libtea/tf.js +2691 -0
  84. package/novac/kits/libterm/ex.js +285 -0
  85. package/novac/kits/libterm/kitdef.js +1927 -0
  86. package/novac/node_modules/chalk/license +9 -0
  87. package/novac/node_modules/chalk/package.json +83 -0
  88. package/novac/node_modules/chalk/readme.md +297 -0
  89. package/novac/node_modules/chalk/source/index.d.ts +325 -0
  90. package/novac/node_modules/chalk/source/index.js +225 -0
  91. package/novac/node_modules/chalk/source/utilities.js +33 -0
  92. package/novac/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
  93. package/novac/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
  94. package/novac/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
  95. package/novac/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
  96. package/novac/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
  97. package/novac/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
  98. package/novac/node_modules/commander/LICENSE +22 -0
  99. package/novac/node_modules/commander/Readme.md +1176 -0
  100. package/novac/node_modules/commander/esm.mjs +16 -0
  101. package/novac/node_modules/commander/index.js +24 -0
  102. package/novac/node_modules/commander/lib/argument.js +150 -0
  103. package/novac/node_modules/commander/lib/command.js +2777 -0
  104. package/novac/node_modules/commander/lib/error.js +39 -0
  105. package/novac/node_modules/commander/lib/help.js +747 -0
  106. package/novac/node_modules/commander/lib/option.js +380 -0
  107. package/novac/node_modules/commander/lib/suggestSimilar.js +101 -0
  108. package/novac/node_modules/commander/package-support.json +19 -0
  109. package/novac/node_modules/commander/package.json +82 -0
  110. package/novac/node_modules/commander/typings/esm.d.mts +3 -0
  111. package/novac/node_modules/commander/typings/index.d.ts +1113 -0
  112. package/novac/node_modules/node-addon-api/LICENSE.md +9 -0
  113. package/novac/node_modules/node-addon-api/README.md +95 -0
  114. package/novac/node_modules/node-addon-api/common.gypi +21 -0
  115. package/novac/node_modules/node-addon-api/except.gypi +25 -0
  116. package/novac/node_modules/node-addon-api/index.js +14 -0
  117. package/novac/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
  118. package/novac/node_modules/node-addon-api/napi-inl.h +7165 -0
  119. package/novac/node_modules/node-addon-api/napi.h +3364 -0
  120. package/novac/node_modules/node-addon-api/node_addon_api.gyp +42 -0
  121. package/novac/node_modules/node-addon-api/node_api.gyp +9 -0
  122. package/novac/node_modules/node-addon-api/noexcept.gypi +26 -0
  123. package/novac/node_modules/node-addon-api/package-support.json +21 -0
  124. package/novac/node_modules/node-addon-api/package.json +480 -0
  125. package/novac/node_modules/node-addon-api/tools/README.md +73 -0
  126. package/novac/node_modules/node-addon-api/tools/check-napi.js +99 -0
  127. package/novac/node_modules/node-addon-api/tools/clang-format.js +71 -0
  128. package/novac/node_modules/node-addon-api/tools/conversion.js +301 -0
  129. package/novac/node_modules/serialize-javascript/LICENSE +27 -0
  130. package/novac/node_modules/serialize-javascript/README.md +149 -0
  131. package/novac/node_modules/serialize-javascript/index.js +297 -0
  132. package/novac/node_modules/serialize-javascript/package.json +33 -0
  133. package/novac/package.json +27 -0
  134. package/novac/scripts/update-bin.js +24 -0
  135. package/novac/src/core/bstd.js +1035 -0
  136. package/novac/src/core/config.js +155 -0
  137. package/novac/src/core/describe.js +187 -0
  138. package/novac/src/core/emitter.js +499 -0
  139. package/novac/src/core/error.js +86 -0
  140. package/novac/src/core/executor.js +5606 -0
  141. package/novac/src/core/formatter.js +686 -0
  142. package/novac/src/core/lexer.js +1026 -0
  143. package/novac/src/core/nova_builtins.js +717 -0
  144. package/novac/src/core/nova_thread_worker.js +166 -0
  145. package/novac/src/core/parser.js +2181 -0
  146. package/novac/src/core/types.js +112 -0
  147. package/novac/src/index.js +28 -0
  148. package/novac/src/runtime/stdlib.js +244 -0
  149. package/package.json +6 -3
  150. package/scripts/update-bin.js +0 -0
  151. package/src/core/bstd.js +838 -362
  152. package/src/core/executor.js +2578 -170
  153. package/src/core/lexer.js +502 -54
  154. package/src/core/nova_builtins.js +21 -3
  155. package/src/core/parser.js +413 -72
  156. package/src/core/types.js +30 -2
  157. package/src/index.js +0 -0
  158. package/examples/example-project/README.md +0 -3
  159. package/examples/example-project/src/main.nova +0 -3
  160. package/src/core/environment.js +0 -0
  161. /package/{examples/example-project/bin/example-project.nv → novac/node_modules/node-addon-api/nothing.c} +0 -0
@@ -0,0 +1,960 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * kitmatrix — Nova Spatial Computation Kit
5
+ *
6
+ * A coordinate space model for geometry, movement, simulation, and layout.
7
+ * Not a rendering engine by default — a pure spatial model with optional
8
+ * terminal rendering (ANSI) and NVML rendering.
9
+ *
10
+ * Usage in Nova:
11
+ * import "kitmatrix"
12
+ *
13
+ * let m = new Matrix(40, 20)
14
+ * let a = m.point(5, 5)
15
+ * let b = m.point(20, 15)
16
+ * m.debug.drawPoints()
17
+ * m.debug.drawGraph(myGraph)
18
+ * m.render.frame() // full ANSI terminal frame
19
+ * m.nvml.page() // generate NVML page source
20
+ */
21
+
22
+ // ── Utilities ────────────────────────────────────────────────────────────────
23
+
24
+ function _deg2rad(d) { return d * Math.PI / 180; }
25
+ function _rad2deg(r) { return r * 180 / Math.PI; }
26
+ function _clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }
27
+ function _approxEq(a, b, eps = 1e-10) { return Math.abs(a - b) < eps; }
28
+
29
+ // ANSI helpers
30
+ const A = {
31
+ reset: '\x1b[0m',
32
+ bold: '\x1b[1m',
33
+ dim: '\x1b[2m',
34
+ fg: (n) => `\x1b[38;5;${n}m`,
35
+ bg: (n) => `\x1b[48;5;${n}m`,
36
+ rgb: (r,g,b) => `\x1b[38;2;${r};${g};${b}m`,
37
+ bgRgb:(r,g,b)=> `\x1b[48;2;${r};${g};${b}m`,
38
+ pos: (r,c) => `\x1b[${r};${c}H`,
39
+ cls: '\x1b[2J',
40
+ home: '\x1b[H',
41
+ hide: '\x1b[?25l',
42
+ show: '\x1b[?25h',
43
+ alt: '\x1b[?1049h',
44
+ main: '\x1b[?1049l',
45
+ };
46
+
47
+ // Box drawing
48
+ const BOX = {
49
+ tl:'┌', tr:'┐', bl:'└', br:'┘', h:'─', v:'│',
50
+ ml:'├', mr:'┤', mt:'┬', mb:'┴', x:'┼',
51
+ dtl:'╔',dtr:'╗',dbl:'╚',dbr:'╝',dh:'═',dv:'║',
52
+ };
53
+
54
+ // ── MatrixAxis ───────────────────────────────────────────────────────────────
55
+
56
+ class MatrixAxis {
57
+ constructor(angle = 0, margin = 1) { this.angle = angle; this.margin = margin; }
58
+ unitVector() { const r=_deg2rad(this.angle); return {x:Math.cos(r),y:Math.sin(r)}; }
59
+ project(x,y) { const u=this.unitVector(); return (x*u.x+y*u.y)*this.margin; }
60
+ toOffset(s) { const u=this.unitVector(),sc=s*this.margin; return {x:u.x*sc,y:u.y*sc}; }
61
+ clone() { return new MatrixAxis(this.angle,this.margin); }
62
+ toString() { return `MatrixAxis(${this.angle}°, margin=${this.margin})`; }
63
+ static horizontal() { return new MatrixAxis(0,1); }
64
+ static vertical() { return new MatrixAxis(90,1); }
65
+ static diagonal() { return new MatrixAxis(45,1); }
66
+ static isometricX() { return new MatrixAxis(30,1); }
67
+ static isometricY() { return new MatrixAxis(150,1); }
68
+ }
69
+
70
+ // ── MatrixVector ─────────────────────────────────────────────────────────────
71
+
72
+ class MatrixVector {
73
+ constructor(dx=0,dy=0) { this.dx=dx; this.dy=dy; }
74
+ magnitude() { return Math.hypot(this.dx,this.dy); }
75
+ magnitudeSq() { return this.dx*this.dx+this.dy*this.dy; }
76
+ normalize() { const m=this.magnitude(); return m===0?new MatrixVector(0,0):new MatrixVector(this.dx/m,this.dy/m); }
77
+ scale(n) { return new MatrixVector(this.dx*n,this.dy*n); }
78
+ dot(v) { return this.dx*v.dx+this.dy*v.dy; }
79
+ cross(v) { return this.dx*v.dy-this.dy*v.dx; }
80
+ angle() { return _rad2deg(Math.atan2(this.dy,this.dx)); }
81
+ add(v) { return new MatrixVector(this.dx+v.dx,this.dy+v.dy); }
82
+ sub(v) { return new MatrixVector(this.dx-v.dx,this.dy-v.dy); }
83
+ negate() { return new MatrixVector(-this.dx,-this.dy); }
84
+ perpendicular(){ return new MatrixVector(-this.dy,this.dx); }
85
+ rotate(deg) { const r=_deg2rad(deg),c=Math.cos(r),s=Math.sin(r); return new MatrixVector(this.dx*c-this.dy*s,this.dx*s+this.dy*c); }
86
+ reflect(n) { const u=n.normalize(),d=this.dot(u)*2; return new MatrixVector(this.dx-u.dx*d,this.dy-u.dy*d); }
87
+ lerp(v,t) { return new MatrixVector(this.dx+(v.dx-this.dx)*t,this.dy+(v.dy-this.dy)*t); }
88
+ projectOnto(v){ const d=v.dot(v); return d===0?MatrixVector.zero():v.scale(this.dot(v)/d); }
89
+ rejectFrom(v) { return this.sub(this.projectOnto(v)); }
90
+ isZero(eps=1e-10) { return this.magnitude()<eps; }
91
+ equals(v,eps=0){ return eps===0?this.dx===v.dx&&this.dy===v.dy:_approxEq(this.dx,v.dx,eps)&&_approxEq(this.dy,v.dy,eps); }
92
+ toArray() { return [this.dx,this.dy]; }
93
+ toObject() { return {dx:this.dx,dy:this.dy}; }
94
+ clone() { return new MatrixVector(this.dx,this.dy); }
95
+ toString() { return `MatrixVector(${this.dx},${this.dy} |${this.magnitude().toFixed(3)}|)`; }
96
+ static fromPoints(a,b){ return new MatrixVector(b.x-a.x,b.y-a.y); }
97
+ static fromAngle(deg,mag=1){ const r=_deg2rad(deg); return new MatrixVector(Math.cos(r)*mag,Math.sin(r)*mag); }
98
+ static zero() { return new MatrixVector(0,0); }
99
+ static unitX() { return new MatrixVector(1,0); }
100
+ static unitY() { return new MatrixVector(0,1); }
101
+ }
102
+
103
+ // ── MatrixPoint ──────────────────────────────────────────────────────────────
104
+
105
+ class MatrixPoint {
106
+ constructor(matrix,x=0,y=0) { this.matrix=matrix; this.x=x; this.y=y; this.label=null; this.color=null; }
107
+ oppositingPoint(cx=0,cy=0) { return new MatrixPoint(this.matrix,2*cx-this.x,2*cy-this.y); }
108
+ distanceTo(o) { return Math.hypot(o.x-this.x,o.y-this.y); }
109
+ distanceSqTo(o) { return (o.x-this.x)**2+(o.y-this.y)**2; }
110
+ manhattanTo(o) { return Math.abs(o.x-this.x)+Math.abs(o.y-this.y); }
111
+ chebyshevTo(o) { return Math.max(Math.abs(o.x-this.x),Math.abs(o.y-this.y)); }
112
+ vectorTo(o) { return new MatrixVector(o.x-this.x,o.y-this.y); }
113
+ midpointTo(o) { return new MatrixPoint(this.matrix,(this.x+o.x)/2,(this.y+o.y)/2); }
114
+ angleTo(o) { return _rad2deg(Math.atan2(o.y-this.y,o.x-this.x)); }
115
+ projectOnAxis(ax){ return ax.project(this.x,this.y); }
116
+ isInBounds(b) { return b.contains(this); }
117
+ translate(dxOrV,dy=0) {
118
+ if (dxOrV instanceof MatrixVector) return new MatrixPoint(this.matrix,this.x+dxOrV.dx,this.y+dxOrV.dy);
119
+ return new MatrixPoint(this.matrix,this.x+dxOrV,this.y+dy);
120
+ }
121
+ rotateAround(ox,oy,deg) {
122
+ const r=_deg2rad(deg),c=Math.cos(r),s=Math.sin(r),dx=this.x-ox,dy=this.y-oy;
123
+ return new MatrixPoint(this.matrix,ox+dx*c-dy*s,oy+dx*s+dy*c);
124
+ }
125
+ scaleFrom(ox,oy,sx,sy=sx) { return new MatrixPoint(this.matrix,ox+(this.x-ox)*sx,oy+(this.y-oy)*sy); }
126
+ lerp(o,t) { return new MatrixPoint(this.matrix,this.x+(o.x-this.x)*t,this.y+(o.y-this.y)*t); }
127
+ snap(sx,sy=sx) { return new MatrixPoint(this.matrix,Math.round(this.x/sx)*sx,Math.round(this.y/sy)*sy); }
128
+ equals(o,eps=0) { return eps===0?this.x===o.x&&this.y===o.y:_approxEq(this.x,o.x,eps)&&_approxEq(this.y,o.y,eps); }
129
+ withLabel(l) { this.label=l; return this; }
130
+ withColor(c) { this.color=c; return this; }
131
+ toArray() { return [this.x,this.y]; }
132
+ toObject() { return {x:this.x,y:this.y}; }
133
+ clone() { const p=new MatrixPoint(this.matrix,this.x,this.y); p.label=this.label; p.color=this.color; return p; }
134
+ toString() { return `MatrixPoint(${this.x},${this.y}${this.label?` "${this.label}"`:''})`; }
135
+ }
136
+
137
+ // ── MatrixBounds ─────────────────────────────────────────────────────────────
138
+
139
+ class MatrixBounds {
140
+ constructor(matrix,minX,minY,maxX,maxY) {
141
+ this.matrix=matrix;
142
+ this.minX=Math.min(minX,maxX); this.minY=Math.min(minY,maxY);
143
+ this.maxX=Math.max(minX,maxX); this.maxY=Math.max(minY,maxY);
144
+ }
145
+ get width() { return this.maxX-this.minX; }
146
+ get height() { return this.maxY-this.minY; }
147
+ get area() { return this.width*this.height; }
148
+ get perimeter(){ return 2*(this.width+this.height); }
149
+ get centerX() { return (this.minX+this.maxX)/2; }
150
+ get centerY() { return (this.minY+this.maxY)/2; }
151
+ get center() { return new MatrixPoint(this.matrix,this.centerX,this.centerY); }
152
+ contains(p) { return p.x>=this.minX&&p.x<=this.maxX&&p.y>=this.minY&&p.y<=this.maxY; }
153
+ clamp(p) { return new MatrixPoint(this.matrix,_clamp(p.x,this.minX,this.maxX),_clamp(p.y,this.minY,this.maxY)); }
154
+ overlaps(o) { return this.minX<=o.maxX&&this.maxX>=o.minX&&this.minY<=o.maxY&&this.maxY>=o.minY; }
155
+ intersection(o){
156
+ const mnX=Math.max(this.minX,o.minX),mnY=Math.max(this.minY,o.minY);
157
+ const mxX=Math.min(this.maxX,o.maxX),mxY=Math.min(this.maxY,o.maxY);
158
+ if (mnX>mxX||mnY>mxY) return null;
159
+ return new MatrixBounds(this.matrix,mnX,mnY,mxX,mxY);
160
+ }
161
+ union(o) { return new MatrixBounds(this.matrix,Math.min(this.minX,o.minX),Math.min(this.minY,o.minY),Math.max(this.maxX,o.maxX),Math.max(this.maxY,o.maxY)); }
162
+ expand(mx,my=mx){ return new MatrixBounds(this.matrix,this.minX-mx,this.minY-my,this.maxX+mx,this.maxY+my); }
163
+ corners() { return [new MatrixPoint(this.matrix,this.minX,this.minY),new MatrixPoint(this.matrix,this.maxX,this.minY),new MatrixPoint(this.matrix,this.maxX,this.maxY),new MatrixPoint(this.matrix,this.minX,this.maxY)]; }
164
+ randomPoint() { return new MatrixPoint(this.matrix,this.minX+Math.random()*this.width,this.minY+Math.random()*this.height); }
165
+ toObject() { return {minX:this.minX,minY:this.minY,maxX:this.maxX,maxY:this.maxY,width:this.width,height:this.height}; }
166
+ toString() { return `MatrixBounds([${this.minX},${this.minY}]→[${this.maxX},${this.maxY}])`; }
167
+ static fromPoints(matrix,pts) { const xs=pts.map(p=>p.x),ys=pts.map(p=>p.y); return new MatrixBounds(matrix,Math.min(...xs),Math.min(...ys),Math.max(...xs),Math.max(...ys)); }
168
+ static fromCenter(matrix,cx,cy,hw,hh=hw){ return new MatrixBounds(matrix,cx-hw,cy-hh,cx+hw,cy+hh); }
169
+ }
170
+
171
+ // ── MatrixTranslation ─────────────────────────────────────────────────────────
172
+
173
+ class MatrixTranslation {
174
+ constructor(matrix,a,b) { this.matrix=matrix; this.a=a; this.b=b; this.dx=b.x-a.x; this.dy=b.y-a.y; }
175
+ for(p) { return new MatrixPoint(this.matrix,p.x+this.dx,p.y+this.dy); }
176
+ inverse() { return new MatrixTranslation(this.matrix,this.b,this.a); }
177
+ toVector() { return new MatrixVector(this.dx,this.dy); }
178
+ magnitude() { return Math.hypot(this.dx,this.dy); }
179
+ toString() { return `MatrixTranslation(dx=${this.dx},dy=${this.dy})`; }
180
+ }
181
+
182
+ // ── MatrixTransform ───────────────────────────────────────────────────────────
183
+
184
+ class MatrixTransform {
185
+ constructor(matrix,{a=1,b=0,c=0,d=1,tx=0,ty=0}={}) { this.matrix=matrix; this.a=a;this.b=b;this.c=c;this.d=d;this.tx=tx;this.ty=ty; }
186
+ apply(p) { return new MatrixPoint(this.matrix,this.a*p.x+this.c*p.y+this.tx,this.b*p.x+this.d*p.y+this.ty); }
187
+ applyVector(v){ return new MatrixVector(this.a*v.dx+this.c*v.dy,this.b*v.dx+this.d*v.dy); }
188
+ combine(o) {
189
+ return new MatrixTransform(this.matrix,{
190
+ a:this.a*o.a+this.c*o.b, b:this.b*o.a+this.d*o.b, c:this.a*o.c+this.c*o.d, d:this.b*o.c+this.d*o.d,
191
+ tx:this.a*o.tx+this.c*o.ty+this.tx, ty:this.b*o.tx+this.d*o.ty+this.ty
192
+ });
193
+ }
194
+ inverse() {
195
+ const det=this.a*this.d-this.b*this.c;
196
+ if (_approxEq(det,0)) throw new Error('MatrixTransform: singular');
197
+ const inv=1/det;
198
+ return new MatrixTransform(this.matrix,{a:this.d*inv,b:-this.b*inv,c:-this.c*inv,d:this.a*inv,tx:(this.c*this.ty-this.d*this.tx)*inv,ty:(this.b*this.tx-this.a*this.ty)*inv});
199
+ }
200
+ determinant(){ return this.a*this.d-this.b*this.c; }
201
+ isIdentity() { return _approxEq(this.a,1)&&_approxEq(this.b,0)&&_approxEq(this.c,0)&&_approxEq(this.d,1)&&_approxEq(this.tx,0)&&_approxEq(this.ty,0); }
202
+ decompose() {
203
+ const sx=Math.hypot(this.a,this.b),shear=(this.a*this.c+this.b*this.d)/(sx*sx);
204
+ const sy=Math.hypot(this.c-shear*this.a,this.d-shear*this.b);
205
+ return {translation:{x:this.tx,y:this.ty},rotation:_rad2deg(Math.atan2(this.b,this.a)),scale:{x:sx,y:sy},shear};
206
+ }
207
+ toArray() { return [this.a,this.b,this.c,this.d,this.tx,this.ty]; }
208
+ toString() { return `MatrixTransform([${this.a},${this.b},${this.c},${this.d}] t=[${this.tx},${this.ty}])`; }
209
+ static identity(m) { return new MatrixTransform(m); }
210
+ static translate(m,dx,dy) { return new MatrixTransform(m,{tx:dx,ty:dy}); }
211
+ static scale(m,sx,sy=sx,cx=0,cy=0){ return new MatrixTransform(m,{a:sx,d:sy,tx:cx-sx*cx,ty:cy-sy*cy}); }
212
+ static rotate(m,deg,cx=0,cy=0){
213
+ const r=_deg2rad(deg),c=Math.cos(r),s=Math.sin(r);
214
+ return new MatrixTransform(m,{a:c,b:s,c:-s,d:c,tx:cx-c*cx+s*cy,ty:cy-s*cx-c*cy});
215
+ }
216
+ static shear(m,shx,shy=0) { return new MatrixTransform(m,{c:shx,b:shy}); }
217
+ static reflectX(m) { return new MatrixTransform(m,{d:-1}); }
218
+ static reflectY(m) { return new MatrixTransform(m,{a:-1}); }
219
+ static reflectOrigin(m) { return new MatrixTransform(m,{a:-1,d:-1}); }
220
+ }
221
+
222
+ // ── MatrixTransformChain ──────────────────────────────────────────────────────
223
+
224
+ class MatrixTransformChain {
225
+ constructor(matrix,transforms=[]) { this.matrix=matrix; this.transforms=[...transforms]; }
226
+ add(t) { this.transforms.push(t); return this; }
227
+ clear() { this.transforms=[]; return this; }
228
+ resolve() { return this.transforms.reduce((acc,t)=>acc.combine(t),MatrixTransform.identity(this.matrix)); }
229
+ apply(p) { return this.resolve().apply(p); }
230
+ applyInverse(p) { return this.resolve().inverse().apply(p); }
231
+ toString() { return `MatrixTransformChain(${this.transforms.length})`; }
232
+ }
233
+
234
+ // ── MatrixConnector ───────────────────────────────────────────────────────────
235
+
236
+ class MatrixConnector {
237
+ constructor(matrix,points=[]) { this.matrix=matrix; this.points=[...points]; }
238
+ length() { let t=0; for(let i=1;i<this.points.length;i++) t+=this.points[i-1].distanceTo(this.points[i]); return t; }
239
+ reverse() { return new MatrixConnector(this.matrix,[...this.points].reverse()); }
240
+ extend(p) { return new MatrixConnector(this.matrix,[...this.points,p]); }
241
+ bounds() { return MatrixBounds.fromPoints(this.matrix,this.points); }
242
+ sample(t) {
243
+ if (!this.points.length) return null;
244
+ if (this.points.length===1) return this.points[0].clone();
245
+ t=_clamp(t,0,1);
246
+ const total=this.length(); if (total===0) return this.points[0].clone();
247
+ let target=total*t,walked=0;
248
+ for (let i=1;i<this.points.length;i++) {
249
+ const seg=this.points[i-1].distanceTo(this.points[i]);
250
+ if (walked+seg>=target) return this.points[i-1].lerp(this.points[i],(target-walked)/seg);
251
+ walked+=seg;
252
+ }
253
+ return this.points[this.points.length-1].clone();
254
+ }
255
+ toString() { return `MatrixConnector(${this.points.length}pts, len=${this.length().toFixed(3)})`; }
256
+ }
257
+
258
+ // ── MatrixPath ────────────────────────────────────────────────────────────────
259
+
260
+ class MatrixPath {
261
+ constructor(matrix,points=[]) { this.matrix=matrix; this.points=[...points]; }
262
+ length() { let t=0; for(let i=1;i<this.points.length;i++) t+=this.points[i-1].distanceTo(this.points[i]); return t; }
263
+ sample(t) { return new MatrixConnector(this.matrix,this.points).sample(t); }
264
+ reverse() { return new MatrixPath(this.matrix,[...this.points].reverse()); }
265
+ append(o) { return new MatrixPath(this.matrix,[...this.points,...o.points]); }
266
+ bounds() { return MatrixBounds.fromPoints(this.matrix,this.points); }
267
+ subdivide(n=2) {
268
+ if (this.points.length<2) return this.clone();
269
+ const pts=[];
270
+ for(let i=0;i<this.points.length-1;i++) for(let j=0;j<n;j++) pts.push(this.points[i].lerp(this.points[i+1],j/n));
271
+ pts.push(this.points[this.points.length-1].clone());
272
+ return new MatrixPath(this.matrix,pts);
273
+ }
274
+ transform(t){ return new MatrixPath(this.matrix,this.points.map(p=>t.apply(p))); }
275
+ clone() { return new MatrixPath(this.matrix,this.points.map(p=>p.clone())); }
276
+ toString() { return `MatrixPath(${this.points.length}pts, len=${this.length().toFixed(3)})`; }
277
+ }
278
+
279
+ // ── MatrixGrid ────────────────────────────────────────────────────────────────
280
+
281
+ class MatrixGrid {
282
+ constructor(matrix,rows,cols,ox=0,oy=0,cw=1,ch=1) {
283
+ this.matrix=matrix; this.rows=rows; this.cols=cols;
284
+ this.originX=ox; this.originY=oy; this.cellW=cw; this.cellH=ch; this._pts=null;
285
+ }
286
+ _build() {
287
+ if (this._pts) return;
288
+ this._pts=[];
289
+ for(let r=0;r<this.rows;r++){
290
+ const row=[];
291
+ for(let c=0;c<this.cols;c++) row.push(new MatrixPoint(this.matrix,this.originX+c*this.cellW,this.originY+r*this.cellH));
292
+ this._pts.push(row);
293
+ }
294
+ }
295
+ at(r,c) { this._build(); return this._pts[r]?.[c]??null; }
296
+ flat() { this._build(); return this._pts.flat(); }
297
+ all() { this._build(); return this._pts.map(r=>[...r]); }
298
+ forEach(fn){ this._build(); this._pts.forEach((row,r)=>row.forEach((p,c)=>fn(p,r,c))); }
299
+ map(fn) { this._build(); return this._pts.map((row,r)=>row.map((p,c)=>fn(p,r,c))); }
300
+ filter(fn) { this._build(); return this._pts.flat().filter((p,i)=>fn(p,Math.floor(i/this.cols),i%this.cols)); }
301
+ neighbors(r,c,diag=false) {
302
+ this._build();
303
+ const dirs=[[0,1],[0,-1],[1,0],[-1,0]];
304
+ if(diag) dirs.push([1,1],[1,-1],[-1,1],[-1,-1]);
305
+ return dirs.map(([dr,dc])=>this.at(r+dr,c+dc)).filter(Boolean);
306
+ }
307
+ snap(p) {
308
+ this._build();
309
+ const c=_clamp(Math.round((p.x-this.originX)/this.cellW),0,this.cols-1);
310
+ const r=_clamp(Math.round((p.y-this.originY)/this.cellH),0,this.rows-1);
311
+ return this.at(r,c);
312
+ }
313
+ indexOf(p) {
314
+ const c=Math.round((p.x-this.originX)/this.cellW),r=Math.round((p.y-this.originY)/this.cellH);
315
+ if(c<0||c>=this.cols||r<0||r>=this.rows) return null;
316
+ return {row:r,col:c};
317
+ }
318
+ cellBounds(r,c) { const x=this.originX+c*this.cellW,y=this.originY+r*this.cellH; return new MatrixBounds(this.matrix,x,y,x+this.cellW,y+this.cellH); }
319
+ toString() { return `MatrixGrid(${this.rows}×${this.cols} cell=${this.cellW}×${this.cellH})`; }
320
+ }
321
+
322
+ // ── MatrixGraph ───────────────────────────────────────────────────────────────
323
+
324
+ class MatrixGraph {
325
+ constructor(matrix) { this.matrix=matrix; this._nodes=new Map(); this._edges=new Map(); this._ids=new WeakMap(); this._nextId=0; }
326
+ _id(p) {
327
+ if(!this._ids.has(p)){const id=this._nextId++;this._ids.set(p,id);this._nodes.set(id,p);this._edges.set(id,new Map());}
328
+ return this._ids.get(p);
329
+ }
330
+ connect(a,b,w=null,dir=false) {
331
+ const ia=this._id(a),ib=this._id(b),wt=w??a.distanceTo(b);
332
+ this._edges.get(ia).set(ib,wt); if(!dir) this._edges.get(ib).set(ia,wt); return this;
333
+ }
334
+ disconnect(a,b,dir=false) {
335
+ const ia=this._id(a),ib=this._id(b);
336
+ this._edges.get(ia)?.delete(ib); if(!dir) this._edges.get(ib)?.delete(ia); return this;
337
+ }
338
+ neighbors(p) { const id=this._id(p); return [...(this._edges.get(id)||new Map()).keys()].map(nid=>this._nodes.get(nid)).filter(Boolean); }
339
+ weight(a,b) { return this._edges.get(this._id(a))?.get(this._id(b))??Infinity; }
340
+ hasEdge(a,b) { return (this._edges.get(this._id(a))||new Map()).has(this._id(b)); }
341
+ nodes() { return [...this._nodes.values()]; }
342
+ get nodeCount(){ return this._nodes.size; }
343
+ get edgeCount(){ let t=0; for(const e of this._edges.values()) t+=e.size; return t/2; }
344
+
345
+ shortestPath(start,goal) {
346
+ const sid=this._id(start),gid=this._id(goal),dist=new Map(),prev=new Map(),q=new Set();
347
+ for(const id of this._nodes.keys()){dist.set(id,Infinity);q.add(id);}
348
+ dist.set(sid,0);
349
+ while(q.size>0){
350
+ let u=null; for(const id of q) if(u===null||dist.get(id)<dist.get(u)) u=id;
351
+ if(u===null||u===gid) break; q.delete(u);
352
+ for(const [v,w] of (this._edges.get(u)||new Map())){ if(!q.has(v)) continue; const alt=dist.get(u)+w; if(alt<dist.get(v)){dist.set(v,alt);prev.set(v,u);} }
353
+ }
354
+ if(dist.get(gid)===Infinity) return {path:[],cost:Infinity};
355
+ const path=[];let cur=gid; while(cur!==undefined){path.unshift(this._nodes.get(cur));cur=prev.get(cur);}
356
+ return {path,cost:dist.get(gid)};
357
+ }
358
+
359
+ astar(start,goal) {
360
+ const sid=this._id(start),gid=this._id(goal),h=(id)=>{const p=this._nodes.get(id);return p?p.distanceTo(goal):0;};
361
+ const gS=new Map(),fS=new Map(),prev=new Map(),open=new Set([sid]);
362
+ for(const id of this._nodes.keys()){gS.set(id,Infinity);fS.set(id,Infinity);}
363
+ gS.set(sid,0);fS.set(sid,h(sid));
364
+ while(open.size>0){
365
+ let u=null; for(const id of open) if(u===null||fS.get(id)<fS.get(u)) u=id;
366
+ if(u===gid) break; open.delete(u);
367
+ for(const [v,w] of (this._edges.get(u)||new Map())){
368
+ const g=gS.get(u)+w; if(g<gS.get(v)){prev.set(v,u);gS.set(v,g);fS.set(v,g+h(v));open.add(v);}
369
+ }
370
+ }
371
+ if(gS.get(gid)===Infinity) return {path:[],cost:Infinity};
372
+ const path=[];let cur=gid; while(cur!==undefined){path.unshift(this._nodes.get(cur));cur=prev.get(cur);}
373
+ return {path,cost:gS.get(gid)};
374
+ }
375
+
376
+ reachable(start) {
377
+ const sid=this._id(start),visited=new Set([sid]),q=[sid];
378
+ while(q.length>0){const u=q.shift();for(const v of (this._edges.get(u)||new Map()).keys()) if(!visited.has(v)){visited.add(v);q.push(v);}}
379
+ return [...visited].map(id=>this._nodes.get(id)).filter(Boolean);
380
+ }
381
+ toString() { return `MatrixGraph(${this.nodeCount}nodes, ${this.edgeCount}edges)`; }
382
+ }
383
+
384
+ // ── MatrixProjection ──────────────────────────────────────────────────────────
385
+
386
+ class MatrixProjection {
387
+ constructor(matrix){ this.matrix=matrix; }
388
+ ontoAxis(p,ax){ const s=ax.project(p.x,p.y),off=ax.toOffset(s/ax.margin); return {scalar:s,projected:new MatrixPoint(this.matrix,off.x,off.y)}; }
389
+ isometric(x,y,z=0,tw=1,th=0.5){ return new MatrixPoint(this.matrix,(x-y)*tw/2,(x+y)*th/2-z*th); }
390
+ oblique(x,y,z=0,za=45,zs=0.5){ const r=_deg2rad(za); return new MatrixPoint(this.matrix,x+z*zs*Math.cos(r),y+z*zs*Math.sin(r)); }
391
+ perspective(x,y,z,f=1,cx=0,cy=0){ if(z===0) return new MatrixPoint(this.matrix,cx,cy); return new MatrixPoint(this.matrix,cx+(x-cx)*f/z,cy+(y-cy)*f/z); }
392
+ remap(p,src,dst){ const tx=(p.x-src.minX)/(src.width||1),ty=(p.y-src.minY)/(src.height||1); return new MatrixPoint(this.matrix,dst.minX+tx*dst.width,dst.minY+ty*dst.height); }
393
+ }
394
+
395
+ // ── MatrixInterpolation ───────────────────────────────────────────────────────
396
+
397
+ class MatrixInterpolation {
398
+ constructor(matrix){ this.matrix=matrix; }
399
+ lerp(a,b,t) { return a.lerp(b,_clamp(t,0,1)); }
400
+ cosine(a,b,t) { return a.lerp(b,(1-Math.cos(t*Math.PI))/2); }
401
+ smoothstep(a,b,t) { const s=_clamp(t,0,1); return a.lerp(b,s*s*(3-2*s)); }
402
+ smootherstep(a,b,t) { const s=_clamp(t,0,1); return a.lerp(b,s*s*s*(s*(6*s-15)+10)); }
403
+ catmullRom(p0,p1,p2,p3,t){
404
+ const t2=t*t,t3=t2*t;
405
+ return new MatrixPoint(this.matrix,
406
+ 0.5*((2*p1.x)+(-p0.x+p2.x)*t+(2*p0.x-5*p1.x+4*p2.x-p3.x)*t2+(-p0.x+3*p1.x-3*p2.x+p3.x)*t3),
407
+ 0.5*((2*p1.y)+(-p0.y+p2.y)*t+(2*p0.y-5*p1.y+4*p2.y-p3.y)*t2+(-p0.y+3*p1.y-3*p2.y+p3.y)*t3)
408
+ );
409
+ }
410
+ bezierQuadratic(p0,cp,p2,t){ const mt=1-t; return new MatrixPoint(this.matrix,mt*mt*p0.x+2*mt*t*cp.x+t*t*p2.x,mt*mt*p0.y+2*mt*t*cp.y+t*t*p2.y); }
411
+ bezierCubic(p0,c1,c2,p3,t){ const mt=1-t,m2=mt*mt,t2=t*t; return new MatrixPoint(this.matrix,m2*mt*p0.x+3*m2*t*c1.x+3*mt*t2*c2.x+t2*t*p3.x,m2*mt*p0.y+3*m2*t*c1.y+3*mt*t2*c2.y+t2*t*p3.y); }
412
+ }
413
+
414
+ // ── MatrixCanvas ──────────────────────────────────────────────────────────────
415
+ // Internal braille/block pixel canvas for terminal rendering.
416
+ // Uses 2×4 braille dots per character cell → 2× horizontal, 4× vertical resolution.
417
+
418
+ class MatrixCanvas {
419
+ constructor(charW, charH) {
420
+ this.charW = charW;
421
+ this.charH = charH;
422
+ this.pixelW = charW * 2; // braille: 2 dots wide
423
+ this.pixelH = charH * 4; // braille: 4 dots tall
424
+ // braille cell dot pattern index: [col0row0, col0row1, col0row2, col0row3, col1row0, col1row1, col1row2, col1row3]
425
+ // Unicode braille: U+2800 + bitmask
426
+ // Bit layout: dot1=0x01 dot2=0x02 dot3=0x04 dot4=0x40 (left col, top→bottom)
427
+ // dot5=0x08 dot6=0x10 dot7=0x20 dot8=0x80 (right col, top→bottom)
428
+ this._dots = new Uint8Array(charW * charH); // bitmask per cell
429
+ this._color = new Array(charW * charH).fill(''); // ANSI color prefix per cell
430
+ this._bg = new Array(charW * charH).fill(''); // ANSI bg per cell
431
+ // Braille bit index: [left-col bits top→bottom, right-col bits top→bottom]
432
+ this._brailleBits = [
433
+ [0x01,0x02,0x04,0x40], // left column, rows 0..3
434
+ [0x08,0x10,0x20,0x80], // right column, rows 0..3
435
+ ];
436
+ this._charBuf = new Array(charW * charH).fill(' '); // fallback char buf
437
+ }
438
+
439
+ _cell(cx, cy) { return cy * this.charW + cx; }
440
+
441
+ // Set a pixel (px,py in pixel space)
442
+ setPixel(px, py, colorAnsi = '') {
443
+ px = Math.round(px); py = Math.round(py);
444
+ if (px < 0 || px >= this.pixelW || py < 0 || py >= this.pixelH) return;
445
+ const cx = Math.floor(px / 2), cy = Math.floor(py / 4);
446
+ const col = px % 2, row = py % 4;
447
+ const idx = this._cell(cx, cy);
448
+ this._dots[idx] |= this._brailleBits[col][row];
449
+ if (colorAnsi) this._color[idx] = colorAnsi;
450
+ }
451
+
452
+ // Clear all pixels
453
+ clear() {
454
+ this._dots.fill(0);
455
+ this._color.fill('');
456
+ this._bg.fill('');
457
+ this._charBuf.fill(' ');
458
+ }
459
+
460
+ // Place a text character at a char-space position
461
+ setChar(cx, cy, ch, colorAnsi = '') {
462
+ if (cx < 0 || cx >= this.charW || cy < 0 || cy >= this.charH) return;
463
+ const idx = this._cell(cx, cy);
464
+ this._charBuf[idx] = ch;
465
+ if (colorAnsi) this._color[idx] = colorAnsi;
466
+ this._dots[idx] = 0; // char overrides braille
467
+ }
468
+
469
+ // Render to a string (no cursor positioning — just rows)
470
+ render(options = {}) {
471
+ const { border = true, title = '' } = options;
472
+ const rows = [];
473
+ if (border) {
474
+ const titleStr = title ? ` ${title} ` : '';
475
+ const totalW = this.charW;
476
+ const pad = Math.max(0, totalW - titleStr.length - 2);
477
+ const lpad = Math.floor(pad / 2), rpad = pad - lpad;
478
+ rows.push(BOX.dtl + BOX.dh.repeat(lpad) + (titleStr ? A.bold + titleStr + A.reset : '') + BOX.dh.repeat(rpad) + BOX.dtr);
479
+ }
480
+ for (let cy = 0; cy < this.charH; cy++) {
481
+ let row = border ? BOX.dv : '';
482
+ for (let cx = 0; cx < this.charW; cx++) {
483
+ const idx = this._cell(cx, cy);
484
+ const bits = this._dots[idx];
485
+ const col = this._color[idx];
486
+ const bg = this._bg[idx];
487
+ const ch = this._charBuf[idx];
488
+ let glyph;
489
+ if (ch !== ' ' && bits === 0) {
490
+ // explicit char placed
491
+ glyph = ch;
492
+ } else if (bits === 0) {
493
+ glyph = ' ';
494
+ } else {
495
+ glyph = String.fromCodePoint(0x2800 + bits);
496
+ }
497
+ row += (col || bg) ? (bg + col + glyph + A.reset) : glyph;
498
+ }
499
+ row += border ? BOX.dv : '';
500
+ rows.push(row);
501
+ }
502
+ if (border) rows.push(BOX.dbl + BOX.dh.repeat(this.charW) + BOX.dbr);
503
+ return rows.join('\n');
504
+ }
505
+ }
506
+
507
+ // ── MatrixRenderer ────────────────────────────────────────────────────────────
508
+ // Terminal + NVML renderer attached to a Matrix.
509
+ // m.render.frame(options) — print a full ANSI frame to stdout
510
+ // m.render.toNVML(options) — return an NVML page source string
511
+ // m.render.toHTML(options) — return an HTML <pre> block string
512
+
513
+ class MatrixRenderer {
514
+ constructor(matrix) {
515
+ this.matrix = matrix;
516
+ this._canvas = null;
517
+ this.options = {
518
+ charW: 80,
519
+ charH: 24,
520
+ padding: 1,
521
+ bg: '',
522
+ pointColor: A.rgb(100,200,255),
523
+ edgeColor: A.rgb(80,80,120),
524
+ pathColor: A.rgb(255,200,80),
525
+ gridColor: A.rgb(60,80,60),
526
+ labelColor: A.rgb(255,255,180),
527
+ axisColor: A.rgb(120,120,200),
528
+ title: '',
529
+ showAxes: true,
530
+ showLabels: true,
531
+ showGrid: false,
532
+ };
533
+ }
534
+
535
+ configure(opts) { Object.assign(this.options, opts); return this; }
536
+
537
+ // Map a matrix (x,y) to canvas pixel (px,py)
538
+ _toPixel(x, y, canvas, matBounds) {
539
+ const o = this.options;
540
+ const mb = matBounds || new MatrixBounds(this.matrix, 0, 0, this.matrix.width, this.matrix.height);
541
+ const padX = o.padding, padY = o.padding;
542
+ const pw = canvas.pixelW - padX * 2;
543
+ const ph = canvas.pixelH - padY * 2;
544
+ const tx = (x - mb.minX) / (mb.width || 1);
545
+ const ty = (y - mb.minY) / (mb.height || 1);
546
+ return {
547
+ px: Math.round(padX + tx * pw),
548
+ py: Math.round(padY + (1 - ty) * ph), // flip Y so +y is up
549
+ };
550
+ }
551
+
552
+ // Bresenham line in pixel space on canvas
553
+ _drawLine(canvas, x0, y0, x1, y1, colorAnsi) {
554
+ let dx = Math.abs(x1-x0), dy = Math.abs(y1-y0);
555
+ let sx = x0<x1?1:-1, sy = y0<y1?1:-1, err = dx-dy;
556
+ while(true){
557
+ canvas.setPixel(x0, y0, colorAnsi);
558
+ if(x0===x1&&y0===y1) break;
559
+ const e2=2*err;
560
+ if(e2>-dy){err-=dy;x0+=sx;}
561
+ if(e2< dx){err+=dx;y0+=sy;}
562
+ }
563
+ }
564
+
565
+ // Draw a circle outline in pixel space
566
+ _drawCircle(canvas, cx, cy, r, colorAnsi) {
567
+ const steps = Math.max(12, Math.round(2 * Math.PI * r));
568
+ for (let i = 0; i < steps; i++) {
569
+ const a = (i / steps) * Math.PI * 2;
570
+ canvas.setPixel(Math.round(cx + Math.cos(a) * r), Math.round(cy + Math.sin(a) * r), colorAnsi);
571
+ }
572
+ }
573
+
574
+ // Build a canvas from the current matrix state
575
+ _build(overrideOpts = {}) {
576
+ const o = Object.assign({}, this.options, overrideOpts);
577
+ const m = this.matrix;
578
+ const c = new MatrixCanvas(o.charW, o.charH);
579
+ const mb = m._registeredBounds || new MatrixBounds(m, 0, 0, m.width, m.height);
580
+ const tp = (x, y) => this._toPixel(x, y, c, mb);
581
+
582
+ // Draw axes
583
+ if (o.showAxes) {
584
+ const originPx = tp(0, 0);
585
+ // x axis
586
+ const xEnd = tp(m.width, 0);
587
+ this._drawLine(c, tp(0, 0).px, tp(0, 0).py, xEnd.px, xEnd.py, o.axisColor);
588
+ // y axis
589
+ const yEnd = tp(0, m.height);
590
+ this._drawLine(c, tp(0, 0).px, tp(0, 0).py, yEnd.px, yEnd.py, o.axisColor);
591
+ // origin marker
592
+ c.setChar(Math.floor(originPx.px/2), Math.floor(originPx.py/4), '0', o.axisColor);
593
+ }
594
+
595
+ // Draw registered grid if any
596
+ if (o.showGrid && m._registeredGrid) {
597
+ const g = m._registeredGrid;
598
+ g.forEach((p) => {
599
+ const {px,py} = tp(p.x, p.y);
600
+ c.setPixel(px, py, o.gridColor);
601
+ });
602
+ }
603
+
604
+ // Draw registered edges (graphs, connectors, paths)
605
+ for (const edge of (m._renderEdges || [])) {
606
+ const {a, b, color} = edge;
607
+ const pa = tp(a.x, a.y), pb = tp(b.x, b.y);
608
+ this._drawLine(c, pa.px, pa.py, pb.px, pb.py, color || o.edgeColor);
609
+ }
610
+
611
+ // Draw path lines
612
+ for (const pathObj of (m._renderPaths || [])) {
613
+ const {points, color} = pathObj;
614
+ for (let i = 1; i < points.length; i++) {
615
+ const pa = tp(points[i-1].x, points[i-1].y);
616
+ const pb = tp(points[i].x, points[i].y);
617
+ this._drawLine(c, pa.px, pa.py, pb.px, pb.py, color || o.pathColor);
618
+ }
619
+ }
620
+
621
+ // Draw points
622
+ for (const p of m._points) {
623
+ const {px, py} = tp(p.x, p.y);
624
+ const col = p.color || o.pointColor;
625
+ this._drawCircle(c, px, py, 2, col);
626
+ c.setPixel(px, py, col);
627
+ if (o.showLabels && p.label) {
628
+ const cx = Math.floor(px/2) + 1, cy = Math.floor(py/4);
629
+ const lbl = p.label.slice(0,6);
630
+ for (let i = 0; i < lbl.length; i++) c.setChar(cx+i, cy, lbl[i], o.labelColor);
631
+ }
632
+ }
633
+
634
+ return { canvas: c, options: o };
635
+ }
636
+
637
+ // ── Terminal render ──────────────────────────────────────────────────────
638
+
639
+ // Register a graph for rendering — draws all its edges
640
+ addGraph(graph, color) {
641
+ if (!this.matrix._renderEdges) this.matrix._renderEdges = [];
642
+ for (const [id, edges] of graph._edges) {
643
+ const a = graph._nodes.get(id);
644
+ for (const [nid] of edges) {
645
+ const b = graph._nodes.get(nid);
646
+ if (a && b) this.matrix._renderEdges.push({ a, b, color });
647
+ }
648
+ }
649
+ return this;
650
+ }
651
+
652
+ // Register a path for rendering
653
+ addPath(path, color) {
654
+ if (!this.matrix._renderPaths) this.matrix._renderPaths = [];
655
+ this.matrix._renderPaths.push({ points: path.points, color });
656
+ return this;
657
+ }
658
+
659
+ // Set the visible bounds explicitly (overrides 0,0→w,h)
660
+ setBounds(bounds) { this.matrix._registeredBounds = bounds; return this; }
661
+
662
+ // Clear all registered render overlays
663
+ clearOverlays() { this.matrix._renderEdges = []; this.matrix._renderPaths = []; return this; }
664
+
665
+ // Print a frame to stdout
666
+ frame(opts = {}) {
667
+ const { canvas, options: o } = this._build(opts);
668
+ process.stdout.write(canvas.render({ border: true, title: o.title || this.matrix.toString() }) + '\n');
669
+ return this;
670
+ }
671
+
672
+ // Print frame with cursor positioning (for live update)
673
+ frameAt(row = 1, col = 1, opts = {}) {
674
+ const { canvas, options: o } = this._build(opts);
675
+ const lines = canvas.render({ border: true, title: o.title || '' }).split('\n');
676
+ let out = A.hide;
677
+ lines.forEach((line, i) => { out += A.pos(row + i, col) + line; });
678
+ out += A.show;
679
+ process.stdout.write(out);
680
+ return this;
681
+ }
682
+
683
+ // Start a live animation loop: fn(matrix, renderer, frame) called each tick
684
+ animate(fn, fps = 12, opts = {}) {
685
+ const ms = Math.round(1000 / fps);
686
+ let frame = 0;
687
+ const charW = opts.charW || this.options.charW;
688
+ const charH = opts.charH || this.options.charH;
689
+ process.stdout.write(A.alt + A.hide + A.cls + A.home);
690
+ const interval = setInterval(() => {
691
+ try {
692
+ fn(this.matrix, this, frame++);
693
+ this.frameAt(1, 1, opts);
694
+ } catch (e) {
695
+ clearInterval(interval);
696
+ process.stdout.write(A.main + A.show);
697
+ throw e;
698
+ }
699
+ }, ms);
700
+ // Stop on Ctrl+C
701
+ process.on('SIGINT', () => {
702
+ clearInterval(interval);
703
+ process.stdout.write(A.main + A.show + '\n');
704
+ process.exit(0);
705
+ });
706
+ return () => { clearInterval(interval); process.stdout.write(A.main + A.show + '\n'); };
707
+ }
708
+
709
+ // ── HTML render ──────────────────────────────────────────────────────────
710
+
711
+ toHTML(opts = {}) {
712
+ const { canvas } = this._build(opts);
713
+ const o = Object.assign({}, this.options, opts);
714
+ const title = o.title || this.matrix.toString();
715
+ // Strip ANSI codes and produce an HTML <pre> block with spans for colors
716
+ const raw = canvas.render({ border: true, title });
717
+ // Simple ANSI→HTML (handles 24-bit RGB and reset)
718
+ let html = raw
719
+ .replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
720
+ .replace(/\x1b\[38;2;(\d+);(\d+);(\d+)m/g, (_,r,g,b) => `<span style="color:rgb(${r},${g},${b})">`)
721
+ .replace(/\x1b\[0m/g, '</span>')
722
+ .replace(/\x1b\[[0-9;]*m/g, '');
723
+ return `<pre class="nvml-matrix" style="font-family:monospace;line-height:1.1;background:#0a0a0f;color:#ccc;padding:1rem;border-radius:6px;overflow:auto">${html}</pre>`;
724
+ }
725
+
726
+ // ── NVML render ──────────────────────────────────────────────────────────
727
+ // Returns a complete .nvml page source string.
728
+
729
+ toNVML(opts = {}) {
730
+ const o = Object.assign({}, this.options, opts);
731
+ const title = o.title || `Matrix ${this.matrix.width}×${this.matrix.height}`;
732
+ const html = this.toHTML(opts);
733
+
734
+ // Build signal data for any named points
735
+ const signals = this.matrix._points
736
+ .filter(p => p.label)
737
+ .map(p => ` ${p.label}_x=${p.x},\n ${p.label}_y=${p.y},`)
738
+ .join('\n');
739
+
740
+ const pointRows = this.matrix._points
741
+ .filter(p => p.label)
742
+ .map(p => ` {tr} [\n {td}='${p.label}'\n {td} [ text -> ${p.label}_x ]\n {td} [ text -> ${p.label}_y ]\n ]`)
743
+ .join('\n');
744
+
745
+ const stateBlock = signals
746
+ ? `@state [\n${signals}\n]\n\n`
747
+ : '';
748
+
749
+ return `@config [\n title='${title}',\n lang='en',\n charset='UTF-8',\n]\n\n${stateBlock}@ss [\n {body} [ font-family='monospace', background='#0a0a0f', color='#ccc', margin='0', padding='2rem' ]\n {h1} [ color='#6c63ff', font-size='1.4rem', margin-bottom='1rem' ]\n {.matrix-wrap} [ overflow='auto', margin-bottom='2rem' ]\n {table} [ border-collapse='collapse', font-size='0.85rem', color='#ccc' ]\n {th} [ padding='0.3rem 0.8rem', background='#1a1a2e', color='#6c63ff', border='1px solid #333' ]\n {td} [ padding='0.3rem 0.8rem', border='1px solid #222', text-align='center' ]\n]\n\n@visual [\n {h1}='${title}'\n\n {div} [\n class='matrix-wrap',\n [..]::code=(\n${html.replace(/\\/g,'\\\\').replace(/`/g,'\\`')}\n )\n ]\n\n${pointRows ? ` {h2}='Named Points'\n {table} [\n {thead} [ {tr} [ {th}='Name' {th}='X' {th}='Y' ] ]\n {tbody} [\n${pointRows}\n ]\n ]\n` : ''}\n]`;
750
+ }
751
+ }
752
+
753
+ // ── MatrixDebug ───────────────────────────────────────────────────────────────
754
+ // ASCII debug + quick terminal output (no braille — plain chars)
755
+
756
+ class MatrixDebug {
757
+ constructor(matrix) { this.matrix = matrix; }
758
+
759
+ drawPoints(opts = {}) {
760
+ const m = this.matrix;
761
+ const scale = opts.scale ?? 2;
762
+ const W = Math.ceil(m.width * scale) + 3;
763
+ const H = Math.ceil(m.height * scale) + 3;
764
+ const buf = Array.from({length:H},()=>Array(W).fill(' '));
765
+ const plot = (x, y, ch, col='') => {
766
+ const cx=Math.round(x*scale)+1, cy=H-Math.round(y*scale)-2;
767
+ if(cx>=0&&cx<W&&cy>=0&&cy<H) buf[cy][cx]={ ch, col };
768
+ };
769
+ // axes
770
+ for(let i=0;i<W;i++) buf[H-2][i]={ ch:'─', col:A.fg(240) };
771
+ for(let i=0;i<H;i++) buf[i][1] ={ ch:'│', col:A.fg(240) };
772
+ buf[H-2][1]={ ch:'└', col:A.fg(240) };
773
+ // points
774
+ for(const p of m._points) {
775
+ const col = p.color || A.rgb(100,200,255);
776
+ plot(p.x, p.y, p.label?p.label[0]:'●', col);
777
+ }
778
+ // output
779
+ console.log(A.bold + (opts.title||m.toString()) + A.reset);
780
+ for(const row of buf){
781
+ let line='';
782
+ for(const cell of row){
783
+ if(typeof cell==='object'&&cell!==null) line+=cell.col+cell.ch+A.reset;
784
+ else line+=' ';
785
+ }
786
+ console.log(line);
787
+ }
788
+ }
789
+
790
+ drawGraph(graph, opts = {}) {
791
+ const m = this.matrix;
792
+ const scale = opts.scale ?? 2;
793
+ const W = Math.ceil(m.width * scale) + 3;
794
+ const H = Math.ceil(m.height * scale) + 3;
795
+ const buf = Array.from({length:H},()=>Array(W).fill(' '));
796
+ const toC = (x,y) => ({cx:Math.round(x*scale)+1, cy:H-Math.round(y*scale)-2});
797
+
798
+ const plotLine = (x0,y0,x1,y1,col) => {
799
+ // Bresenham in char space
800
+ let dx=Math.abs(x1-x0),dy=Math.abs(y1-y0),sx=x0<x1?1:-1,sy=y0<y1?1:-1,err=dx-dy;
801
+ while(true){
802
+ if(x0>=0&&x0<W&&y0>=0&&y0<H) buf[y0][x0]={ ch:dx>dy?'─':'│', col };
803
+ if(x0===x1&&y0===y1) break;
804
+ const e2=2*err; if(e2>-dy){err-=dy;x0+=sx;} if(e2<dx){err+=dx;y0+=sy;}
805
+ }
806
+ };
807
+
808
+ // axes
809
+ for(let i=0;i<W;i++) buf[H-2][i]={ ch:'─', col:A.fg(240) };
810
+ for(let i=0;i<H;i++) buf[i][1] ={ ch:'│', col:A.fg(240) };
811
+ buf[H-2][1]={ ch:'└', col:A.fg(240) };
812
+
813
+ // edges
814
+ const seen = new Set();
815
+ for(const [id,edges] of graph._edges){
816
+ const a = graph._nodes.get(id);
817
+ if(!a) continue;
818
+ for(const [nid] of edges){
819
+ const key=[Math.min(id,nid),Math.max(id,nid)].join('-');
820
+ if(seen.has(key)) continue; seen.add(key);
821
+ const b = graph._nodes.get(nid);
822
+ if(!b) continue;
823
+ const pa=toC(a.x,a.y), pb=toC(b.x,b.y);
824
+ plotLine(pa.cx,pa.cy,pb.cx,pb.cy,A.rgb(80,100,160));
825
+ }
826
+ }
827
+ // nodes
828
+ for(const p of graph.nodes()){
829
+ const {cx,cy}=toC(p.x,p.y);
830
+ const col=p.color||A.rgb(100,200,255);
831
+ if(cx>=0&&cx<W&&cy>=0&&cy<H) buf[cy][cx]={ ch:p.label?p.label[0]:'●', col };
832
+ if(p.label){
833
+ for(let i=0;i<Math.min(p.label.length,5);i++){
834
+ const lx=cx+i+1;
835
+ if(lx<W) buf[cy][lx]={ ch:p.label[i], col:A.rgb(255,255,180) };
836
+ }
837
+ }
838
+ }
839
+
840
+ console.log(A.bold+(opts.title||`Graph: ${graph.nodeCount}n ${graph.edgeCount}e`)+A.reset);
841
+ for(const row of buf){
842
+ let line='';
843
+ for(const cell of row){
844
+ if(typeof cell==='object'&&cell!==null) line+=cell.col+cell.ch+A.reset;
845
+ else line+=' ';
846
+ }
847
+ console.log(line);
848
+ }
849
+ }
850
+
851
+ drawPath(path, opts = {}) {
852
+ console.log(A.bold+(opts.title||path.toString())+A.reset);
853
+ path.points.forEach((p,i) => {
854
+ const bar = '█'.repeat(Math.min(40, Math.round(p.x)));
855
+ const col = p.color||A.rgb(255,200,80);
856
+ console.log(` ${String(i).padStart(3,' ')} (${String(p.x).padStart(6,' ')}, ${String(p.y).padStart(6,' ')}) ${col}${bar}${A.reset}${p.label?' '+p.label:''}`);
857
+ });
858
+ }
859
+
860
+ drawGrid(grid, opts = {}) {
861
+ console.log(A.bold+(opts.title||grid.toString())+A.reset);
862
+ const col = A.fg(240);
863
+ for(let r=0;r<grid.rows;r++){
864
+ let line=' ';
865
+ for(let c=0;c<grid.cols;c++){
866
+ const p=grid.at(r,c);
867
+ const ch=p?.label?p.label[0]:'+';
868
+ const pcol=p?.color||col;
869
+ line+=pcol+ch+A.reset+' ';
870
+ }
871
+ console.log(line);
872
+ }
873
+ }
874
+
875
+ drawAxes() {
876
+ const m=this.matrix;
877
+ console.log(A.bold+`Matrix ${m.width}×${m.height} axes:`+A.reset);
878
+ m.axes.forEach((ax,i)=>{
879
+ const u=ax.unitVector();
880
+ console.log(` ${A.fg(99)}[${i}]${A.reset} angle=${ax.angle}° margin=${ax.margin} unit=(${u.x.toFixed(3)},${u.y.toFixed(3)})`);
881
+ });
882
+ }
883
+
884
+ info() {
885
+ const m=this.matrix;
886
+ console.log([
887
+ A.bold+`Matrix {`+A.reset,
888
+ ` ${A.fg(99)}size${A.reset}: ${m.width} × ${m.height}`,
889
+ ` ${A.fg(99)}axes${A.reset}: ${m.axes.length}`,
890
+ ` ${A.fg(99)}points${A.reset}: ${m._points.length}`,
891
+ `}`
892
+ ].join('\n'));
893
+ }
894
+ }
895
+
896
+ // ── Matrix ────────────────────────────────────────────────────────────────────
897
+
898
+ class Matrix {
899
+ constructor(width=100,height=100,axes=[]) {
900
+ this.width = width;
901
+ this.height = height;
902
+ this.axes = axes.length>0?axes:[new MatrixAxis(0,1),new MatrixAxis(90,1)];
903
+ this._points = [];
904
+ this._renderEdges = [];
905
+ this._renderPaths = [];
906
+ this.debug = new MatrixDebug(this);
907
+ this.render = new MatrixRenderer(this);
908
+ this.interp = new MatrixInterpolation(this);
909
+ this.projection = new MatrixProjection(this);
910
+ }
911
+
912
+ point(x=0,y=0) { const p=new MatrixPoint(this,x,y); this._points.push(p); return p; }
913
+ removePoint(p) { const i=this._points.indexOf(p); if(i!==-1) this._points.splice(i,1); }
914
+ clearPoints() { this._points=[]; }
915
+
916
+ distance(a,b) { return a.distanceTo(b); }
917
+ manhattan(a,b) { return a.manhattanTo(b); }
918
+ chebyshev(a,b) { return a.chebyshevTo(b); }
919
+ midpoint(a,b) { return a.midpointTo(b); }
920
+ angle(a,b) { return a.angleTo(b); }
921
+ axisDistance(a,b) { return this.axes.reduce((s,ax)=>s+Math.abs(ax.project(b.x-a.x,b.y-a.y)),0); }
922
+ getLengthOf(points) { let t=0; for(let i=1;i<points.length;i++) t+=points[i-1].distanceTo(points[i]); return t; }
923
+
924
+ bounds(mnX,mnY,mxX,mxY) { return new MatrixBounds(this,mnX,mnY,mxX,mxY); }
925
+ boundsFromPoints(pts) { return MatrixBounds.fromPoints(this,pts); }
926
+ spaceBounds() { return new MatrixBounds(this,0,0,this.width,this.height); }
927
+
928
+ grid(rows,cols,ox=0,oy=0,cw=1,ch=1) { return new MatrixGrid(this,rows,cols,ox,oy,cw,ch); }
929
+ translation(a,b) { return new MatrixTranslation(this,a,b); }
930
+ transform(cfg) { return new MatrixTransform(this,cfg); }
931
+ translateTransform(dx,dy) { return MatrixTransform.translate(this,dx,dy); }
932
+ rotateTransform(deg,cx,cy) { return MatrixTransform.rotate(this,deg,cx,cy); }
933
+ scaleTransform(sx,sy,cx,cy) { return MatrixTransform.scale(this,sx,sy,cx,cy); }
934
+ shearTransform(shx,shy) { return MatrixTransform.shear(this,shx,shy); }
935
+ chain(transforms) { return new MatrixTransformChain(this,transforms); }
936
+ path(points) { return new MatrixPath(this,points); }
937
+ connector(points) { return new MatrixConnector(this,points); }
938
+ graph() { return new MatrixGraph(this); }
939
+ vector(dx,dy) { return new MatrixVector(dx,dy); }
940
+ lerp(a,b,t) { return this.interp.lerp(a,b,t); }
941
+ smoothstep(a,b,t) { return this.interp.smoothstep(a,b,t); }
942
+ bezierQuadratic(p0,cp,p2,t) { return this.interp.bezierQuadratic(p0,cp,p2,t); }
943
+ bezierCubic(p0,c1,c2,p3,t) { return this.interp.bezierCubic(p0,c1,c2,p3,t); }
944
+ project(p,ax) { return this.projection.ontoAxis(p,ax); }
945
+ isometric(x,y,z,tw,th) { return this.projection.isometric(x,y,z,tw,th); }
946
+ snap(p,sx,sy) { return p.snap(sx,sy); }
947
+ normalize(v) { return v.normalize?v.normalize():new MatrixVector(v.x,v.y).normalize(); }
948
+ clone(p) { return p.clone(); }
949
+ clonePath(p) { return p.clone(); }
950
+ toString() { return `Matrix(${this.width}×${this.height} axes=${this.axes.length} pts=${this._points.length})`; }
951
+ }
952
+
953
+ module.exports = {
954
+ kitdef: {
955
+ Matrix, MatrixAxis, MatrixPoint, MatrixVector,
956
+ MatrixBounds, MatrixTranslation, MatrixTransform, MatrixTransformChain,
957
+ MatrixConnector, MatrixPath, MatrixGrid, MatrixGraph,
958
+ MatrixProjection, MatrixInterpolation, MatrixDebug, MatrixRenderer, MatrixCanvas,
959
+ }
960
+ };