copper3d 3.6.4 → 3.6.5
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/Utils/segmentation/NrrdTools.d.ts +41 -2
- package/dist/Utils/segmentation/NrrdTools.js +30 -2
- package/dist/Utils/segmentation/NrrdTools.js.map +1 -1
- package/dist/Utils/segmentation/tools/AiAssistTool.d.ts +108 -5
- package/dist/Utils/segmentation/tools/AiAssistTool.js +408 -25
- package/dist/Utils/segmentation/tools/AiAssistTool.js.map +1 -1
- package/dist/bundle.esm.js +447 -29
- package/dist/bundle.umd.js +447 -29
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/types/Utils/segmentation/NrrdTools.d.ts +41 -2
- package/dist/types/Utils/segmentation/tools/AiAssistTool.d.ts +108 -5
- package/dist/types/index.d.ts +1 -1
- package/package.json +2 -1
package/dist/bundle.umd.js
CHANGED
|
@@ -79124,14 +79124,29 @@ void main() {
|
|
|
79124
79124
|
this.committedVol = null;
|
|
79125
79125
|
this.promptTool = "point";
|
|
79126
79126
|
this.polarity = 1; // 1 = foreground (positive), 0 = background
|
|
79127
|
-
/**
|
|
79128
|
-
|
|
79127
|
+
/** The label value (1-255) the AI currently paints into = the active Segmentation.
|
|
79128
|
+
* Each Segmentation is a label value in the single-channel scratch volume; its
|
|
79129
|
+
* colour lives in the volume's colorMap (set via setSegmentColor). */
|
|
79130
|
+
this.activeLabel = 1;
|
|
79129
79131
|
/** Scribble brush radius (px), user-adjustable via slider. */
|
|
79130
79132
|
this.scribbleSize = 5;
|
|
79131
79133
|
/** Accumulated prompts for the CURRENT slice; reset on slice/axis change. */
|
|
79132
79134
|
this.points = [];
|
|
79133
79135
|
this.scribble = [];
|
|
79136
|
+
/** Lasso contour sent to the backend (densified along the curve at finish). */
|
|
79137
|
+
this.lasso = [];
|
|
79134
79138
|
this.activeSlice = -1;
|
|
79139
|
+
// ── Lasso v2 editing state (discrete clicked vertices → smooth closed curve) ──
|
|
79140
|
+
/** Placed lasso vertices (voxel + screen + polarity). Empty = not editing. */
|
|
79141
|
+
this.lassoVerts = [];
|
|
79142
|
+
/** Undo / redo snapshot stacks of the vertex list (for Ctrl+Z / Ctrl+Y). */
|
|
79143
|
+
this.lassoUndo = [];
|
|
79144
|
+
this.lassoRedo = [];
|
|
79145
|
+
/** Index of the vertex currently hovered (shows a red ✕; click removes it). −1 = none. */
|
|
79146
|
+
this.lassoHoverIdx = -1;
|
|
79147
|
+
/** Double-click detection (a dbl-click finishes the lasso). */
|
|
79148
|
+
this.lastLassoDownTime = 0;
|
|
79149
|
+
this.lastLassoDownPos = { x: 0, y: 0 };
|
|
79135
79150
|
// Drag state (box / scribble) — voxel coords for the prompt
|
|
79136
79151
|
this.dragging = false;
|
|
79137
79152
|
this.dragStart = null;
|
|
@@ -79142,8 +79157,14 @@ void main() {
|
|
|
79142
79157
|
// Live cursor position (screen px) for the scribble brush-size preview ring —
|
|
79143
79158
|
// updated on every hover move so the ring follows the mouse like the paint brush.
|
|
79144
79159
|
this.hoverScreen = null;
|
|
79160
|
+
/** Hide the point seed markers once a prediction has painted (only the mask
|
|
79161
|
+
* should remain). A new point click re-shows them until its mask returns. */
|
|
79162
|
+
this.hidePointMarkers = false;
|
|
79145
79163
|
/** Fired when a prompt gesture completes — app calls backend, then applyMask(). */
|
|
79146
79164
|
this.onPrompt = null;
|
|
79165
|
+
/** Fired whenever the lasso vertex set changes (add/delete/undo/redo/finish/cancel),
|
|
79166
|
+
* so the panel can reactively show the Finish button + vertex count. */
|
|
79167
|
+
this.onLassoChange = null;
|
|
79147
79168
|
this.host = host;
|
|
79148
79169
|
}
|
|
79149
79170
|
// ── Configuration (driven from the panel via NrrdTools) ────────────────────
|
|
@@ -79161,16 +79182,51 @@ void main() {
|
|
|
79161
79182
|
setScribbleSize(size) {
|
|
79162
79183
|
this.scribbleSize = Math.max(1, Math.min(40, Math.round(size)));
|
|
79163
79184
|
}
|
|
79164
|
-
|
|
79165
|
-
|
|
79166
|
-
|
|
79167
|
-
|
|
79168
|
-
|
|
79169
|
-
if (c !== this.
|
|
79185
|
+
/** Select the active Segmentation by its label value (1-255). Switching starts a
|
|
79186
|
+
* fresh prompt set — otherwise accumulated prompts would be re-predicted and
|
|
79187
|
+
* repainted into the new label, recolouring the previous segmentation's region. */
|
|
79188
|
+
setActiveSegment(label) {
|
|
79189
|
+
const c = Math.max(1, Math.min(255, Math.round(label)));
|
|
79190
|
+
if (c !== this.activeLabel)
|
|
79170
79191
|
this.resetPrompts();
|
|
79171
|
-
this.
|
|
79192
|
+
this.activeLabel = c;
|
|
79193
|
+
}
|
|
79194
|
+
getActiveLabel() { return this.activeLabel; }
|
|
79195
|
+
/** Set a Segmentation's colour (its label's entry in the scratch volume colorMap).
|
|
79196
|
+
* The 2D overlay repaints next frame (copper3d's render loop re-reads the map). */
|
|
79197
|
+
setSegmentColor(label, color) {
|
|
79198
|
+
var _a;
|
|
79199
|
+
(_a = this.scratchVol) === null || _a === void 0 ? void 0 : _a.setChannelColor(Math.round(label), color);
|
|
79200
|
+
}
|
|
79201
|
+
/** "New segmentation": freeze the current regions (so they persist) and switch to
|
|
79202
|
+
* a fresh label, exactly like the old "New region" but targeting a new label. */
|
|
79203
|
+
newSegment(label) {
|
|
79204
|
+
this.commitRegion();
|
|
79205
|
+
this.setActiveSegment(label);
|
|
79206
|
+
}
|
|
79207
|
+
/** Delete a Segmentation: erase every voxel painted with its label from BOTH the
|
|
79208
|
+
* live scratch and the frozen (committed) volume, so the region disappears from
|
|
79209
|
+
* the view (otherwise orphaned label voxels would render with the wrong colour).
|
|
79210
|
+
* Goes via setRawData so the volume's version counter bumps (overlay repaints). */
|
|
79211
|
+
clearSegment(label) {
|
|
79212
|
+
const lbl = Math.round(label);
|
|
79213
|
+
const zeroOut = (v) => {
|
|
79214
|
+
if (!v)
|
|
79215
|
+
return;
|
|
79216
|
+
const raw = v.getRawData().slice();
|
|
79217
|
+
let changed = false;
|
|
79218
|
+
for (let i = 0; i < raw.length; i++) {
|
|
79219
|
+
if (raw[i] === lbl) {
|
|
79220
|
+
raw[i] = 0;
|
|
79221
|
+
changed = true;
|
|
79222
|
+
}
|
|
79223
|
+
}
|
|
79224
|
+
if (changed)
|
|
79225
|
+
v.setRawData(raw);
|
|
79226
|
+
};
|
|
79227
|
+
zeroOut(this.scratchVol);
|
|
79228
|
+
zeroOut(this.committedVol);
|
|
79172
79229
|
}
|
|
79173
|
-
getChannel() { return this.channel; }
|
|
79174
79230
|
getScratchVolume() { return this.scratchVol; }
|
|
79175
79231
|
/** Serialize the scratch volume as per-z-slice RLE (only NON-empty slices),
|
|
79176
79232
|
* for persisting to ai_generated_nii_LPS on the backend. Binary (any channel →
|
|
@@ -79212,6 +79268,50 @@ void main() {
|
|
|
79212
79268
|
}
|
|
79213
79269
|
return { axis: "z", width, height, slices: out };
|
|
79214
79270
|
}
|
|
79271
|
+
/** Per-SEGMENTATION serialization for the 3D build: one binary RLE per non-empty
|
|
79272
|
+
* z-slice PER label (no binarize-merge), so the backend writes a multi-label
|
|
79273
|
+
* NIfTI and colours the GLB per segmentation. Includes both live and frozen
|
|
79274
|
+
* regions (both live in scratchVol). */
|
|
79275
|
+
getScratchSegments() {
|
|
79276
|
+
if (!this.scratchVol)
|
|
79277
|
+
return null;
|
|
79278
|
+
const { depth } = this.scratchVol.getDimensions();
|
|
79279
|
+
const byLabel = new Map();
|
|
79280
|
+
let width = 0;
|
|
79281
|
+
let height = 0;
|
|
79282
|
+
for (let z = 0; z < depth; z++) {
|
|
79283
|
+
const { data, width: w, height: h } = this.scratchVol.getSliceUint8(z, "z");
|
|
79284
|
+
width = w;
|
|
79285
|
+
height = h;
|
|
79286
|
+
const labels = new Set();
|
|
79287
|
+
for (let i = 0; i < data.length; i++) {
|
|
79288
|
+
if (data[i])
|
|
79289
|
+
labels.add(data[i]);
|
|
79290
|
+
}
|
|
79291
|
+
for (const label of labels) {
|
|
79292
|
+
const runs = [];
|
|
79293
|
+
let value = 0; // always begin with a 0-run (matches backend rle_decode)
|
|
79294
|
+
let count = 0;
|
|
79295
|
+
for (let i = 0; i < data.length; i++) {
|
|
79296
|
+
const v = data[i] === label ? 1 : 0;
|
|
79297
|
+
if (v === value) {
|
|
79298
|
+
count++;
|
|
79299
|
+
}
|
|
79300
|
+
else {
|
|
79301
|
+
runs.push(count);
|
|
79302
|
+
value = v;
|
|
79303
|
+
count = 1;
|
|
79304
|
+
}
|
|
79305
|
+
}
|
|
79306
|
+
runs.push(count);
|
|
79307
|
+
if (!byLabel.has(label))
|
|
79308
|
+
byLabel.set(label, []);
|
|
79309
|
+
byLabel.get(label).push({ sliceIndex: z, rle: runs });
|
|
79310
|
+
}
|
|
79311
|
+
}
|
|
79312
|
+
const segments = [...byLabel.entries()].map(([label, slices]) => ({ label, slices }));
|
|
79313
|
+
return { axis: "z", width, height, segments };
|
|
79314
|
+
}
|
|
79215
79315
|
// ── Sandbox lifecycle ──────────────────────────────────────────────────────
|
|
79216
79316
|
/** Create (or reuse) the scratch volume sized to the layer grid and register
|
|
79217
79317
|
* it under maskData.volumes['aiScratch']. Snapshots the empty buffer. */
|
|
@@ -79232,14 +79332,10 @@ void main() {
|
|
|
79232
79332
|
this.scratchVol.clear();
|
|
79233
79333
|
}
|
|
79234
79334
|
volumes[AI_SCRATCH_LAYER] = this.scratchVol;
|
|
79235
|
-
//
|
|
79236
|
-
//
|
|
79237
|
-
//
|
|
79238
|
-
|
|
79239
|
-
const c = AI_MASK_CHANNEL_COLORS[ch];
|
|
79240
|
-
if (c)
|
|
79241
|
-
this.scratchVol.setChannelColor(ch, { r: c.r, g: c.g, b: c.b, a: c.a });
|
|
79242
|
-
}
|
|
79335
|
+
// Segmentation colours are now driven per-label from the app (store → useAiAssist
|
|
79336
|
+
// → setSegmentColor) right after enter, so the scratch can carry an arbitrary
|
|
79337
|
+
// number of independently-coloured segmentations. No fixed palette is applied
|
|
79338
|
+
// here. Scoped to THIS volume only — global/clinician palettes are untouched.
|
|
79243
79339
|
this.snapshotData = this.scratchVol.getRawData().slice();
|
|
79244
79340
|
this.committedVol = null; // nothing frozen at session start
|
|
79245
79341
|
this.resetPrompts();
|
|
@@ -79285,10 +79381,26 @@ void main() {
|
|
|
79285
79381
|
resetPrompts() {
|
|
79286
79382
|
this.points = [];
|
|
79287
79383
|
this.scribble = [];
|
|
79384
|
+
this.lasso = [];
|
|
79288
79385
|
this.box = undefined;
|
|
79289
79386
|
this.dragScreenStart = null;
|
|
79290
79387
|
this.dragScreen = null;
|
|
79291
79388
|
this.screenScribble = [];
|
|
79389
|
+
this.clearLassoEditing();
|
|
79390
|
+
}
|
|
79391
|
+
/** Drop the in-progress lasso vertices + undo/redo history (does not touch a
|
|
79392
|
+
* prediction already painted from a finished lasso). */
|
|
79393
|
+
clearLassoEditing() {
|
|
79394
|
+
this.lassoVerts = [];
|
|
79395
|
+
this.lassoUndo = [];
|
|
79396
|
+
this.lassoRedo = [];
|
|
79397
|
+
this.lassoHoverIdx = -1;
|
|
79398
|
+
this.notifyLasso();
|
|
79399
|
+
}
|
|
79400
|
+
/** Tell the app layer the lasso vertex set changed (drives the Finish button). */
|
|
79401
|
+
notifyLasso() {
|
|
79402
|
+
var _a;
|
|
79403
|
+
(_a = this.onLassoChange) === null || _a === void 0 ? void 0 : _a.call(this, this.lassoVerts.length, this.lassoVerts.length > 0);
|
|
79292
79404
|
}
|
|
79293
79405
|
// ── Coordinate mapping (all three views) ───────────────────────────────────
|
|
79294
79406
|
//
|
|
@@ -79349,14 +79461,53 @@ void main() {
|
|
|
79349
79461
|
return null;
|
|
79350
79462
|
return { vx, vy, w: sd.w, h: sd.h };
|
|
79351
79463
|
}
|
|
79464
|
+
/** Voxel-slice (vx,vy) → screen offset (px). Exact inverse of toVoxel, so
|
|
79465
|
+
* markers / lasso curves drawn from voxel coords track pan AND zoom each frame
|
|
79466
|
+
* (the freehand previews store screen px directly; persistent overlays don't). */
|
|
79467
|
+
voxelToScreen(vx, vy) {
|
|
79468
|
+
const sd = this.sliceDims();
|
|
79469
|
+
if (!sd)
|
|
79470
|
+
return null;
|
|
79471
|
+
const nrrd = this.ctx.nrrd_states;
|
|
79472
|
+
const img = nrrd.image;
|
|
79473
|
+
let hMM, vMM, flipV;
|
|
79474
|
+
switch (this.ctx.protectedData.axis) {
|
|
79475
|
+
case "z":
|
|
79476
|
+
hMM = img.nrrd_x_mm;
|
|
79477
|
+
vMM = img.nrrd_y_mm;
|
|
79478
|
+
flipV = false;
|
|
79479
|
+
break;
|
|
79480
|
+
case "y":
|
|
79481
|
+
hMM = img.nrrd_x_mm;
|
|
79482
|
+
vMM = img.nrrd_z_mm;
|
|
79483
|
+
flipV = true;
|
|
79484
|
+
break;
|
|
79485
|
+
case "x":
|
|
79486
|
+
hMM = img.nrrd_z_mm;
|
|
79487
|
+
vMM = img.nrrd_y_mm;
|
|
79488
|
+
flipV = false;
|
|
79489
|
+
break;
|
|
79490
|
+
default: return null;
|
|
79491
|
+
}
|
|
79492
|
+
const vyRaw = flipV ? sd.h - 1 - vy : vy;
|
|
79493
|
+
const x = (vx * hMM / sd.w) * nrrd.view.sizeFactor;
|
|
79494
|
+
const y = (vyRaw * vMM / sd.h) * nrrd.view.sizeFactor;
|
|
79495
|
+
return { x, y };
|
|
79496
|
+
}
|
|
79352
79497
|
// ── Pointer handlers (left button; right stays pan in EventRouter) ──────────
|
|
79353
79498
|
onPointerDown(e) {
|
|
79354
79499
|
const hit = this.toVoxel(e);
|
|
79355
79500
|
if (!hit)
|
|
79356
79501
|
return;
|
|
79357
79502
|
this.syncSlice();
|
|
79503
|
+
// Lasso is click-to-place vertices (NOT a drag) — own handler.
|
|
79504
|
+
if (this.promptTool === "lasso") {
|
|
79505
|
+
this.onLassoPointerDown(e, hit.vx, hit.vy);
|
|
79506
|
+
return;
|
|
79507
|
+
}
|
|
79358
79508
|
if (this.promptTool === "point") {
|
|
79359
79509
|
this.points.push({ x: hit.vx, y: hit.vy, label: this.polarity });
|
|
79510
|
+
this.hidePointMarkers = false; // show the new seed until its mask returns
|
|
79360
79511
|
this.emit();
|
|
79361
79512
|
}
|
|
79362
79513
|
else {
|
|
@@ -79372,9 +79523,43 @@ void main() {
|
|
|
79372
79523
|
}
|
|
79373
79524
|
}
|
|
79374
79525
|
}
|
|
79526
|
+
/** Lasso click: double-click finishes; click on a vertex deletes it; otherwise
|
|
79527
|
+
* add a vertex. Nothing is sent to the backend until finishLasso(). */
|
|
79528
|
+
onLassoPointerDown(e, vx, vy) {
|
|
79529
|
+
const now = Date.now();
|
|
79530
|
+
const dxl = e.offsetX - this.lastLassoDownPos.x;
|
|
79531
|
+
const dyl = e.offsetY - this.lastLassoDownPos.y;
|
|
79532
|
+
const isDbl = now - this.lastLassoDownTime < 300 &&
|
|
79533
|
+
dxl * dxl + dyl * dyl <= 36; // ~6px
|
|
79534
|
+
this.lastLassoDownTime = now;
|
|
79535
|
+
this.lastLassoDownPos = { x: e.offsetX, y: e.offsetY };
|
|
79536
|
+
// Double-click → finish (the 1st click of the pair already placed the vertex).
|
|
79537
|
+
if (isDbl && this.lassoVerts.length >= 3) {
|
|
79538
|
+
this.finishLasso();
|
|
79539
|
+
return;
|
|
79540
|
+
}
|
|
79541
|
+
// Click on an existing vertex → delete it (recompute the curve).
|
|
79542
|
+
const idx = this.lassoVertAt(e.offsetX, e.offsetY);
|
|
79543
|
+
if (idx >= 0) {
|
|
79544
|
+
this.pushLassoUndo();
|
|
79545
|
+
this.lassoVerts.splice(idx, 1);
|
|
79546
|
+
this.lassoHoverIdx = -1;
|
|
79547
|
+
this.notifyLasso();
|
|
79548
|
+
return;
|
|
79549
|
+
}
|
|
79550
|
+
// Otherwise add a new vertex.
|
|
79551
|
+
this.pushLassoUndo();
|
|
79552
|
+
this.lassoVerts.push({ vx, vy, sx: e.offsetX, sy: e.offsetY, label: this.polarity });
|
|
79553
|
+
this.notifyLasso();
|
|
79554
|
+
}
|
|
79375
79555
|
onPointerMove(e) {
|
|
79376
|
-
// Track hover for the scribble preview ring even when
|
|
79556
|
+
// Track hover for the scribble preview ring / point crosshair even when idle.
|
|
79377
79557
|
this.hoverScreen = { x: e.offsetX, y: e.offsetY };
|
|
79558
|
+
// Lasso: update which vertex (if any) is hovered, for the red ✕ delete affordance.
|
|
79559
|
+
if (this.promptTool === "lasso") {
|
|
79560
|
+
this.lassoHoverIdx = this.lassoVertAt(e.offsetX, e.offsetY);
|
|
79561
|
+
return; // lasso is click-based; no drag accumulation
|
|
79562
|
+
}
|
|
79378
79563
|
if (!this.dragging)
|
|
79379
79564
|
return;
|
|
79380
79565
|
// Track screen pos first so the live preview follows even slightly out of bounds.
|
|
@@ -79427,12 +79612,116 @@ void main() {
|
|
|
79427
79612
|
box: this.box,
|
|
79428
79613
|
scribble: this.scribble.length ? [...this.scribble] : undefined,
|
|
79429
79614
|
scribbleRadius: this.scribbleSize,
|
|
79615
|
+
lasso: this.lasso.length ? [...this.lasso] : undefined,
|
|
79430
79616
|
});
|
|
79431
79617
|
}
|
|
79618
|
+
// ── Lasso v2 (discrete vertices → smooth closed curve) ──────────────────────
|
|
79619
|
+
/** Index of the lasso vertex under (sx,sy) within the hit radius, else −1.
|
|
79620
|
+
* Uses live voxel→screen so it tracks pan/zoom. Topmost (latest) wins. */
|
|
79621
|
+
lassoVertAt(sx, sy) {
|
|
79622
|
+
var _a;
|
|
79623
|
+
const r2 = AiAssistTool.LASSO_HIT_R * AiAssistTool.LASSO_HIT_R;
|
|
79624
|
+
for (let i = this.lassoVerts.length - 1; i >= 0; i--) {
|
|
79625
|
+
const v = this.lassoVerts[i];
|
|
79626
|
+
const s = (_a = this.voxelToScreen(v.vx, v.vy)) !== null && _a !== void 0 ? _a : { x: v.sx, y: v.sy };
|
|
79627
|
+
const dx = sx - s.x, dy = sy - s.y;
|
|
79628
|
+
if (dx * dx + dy * dy <= r2)
|
|
79629
|
+
return i;
|
|
79630
|
+
}
|
|
79631
|
+
return -1;
|
|
79632
|
+
}
|
|
79633
|
+
/** Snapshot the vertex list for undo (clears redo). */
|
|
79634
|
+
pushLassoUndo() {
|
|
79635
|
+
this.lassoUndo.push(this.lassoVerts.map((v) => (Object.assign({}, v))));
|
|
79636
|
+
this.lassoRedo = [];
|
|
79637
|
+
if (this.lassoUndo.length > 100)
|
|
79638
|
+
this.lassoUndo.shift();
|
|
79639
|
+
}
|
|
79640
|
+
/** Public: undo the last add/delete of a lasso vertex (Ctrl+Z while editing). */
|
|
79641
|
+
lassoUndoAction() {
|
|
79642
|
+
if (!this.lassoUndo.length)
|
|
79643
|
+
return;
|
|
79644
|
+
this.lassoRedo.push(this.lassoVerts.map((v) => (Object.assign({}, v))));
|
|
79645
|
+
this.lassoVerts = this.lassoUndo.pop();
|
|
79646
|
+
this.lassoHoverIdx = -1;
|
|
79647
|
+
this.notifyLasso();
|
|
79648
|
+
}
|
|
79649
|
+
/** Public: redo (Ctrl+Y / Ctrl+Shift+Z while editing). */
|
|
79650
|
+
lassoRedoAction() {
|
|
79651
|
+
if (!this.lassoRedo.length)
|
|
79652
|
+
return;
|
|
79653
|
+
this.lassoUndo.push(this.lassoVerts.map((v) => (Object.assign({}, v))));
|
|
79654
|
+
this.lassoVerts = this.lassoRedo.pop();
|
|
79655
|
+
this.lassoHoverIdx = -1;
|
|
79656
|
+
this.notifyLasso();
|
|
79657
|
+
}
|
|
79658
|
+
/** Public: abandon the in-progress lasso (Esc). */
|
|
79659
|
+
cancelLasso() { this.clearLassoEditing(); }
|
|
79660
|
+
/** Public: true while the user is placing lasso vertices (≥1 vertex). */
|
|
79661
|
+
isLassoEditing() { return this.lassoVerts.length > 0; }
|
|
79662
|
+
/** Public: number of placed lasso vertices (drives the Finish button label/enable). */
|
|
79663
|
+
getLassoVertCount() { return this.lassoVerts.length; }
|
|
79664
|
+
/** Vertices ordered by polar angle around their centroid, so the closed curve is
|
|
79665
|
+
* a SIMPLE (non-self-intersecting) loop regardless of click order — otherwise
|
|
79666
|
+
* connecting points in click order + closing easily produces a bow-tie crossing.
|
|
79667
|
+
* Trade-off: deep concavities collapse to their star-shaped hull, which is fine
|
|
79668
|
+
* for a "rough loop around a blob". */
|
|
79669
|
+
orderedLassoVerts() {
|
|
79670
|
+
const n = this.lassoVerts.length;
|
|
79671
|
+
if (n < 3)
|
|
79672
|
+
return this.lassoVerts.slice();
|
|
79673
|
+
let cx = 0, cy = 0;
|
|
79674
|
+
for (const v of this.lassoVerts) {
|
|
79675
|
+
cx += v.vx;
|
|
79676
|
+
cy += v.vy;
|
|
79677
|
+
}
|
|
79678
|
+
cx /= n;
|
|
79679
|
+
cy /= n;
|
|
79680
|
+
return this.lassoVerts.slice().sort((a, b) => Math.atan2(a.vy - cy, a.vx - cx) - Math.atan2(b.vy - cy, b.vx - cx));
|
|
79681
|
+
}
|
|
79682
|
+
/** Public: close the curve, densify it, and send it to the backend as the lasso
|
|
79683
|
+
* contour. Needs ≥3 vertices (a real area). Then clears the editing state. */
|
|
79684
|
+
finishLasso() {
|
|
79685
|
+
if (this.lassoVerts.length < 3)
|
|
79686
|
+
return;
|
|
79687
|
+
// Order by angle (no self-crossing), then sample the SAME closed curve the user
|
|
79688
|
+
// saw → contour points (so the mask matches the curve, not a straight polygon).
|
|
79689
|
+
const contour = this.sampleClosedSpline(this.orderedLassoVerts().map((v) => ({ x: v.vx, y: v.vy })), 16);
|
|
79690
|
+
const label = this.lassoVerts[0].label; // a lasso carries one polarity (set at start)
|
|
79691
|
+
this.lasso = contour.map((p) => ({ x: Math.round(p.x), y: Math.round(p.y), label }));
|
|
79692
|
+
this.clearLassoEditing();
|
|
79693
|
+
this.emit();
|
|
79694
|
+
}
|
|
79695
|
+
/** Sample a smooth CLOSED Catmull-Rom spline through `pts` → denser polyline.
|
|
79696
|
+
* `segs` samples per control-point span. <3 pts → returns a copy unchanged. */
|
|
79697
|
+
sampleClosedSpline(pts, segs) {
|
|
79698
|
+
const n = pts.length;
|
|
79699
|
+
if (n < 3)
|
|
79700
|
+
return pts.slice();
|
|
79701
|
+
const at = (i) => pts[((i % n) + n) % n];
|
|
79702
|
+
const out = [];
|
|
79703
|
+
for (let i = 0; i < n; i++) {
|
|
79704
|
+
const p0 = at(i - 1), p1 = at(i), p2 = at(i + 1), p3 = at(i + 2);
|
|
79705
|
+
for (let s = 0; s < segs; s++) {
|
|
79706
|
+
const t = s / segs, t2 = t * t, t3 = t2 * t;
|
|
79707
|
+
out.push({
|
|
79708
|
+
x: 0.5 * (2 * p1.x + (-p0.x + p2.x) * t +
|
|
79709
|
+
(2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * t2 +
|
|
79710
|
+
(-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t3),
|
|
79711
|
+
y: 0.5 * (2 * p1.y + (-p0.y + p2.y) * t +
|
|
79712
|
+
(2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 +
|
|
79713
|
+
(-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3),
|
|
79714
|
+
});
|
|
79715
|
+
}
|
|
79716
|
+
}
|
|
79717
|
+
return out;
|
|
79718
|
+
}
|
|
79432
79719
|
// ── Apply backend result → scratch volume ──────────────────────────────────
|
|
79433
79720
|
applyMask(result) {
|
|
79434
79721
|
if (!this.scratchVol)
|
|
79435
79722
|
return;
|
|
79723
|
+
// A prediction landed → hide the point seed markers; only the mask should show.
|
|
79724
|
+
this.hidePointMarkers = true;
|
|
79436
79725
|
// 3D result (engine B): write each slice across the covered span.
|
|
79437
79726
|
if (result.slices && result.sliceRange) {
|
|
79438
79727
|
const [lo] = result.sliceRange;
|
|
@@ -79462,7 +79751,7 @@ void main() {
|
|
|
79462
79751
|
pos += run;
|
|
79463
79752
|
value ^= 1;
|
|
79464
79753
|
}
|
|
79465
|
-
const ch = this.
|
|
79754
|
+
const ch = this.activeLabel;
|
|
79466
79755
|
try {
|
|
79467
79756
|
const out = this.scratchVol.getSliceUint8(sliceIndex, axis).data; // copy
|
|
79468
79757
|
// Frozen same-channel pixels for this slice (if any region was committed).
|
|
@@ -79535,10 +79824,7 @@ void main() {
|
|
|
79535
79824
|
}
|
|
79536
79825
|
targetCtx.restore();
|
|
79537
79826
|
}
|
|
79538
|
-
// Scribble brush-size preview ring (when NOT dragging)
|
|
79539
|
-
// the cursor whose radius = scribbleSize, so the user sees the brush size and
|
|
79540
|
-
// how the slider changes it (mirrors the paint brush's preview ring). Drawn
|
|
79541
|
-
// every frame from the live hover position by copper3d's render loop.
|
|
79827
|
+
// Scribble brush-size preview ring (when NOT dragging).
|
|
79542
79828
|
if (this.promptTool === "scribble" && !this.dragging && this.hoverScreen) {
|
|
79543
79829
|
targetCtx.save();
|
|
79544
79830
|
const fg = this.polarity === 1;
|
|
@@ -79551,8 +79837,105 @@ void main() {
|
|
|
79551
79837
|
targetCtx.stroke();
|
|
79552
79838
|
targetCtx.restore();
|
|
79553
79839
|
}
|
|
79840
|
+
// Point markers + hover crosshair so the clinician sees exactly where each seed
|
|
79841
|
+
// lands (and where the next click will go) before/while the prediction returns.
|
|
79842
|
+
if (this.promptTool === "point") {
|
|
79843
|
+
if (!this.hidePointMarkers) {
|
|
79844
|
+
for (const p of this.points) {
|
|
79845
|
+
const s = this.voxelToScreen(p.x, p.y);
|
|
79846
|
+
if (s)
|
|
79847
|
+
this.drawSeedMarker(targetCtx, s.x, s.y, p.label === 1);
|
|
79848
|
+
}
|
|
79849
|
+
}
|
|
79850
|
+
if (!this.dragging && this.hoverScreen) {
|
|
79851
|
+
targetCtx.save();
|
|
79852
|
+
targetCtx.strokeStyle = this.polarity === 1
|
|
79853
|
+
? "rgba(120,255,180,0.55)" : "rgba(255,120,120,0.55)";
|
|
79854
|
+
targetCtx.lineWidth = 1;
|
|
79855
|
+
const { x, y } = this.hoverScreen;
|
|
79856
|
+
targetCtx.beginPath();
|
|
79857
|
+
targetCtx.moveTo(x - 7, y);
|
|
79858
|
+
targetCtx.lineTo(x + 7, y);
|
|
79859
|
+
targetCtx.moveTo(x, y - 7);
|
|
79860
|
+
targetCtx.lineTo(x, y + 7);
|
|
79861
|
+
targetCtx.stroke();
|
|
79862
|
+
targetCtx.restore();
|
|
79863
|
+
}
|
|
79864
|
+
}
|
|
79865
|
+
// Lasso v2: smooth closed curve through the placed vertices (closed + filled
|
|
79866
|
+
// from ≥2 vertices), vertex dots, and a red ✕ on the hovered vertex.
|
|
79867
|
+
if (this.promptTool === "lasso" && this.lassoVerts.length > 0) {
|
|
79868
|
+
this.renderLassoOverlay(targetCtx);
|
|
79869
|
+
}
|
|
79554
79870
|
}
|
|
79555
|
-
|
|
79871
|
+
/** A seed marker (small filled dot + ring) for point prompts. */
|
|
79872
|
+
drawSeedMarker(ctx, x, y, fg) {
|
|
79873
|
+
ctx.save();
|
|
79874
|
+
const col = fg ? "rgba(120,255,180,0.95)" : "rgba(255,120,120,0.95)";
|
|
79875
|
+
ctx.fillStyle = col;
|
|
79876
|
+
ctx.strokeStyle = "rgba(0,0,0,0.55)";
|
|
79877
|
+
ctx.lineWidth = 1.5;
|
|
79878
|
+
ctx.beginPath();
|
|
79879
|
+
ctx.arc(x, y, 3.5, 0, Math.PI * 2);
|
|
79880
|
+
ctx.fill();
|
|
79881
|
+
ctx.stroke();
|
|
79882
|
+
ctx.restore();
|
|
79883
|
+
}
|
|
79884
|
+
/** Render the lasso editing overlay (curve + fill + vertices + hover ✕). */
|
|
79885
|
+
renderLassoOverlay(ctx) {
|
|
79886
|
+
var _a;
|
|
79887
|
+
const fg = this.lassoVerts[0].label === 1;
|
|
79888
|
+
const stroke = fg ? "rgba(120,255,180,0.95)" : "rgba(255,120,120,0.95)";
|
|
79889
|
+
const fill = fg ? "rgba(120,255,180,0.14)" : "rgba(255,120,120,0.14)";
|
|
79890
|
+
// PATH: angularly-ordered vertices (simple, non-crossing loop) in screen space.
|
|
79891
|
+
const ov = this.orderedLassoVerts().map((v) => { var _a; return (_a = this.voxelToScreen(v.vx, v.vy)) !== null && _a !== void 0 ? _a : { x: v.sx, y: v.sy }; });
|
|
79892
|
+
const path = ov.length >= 3 ? this.sampleClosedSpline(ov, 16) : ov;
|
|
79893
|
+
ctx.save();
|
|
79894
|
+
if (path.length >= 2) {
|
|
79895
|
+
ctx.beginPath();
|
|
79896
|
+
ctx.moveTo(path[0].x, path[0].y);
|
|
79897
|
+
for (let i = 1; i < path.length; i++)
|
|
79898
|
+
ctx.lineTo(path[i].x, path[i].y);
|
|
79899
|
+
ctx.closePath();
|
|
79900
|
+
ctx.fillStyle = fill;
|
|
79901
|
+
ctx.fill();
|
|
79902
|
+
ctx.lineCap = "round";
|
|
79903
|
+
ctx.lineJoin = "round";
|
|
79904
|
+
ctx.lineWidth = 1.75;
|
|
79905
|
+
ctx.strokeStyle = stroke;
|
|
79906
|
+
ctx.stroke();
|
|
79907
|
+
}
|
|
79908
|
+
// Vertex handles — drawn from the ORIGINAL vertex order so lassoHoverIdx (an
|
|
79909
|
+
// index into lassoVerts from the hit-test) marks the correct one for the ✕.
|
|
79910
|
+
for (let i = 0; i < this.lassoVerts.length; i++) {
|
|
79911
|
+
const v = this.lassoVerts[i];
|
|
79912
|
+
const p = (_a = this.voxelToScreen(v.vx, v.vy)) !== null && _a !== void 0 ? _a : { x: v.sx, y: v.sy };
|
|
79913
|
+
if (i === this.lassoHoverIdx) {
|
|
79914
|
+
// Red ✕ — click to delete this vertex.
|
|
79915
|
+
ctx.strokeStyle = "rgba(255,90,90,0.98)";
|
|
79916
|
+
ctx.lineWidth = 2;
|
|
79917
|
+
ctx.beginPath();
|
|
79918
|
+
ctx.moveTo(p.x - 5, p.y - 5);
|
|
79919
|
+
ctx.lineTo(p.x + 5, p.y + 5);
|
|
79920
|
+
ctx.moveTo(p.x + 5, p.y - 5);
|
|
79921
|
+
ctx.lineTo(p.x - 5, p.y + 5);
|
|
79922
|
+
ctx.stroke();
|
|
79923
|
+
}
|
|
79924
|
+
else {
|
|
79925
|
+
ctx.fillStyle = stroke;
|
|
79926
|
+
ctx.strokeStyle = "rgba(0,0,0,0.55)";
|
|
79927
|
+
ctx.lineWidth = 1.25;
|
|
79928
|
+
ctx.beginPath();
|
|
79929
|
+
ctx.arc(p.x, p.y, 3, 0, Math.PI * 2);
|
|
79930
|
+
ctx.fill();
|
|
79931
|
+
ctx.stroke();
|
|
79932
|
+
}
|
|
79933
|
+
}
|
|
79934
|
+
ctx.restore();
|
|
79935
|
+
}
|
|
79936
|
+
}
|
|
79937
|
+
/** Screen-pixel radius for hit-testing a hovered vertex. */
|
|
79938
|
+
AiAssistTool.LASSO_HIT_R = 8;
|
|
79556
79939
|
|
|
79557
79940
|
/**
|
|
79558
79941
|
* DrawToolCore — Tool orchestration and event routing.
|
|
@@ -82587,16 +82970,39 @@ void main() {
|
|
|
82587
82970
|
// — Driver methods called by the app-layer composable —
|
|
82588
82971
|
aiSetPromptTool(tool) { this.drawCore.aiAssistTool.setPromptTool(tool); }
|
|
82589
82972
|
aiSetPolarity(label) { this.drawCore.aiAssistTool.setPolarity(label); }
|
|
82590
|
-
/**
|
|
82591
|
-
|
|
82973
|
+
/** Select the active Segmentation by its label value (the AI paints into it). */
|
|
82974
|
+
aiSetActiveSegment(label) { this.drawCore.aiAssistTool.setActiveSegment(label); }
|
|
82975
|
+
/** Set a Segmentation's colour (its label's colorMap entry; 2D overlay repaints). */
|
|
82976
|
+
aiSetSegmentColor(label, color) {
|
|
82977
|
+
this.drawCore.aiAssistTool.setSegmentColor(label, color);
|
|
82978
|
+
}
|
|
82979
|
+
/** "New segmentation": freeze current regions + switch to a new label. */
|
|
82980
|
+
aiNewSegment(label) { this.drawCore.aiAssistTool.newSegment(label); }
|
|
82981
|
+
/** Delete a Segmentation: erase all its label's voxels from scratch + committed. */
|
|
82982
|
+
aiClearSegment(label) { this.drawCore.aiAssistTool.clearSegment(label); }
|
|
82592
82983
|
/** Set the scribble brush radius (px). */
|
|
82593
82984
|
aiSetScribbleSize(size) { this.drawCore.aiAssistTool.setScribbleSize(size); }
|
|
82594
82985
|
/** Register the callback invoked when a prompt gesture completes (app → backend). */
|
|
82595
82986
|
aiOnPrompt(cb) { this.drawCore.aiAssistTool.onPrompt = cb; }
|
|
82987
|
+
/** Register the callback fired when the lasso vertex set changes (drives the Finish button). */
|
|
82988
|
+
aiOnLassoChange(cb) { this.drawCore.aiAssistTool.onLassoChange = cb; }
|
|
82596
82989
|
/** Apply a backend mask result into the scratch volume (overlay repaints next frame). */
|
|
82597
82990
|
aiApplyMask(result) { this.drawCore.aiAssistTool.applyMask(result); }
|
|
82598
82991
|
/** Clear the in-progress prompt set (e.g. slice change). */
|
|
82599
82992
|
aiClearPrompts() { this.drawCore.aiAssistTool.resetPrompts(); }
|
|
82993
|
+
// — Lasso v2 (discrete-vertex closed-curve) controls —
|
|
82994
|
+
/** Close + send the in-progress lasso to the backend (needs ≥3 vertices). */
|
|
82995
|
+
aiFinishLasso() { this.drawCore.aiAssistTool.finishLasso(); }
|
|
82996
|
+
/** Undo the last lasso vertex add/delete. */
|
|
82997
|
+
aiLassoUndo() { this.drawCore.aiAssistTool.lassoUndoAction(); }
|
|
82998
|
+
/** Redo the last undone lasso vertex change. */
|
|
82999
|
+
aiLassoRedo() { this.drawCore.aiAssistTool.lassoRedoAction(); }
|
|
83000
|
+
/** Abandon the in-progress lasso (Esc). */
|
|
83001
|
+
aiCancelLasso() { this.drawCore.aiAssistTool.cancelLasso(); }
|
|
83002
|
+
/** True while the user is placing lasso vertices. */
|
|
83003
|
+
aiIsLassoEditing() { return this.drawCore.aiAssistTool.isLassoEditing(); }
|
|
83004
|
+
/** Number of placed lasso vertices (drives the Finish button). */
|
|
83005
|
+
aiLassoVertCount() { return this.drawCore.aiAssistTool.getLassoVertCount(); }
|
|
82600
83006
|
/** "New region": freeze current regions (they persist) + start a fresh prompt set. */
|
|
82601
83007
|
aiCommitRegion() { this.drawCore.aiAssistTool.commitRegion(); }
|
|
82602
83008
|
/** Discard all AI scratch painting since enter (sandbox discard). */
|
|
@@ -82607,6 +83013,11 @@ void main() {
|
|
|
82607
83013
|
aiGetScratchSlices() {
|
|
82608
83014
|
return this.drawCore.aiAssistTool.getScratchSlices();
|
|
82609
83015
|
}
|
|
83016
|
+
/** Per-segmentation serialization (one binary RLE per label per z-slice) for the
|
|
83017
|
+
* multi-label / per-colour 3D build. */
|
|
83018
|
+
aiGetScratchSegments() {
|
|
83019
|
+
return this.drawCore.aiAssistTool.getScratchSegments();
|
|
83020
|
+
}
|
|
82610
83021
|
/**
|
|
82611
83022
|
* Merge the AI scratch into a target layer as a single undoable group (sandbox
|
|
82612
83023
|
* merge — best-practice: non-destructive + one Ctrl+Z). The scratch's channel
|
|
@@ -83009,8 +83420,15 @@ void main() {
|
|
|
83009
83420
|
}
|
|
83010
83421
|
|
|
83011
83422
|
// import * as kiwrious from "copper3d_plugin_heart_k";
|
|
83012
|
-
|
|
83013
|
-
|
|
83423
|
+
// "v3.6.5" is injected at build time by rollup @rollup/plugin-replace, sourced from package.json version
|
|
83424
|
+
const REVISION = "v3.6.5";
|
|
83425
|
+
// Expose on global so the version can be read in a production browser console via window.__COPPER3D_VERSION__
|
|
83426
|
+
if (typeof window !== "undefined") {
|
|
83427
|
+
window.__COPPER3D_VERSION__ = REVISION;
|
|
83428
|
+
}
|
|
83429
|
+
if (typeof console !== "undefined") {
|
|
83430
|
+
console.log(`%cCopper3D Visualisation %c${REVISION}`, "padding: 3px;color:white; background:#023047", "padding: 3px;color:white; background:#f50a25");
|
|
83431
|
+
}
|
|
83014
83432
|
|
|
83015
83433
|
exports.AI_CHANNEL_HEX_COLORS = AI_CHANNEL_HEX_COLORS;
|
|
83016
83434
|
exports.AI_MASK_CHANNEL_COLORS = AI_MASK_CHANNEL_COLORS;
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,6 @@ import { CHANNEL_COLORS, CHANNEL_HEX_COLORS, AI_MASK_CHANNEL_COLORS, AI_CHANNEL_
|
|
|
28
28
|
import type { LayerId, ChannelValue } from "./Utils/segmentation/core/index";
|
|
29
29
|
import type { AiPromptTool, AiPromptPoint, AiPromptPayload, AiMaskResult } from "./Utils/segmentation/tools/AiAssistTool";
|
|
30
30
|
import "./css/style.css";
|
|
31
|
-
export declare const REVISION = "v3.6.
|
|
31
|
+
export declare const REVISION = "v3.6.5";
|
|
32
32
|
export { copperRenderer, copperRendererOnDemond, copperMSceneRenderer, setHDRFilePath, addLabelToScene, convert3DPostoScreenPos, convertScreenPosto3DPos, addBoxHelper, fullScreenListenner, configKiwriousHeart, copperScene, copperSceneOnDemond, copperMScene, CameraViewPoint, kiwrious, NrrdTools, loading, Copper3dTrackballControls, createTexture2D_NRRD, MeshNodeTool, throttle, removeGuiFolderChilden, CHANNEL_COLORS, CHANNEL_HEX_COLORS, AI_MASK_CHANNEL_COLORS, AI_CHANNEL_HEX_COLORS, rgbaToHex, rgbaToCss, GaussianSmoother, SurfaceAnnotator, };
|
|
33
33
|
export type { positionType, screenPosType, optsType, nrrdMeshesType, nrrdSliceType, SensorDecodedValue_kiwrious, SensorReadResult_kiwrious, HeartRateResult_kiwrious, loadingBarType, IPaintImage, exportPaintImageType, IOptVTKLoader, aligned4DSurfaceType, aligned4DOptsType, Aligned4DController, ICommXYZ, IGUIStates, IGuiParameterSettings, INrrdStates, NrrdState, GuiState, IGuiMeta, ToolMode, IAnnotationCallbacks, LayerId, ChannelValue, AiPromptTool, AiPromptPoint, AiPromptPayload, AiMaskResult, SurfaceAnnotatorOptions, Annotation, AnnotationMode, ExportOptions, };
|
package/dist/index.js
CHANGED
|
@@ -22,7 +22,14 @@ import { removeGuiFolderChilden } from "./Utils/segmentation/coreTools/gui";
|
|
|
22
22
|
import { SurfaceAnnotator } from "./Utils/surfaceAnnotation";
|
|
23
23
|
import { CHANNEL_COLORS, CHANNEL_HEX_COLORS, AI_MASK_CHANNEL_COLORS, AI_CHANNEL_HEX_COLORS, rgbaToHex, rgbaToCss } from "./Utils/segmentation/core/index";
|
|
24
24
|
import "./css/style.css";
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
// __REVISION__ is injected at build time by rollup @rollup/plugin-replace, sourced from package.json version
|
|
26
|
+
export const REVISION = __REVISION__;
|
|
27
|
+
// Expose on global so the version can be read in a production browser console via window.__COPPER3D_VERSION__
|
|
28
|
+
if (typeof window !== "undefined") {
|
|
29
|
+
window.__COPPER3D_VERSION__ = REVISION;
|
|
30
|
+
}
|
|
31
|
+
if (typeof console !== "undefined") {
|
|
32
|
+
console.log(`%cCopper3D Visualisation %c${REVISION}`, "padding: 3px;color:white; background:#023047", "padding: 3px;color:white; background:#f50a25");
|
|
33
|
+
}
|
|
27
34
|
export { copperRenderer, copperRendererOnDemond, copperMSceneRenderer, setHDRFilePath, addLabelToScene, convert3DPostoScreenPos, convertScreenPosto3DPos, addBoxHelper, fullScreenListenner, configKiwriousHeart, copperScene, copperSceneOnDemond, copperMScene, CameraViewPoint, kiwrious, NrrdTools, loading, Copper3dTrackballControls, createTexture2D_NRRD, MeshNodeTool, throttle, removeGuiFolderChilden, CHANNEL_COLORS, CHANNEL_HEX_COLORS, AI_MASK_CHANNEL_COLORS, AI_CHANNEL_HEX_COLORS, rgbaToHex, rgbaToCss, GaussianSmoother, SurfaceAnnotator, };
|
|
28
35
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EACL,eAAe,EAGf,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAY,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,QAAQ,MAAM,iCAAiC,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAC9E,iDAAiD;AAEjD,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAE5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AA0B7D,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAS1J,OAAO,iBAAiB,CAAC;AAEzB,MAAM,CAAC,MAAM,QAAQ,GAAG,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EACL,eAAe,EAGf,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAY,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,QAAQ,MAAM,iCAAiC,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAC9E,iDAAiD;AAEjD,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAE5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AA0B7D,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAS1J,OAAO,iBAAiB,CAAC;AAEzB,6GAA6G;AAC7G,MAAM,CAAC,MAAM,QAAQ,GAAG,YAAY,CAAC;AAErC,8GAA8G;AAC9G,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;IAChC,MAAc,CAAC,oBAAoB,GAAG,QAAQ,CAAC;CACjD;AAED,IAAI,OAAO,OAAO,KAAK,WAAW,EAAE;IAClC,OAAO,CAAC,GAAG,CACT,8BAA8B,QAAQ,EAAE,EACxC,8CAA8C,EAC9C,8CAA8C,CAC/C,CAAC;CACH;AAED,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,uBAAuB,EACvB,uBAAuB,EACvB,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,SAAS,EACT,OAAO,EACP,yBAAyB,EACzB,oBAAoB,EACpB,YAAY,EACZ,QAAQ,EACR,sBAAsB,EACtB,cAAc,EACd,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,gBAAgB,GACjB,CAAC"}
|