@sfgrp/sled
Version:
UI for image segmentation of slide collections
574 lines (573 loc) • 16 kB
JavaScript
import { computed as z, openBlock as L, createElementBlock as S, normalizeStyle as W, ref as k, onMounted as Z, onBeforeUnmount as _, createElementVNode as D, Fragment as q, renderList as I, createBlock as E, createVNode as G, createCommentVNode as T, withDirectives as X, toDisplayString as ue, vModelSelect as se, vModelCheckbox as oe, mergeModels as K, useModel as P, watch as H } from "vue";
const re = ["x1", "y1", "x2", "y2"], J = {
__name: "SvgLine",
props: {
x1: {
type: [Number, String],
required: !0
},
x2: {
type: [Number, String],
required: !0
},
y1: {
type: [Number, String],
required: !0
},
y2: {
type: [Number, String],
required: !0
},
scale: {
type: [Number, String],
required: !0
},
lineThickness: {
type: [Number, String]
}
},
setup(e) {
const M = e, u = z(() => M.y1 === M.y2), o = z(() => ({
stroke: "rgb(255,0,0)",
strokeWidth: M.lineThickness,
strokeLinecap: "round",
cursor: u.value ? "ns-resize" : "ew-resize"
}));
return (a, i) => (L(), S("line", {
x1: e.x1 / e.scale,
y1: e.y1 / e.scale,
x2: e.x2 / e.scale,
y2: e.y2 / e.scale,
style: W(o.value)
}, null, 12, re));
}
}, ce = ["cx", "cy"], Q = {
__name: "SvgCircle",
props: {
hLines: {
type: Array,
required: !0
},
vLines: {
type: Array,
required: !0
},
scale: {
type: Number,
default: 1
},
ix: {
type: Number,
default: 0
},
iy: {
type: Number,
default: 0
},
strokeColor: {
type: String,
default: "black"
}
},
emits: ["dragging"],
setup(e, { emit: M }) {
const u = e, o = M;
function a() {
o("dragging", [u.ix, u.iy]);
}
const i = z(
() => u.ix < 0 ? (0.7 * u.vLines[0] + 0.3 * u.vLines[u.vLines.length - 1]) / u.scale : u.vLines[u.ix] / u.scale
), v = z(
() => u.iy < 0 ? (0.7 * u.hLines[0] + 0.3 * u.hLines[u.hLines.length - 1]) / u.scale : u.hLines[u.iy] / u.scale
), s = z(() => ({
stroke: u.strokeColor,
strokeWidth: 2,
strokeOpacity: 0.7,
fillOpacity: 0,
zIndex: 3
}));
return (x, d) => (L(), S("circle", {
cx: i.value,
cy: v.value,
r: 10,
style: W(s.value),
onMousedown: a
}, null, 44, ce));
}
}, ve = ["width", "height"], de = ["width", "height", "xlink:href"], ge = {
__name: "SvgComponent",
props: {
imageData: {
type: String,
required: !0
},
imageWidth: {
type: Number,
required: !0
},
imageHeight: {
type: Number,
required: !0
},
scale: {
type: Number,
default: 1
},
hLines: {
type: Array,
required: !0
},
vLines: {
type: Array,
required: !0
},
lineThickness: {
type: Number
}
},
emits: [
"circleUL",
"circleLR",
"dragUL",
"dragLR",
"dragVline",
"dragHline"
],
setup(e, { emit: M }) {
const u = e, o = M;
k([0, 0, -1]), k([0, 0, -1]);
const a = k(!1), i = k(), v = k([]), s = k(), x = k();
function d() {
a.value = !1, s.value = void 0;
}
function g(b = 0) {
return Math.random().toString(16).substr(2, 8) + b;
}
function h({ clientX: b, clientY: r }) {
var m;
if (a.value) {
const c = (m = x.value) == null ? void 0 : m.getBoundingClientRect();
v.value;
const w = b - c.left - u.vLines[v.value[0]] / u.scale, V = r - c.top - u.hLines[v.value[1]] / u.scale;
s.value ? o(s.value, [w, V]) : v.value[0] >= 0 ? o("dragVline", [w, V, v.value[0]]) : o("dragHline", [w, V, v.value[1]]);
}
}
return Z(() => {
window.addEventListener("mouseup", d), window.addEventListener("mousemove", h);
}), _(() => {
window.removeEventListener("mouseup", d), window.removeEventListener("mousemove", h);
}), (b, r) => (L(), S("svg", {
ref_key: "rootRef",
ref: x,
width: e.imageWidth / e.scale,
height: e.imageHeight / e.scale,
style: { zIndex: 2, position: "absolute" }
}, [
D("image", {
xmlns: "http://www.w3.org/2000/svg",
"xmlns:xlink": "http://www.w3.org/1999/xlink",
width: e.imageWidth / e.scale,
height: e.imageHeight / e.scale,
"xlink:href": e.imageData
}, null, 8, de),
e.hLines.length > 1 && e.vLines.length > 1 ? (L(), S(q, { key: 0 }, [
(L(!0), S(q, null, I(e.hLines, (m, c) => (L(), E(J, {
key: g(c),
x1: e.vLines[0],
y1: e.hLines[c],
x2: e.vLines[e.vLines.length - 1],
y2: e.hLines[c],
scale: e.scale,
"line-thickness": e.lineThickness,
onMousedown: () => {
a.value = !0, v.value = [-1, c];
}
}, null, 8, ["x1", "y1", "x2", "y2", "scale", "line-thickness", "onMousedown"]))), 128)),
(L(!0), S(q, null, I(e.vLines, (m, c) => (L(), E(J, {
key: g(c),
x1: e.vLines[c],
y1: e.hLines[0],
x2: e.vLines[c],
y2: e.hLines[e.hLines.length - 1],
scale: e.scale,
"line-thickness": e.lineThickness,
index: c,
dragging: a.value,
onMousedown: () => {
a.value = !0, v.value = [c, -1];
},
onMousemove: r[0] || (r[0] = (w) => i.value = w)
}, null, 8, ["x1", "y1", "x2", "y2", "scale", "line-thickness", "index", "dragging", "onMousedown"]))), 128)),
G(Q, {
ix: 0,
iy: 0,
"h-lines": e.hLines,
"v-lines": e.vLines,
scale: e.scale,
"stroke-color": "red",
style: { cursor: "move" },
onDragging: r[1] || (r[1] = (m) => {
a.value = !0, s.value = "dragUL", v.value = m;
})
}, null, 8, ["h-lines", "v-lines", "scale"]),
G(Q, {
ix: e.vLines.length - 1,
iy: e.hLines.length - 1,
"h-lines": e.hLines,
"v-lines": e.vLines,
scale: e.scale,
"stroke-color": "red",
style: { cursor: "nwse-resize" },
onDragging: r[2] || (r[2] = (m) => {
a.value = !0, s.value = "dragLR", v.value = m;
})
}, null, 8, ["ix", "iy", "h-lines", "v-lines", "scale"])
], 64)) : T("", !0)
], 8, ve));
}
}, he = ["innerHTML"], me = ["disabled"], ye = /* @__PURE__ */ D("option", { value: "none" }, "None", -1), fe = ["value"], Le = ["disabled"], R = 5, xe = {
__name: "Cell",
props: {
modelValue: {
type: Object,
required: !0
},
metadata: {
type: Object,
default: void 0
},
scale: {
type: Number,
default: 1
},
locked: {
type: Boolean,
default: !1
}
},
emits: ["update:modelValue", "onChange"],
setup(e, { emit: M }) {
const u = {
position: "absolute",
right: "10px",
bottom: "10px"
}, o = {
position: "absolute",
top: "4px",
backgroundColor: "#FFF",
borderRadius: "3px",
padding: "4px",
opacity: 0.9
}, a = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)"
}, i = e, v = M, s = z({
get() {
return i.modelValue;
},
set(g) {
v("update:modelValue", g);
}
}), x = z({
get() {
return !s.value.metadata;
},
set(g) {
s.value.metadata = g ? null : s.value.metadata || "none";
}
}), d = z(() => {
const { upperCorner: g, lowerCorner: h } = s.value;
return {
position: "absolute",
top: `${g.y / i.scale + R}px`,
left: `${g.x / i.scale + R}px`,
width: `${(h.x - g.x) / i.scale - R * 2}px`,
height: `${(h.y - g.y) / i.scale - R * 2}px`,
zIndex: 2
};
});
return (g, h) => {
var b;
return L(), S("div", {
style: W(d.value)
}, [
(b = s.value) != null && b.textfield ? (L(), S("span", {
key: 0,
class: "cell-textfield",
style: o,
innerHTML: s.value.textfield
}, null, 8, he)) : T("", !0),
x.value ? T("", !0) : X((L(), S("select", {
key: 1,
class: "cell-select",
"onUpdate:modelValue": h[0] || (h[0] = (r) => s.value.metadata = r),
disabled: e.locked,
style: a,
onChange: h[1] || (h[1] = (r) => v("onChange", s.value))
}, [
ye,
(L(!0), S(q, null, I(e.metadata, (r, m) => (L(), S("option", {
key: m,
value: m
}, ue(r), 9, fe))), 128))
], 40, me)), [
[se, s.value.metadata]
]),
X(D("input", {
type: "checkbox",
class: "cell-checkbox",
disabled: e.locked,
style: u,
"onUpdate:modelValue": h[2] || (h[2] = (r) => x.value = r)
}, null, 8, Le), [
[oe, x.value]
])
], 4);
};
}
}, be = /* @__PURE__ */ Object.assign({
name: "Sled"
}, {
__name: "Sled",
props: /* @__PURE__ */ K({
metadataAssignment: {
type: Object,
default: () => ({})
},
imageWidth: {
type: Number,
required: !0
},
imageHeight: {
type: Number,
required: !0
},
fileImage: {
type: String
},
lineWeight: {
type: [Number, String],
default: 4
},
autosize: {
type: Boolean,
default: !0
},
locked: {
type: Boolean,
default: !1
}
}, {
verticalLines: {
type: Array,
required: !0
},
verticalLinesModifiers: {},
horizontalLines: {
type: Array,
required: !0
},
horizontalLinesModifiers: {}
}),
emits: /* @__PURE__ */ K(["onComputeCells", "resize"], ["update:verticalLines", "update:horizontalLines"]),
setup(e, { expose: M, emit: u }) {
const o = e, a = P(e, "verticalLines"), i = P(e, "horizontalLines"), v = u, s = k(0), x = k(0), d = k([]), g = k(0), h = k(0), b = k(void 0), r = k(1), m = k(null), c = z(() => i.value.length - 1), w = z(() => a.value.length - 1);
H(
[i, a],
() => {
const l = F(i.value), n = F(a.value);
l.isSorted || (i.value = l.arr), n.isSorted || (a.value = n.arr), te();
},
{
deep: !0,
immediate: !0
}
), H(
() => o.imageHeight,
(l) => {
x.value = l, B();
},
{ immediate: !0 }
), H(
() => o.imageWidth,
(l) => {
s.value = l, B();
},
{ immediate: !0 }
), H(
() => o.fileImage,
(l) => {
g.value = s.value, h.value = x.value, B();
}
), H(
() => o.autosize,
(l) => {
l ? (b.value = new ResizeObserver(Y), b.value.observe(m.value)) : b.value.disconnect(), r.value = j();
}
), Z(() => {
o.autosize && (b.value = new ResizeObserver(Y), b.value.observe(m.value));
}), _(() => {
b.value.disconnect();
});
function V(l, n) {
d.value[l] = n, v("onComputeCells", d.value);
}
function p(l, n) {
const [t] = l;
return t + n < 0;
}
function U(l) {
if (!p(a.value, l))
for (let n = 0; n < a.value.length; n++)
$(n, l);
}
function A(l) {
if (!p(i.value, l))
for (let n = 0; n < i.value.length; n++)
O(n, l);
}
function $(l, n) {
const t = Math.round(a.value[l] + n);
t < 0 || t > o.imageWidth || (a.value[l] = t);
}
function O(l, n) {
const t = Math.round(i.value[l] + n);
t < 0 || t > o.imageHeight || (i.value[l] = t);
}
function F(l) {
const n = l.toSorted((t, y) => t - y);
return {
isSorted: n.every((t, y) => l[y] === t),
arr: n
};
}
function B() {
if (g.value > 1) {
const l = s.value / g.value, n = a.value.length;
let t = 0;
for (t = 0; t < n; t++)
a.value[t] = Math.round(a.value[t] * l);
}
if (h.value > 1) {
const l = x.value / h.value, n = i.value.length;
let t = 0;
for (t = 0; t < n; t++)
i.value[t] = Math.round(i.value[t] * l);
}
g.value = s.value, h.value = x.value;
}
function ee() {
if (i.value.length > 1 && a.value.length > 1) {
d.value = [];
const l = (i.value[c.value] - i.value[0]) / c.value, n = (a.value[w.value] - a.value[0]) / w.value;
for (let t = 0; t < c.value; t++)
i.value[t] = Math.round(i.value[0] + t * l);
for (let t = 0; t < w.value; t++)
a.value[t] = Math.round(a.value[0] + t * n);
}
}
function te() {
if (i.value.length > 0 && a.value.length > 0) {
let l, n, t = -1;
for (let y = 0; y < c.value; y++)
for (let C = 0; C < w.value; C++) {
t = w.value * y + C, l = { x: a.value[C], y: i.value[y] }, n = { x: a.value[C + 1], y: i.value[y + 1] };
const { metadata: f = null, textfield: N } = d.value[t] || {};
d.value[t] = {
index: t,
upperCorner: l,
lowerCorner: n,
row: y,
column: C,
metadata: f,
textfield: N
};
}
d.value = t === -1 ? [] : d.value.slice(0, t + 1), v("onComputeCells", d.value);
}
}
function le(l) {
const [n, t] = l, y = a.value.at(-1), C = i.value.at(-1);
if (!(y + n > o.imageWidth || C + t > o.imageHeight)) {
for (let f = 1; f < a.value.length; f++) {
const N = Math.round(a.value[f] + f * n / w.value);
N > 0 && N <= o.imageWidth && (a.value[f] = Math.round(a.value[f] + f * n / w.value));
}
for (let f = 1; f < i.value.length; f++) {
const N = Math.round(i.value[f] + f * t / c.value);
N > 0 && N <= o.imageHeight && (i.value[f] = Math.round(i.value[f] + f * t / c.value));
}
}
}
function ne(l) {
const [n, t] = l;
U(n), A(t);
}
function ae(l) {
const n = l[1], t = l[2];
O(t, n);
}
function ie(l) {
const n = l[0], t = l[2];
$(t, n);
}
function j() {
if (o.autosize) {
const l = m.value.getBoundingClientRect(), n = l.height < x.value ? x.value / l.height : 1, t = l.width < s.value ? s.value / l.width : 1;
return n > t ? n : t;
} else
return 1;
}
function Y(l, n) {
const t = m.value.getBoundingClientRect();
r.value = j(), v("resize", {
...t,
scale: r.value
});
}
return M({
moveX: U,
moveY: A,
moveH: O,
moveV: $,
equalizeLines: ee
}), (l, n) => (L(), S("div", {
ref_key: "rootRef",
ref: m,
style: W({
display: "block",
position: "relative",
height: `${x.value}px`
})
}, [
e.fileImage ? (L(), E(ge, {
key: 0,
"image-width": s.value,
"image-height": x.value,
"image-data": e.fileImage,
"h-lines": i.value,
"v-lines": a.value,
scale: r.value,
"line-thickness": e.lineWeight,
onDragUL: ne,
onDragLR: le,
onDragHline: ae,
onDragVline: ie
}, null, 8, ["image-width", "image-height", "image-data", "h-lines", "v-lines", "scale", "line-thickness"])) : T("", !0),
(L(!0), S(q, null, I(d.value, (t, y) => (L(), E(xe, {
key: y,
locked: e.locked,
metadata: e.metadataAssignment,
scale: r.value,
modelValue: d.value[y],
"onUpdate:modelValue": (C) => d.value[y] = C,
onOnChange: (C) => V(y, C)
}, null, 8, ["locked", "metadata", "scale", "modelValue", "onUpdate:modelValue", "onOnChange"]))), 128))
], 4));
}
});
export {
be as default
};