numbl 0.4.0 → 0.4.1

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/dist-lib/lib.js CHANGED
@@ -287,10 +287,12 @@ var RuntimeClassInstanceArray = class extends Refcounted {
287
287
  kind = "class_instance_array";
288
288
  className;
289
289
  elements;
290
- constructor(className, elements) {
290
+ shape;
291
+ constructor(className, elements, shape) {
291
292
  super();
292
293
  this.className = className;
293
294
  this.elements = elements;
295
+ this.shape = shape ?? [1, elements.length];
294
296
  for (const el of elements) incref(el);
295
297
  }
296
298
  _destroy(rt) {
@@ -797,7 +799,7 @@ function displayValue(v) {
797
799
  case "dictionary":
798
800
  return formatDictionary(v);
799
801
  case "class_instance_array":
800
- return ` 1x${v.elements.length} ${v.className} array`;
802
+ return ` ${v.shape[0]}x${v.shape[1]} ${v.className} array`;
801
803
  }
802
804
  }
803
805
  var formatStructArray = (v) => {
@@ -20391,10 +20393,11 @@ function sparseElemMul(a, b) {
20391
20393
  }
20392
20394
  function sparseElemMulDense(S, D) {
20393
20395
  const [dRows, dCols] = tensorSize2D(D);
20394
- if (S.m !== dRows || S.n !== dCols)
20396
+ if (!(dRows === S.m || dRows === 1) || !(dCols === S.n || dCols === 1))
20395
20397
  throw new RuntimeError(
20396
20398
  `Matrix dimensions must agree: [${S.m},${S.n}] vs [${dRows},${dCols}]`
20397
20399
  );
20400
+ const denseIdx = (row, col) => (dCols === 1 ? 0 : col) * dRows + (dRows === 1 ? 0 : row);
20398
20401
  const hasImag = isComplexSparse(S) || D.imag !== void 0;
20399
20402
  const irList = [];
20400
20403
  const prList = [];
@@ -20404,7 +20407,7 @@ function sparseElemMulDense(S, D) {
20404
20407
  jc[col] = irList.length;
20405
20408
  for (let k = S.jc[col]; k < S.jc[col + 1]; k++) {
20406
20409
  const row = S.ir[k];
20407
- const idx = col * S.m + row;
20410
+ const idx = denseIdx(row, col);
20408
20411
  const aRe = S.pr[k], aIm = S.pi ? S.pi[k] : 0;
20409
20412
  const bRe = D.data[idx], bIm = D.imag ? D.imag[idx] : 0;
20410
20413
  const re = aRe * bRe - aIm * bIm;
@@ -22513,12 +22516,24 @@ function transposeCore(v, conjugate) {
22513
22516
  if (v._isLogical) t._isLogical = true;
22514
22517
  return t;
22515
22518
  }
22519
+ function transposeClassInstanceArray(v) {
22520
+ const [r, c] = v.shape;
22521
+ const out = new Array(v.elements.length);
22522
+ for (let i = 0; i < r; i++) {
22523
+ for (let j = 0; j < c; j++) {
22524
+ out[i * c + j] = v.elements[j * r + i];
22525
+ }
22526
+ }
22527
+ return new RuntimeClassInstanceArray(v.className, out, [c, r]);
22528
+ }
22516
22529
  function mTranspose(v) {
22517
22530
  if (isRuntimeSparseMatrix(v)) return sparseTranspose(v);
22518
22531
  if (isRuntimeComplexNumber(v)) return v;
22519
22532
  if (isRuntimeNumber(v) || isRuntimeLogical(v)) return v;
22520
22533
  if (isRuntimeCell(v)) return transposeCellArray(v);
22521
22534
  if (isRuntimeChar(v)) return v;
22535
+ if (isRuntimeClassInstance(v)) return v;
22536
+ if (isRuntimeClassInstanceArray(v)) return transposeClassInstanceArray(v);
22522
22537
  if (!isRuntimeTensor(v))
22523
22538
  throw new RuntimeError("Cannot transpose non-numeric value");
22524
22539
  return transposeCore(v, false);
@@ -22529,6 +22544,8 @@ function mConjugateTranspose(v) {
22529
22544
  if (isRuntimeNumber(v) || isRuntimeLogical(v)) return v;
22530
22545
  if (isRuntimeCell(v)) return transposeCellArray(v);
22531
22546
  if (isRuntimeChar(v)) return v;
22547
+ if (isRuntimeClassInstance(v)) return v;
22548
+ if (isRuntimeClassInstanceArray(v)) return transposeClassInstanceArray(v);
22532
22549
  if (!isRuntimeTensor(v))
22533
22550
  throw new RuntimeError("Cannot transpose non-numeric value");
22534
22551
  return transposeCore(v, true);
@@ -25213,6 +25230,60 @@ function cellCatAlongDim(values, dimIdx) {
25213
25230
  }
25214
25231
 
25215
25232
  // src/numbl-core/runtime/struct-access.ts
25233
+ function handlePropToRuntime(v) {
25234
+ if (typeof v === "number") return RTV.num(v);
25235
+ if (typeof v === "boolean") return RTV.logical(v);
25236
+ if (typeof v === "string") return RTV.char(v);
25237
+ if (Array.isArray(v)) {
25238
+ const data = allocFloat64Array(v);
25239
+ return RTV.tensor(data, [1, v.length]);
25240
+ }
25241
+ return v;
25242
+ }
25243
+ var HANDLE_DEFAULTS = {
25244
+ quiver3: {
25245
+ LineWidth: 0.5,
25246
+ LineStyle: "-",
25247
+ ShowArrowHead: true,
25248
+ AutoScale: true,
25249
+ AutoScaleFactor: 0.9,
25250
+ Marker: "none",
25251
+ Color: [0, 0.447, 0.741]
25252
+ }
25253
+ };
25254
+ function resolveHandleKey(trace, field) {
25255
+ if (field in trace) return field;
25256
+ const camel = field.charAt(0).toLowerCase() + field.slice(1);
25257
+ if (camel in trace) return camel;
25258
+ if (field.endsWith("Data")) {
25259
+ const base = field.slice(0, -4);
25260
+ const short = base.charAt(0).toLowerCase() + base.slice(1);
25261
+ if (short in trace) return short;
25262
+ }
25263
+ return null;
25264
+ }
25265
+ function runtimeToHandleValue(value, current) {
25266
+ if (typeof current === "boolean") {
25267
+ if (typeof value === "boolean") return value;
25268
+ if (typeof value === "number") return value !== 0;
25269
+ if (isRuntimeString(value) || isRuntimeChar(value)) {
25270
+ const s = (isRuntimeChar(value) ? value.value : value).toLowerCase();
25271
+ return !(s === "off" || s === "false" || s === "0");
25272
+ }
25273
+ return true;
25274
+ }
25275
+ if (typeof current === "string") {
25276
+ return isRuntimeChar(value) ? value.value : isRuntimeString(value) ? value : String(value);
25277
+ }
25278
+ if (typeof current === "number" || current === void 0) {
25279
+ if (typeof value === "number") return value;
25280
+ if (isRuntimeNumber(value)) return value;
25281
+ }
25282
+ if (Array.isArray(current) && isRuntimeTensor(value)) {
25283
+ return Array.from(value.data);
25284
+ }
25285
+ return value;
25286
+ }
25216
25287
  function getRTValueField(base, field) {
25217
25288
  if (isRuntimeStruct(base) || isRuntimeClassInstance(base)) {
25218
25289
  const val = base.fields.get(field);
@@ -25236,6 +25307,15 @@ function getRTValueField(base, field) {
25236
25307
  });
25237
25308
  return horzcat(...values);
25238
25309
  }
25310
+ if (isRuntimeGraphicsHandle(base)) {
25311
+ const key = resolveHandleKey(base._trace, field);
25312
+ if (key !== null) return handlePropToRuntime(base._trace[key]);
25313
+ const dflt = HANDLE_DEFAULTS[base._traceType]?.[field];
25314
+ if (dflt !== void 0) return handlePropToRuntime(dflt);
25315
+ throw new RuntimeError(
25316
+ `No property '${field}' on ${base._traceType} handle`
25317
+ );
25318
+ }
25239
25319
  throw new RuntimeError(`Cannot access field ${field} on ${kstr(base)}`);
25240
25320
  }
25241
25321
  function setRTValueField(base, field, value, rt) {
@@ -25280,6 +25360,12 @@ function setRTValueField(base, field, value, rt) {
25280
25360
  `Cannot assign field '${field}' on a non-scalar struct array without indexing`
25281
25361
  );
25282
25362
  }
25363
+ if (isRuntimeGraphicsHandle(base)) {
25364
+ const key = resolveHandleKey(base._trace, field) ?? field.charAt(0).toLowerCase() + field.slice(1);
25365
+ const current = base._trace[key] ?? HANDLE_DEFAULTS[base._traceType]?.[field];
25366
+ base._trace[key] = runtimeToHandleValue(value, current);
25367
+ return base;
25368
+ }
25283
25369
  if (isRuntimeNumber(base) && base === 0) {
25284
25370
  return RTV.struct(/* @__PURE__ */ new Map([[field, value]]));
25285
25371
  }
@@ -31218,7 +31304,7 @@ function getShape(v) {
31218
31304
  if (isRuntimeSparseMatrix(v)) return [v.m, v.n];
31219
31305
  if (isRuntimeCell(v)) return v.shape;
31220
31306
  if (isRuntimeStructArray(v)) return [1, v.elements.length];
31221
- if (isRuntimeClassInstanceArray(v)) return [1, v.elements.length];
31307
+ if (isRuntimeClassInstanceArray(v)) return [...v.shape];
31222
31308
  return [1, 1];
31223
31309
  }
31224
31310
  defineBuiltin({
@@ -31396,7 +31482,8 @@ defineBuiltin({
31396
31482
  }
31397
31483
  if (isRuntimeString(v)) return 1;
31398
31484
  if (isRuntimeStructArray(v)) return v.elements.length;
31399
- if (isRuntimeClassInstanceArray(v)) return v.elements.length;
31485
+ if (isRuntimeClassInstanceArray(v))
31486
+ return v.elements.length === 0 ? 0 : Math.max(...v.shape);
31400
31487
  return 1;
31401
31488
  }
31402
31489
  }
@@ -31509,8 +31596,15 @@ defineBuiltin({
31509
31596
  if (isRuntimeClassInstanceArray(v)) return mkChar(v.className);
31510
31597
  if (isRuntimeFunction(v)) return mkChar("function_handle");
31511
31598
  if (isRuntimeDummyHandle(v)) return mkChar("dummy_handle");
31512
- if (isRuntimeGraphicsHandle(v))
31513
- return mkChar("matlab.graphics.primitive.Surface");
31599
+ if (isRuntimeGraphicsHandle(v)) {
31600
+ const handleClass = {
31601
+ contour: "matlab.graphics.chart.primitive.Contour",
31602
+ quiver3: "matlab.graphics.chart.primitive.Quiver"
31603
+ };
31604
+ return mkChar(
31605
+ handleClass[v._traceType] ?? "matlab.graphics.primitive.Surface"
31606
+ );
31607
+ }
31514
31608
  return mkChar("unknown");
31515
31609
  }
31516
31610
  }
@@ -36551,6 +36645,7 @@ defineBuiltin({
36551
36645
  const blocks = args.map((a) => {
36552
36646
  if (isRuntimeNumber(a))
36553
36647
  return RTV.tensor(allocFloat64Array([a]), [1, 1]);
36648
+ if (isRuntimeSparseMatrix(a)) return sparseToDense(a);
36554
36649
  if (!isRuntimeTensor(a))
36555
36650
  throw new RuntimeError("blkdiag: arguments must be numeric");
36556
36651
  return a;
@@ -37919,6 +38014,21 @@ defineBuiltin({
37919
38014
  const shape = reps.length >= 2 ? reps : [reps[0], reps[0]];
37920
38015
  return RTV.tensor(data, shape, imag2);
37921
38016
  }
38017
+ if (isRuntimeCell(v)) {
38018
+ const srcShape2 = v.shape.length >= 2 ? v.shape : [1, v.shape[0] ?? v.data.length];
38019
+ const [sm, sn] = [srcShape2[0], srcShape2[1]];
38020
+ const r0 = reps[0] ?? 1;
38021
+ const r1 = reps.length >= 2 ? reps[1] : reps[0] ?? 1;
38022
+ const om = sm * r0;
38023
+ const on = sn * r1;
38024
+ const out2 = new Array(om * on);
38025
+ for (let J = 0; J < on; J++) {
38026
+ for (let I = 0; I < om; I++) {
38027
+ out2[I + J * om] = v.data[I % sm + J % sn * sm];
38028
+ }
38029
+ }
38030
+ return RTV.cell(out2, [om, on]);
38031
+ }
37922
38032
  if (!isRuntimeTensor(v))
37923
38033
  throw new RuntimeError("repmat: first argument must be numeric");
37924
38034
  if (reps.every((r) => r === 1)) {
@@ -43992,6 +44102,13 @@ for (const name of ["groot", "gcf", "gca", "shg", "newplot"]) {
43992
44102
  })
43993
44103
  });
43994
44104
  }
44105
+ registerIBuiltin({
44106
+ name: "camlight",
44107
+ resolve: () => ({
44108
+ outputTypes: [{ kind: "unknown" }],
44109
+ apply: (_args, nargout) => nargout >= 1 ? RTV.dummyHandle() : void 0
44110
+ })
44111
+ });
43995
44112
  registerIBuiltin({
43996
44113
  name: "get",
43997
44114
  resolve: () => ({
@@ -44146,7 +44263,6 @@ registerIBuiltin({
44146
44263
  })
44147
44264
  });
44148
44265
  for (const cm of [
44149
- "parula",
44150
44266
  "jet",
44151
44267
  "hsv",
44152
44268
  "hot",
@@ -44168,6 +44284,44 @@ for (const cm of [
44168
44284
  })
44169
44285
  });
44170
44286
  }
44287
+ var PARULA_ANCHORS = [
44288
+ [0.2422, 0.1504, 0.6603],
44289
+ [0.278, 0.3556, 0.9777],
44290
+ [0.1085, 0.5267, 0.8943],
44291
+ [0.0469, 0.6353, 0.7861],
44292
+ [0.2161, 0.7269, 0.6499],
44293
+ [0.5044, 0.7993, 0.4519],
44294
+ [0.8328, 0.8056, 0.2008],
44295
+ [0.9871, 0.8092, 0.1432],
44296
+ [0.9763, 0.9831, 0.0538]
44297
+ ];
44298
+ function parulaColormap(m) {
44299
+ const data = allocFloat64Array(m * 3);
44300
+ const last = PARULA_ANCHORS.length - 1;
44301
+ for (let i = 0; i < m; i++) {
44302
+ const t = m <= 1 ? 0 : i / (m - 1) * last;
44303
+ const k = Math.min(last - 1, Math.floor(t));
44304
+ const f = t - k;
44305
+ const a = PARULA_ANCHORS[k];
44306
+ const b = PARULA_ANCHORS[Math.min(last, k + 1)];
44307
+ for (let c = 0; c < 3; c++) {
44308
+ data[c * m + i] = a[c] + f * (b[c] - a[c]);
44309
+ }
44310
+ }
44311
+ return RTV.tensor(data, [m, 3]);
44312
+ }
44313
+ registerIBuiltin({
44314
+ name: "parula",
44315
+ resolve: () => ({
44316
+ outputTypes: [{ kind: "unknown" }],
44317
+ apply: (args) => {
44318
+ if (args.length >= 1 && (isRuntimeNumber(args[0]) || isRuntimeTensor(args[0]))) {
44319
+ return parulaColormap(Math.max(0, Math.round(toNumber(args[0]))));
44320
+ }
44321
+ return RTV.char("parula");
44322
+ }
44323
+ })
44324
+ });
44171
44325
  var appdataStore = /* @__PURE__ */ new Map();
44172
44326
  function resetAppdataStore() {
44173
44327
  const rt = getCurrentRuntime();
@@ -46222,6 +46376,10 @@ var H = {
46222
46376
  signatures: ["nexttile", "nexttile(IDX)"],
46223
46377
  description: "Advance to the next tile in the current tiled layout, or move to tile IDX. Auto-creates a flow layout if none exists."
46224
46378
  },
46379
+ surface: {
46380
+ signatures: ["surface(X,Y,Z)", "surface(Z)", "surface(X,Y,Z,C)"],
46381
+ description: "Primitive surface plot. Like surf, but adds to the current axes without clearing existing objects (does not respect hold)."
46382
+ },
46225
46383
  title: {
46226
46384
  signatures: ["title(TXT)"],
46227
46385
  description: "Set title of current axes."
@@ -46258,6 +46416,10 @@ var H = {
46258
46416
  signatures: ["clf"],
46259
46417
  description: "Clear current figure."
46260
46418
  },
46419
+ cla: {
46420
+ signatures: ["cla", "cla(ax)", "cla reset", "cla(ax,'reset')"],
46421
+ description: "Clear current axes. With 'reset', also reset axes properties to defaults."
46422
+ },
46261
46423
  sgtitle: {
46262
46424
  signatures: ["sgtitle(TXT)"],
46263
46425
  description: "Set super-title for subplot grid."
@@ -46310,6 +46472,10 @@ var H = {
46310
46472
  signatures: ["shg"],
46311
46473
  description: "Show current figure."
46312
46474
  },
46475
+ camlight: {
46476
+ signatures: ["camlight", "camlight(position)", "cl = camlight(___)"],
46477
+ description: "Create a light in camera coordinates. Stub in numbl (no lighting model): accepts all arguments and returns a placeholder handle."
46478
+ },
46313
46479
  newplot: {
46314
46480
  signatures: ["newplot"],
46315
46481
  description: "Prepare axes for new plot."
@@ -47434,6 +47600,12 @@ function parseContourArgs(args, filled) {
47434
47600
  let rows;
47435
47601
  let cols;
47436
47602
  let nLevels = 10;
47603
+ let levels;
47604
+ const applyLevelArg = (arg) => {
47605
+ const vals = toNumberArray(arg);
47606
+ if (vals.length <= 1) nLevels = vals.length === 1 ? vals[0] : nLevels;
47607
+ else levels = vals;
47608
+ };
47437
47609
  if (numericCount === 1) {
47438
47610
  const info = getMatrixInfo(args[pos++]);
47439
47611
  rows = info.rows;
@@ -47447,8 +47619,7 @@ function parseContourArgs(args, filled) {
47447
47619
  rows = info.rows;
47448
47620
  cols = info.cols;
47449
47621
  zData = info.data;
47450
- nLevels = typeof args[pos] === "number" ? args[pos] : toNumber(args[pos]);
47451
- pos++;
47622
+ applyLevelArg(args[pos++]);
47452
47623
  const gen = generateMeshgrid(rows, cols);
47453
47624
  xData = gen.x;
47454
47625
  yData = gen.y;
@@ -47469,15 +47640,144 @@ function parseContourArgs(args, filled) {
47469
47640
  rows = zInfo.rows;
47470
47641
  cols = zInfo.cols;
47471
47642
  zData = zInfo.data;
47472
- nLevels = typeof args[pos] === "number" ? args[pos] : toNumber(args[pos]);
47473
- pos++;
47643
+ applyLevelArg(args[pos++]);
47474
47644
  const expanded = expandXY(x, y, rows, cols);
47475
47645
  xData = expanded.x;
47476
47646
  yData = expanded.y;
47477
47647
  } else {
47478
47648
  throw new Error("contour requires at least 1 argument");
47479
47649
  }
47480
- return { x: xData, y: yData, z: zData, rows, cols, nLevels, filled };
47650
+ let lineWidth;
47651
+ let lineStyle;
47652
+ let lineColor;
47653
+ for (; pos + 1 < args.length; pos += 2) {
47654
+ if (!isStringArg(args[pos])) break;
47655
+ const name = getStringValue(args[pos]).toLowerCase();
47656
+ const val = args[pos + 1];
47657
+ switch (name) {
47658
+ case "linewidth":
47659
+ lineWidth = toNumber(val);
47660
+ break;
47661
+ case "linestyle":
47662
+ lineStyle = getStringValue(val);
47663
+ break;
47664
+ case "linecolor":
47665
+ case "color":
47666
+ lineColor = isStringArg(val) ? getStringValue(val) : toNumberArray(val);
47667
+ break;
47668
+ case "levellist":
47669
+ case "levels": {
47670
+ const vals = toNumberArray(val);
47671
+ if (vals.length >= 1) levels = vals;
47672
+ break;
47673
+ }
47674
+ }
47675
+ }
47676
+ if (levels) nLevels = levels.length;
47677
+ return {
47678
+ x: xData,
47679
+ y: yData,
47680
+ z: zData,
47681
+ rows,
47682
+ cols,
47683
+ nLevels,
47684
+ ...levels ? { levels } : {},
47685
+ ...lineWidth !== void 0 ? { lineWidth } : {},
47686
+ ...lineStyle !== void 0 ? { lineStyle } : {},
47687
+ ...lineColor !== void 0 ? { lineColor } : {},
47688
+ filled
47689
+ };
47690
+ }
47691
+ function marchingSquaresCell(z00, z10, z01, z11, x00, y00, x10, y10, x01, y01, x11, y11, level) {
47692
+ const code = (z00 >= level ? 1 : 0) | (z10 >= level ? 2 : 0) | (z01 >= level ? 4 : 0) | (z11 >= level ? 8 : 0);
47693
+ if (code === 0 || code === 15) return [];
47694
+ const lerp = (a, b, za, zb) => a + (level - za) / (zb - za || 1) * (b - a);
47695
+ const bx = lerp(x00, x10, z00, z10), by = lerp(y00, y10, z00, z10);
47696
+ const rx = lerp(x10, x11, z10, z11), ry = lerp(y10, y11, z10, z11);
47697
+ const tx = lerp(x01, x11, z01, z11), ty = lerp(y01, y11, z01, z11);
47698
+ const lx = lerp(x00, x01, z00, z01), ly = lerp(y00, y01, z00, z01);
47699
+ const segs = [];
47700
+ switch (code) {
47701
+ case 1:
47702
+ case 14:
47703
+ segs.push([bx, by, lx, ly]);
47704
+ break;
47705
+ case 2:
47706
+ case 13:
47707
+ segs.push([bx, by, rx, ry]);
47708
+ break;
47709
+ case 3:
47710
+ case 12:
47711
+ segs.push([lx, ly, rx, ry]);
47712
+ break;
47713
+ case 4:
47714
+ case 11:
47715
+ segs.push([lx, ly, tx, ty]);
47716
+ break;
47717
+ case 5:
47718
+ case 10:
47719
+ segs.push([bx, by, tx, ty]);
47720
+ break;
47721
+ case 6:
47722
+ case 9:
47723
+ segs.push([bx, by, lx, ly]);
47724
+ segs.push([tx, ty, rx, ry]);
47725
+ break;
47726
+ case 7:
47727
+ case 8:
47728
+ segs.push([tx, ty, rx, ry]);
47729
+ break;
47730
+ }
47731
+ return segs;
47732
+ }
47733
+ function computeContourMatrix(trace) {
47734
+ const { x, y, z, rows, cols } = trace;
47735
+ const at = (a, i, j) => a[j * rows + i];
47736
+ let zMin = Infinity;
47737
+ let zMax = -Infinity;
47738
+ for (const v of z) {
47739
+ if (Number.isFinite(v)) {
47740
+ if (v < zMin) zMin = v;
47741
+ if (v > zMax) zMax = v;
47742
+ }
47743
+ }
47744
+ let levelList;
47745
+ if (trace.levels && trace.levels.length > 0) {
47746
+ levelList = trace.levels.filter((v) => Number.isFinite(v));
47747
+ } else {
47748
+ const n = Math.max(1, Math.round(trace.nLevels));
47749
+ levelList = [];
47750
+ if (Number.isFinite(zMin) && Number.isFinite(zMax) && zMax > zMin) {
47751
+ const step = (zMax - zMin) / (n + 1);
47752
+ for (let k = 1; k <= n; k++) levelList.push(zMin + k * step);
47753
+ }
47754
+ }
47755
+ const data = [];
47756
+ for (const level of levelList) {
47757
+ for (let j = 0; j < cols - 1; j++) {
47758
+ for (let i = 0; i < rows - 1; i++) {
47759
+ const segs = marchingSquaresCell(
47760
+ at(z, i, j),
47761
+ at(z, i + 1, j),
47762
+ at(z, i, j + 1),
47763
+ at(z, i + 1, j + 1),
47764
+ at(x, i, j),
47765
+ at(y, i, j),
47766
+ at(x, i + 1, j),
47767
+ at(y, i + 1, j),
47768
+ at(x, i, j + 1),
47769
+ at(y, i, j + 1),
47770
+ at(x, i + 1, j + 1),
47771
+ at(y, i + 1, j + 1),
47772
+ level
47773
+ );
47774
+ for (const [x1, y1, x2, y2] of segs) {
47775
+ data.push(level, 2, x1, y1, x2, y2);
47776
+ }
47777
+ }
47778
+ }
47779
+ }
47780
+ return { data, n: data.length / 2, levelList };
47481
47781
  }
47482
47782
  function parseMeshArgs(args) {
47483
47783
  const trace = parseSurfArgs(args);
@@ -48425,6 +48725,213 @@ function computeQuiverAutoScale(x, y, u, v, rows, cols, factor) {
48425
48725
  if (maxMag === 0) return 1;
48426
48726
  return factor * spacing / maxMag;
48427
48727
  }
48728
+ function parseQuiver3Args(args) {
48729
+ if (args.length < 4) throw new Error("quiver3 requires at least 4 arguments");
48730
+ let numericCount = 0;
48731
+ for (let i = 0; i < args.length; i++) {
48732
+ if (isNumericArg(args[i])) numericCount++;
48733
+ else break;
48734
+ }
48735
+ let pos = 0;
48736
+ let xData;
48737
+ let yData;
48738
+ let zData;
48739
+ let uData;
48740
+ let vData;
48741
+ let wData;
48742
+ let rows;
48743
+ let cols;
48744
+ const arity = numericCount >= 6 ? 6 : 4;
48745
+ if (arity === 4) {
48746
+ const zInfo = getMatrixInfo(args[pos++]);
48747
+ rows = zInfo.rows;
48748
+ cols = zInfo.cols;
48749
+ zData = zInfo.data;
48750
+ uData = getMatrixInfo(args[pos++]).data;
48751
+ vData = getMatrixInfo(args[pos++]).data;
48752
+ wData = getMatrixInfo(args[pos++]).data;
48753
+ const n = zData.length;
48754
+ if (rows === 1 || cols === 1) {
48755
+ xData = new Array(n);
48756
+ yData = new Array(n);
48757
+ for (let i = 0; i < n; i++) {
48758
+ xData[i] = i + 1;
48759
+ yData[i] = 1;
48760
+ }
48761
+ } else {
48762
+ const gen = generateMeshgrid(rows, n / rows);
48763
+ xData = gen.x;
48764
+ yData = gen.y;
48765
+ }
48766
+ } else {
48767
+ const X = args[pos++];
48768
+ const Y = args[pos++];
48769
+ const zInfo = getMatrixInfo(args[pos++]);
48770
+ rows = zInfo.rows;
48771
+ cols = zInfo.cols;
48772
+ zData = zInfo.data;
48773
+ uData = getMatrixInfo(args[pos++]).data;
48774
+ vData = getMatrixInfo(args[pos++]).data;
48775
+ wData = getMatrixInfo(args[pos++]).data;
48776
+ const n = uData.length;
48777
+ const xFlat = toNumberArray(X);
48778
+ const yFlat = toNumberArray(Y);
48779
+ if (xFlat.length === n && yFlat.length === n) {
48780
+ xData = xFlat;
48781
+ yData = yFlat;
48782
+ } else {
48783
+ const expanded = expandXY(X, Y, rows, cols);
48784
+ xData = expanded.x;
48785
+ yData = expanded.y;
48786
+ }
48787
+ }
48788
+ let autoScale = true;
48789
+ let autoScaleFactor = 0.9;
48790
+ let reportedASF = 0.9;
48791
+ if (pos < args.length && isNumericArg(args[pos]) && !isStringArg(args[pos])) {
48792
+ const s = toNumber(args[pos]);
48793
+ if (s === 0) {
48794
+ autoScale = false;
48795
+ } else {
48796
+ autoScaleFactor = s * 0.9;
48797
+ reportedASF = s;
48798
+ }
48799
+ pos++;
48800
+ }
48801
+ const trace = {
48802
+ x: xData,
48803
+ y: yData,
48804
+ z: zData,
48805
+ u: uData,
48806
+ v: vData,
48807
+ w: wData,
48808
+ showArrowHead: true
48809
+ };
48810
+ while (pos < args.length && isStringArg(args[pos]) && !isQuiverNameValueKey(args[pos])) {
48811
+ const s = getStringIfString(args[pos]);
48812
+ if (s === void 0) break;
48813
+ if (s === "off") {
48814
+ autoScale = false;
48815
+ pos++;
48816
+ continue;
48817
+ }
48818
+ if (s === "filled") {
48819
+ trace.markerFilled = true;
48820
+ pos++;
48821
+ continue;
48822
+ }
48823
+ const spec = parseLineSpec(s);
48824
+ if (spec) {
48825
+ if (spec.color) trace.color = COLOR_SHORT[spec.color];
48826
+ if (spec.lineStyle) trace.lineStyle = spec.lineStyle;
48827
+ if (spec.marker) {
48828
+ trace.marker = spec.marker;
48829
+ trace.showArrowHead = false;
48830
+ }
48831
+ pos++;
48832
+ continue;
48833
+ }
48834
+ const c = resolveColor(s);
48835
+ if (c) {
48836
+ trace.color = c;
48837
+ pos++;
48838
+ continue;
48839
+ }
48840
+ break;
48841
+ }
48842
+ while (pos < args.length) {
48843
+ const key = isQuiverNameValueKey(args[pos]);
48844
+ if (!key) break;
48845
+ pos++;
48846
+ if (pos >= args.length) break;
48847
+ const value = args[pos++];
48848
+ switch (key) {
48849
+ case "color": {
48850
+ const c = resolveColor(value);
48851
+ if (c) trace.color = c;
48852
+ break;
48853
+ }
48854
+ case "linestyle":
48855
+ trace.lineStyle = getStringValue(value);
48856
+ break;
48857
+ case "linewidth":
48858
+ trace.lineWidth = typeof value === "number" ? value : toNumber(value);
48859
+ break;
48860
+ case "marker": {
48861
+ const s = getStringValue(value);
48862
+ trace.marker = s === "none" ? void 0 : s;
48863
+ break;
48864
+ }
48865
+ case "showarrowhead": {
48866
+ const s = getStringValue(value).toLowerCase();
48867
+ trace.showArrowHead = !(s === "off" || s === "false" || s === "0");
48868
+ break;
48869
+ }
48870
+ case "autoscale": {
48871
+ const s = getStringValue(value).toLowerCase();
48872
+ autoScale = !(s === "off" || s === "false" || s === "0");
48873
+ break;
48874
+ }
48875
+ case "autoscalefactor": {
48876
+ const n = typeof value === "number" ? value : toNumber(value);
48877
+ autoScaleFactor = n * 0.9;
48878
+ reportedASF = n;
48879
+ break;
48880
+ }
48881
+ }
48882
+ }
48883
+ trace.autoScale = autoScale;
48884
+ trace.autoScaleFactor = reportedASF;
48885
+ if (autoScale) {
48886
+ const factor = computeQuiver3AutoScale(
48887
+ xData,
48888
+ yData,
48889
+ zData,
48890
+ uData,
48891
+ vData,
48892
+ wData,
48893
+ autoScaleFactor
48894
+ );
48895
+ if (factor !== 1) {
48896
+ trace.u = uData.map((x) => x * factor);
48897
+ trace.v = vData.map((x) => x * factor);
48898
+ trace.w = wData.map((x) => x * factor);
48899
+ }
48900
+ }
48901
+ return trace;
48902
+ }
48903
+ function computeQuiver3AutoScale(x, y, z, u, v, w, factor) {
48904
+ const n = u.length;
48905
+ if (n === 0) return 1;
48906
+ const range2 = (a) => {
48907
+ let lo = Infinity;
48908
+ let hi = -Infinity;
48909
+ for (const t of a) {
48910
+ if (isFinite(t)) {
48911
+ if (t < lo) lo = t;
48912
+ if (t > hi) hi = t;
48913
+ }
48914
+ }
48915
+ return hi > lo ? hi - lo : 0;
48916
+ };
48917
+ const xr = range2(x);
48918
+ const yr = range2(y);
48919
+ const zr = range2(z);
48920
+ const dims = [xr, yr, zr].filter((d) => d > 0);
48921
+ let spacing;
48922
+ if (dims.length === 3) spacing = Math.cbrt(xr * yr * zr / n);
48923
+ else if (dims.length === 2) spacing = Math.sqrt(dims[0] * dims[1] / n);
48924
+ else if (dims.length === 1) spacing = dims[0] / Math.max(1, n - 1);
48925
+ else spacing = 1;
48926
+ spacing = spacing || 1;
48927
+ let maxMag = 0;
48928
+ for (let i = 0; i < n; i++) {
48929
+ const m = Math.sqrt(u[i] * u[i] + v[i] * v[i] + w[i] * w[i]);
48930
+ if (isFinite(m) && m > maxMag) maxMag = m;
48931
+ }
48932
+ if (maxMag === 0) return 1;
48933
+ return factor * spacing / maxMag;
48934
+ }
48428
48935
 
48429
48936
  // src/numbl-core/runtime/syncChannel.ts
48430
48937
  var syncSleepWarned = false;
@@ -48516,6 +49023,9 @@ function plotInstr(plotInstructions, instr) {
48516
49023
  case "clf":
48517
49024
  plotInstructions.push({ type: instr.type });
48518
49025
  break;
49026
+ case "cla":
49027
+ plotInstructions.push({ type: "cla", reset: instr.reset });
49028
+ break;
48519
49029
  case "set_subplot":
48520
49030
  plotInstructions.push({
48521
49031
  type: "set_subplot",
@@ -48600,6 +49110,10 @@ function surfCall(plotInstructions, args) {
48600
49110
  const trace = parseSurfArgs(args);
48601
49111
  plotInstructions.push({ type: "surf", trace });
48602
49112
  }
49113
+ function surfaceCall(plotInstructions, args) {
49114
+ const trace = parseSurfArgs(args);
49115
+ plotInstructions.push({ type: "surface", trace });
49116
+ }
48603
49117
  function imagescCall(plotInstructions, args) {
48604
49118
  const trace = parseImagescArgs(args);
48605
49119
  plotInstructions.push({ type: "imagesc", trace });
@@ -48852,6 +49366,10 @@ function quiverCall(plotInstructions, args) {
48852
49366
  plotInstructions.push({ type: "quiver", traces });
48853
49367
  }
48854
49368
  }
49369
+ function quiver3Call(plotInstructions, args) {
49370
+ const trace = parseQuiver3Args(args);
49371
+ plotInstructions.push({ type: "quiver3", trace });
49372
+ }
48855
49373
  function legendCall(plotInstructions, args) {
48856
49374
  const labels = [];
48857
49375
  for (let i = 0; i < args.length; i++) {
@@ -49100,6 +49618,9 @@ function dispatchPlotBuiltin(name, args, instructions, state) {
49100
49618
  case "surf":
49101
49619
  surfCall(instructions, args);
49102
49620
  return true;
49621
+ case "surface":
49622
+ surfaceCall(instructions, args);
49623
+ return true;
49103
49624
  case "scatter":
49104
49625
  scatterCall(instructions, args);
49105
49626
  return true;
@@ -49179,6 +49700,9 @@ function dispatchPlotBuiltin(name, args, instructions, state) {
49179
49700
  case "quiver":
49180
49701
  quiverCall(instructions, args);
49181
49702
  return true;
49703
+ case "quiver3":
49704
+ quiver3Call(instructions, args);
49705
+ return true;
49182
49706
  case "view":
49183
49707
  viewCall(instructions, args);
49184
49708
  return true;
@@ -49244,6 +49768,18 @@ function dispatchPlotBuiltin(name, args, instructions, state) {
49244
49768
  case "clf":
49245
49769
  plotInstr(instructions, { type: "clf" });
49246
49770
  return true;
49771
+ case "cla": {
49772
+ let reset = false;
49773
+ for (const a of args) {
49774
+ try {
49775
+ if (toString(a).toLowerCase().replace(/^["']|["']$/g, "") === "reset")
49776
+ reset = true;
49777
+ } catch {
49778
+ }
49779
+ }
49780
+ plotInstr(instructions, { type: "cla", reset });
49781
+ return true;
49782
+ }
49247
49783
  case "shading": {
49248
49784
  if (args.length > 0) {
49249
49785
  plotInstr(instructions, { type: "set_shading", shading: args[0] });
@@ -49421,6 +49957,7 @@ var PLOT_DISPATCH_NAMES = [
49421
49957
  "plot",
49422
49958
  "plot3",
49423
49959
  "surf",
49960
+ "surface",
49424
49961
  "scatter",
49425
49962
  "imagesc",
49426
49963
  "pcolor",
@@ -49448,6 +49985,7 @@ var PLOT_DISPATCH_NAMES = [
49448
49985
  "donutchart",
49449
49986
  "heatmap",
49450
49987
  "quiver",
49988
+ "quiver3",
49451
49989
  "view",
49452
49990
  "legend",
49453
49991
  "figure",
@@ -49463,6 +50001,7 @@ var PLOT_DISPATCH_NAMES = [
49463
50001
  "grid",
49464
50002
  "close",
49465
50003
  "clf",
50004
+ "cla",
49466
50005
  "shading",
49467
50006
  "colorbar",
49468
50007
  "colormap",
@@ -49986,6 +50525,7 @@ var SPECIAL_BUILTIN_NAMES = [
49986
50525
  "plot",
49987
50526
  "plot3",
49988
50527
  "surf",
50528
+ "surface",
49989
50529
  "scatter",
49990
50530
  "imagesc",
49991
50531
  "pcolor",
@@ -50015,6 +50555,7 @@ var SPECIAL_BUILTIN_NAMES = [
50015
50555
  "donutchart",
50016
50556
  "heatmap",
50017
50557
  "quiver",
50558
+ "quiver3",
50018
50559
  "streamline",
50019
50560
  "stream2",
50020
50561
  "ishold",
@@ -50032,6 +50573,7 @@ var SPECIAL_BUILTIN_NAMES = [
50032
50573
  "sgtitle",
50033
50574
  "shading",
50034
50575
  "clf",
50576
+ "cla",
50035
50577
  "colormap",
50036
50578
  "view",
50037
50579
  "zlabel",
@@ -50924,13 +51466,21 @@ function registerSpecialBuiltins(rt) {
50924
51466
  }
50925
51467
  });
50926
51468
  registerSpecialVoid("delete", (args) => {
50927
- const io = requireFileIO();
50928
- if (!io.deleteFile)
50929
- throw new RuntimeError("delete is not available in this environment");
50930
51469
  const margs = args.map((a) => ensureRuntimeValue(a));
50931
51470
  if (margs.length < 1)
50932
51471
  throw new RuntimeError("delete requires at least 1 argument");
50933
51472
  for (const arg of margs) {
51473
+ if (isRuntimeGraphicsHandle(arg)) {
51474
+ const instr = arg._trace.__instruction;
51475
+ if (instr) {
51476
+ const idx = rt.plotInstructions.indexOf(instr);
51477
+ if (idx >= 0) rt.plotInstructions.splice(idx, 1);
51478
+ }
51479
+ continue;
51480
+ }
51481
+ const io = requireFileIO();
51482
+ if (!io.deleteFile)
51483
+ throw new RuntimeError("delete is not available in this environment");
50934
51484
  io.deleteFile(toString(arg));
50935
51485
  }
50936
51486
  });
@@ -51399,7 +51949,7 @@ function registerSpecialBuiltins(rt) {
51399
51949
  return nargout >= 1 ? RTV.num(1) : void 0;
51400
51950
  });
51401
51951
  }
51402
- const PLOT_VOID = ["hold", "grid", "shading"];
51952
+ const PLOT_VOID = ["hold", "grid", "shading", "cla"];
51403
51953
  for (const name of PLOT_VOID) {
51404
51954
  registerSpecialVoid(name, (args) => {
51405
51955
  dispatchPlotBuiltin(
@@ -52240,7 +52790,8 @@ var PLOT_DISPATCH_NAMES_JIT = /* @__PURE__ */ new Set([
52240
52790
  "piechart",
52241
52791
  "donutchart",
52242
52792
  "heatmap",
52243
- "quiver"
52793
+ "quiver",
52794
+ "quiver3"
52244
52795
  ]);
52245
52796
  function dispatchPlotCall(rt, name, args) {
52246
52797
  if (PLOT_DISPATCH_NAMES_JIT.has(name)) {
@@ -53223,45 +53774,70 @@ function classInstanceParenIndex(rt, mv, base, indices, nargout, skipSubsref) {
53223
53774
  }
53224
53775
  throw new RuntimeError(`Index exceeds class instance dimensions`);
53225
53776
  }
53777
+ function resolveObjSubscript(raw, dimSize) {
53778
+ if (raw === COLON_SENTINEL) {
53779
+ return Array.from({ length: dimSize }, (_, i) => i);
53780
+ }
53781
+ if (typeof raw === "number") return [Math.round(raw) - 1];
53782
+ const rv = ensureRuntimeValue(raw);
53783
+ if (isRuntimeNumber(rv)) return [Math.round(toNumber(rv)) - 1];
53784
+ if (isRuntimeLogical(rv)) return rv ? [0] : [];
53785
+ if (isRuntimeTensor(rv)) {
53786
+ if (rv._isLogical) {
53787
+ const out = [];
53788
+ for (let k = 0; k < rv.data.length; k++) if (rv.data[k]) out.push(k);
53789
+ return out;
53790
+ }
53791
+ return Array.from(rv.data, (x) => Math.round(x) - 1);
53792
+ }
53793
+ throw new RuntimeError("Invalid index type for class instance array");
53794
+ }
53795
+ function makeObjResult(className, elements, shape) {
53796
+ if (elements.length === 1) return elements[0];
53797
+ return {
53798
+ kind: "class_instance_array",
53799
+ className,
53800
+ elements,
53801
+ shape
53802
+ };
53803
+ }
53226
53804
  function classInstanceArrayParenIndex(mv, indices) {
53805
+ const [rows, cols] = mv.shape;
53806
+ const total = mv.elements.length;
53807
+ if (indices.length === 2) {
53808
+ const ri = resolveObjSubscript(indices[0], rows);
53809
+ const ci = resolveObjSubscript(indices[1], cols);
53810
+ const selected2 = [];
53811
+ for (const j of ci) {
53812
+ if (j < 0 || j >= cols)
53813
+ throw new RuntimeError("Index exceeds array bounds");
53814
+ for (const i of ri) {
53815
+ if (i < 0 || i >= rows)
53816
+ throw new RuntimeError("Index exceeds array bounds");
53817
+ selected2.push(mv.elements[j * rows + i]);
53818
+ }
53819
+ }
53820
+ return makeObjResult(mv.className, selected2, [ri.length, ci.length]);
53821
+ }
53227
53822
  if (indices.length !== 1) {
53228
53823
  throw new RuntimeError(
53229
- "Class instance arrays only support single-subscript indexing"
53824
+ "Class instance arrays support one- or two-subscript indexing"
53230
53825
  );
53231
53826
  }
53232
53827
  const idx = indices[0];
53233
53828
  if (idx === COLON_SENTINEL) {
53234
- return mv;
53829
+ return makeObjResult(mv.className, mv.elements.slice(), [total, 1]);
53235
53830
  }
53236
- if (typeof idx === "number") {
53237
- const i = Math.round(idx) - 1;
53238
- if (i < 0 || i >= mv.elements.length)
53831
+ const linear = resolveObjSubscript(idx, total);
53832
+ const selected = [];
53833
+ for (const i of linear) {
53834
+ if (i < 0 || i >= total)
53239
53835
  throw new RuntimeError("Index exceeds array bounds");
53240
- return mv.elements[i];
53241
- }
53242
- const rv = ensureRuntimeValue(idx);
53243
- if (isRuntimeLogical(rv)) {
53244
- const i = rv ? 0 : -1;
53245
- if (i < 0 || i >= mv.elements.length)
53246
- throw new RuntimeError("Index exceeds array bounds");
53247
- return mv.elements[i];
53248
- }
53249
- if (isRuntimeTensor(rv)) {
53250
- const selected = [];
53251
- for (let k = 0; k < rv.data.length; k++) {
53252
- const i = Math.round(rv.data[k]) - 1;
53253
- if (i < 0 || i >= mv.elements.length)
53254
- throw new RuntimeError("Index exceeds array bounds");
53255
- selected.push(mv.elements[i]);
53256
- }
53257
- if (selected.length === 1) return selected[0];
53258
- return {
53259
- kind: "class_instance_array",
53260
- className: mv.className,
53261
- elements: selected
53262
- };
53836
+ selected.push(mv.elements[i]);
53263
53837
  }
53264
- throw new RuntimeError("Invalid index type for class instance array");
53838
+ const isColumn = cols === 1 && rows !== 1;
53839
+ const shape = isColumn ? [selected.length, 1] : [1, selected.length];
53840
+ return makeObjResult(mv.className, selected, shape);
53265
53841
  }
53266
53842
  function resolveIndicesForClassInstance(rt, mv, base, indices) {
53267
53843
  const numIndices = indices.length;
@@ -53958,6 +54534,43 @@ var Runtime = class _Runtime {
53958
54534
  return RTV.dummyHandle();
53959
54535
  }
53960
54536
  };
54537
+ const contourOverride = (filled) => (_nargout, args) => {
54538
+ const margs = args.map((a) => ensureRuntimeValue(a));
54539
+ contourCall(this.plotInstructions, margs, filled);
54540
+ if (_nargout < 1) return void 0;
54541
+ const last = this.plotInstructions[this.plotInstructions.length - 1];
54542
+ if (!last || last.type !== "contour") return RTV.dummyHandle();
54543
+ const trace = last.trace;
54544
+ const cm = computeContourMatrix(trace);
54545
+ const C = RTV.tensor(allocFloat64Array(cm.data), [2, cm.n]);
54546
+ if (_nargout < 2) return C;
54547
+ const H2 = RTV.graphicsHandle(
54548
+ {
54549
+ LineWidth: trace.lineWidth ?? 0.5,
54550
+ LineStyle: trace.lineStyle ?? "-",
54551
+ LineColor: trace.lineColor ?? "flat",
54552
+ LevelList: cm.levelList,
54553
+ __instruction: last
54554
+ },
54555
+ "contour"
54556
+ );
54557
+ return [C, H2];
54558
+ };
54559
+ this.builtins["contour"] = contourOverride(false);
54560
+ this.builtins["contourf"] = contourOverride(true);
54561
+ this.builtins["quiver3"] = (_nargout, args) => {
54562
+ const margs = args.map((a) => ensureRuntimeValue(a));
54563
+ quiver3Call(this.plotInstructions, margs);
54564
+ if (_nargout < 1) return void 0;
54565
+ const last = this.plotInstructions[this.plotInstructions.length - 1];
54566
+ if (last && last.type === "quiver3") {
54567
+ return RTV.graphicsHandle(
54568
+ last.trace,
54569
+ "quiver3"
54570
+ );
54571
+ }
54572
+ return RTV.dummyHandle();
54573
+ };
53961
54574
  this.builtins["fplot"] = (_nargout, args) => {
53962
54575
  fplotCall(this, this.plotInstructions, args.map(ensureRuntimeValue));
53963
54576
  };
@@ -54505,14 +55118,15 @@ var Runtime = class _Runtime {
54505
55118
  transpose(v) {
54506
55119
  if (typeof v !== "number") {
54507
55120
  const mv = ensureRuntimeValue(v);
54508
- if (isRuntimeClassInstance(mv)) return this.dispatch("transpose", 1, [v]);
55121
+ if (isRuntimeClassInstance(mv) || isRuntimeClassInstanceArray(mv))
55122
+ return this.dispatch("transpose", 1, [v]);
54509
55123
  }
54510
55124
  return transpose(v);
54511
55125
  }
54512
55126
  ctranspose(v) {
54513
55127
  if (typeof v !== "number") {
54514
55128
  const mv = ensureRuntimeValue(v);
54515
- if (isRuntimeClassInstance(mv))
55129
+ if (isRuntimeClassInstance(mv) || isRuntimeClassInstanceArray(mv))
54516
55130
  return this.dispatch("ctranspose", 1, [v]);
54517
55131
  }
54518
55132
  return ctranspose(v);
@@ -54596,7 +55210,9 @@ var Runtime = class _Runtime {
54596
55210
  const mvals = flat.map(
54597
55211
  (e) => typeof e === "number" ? null : ensureRuntimeValue(e)
54598
55212
  );
54599
- if (mvals.some((v) => v && isRuntimeClassInstance(v))) {
55213
+ if (mvals.some(
55214
+ (v) => v && (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v))
55215
+ )) {
54600
55216
  if (this._classHasMethod(mvals, "horzcat")) {
54601
55217
  return this.dispatch("horzcat", 1, flat);
54602
55218
  }
@@ -54609,7 +55225,9 @@ var Runtime = class _Runtime {
54609
55225
  const mvals = rows.map(
54610
55226
  (r) => typeof r === "number" ? null : ensureRuntimeValue(r)
54611
55227
  );
54612
- if (mvals.some((v) => v && isRuntimeClassInstance(v))) {
55228
+ if (mvals.some(
55229
+ (v) => v && (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v))
55230
+ )) {
54613
55231
  if (this._classHasMethod(mvals, "vertcat")) {
54614
55232
  return this.dispatch("vertcat", 1, rows);
54615
55233
  }
@@ -54621,7 +55239,7 @@ var Runtime = class _Runtime {
54621
55239
  _classHasMethod(mvals, methodName) {
54622
55240
  if (!this.resolveClassMethod) return false;
54623
55241
  for (const v of mvals) {
54624
- if (v && isRuntimeClassInstance(v)) {
55242
+ if (v && (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v))) {
54625
55243
  if (this.resolveClassMethod(v.className, methodName) !== null) {
54626
55244
  return true;
54627
55245
  }
@@ -54932,31 +55550,66 @@ function buildStackField(e) {
54932
55550
  }
54933
55551
  return RTV.structArray(fieldNames, elements);
54934
55552
  }
54935
- function collectClassInstances(items) {
54936
- const out = [];
54937
- for (const item of items) {
54938
- const rv = ensureRuntimeValue(item);
54939
- if (isRuntimeClassInstance(rv)) {
54940
- out.push(rv);
54941
- } else if (isRuntimeClassInstanceArray(rv)) {
54942
- out.push(...rv.elements);
54943
- } else {
55553
+ function asObjArrayParts(item) {
55554
+ const rv = ensureRuntimeValue(item);
55555
+ if (isRuntimeClassInstance(rv))
55556
+ return { rows: 1, cols: 1, elements: [rv], className: rv.className };
55557
+ if (isRuntimeClassInstanceArray(rv))
55558
+ return {
55559
+ rows: rv.shape[0],
55560
+ cols: rv.shape[1],
55561
+ elements: rv.elements,
55562
+ className: rv.className
55563
+ };
55564
+ if (isRuntimeTensor(rv) && rv.data.length === 0) return null;
55565
+ throw new RuntimeError(`Cannot concatenate ${kstr(rv)} with class instances`);
55566
+ }
55567
+ function objColumn(p2, j) {
55568
+ return p2.elements.slice(j * p2.rows, j * p2.rows + p2.rows);
55569
+ }
55570
+ function defaultClassInstanceHorzcat(items) {
55571
+ const parts = items.map(asObjArrayParts).filter((p2) => p2 !== null && p2.elements.length > 0);
55572
+ if (parts.length === 0)
55573
+ throw new RuntimeError("Cannot concatenate empty class instances");
55574
+ const rows = parts[0].rows;
55575
+ let cols = 0;
55576
+ const elements = [];
55577
+ for (const p2 of parts) {
55578
+ if (p2.rows !== rows)
54944
55579
  throw new RuntimeError(
54945
- `Cannot concatenate ${kstr(rv)} with class instances`
55580
+ "Dimensions of arrays being concatenated are not consistent"
54946
55581
  );
54947
- }
55582
+ elements.push(...p2.elements);
55583
+ cols += p2.cols;
54948
55584
  }
54949
- return out;
54950
- }
54951
- function defaultClassInstanceHorzcat(items) {
54952
- const elements = collectClassInstances(items);
54953
55585
  if (elements.length === 1) return elements[0];
54954
- return new RuntimeClassInstanceArray(elements[0].className, elements);
55586
+ return new RuntimeClassInstanceArray(parts[0].className, elements, [
55587
+ rows,
55588
+ cols
55589
+ ]);
54955
55590
  }
54956
55591
  function defaultClassInstanceVertcat(rows) {
54957
- const elements = collectClassInstances(rows);
55592
+ const parts = rows.map(asObjArrayParts).filter((p2) => p2 !== null && p2.elements.length > 0);
55593
+ if (parts.length === 0)
55594
+ throw new RuntimeError("Cannot concatenate empty class instances");
55595
+ const cols = parts[0].cols;
55596
+ let totalRows = 0;
55597
+ for (const p2 of parts) {
55598
+ if (p2.cols !== cols)
55599
+ throw new RuntimeError(
55600
+ "Dimensions of arrays being concatenated are not consistent"
55601
+ );
55602
+ totalRows += p2.rows;
55603
+ }
55604
+ const elements = [];
55605
+ for (let j = 0; j < cols; j++) {
55606
+ for (const p2 of parts) elements.push(...objColumn(p2, j));
55607
+ }
54958
55608
  if (elements.length === 1) return elements[0];
54959
- return new RuntimeClassInstanceArray(elements[0].className, elements);
55609
+ return new RuntimeClassInstanceArray(parts[0].className, elements, [
55610
+ totalRows,
55611
+ cols
55612
+ ]);
54960
55613
  }
54961
55614
 
54962
55615
  // src/numbl-core/jsUserFunctions.ts
@@ -58647,16 +59300,27 @@ function callUserFunction(fn, args, nargout, narginOverride) {
58647
59300
  const fnEnv = new Environment();
58648
59301
  fnEnv.rt = this.rt;
58649
59302
  fnEnv.persistentFuncId = `${this.currentFile}:${fn.name}`;
58650
- const processedArgs = this.processArgumentsBlocks(fn, sharedArgs);
58651
59303
  const hasVarargin = fn.params.length > 0 && fn.params[fn.params.length - 1] === "varargin";
58652
59304
  const regularParams = hasVarargin ? fn.params.slice(0, -1) : fn.params;
58653
- for (let i = 0; i < regularParams.length; i++) {
58654
- if (i < processedArgs.length) {
58655
- fnEnv.set(regularParams[i], ensureRuntimeValue(processedArgs[i]));
59305
+ let numPositional = regularParams.length;
59306
+ const inputBlocks = (fn.argumentsBlocks ?? []).filter(
59307
+ (b) => b.kind !== "Output"
59308
+ );
59309
+ for (const block of inputBlocks) {
59310
+ for (const e of block.entries) {
59311
+ const dot2 = e.name.indexOf(".");
59312
+ if (dot2 < 0) continue;
59313
+ const idx = fn.params.indexOf(e.name.slice(0, dot2));
59314
+ if (idx >= 0 && idx < numPositional) numPositional = idx;
59315
+ }
59316
+ }
59317
+ for (let i = 0; i < numPositional && i < regularParams.length; i++) {
59318
+ if (i < sharedArgs.length && sharedArgs[i] !== void 0) {
59319
+ fnEnv.set(regularParams[i], ensureRuntimeValue(sharedArgs[i]));
58656
59320
  }
58657
59321
  }
58658
59322
  if (hasVarargin) {
58659
- const extraArgs = processedArgs.slice(regularParams.length).map((a) => ensureRuntimeValue(a));
59323
+ const extraArgs = sharedArgs.slice(regularParams.length).map((a) => ensureRuntimeValue(a));
58660
59324
  fnEnv.set("varargin", RTV.cell(extraArgs, [1, extraArgs.length]));
58661
59325
  }
58662
59326
  fnEnv.set("$nargin", narginOverride ?? args.length);
@@ -58676,6 +59340,7 @@ function callUserFunction(fn, args, nargout, narginOverride) {
58676
59340
  this.rt.pushCallFrame(fn.name);
58677
59341
  this.rt.pushCleanupScope();
58678
59342
  try {
59343
+ this.processArgumentsBlocks(fn, sharedArgs);
58679
59344
  this.execStmts(fn.body);
58680
59345
  if (fnEnv.persistentFuncId) {
58681
59346
  for (const name of fnEnv.persistentNames) {
@@ -58919,12 +59584,13 @@ function findExternalMethod(classInfo, methodName) {
58919
59584
  return null;
58920
59585
  }
58921
59586
  const ast = this.ctx.getCachedAST(mf.fileName);
59587
+ let primary = null;
58922
59588
  for (const stmt of ast.body) {
58923
- if (stmt.type === "Function" && stmt.name === methodName) {
58924
- return funcDefFromStmt(stmt);
58925
- }
59589
+ if (stmt.type !== "Function") continue;
59590
+ if (stmt.name === methodName) return funcDefFromStmt(stmt);
59591
+ if (!primary) primary = funcDefFromStmt(stmt);
58926
59592
  }
58927
- return null;
59593
+ return primary;
58928
59594
  }
58929
59595
  function collectClassProperties(classInfo) {
58930
59596
  const propertyNames = [...classInfo.propertyNames];
@@ -58998,7 +59664,7 @@ function evalInLocalScope(codeArg, fileName) {
58998
59664
  }
58999
59665
  function processArgumentsBlocks(fn, args) {
59000
59666
  const argBlocks = fn.argumentsBlocks;
59001
- if (!argBlocks || argBlocks.length === 0) return args;
59667
+ if (!argBlocks || argBlocks.length === 0) return;
59002
59668
  for (const block of argBlocks) {
59003
59669
  if (block.kind === "Output") continue;
59004
59670
  const entries = block.entries;
@@ -59019,48 +59685,27 @@ function processArgumentsBlocks(fn, args) {
59019
59685
  }
59020
59686
  return true;
59021
59687
  });
59022
- if (nvGroups.size > 0) {
59023
- const processedArgs2 = [...args];
59024
- const nvParamIndex = regularEntries.length;
59025
- for (const [paramName, fields] of nvGroups) {
59026
- const nvArgs = args.slice(nvParamIndex);
59027
- const defaults = {};
59028
- for (const { field, defaultExpr } of fields) {
59029
- if (defaultExpr) {
59030
- try {
59031
- defaults[field] = this.evalExpr(defaultExpr);
59032
- } catch {
59033
- }
59034
- }
59035
- }
59036
- const struct = this.rt.buildNameValueStruct(nvArgs, defaults);
59037
- const paramIdx = fn.params.indexOf(paramName);
59038
- const targetIdx = paramIdx >= 0 ? paramIdx : nvParamIndex;
59039
- processedArgs2.length = Math.max(processedArgs2.length, targetIdx + 1);
59040
- processedArgs2[targetIdx] = struct;
59041
- }
59042
- for (let i = 0; i < regularEntries.length; i++) {
59043
- if (processedArgs2[i] === void 0 && regularEntries[i].defaultValue) {
59044
- try {
59045
- processedArgs2[i] = this.evalExpr(regularEntries[i].defaultValue);
59046
- } catch {
59047
- }
59048
- }
59688
+ for (const entry of regularEntries) {
59689
+ const pIdx = fn.params.indexOf(entry.name);
59690
+ if (pIdx < 0) continue;
59691
+ const provided = pIdx < args.length && args[pIdx] !== void 0;
59692
+ if (!provided && entry.defaultValue) {
59693
+ this.env.set(
59694
+ entry.name,
59695
+ ensureRuntimeValue(this.evalExpr(entry.defaultValue))
59696
+ );
59049
59697
  }
59050
- return processedArgs2;
59051
59698
  }
59052
- const processedArgs = [...args];
59053
- for (let i = 0; i < entries.length; i++) {
59054
- if (processedArgs[i] === void 0 && entries[i].defaultValue) {
59055
- try {
59056
- processedArgs[i] = this.evalExpr(entries[i].defaultValue);
59057
- } catch {
59058
- }
59699
+ for (const [paramName, fields] of nvGroups) {
59700
+ const pIdx = fn.params.indexOf(paramName);
59701
+ const nvArgs = args.slice(pIdx >= 0 ? pIdx : args.length);
59702
+ const defaults = {};
59703
+ for (const { field, defaultExpr } of fields) {
59704
+ if (defaultExpr) defaults[field] = this.evalExpr(defaultExpr);
59059
59705
  }
59706
+ this.env.set(paramName, this.rt.buildNameValueStruct(nvArgs, defaults));
59060
59707
  }
59061
- return processedArgs;
59062
59708
  }
59063
- return args;
59064
59709
  }
59065
59710
 
59066
59711
  // src/numbl-core/interpreter/interpreter.ts
@@ -86363,15 +87008,18 @@ var Workspace = class _Workspace {
86363
87008
  );
86364
87009
  }
86365
87010
  let primary = null;
87011
+ let firstFn = null;
86366
87012
  for (const stmt of ast.body) {
86367
87013
  if (stmt.type !== "Function") continue;
87014
+ if (!firstFn) firstFn = stmt;
86368
87015
  if (stmt.name === methodName) {
86369
87016
  primary = stmt;
86370
87017
  }
86371
87018
  }
87019
+ primary ??= firstFn;
86372
87020
  if (!primary) {
86373
87021
  throw new UnsupportedConstruct(
86374
- `external method file '${mf.fileName}' has no function named '${methodName}'`,
87022
+ `external method file '${mf.fileName}' has no function`,
86375
87023
  info.ast.span
86376
87024
  );
86377
87025
  }