videx-3d
Version:
React 3D component library designed for sub surface visualizations in the browser
1,513 lines (1,434 loc) • 234 kB
JavaScript
(function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode("svg.well-map g.tracks g.track:hover{cursor:help}svg.well-map g.tracks g.track.interactive:hover{cursor:pointer;filter:brightness(1.1)}svg.well-map g.tracks g.track.interactive.selected:hover{cursor:pointer;filter:none}")),document.head.appendChild(e)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}})();
import * as K from "three";
import { OrthographicCamera as fi, BufferGeometry as di, Float32BufferAttribute as Xt, RawShaderMaterial as pi, Uniform as z, Mesh as ht, MeshBasicMaterial as Ht, Vector3 as X, Vector2 as de, createCanvasElement as hi, WebGLRenderTarget as xt, NoColorSpace as Dn, NearestFilter as Ze, UnsignedByteType as mi, RedFormat as En, ShaderMaterial as Fe, GLSL3 as vi, Matrix4 as et, CanvasTexture as Ln, DataTexture as kn, RGBAFormat as yt, FloatType as Nt, DepthTexture as gi, HalfFloatType as In, Layers as Wn, DoubleSide as xe, NoBlending as xi, Color as ee, Ray as yi, Plane as bi, BackSide as Gn, FrontSide as mt, LinearFilter as Ie, Box3 as Fn, AdditiveBlending as On, InstancedMesh as Ft, Line as Un, SRGBColorSpace as _i, UniformsUtils as We, Matrix3 as wi, ShaderLib as jt, MultiplyOperation as Ti, TangentSpaceNormalMap as Mi, InstancedBufferAttribute as Ai, LineBasicMaterial as Pi, MeshStandardMaterial as fe, MeshLambertMaterial as Ci, CylinderGeometry as Si, ConeGeometry as Bn, Sphere as Ri, InstancedInterleavedBuffer as zi, InstancedBufferGeometry as Di, BufferAttribute as Qt, InterleavedBufferAttribute as at } from "three";
import { jsx as w, jsxs as j, Fragment as He } from "react/jsx-runtime";
import { useThree as Ge, useFrame as Pe, extend as Hn } from "@react-three/fiber";
import { createRef as Ei, useMemo as A, forwardRef as ye, useCallback as re, useEffect as R, useLayoutEffect as Nn, useRef as N, createContext as Ce, useContext as ie, useState as F, startTransition as Jt, Fragment as Vt, useImperativeHandle as Oe } from "react";
import { createRoot as Li } from "react-dom/client";
import { create as bt, useStore as ki } from "zustand";
import { subscribeWithSelector as Ii } from "zustand/middleware";
import { createEndpoint as $t, transfer as Wi, proxy as Gi } from "comlink";
import "p-limit";
import "curve-interpolator";
import { n as Fi, s as Oi, P as Je, a as Qe, b as Zt, c as Ui, d as pe, m as jn, e as Bi, f as Hi, g as Ni, h as Vn, i as $n, u as Re, W as ji, j as Vi, k as en, l as tn, o as $i, p as Yi } from "./chunk-iY0wQ9Z6.js";
import { C as Ga, q as Fa, r as Oa, w as Ua, t as Ba, v as Ha } from "./chunk-iY0wQ9Z6.js";
import "three/src/math/MathUtils.js";
import "proj4";
import Yn from "rbush";
import { Text as nn, useTexture as qi } from "@react-three/drei";
import { range as Ki, group as on } from "d3-array";
import { axisLeft as rn } from "d3-axis";
import { format as Xi } from "d3-format";
import { scaleLinear as Ue } from "d3-scale";
import { select as Ot, pointer as sn } from "d3-selection";
import Qi from "react-use-measure";
import { path as tt } from "d3-path";
import { drag as Ji } from "d3-drag";
import { c as qn, t as an, a as Zi, g as eo, C as to, T as Yt, b as no } from "./chunk-CnY6Tmof.js";
import { nanoid as vt } from "nanoid";
import { piecewise as io, interpolateHcl as oo } from "d3-interpolate";
import { c as ro, a as so } from "./chunk-BX-cez1_.js";
import { b as ja, d as Va } from "./chunk-BX-cez1_.js";
import ao from "p-queue";
const he = bt(
Ii((n, t) => ({
visible: !0,
update: {
required: !1,
ref: null,
setRef: (e) => n((i) => ({ update: { ...i.update, ref: e } }))
},
layers: {},
annotations: {},
instances: [],
clear: () => {
n({
layers: {},
annotations: {},
instances: []
});
},
setInstances: (e) => n({ instances: e }),
layerExist: (e) => !!t().layers[e],
toggleVisibility: () => {
n((e) => ({
visible: !e.visible,
update: { ...e.update, required: !0 }
}));
},
createLayer: (e) => {
const i = e.id;
return n((o) => {
const r = o.layers;
if (r[i])
throw Error("Layer already exist!");
return { layers: { ...r, [i]: e } };
}), () => {
n((o) => {
const r = { ...o.layers };
return delete r[i], {
layers: r,
update: { ...o.update, required: !0 }
};
});
};
},
updateLayer: (e, i) => {
n((o) => {
const r = o.layers;
if (!r[e])
throw Error("Layer does not exist!");
return {
layers: { ...r, [e]: { ...r[e], ...i } },
update: { ...o.update, required: !0 }
};
});
},
addLayerAnnotations: (e, i, o) => {
n((r) => {
o.forEach((s) => {
s.scope = i;
});
const a = r.annotations, l = a[e] ? a[e].filter((s) => s.scope !== i) : [];
return {
annotations: {
...a,
[e]: [...l, ...o]
},
update: { ...r.update, required: !0 }
};
});
},
removeLayerAnnotations: (e, i) => {
n((o) => {
const r = o.annotations, a = r[e] ? r[e].filter((l) => l.scope !== i) : [];
return {
annotations: {
...r,
[e]: a
},
update: { ...o.update, required: !0 }
};
});
}
}))
), lo = () => {
he.setState((n) => {
if (!n.visible)
return {
instances: [],
update: { ...n.update, required: !1, ref: null }
};
const t = n.layers, e = new Map(n.instances.map((a) => [a.id, a])), i = [], o = [0, 0];
Object.keys(t).forEach((a) => {
const l = t[a];
l.visible && n.annotations[a]?.forEach((c) => {
const s = `${a}_${c.scope}_${c.id}`;
let f;
const d = e.get(s);
d ? (f = d, f.annotation.name = c.name, f.annotation.data = c.data, f.annotation.direction = c.direction, f.annotation.position = c.position, f.annotation.matrixWorld = c.matrixWorld, f.annotation.priority = c.priority) : f = {
id: s,
ref: l.labelComponent ? Ei() : null,
layer: l,
annotation: c,
priority: 0,
rank: 0,
state: {
visible: !1,
position: c.position,
distance: 1 / 0,
health: 0,
labelWidht: 0,
labelHeight: 0,
screenPosition: [0, 0],
zIndex: 0
}
}, f.priority = (c.priority || 0) + (l.priority || 0), o[0] = Math.min(o[0], f.priority), o[1] = Math.max(o[1], f.priority), i.push(f);
});
});
const r = Math.abs(o[1] - o[0]);
return i.forEach((a) => {
a.priority = r > 0 ? (a.priority - o[0]) / r : 0;
}), {
instances: i,
update: { ...n.update, required: !1, ref: null }
};
});
};
he.subscribe(
(n) => n.update.required,
(n) => {
if (n) {
const t = he.getState().update;
t.ref && clearTimeout(t.ref);
const e = setTimeout(
() => lo(),
500
);
t.setRef(e);
}
}
);
const Ne = (n, t) => {
const e = he((r) => r.addLayerAnnotations), i = he((r) => r.removeLayerAnnotations);
return A(() => ({
addAnnotations: (r) => {
if (r.length)
return e(n, t, r), () => {
i(n, t);
};
}
}), [e, i, n, t]);
}, co = {
pointerEvents: "none",
width: "100%",
height: "100%"
}, uo = {
position: "absolute",
top: 0,
left: 0,
visibility: "hidden",
userSelect: "none",
cursor: "pointer",
pointerEvents: "visible"
}, fo = ye(
({ id: n, state: t, layer: e, annotation: i }, o) => {
const r = re(() => {
e.onClick && e.onClick({ instanceId: n, ...i });
}, [i, n, e]), a = re(() => {
t.labelHovered = !0;
}, [t]), l = re(() => {
t.labelHovered = !1;
}, [t]);
return /* @__PURE__ */ w(
"div",
{
ref: o,
style: uo,
onClick: r,
onPointerEnter: a,
onPointerLeave: l,
children: e.labelComponent && /* @__PURE__ */ w(e.labelComponent, { instanceId: n, ...i })
}
);
}
), po = () => {
const n = ki(he, (t) => t.instances);
return /* @__PURE__ */ w("div", { style: co, children: n.map((t) => /* @__PURE__ */ w(
fo,
{
ref: t.ref,
id: t.id,
state: t.state,
layer: t.layer,
annotation: t.annotation
},
t.id
)) });
};
var ho = `precision highp float;
uniform float opacity;
uniform sampler2D source;
varying vec2 vUv;
void main() {
vec4 texel = texture2D(source, vUv);
gl_FragColor = opacity * texel;
}`, mo = `attribute vec2 uv;
attribute vec3 position;
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = vec4(position, 1.0);
}`;
class Kn {
camera;
mesh;
copyMaterial;
constructor() {
const t = new fi(-1, 1, 1, -1, 0, 1), e = new di();
e.setAttribute(
"position",
new Xt([-1, 3, 0, -1, -1, 0, 3, -1, 0], 3)
), e.setAttribute(
"uv",
new Xt([0, 2, 0, 0, 2, 0], 2)
);
const i = new pi({
uniforms: {
opacity: new z(1),
source: new z(null)
},
vertexShader: mo,
fragmentShader: ho,
transparent: !0,
depthTest: !1,
depthWrite: !1
});
this.camera = t, this.mesh = new ht(e, i), this.copyMaterial = i;
}
renderMaterial(t, e, i) {
this.mesh.material = i, t.setRenderTarget(e), t.render(this.mesh, this.camera);
}
renderTexture(t, e, i, o = 1) {
this.copyMaterial.uniforms.source.value = i, this.copyMaterial.uniforms.opacity.value = o, this.renderMaterial(t, e, this.copyMaterial);
}
}
const vo = new Kn();
class Xn {
writeToScreen = !1;
}
class go extends Xn {
scene;
camera;
constructor(t, e) {
super(), this.scene = t, this.camera = e;
}
render(t, e) {
t.setRenderTarget(this.writeToScreen ? null : e), t.clear(), t.render(this.scene, this.camera);
}
}
class xo extends Xn {
material = new Ht({
transparent: !0,
depthTest: !1,
depthWrite: !1,
toneMapped: !0,
premultipliedAlpha: !0
});
constructor() {
super(), this.writeToScreen = !0;
}
render(t, e) {
this.material.map = e.texture, vo.renderMaterial(
t,
this.writeToScreen ? null : e,
this.material
);
}
}
var yo = `uniform float logDepthBufFC;
uniform sampler2D depthTexture;
uniform sampler2D dataTexture;
uniform mat4 pMatrix;
uniform mat4 vMatrix;
varying vec2 vUv;
layout(location = 0) out float value;
const float epsilon = 1e-8;
const vec4 viewSpace = vec4(-0.99, 0.99, -0.99, 0.99);
bool isPerspectiveMatrix(mat4 m) {
return m[2][3] == -1.0;
}
void main() {
vec4 instance = texture2D(dataTexture, vUv);
vec3 position = instance.xyz;
vec4 clipSpace = pMatrix * vMatrix * vec4(position, 1.0);
vec3 ndc = clipSpace.xyz / clipSpace.w;
bool inViewSpace = (ndc.z >= 0. &&
ndc.z <= 1. &&
ndc.x >= viewSpace.x &&
ndc.x <= viewSpace.y &&
ndc.y >= viewSpace.z &&
ndc.y <= viewSpace.w);
float offsetRadius = instance.w;
vec3 offsetDirection = normalize(cameraPosition - position);
vec3 offsetPosition = position + offsetDirection * offsetRadius;
vec4 offsetClip = pMatrix * vMatrix * vec4(offsetPosition, 1.0);
vec3 offsetNdc = offsetClip.xyz / offsetClip.w;
float pointDepth = offsetNdc.z * 0.5 + 0.5;
#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) || defined( USE_LOGDEPTHBUF )
pointDepth = isPerspectiveMatrix(pMatrix) ? log2(1.0 + offsetClip.w) * logDepthBufFC * 0.5 : pointDepth;
#endif
vec2 uvDepth = offsetNdc.xy * 0.5 + 0.5;
float sceneDepth = texture2D(depthTexture, uvDepth).r;
value = 0.0;
if((sceneDepth + epsilon) < pointDepth)
value += 1.0 / 255.0;
if(inViewSpace)
value += 2.0 / 255.0;
}`, bo = `varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = vec4(position, 1.0);
}`;
const lt = new X(), _o = [
/* 0 */
0,
/* 1 */
Qe,
/* 2 */
Zt,
/* 3 */
Je - Qe,
/* 4 */
Je,
/* 5 */
-Je + Qe,
/* 6 */
-Zt,
/* 7 */
-Qe
], wo = [
[2, 6],
[7, 3],
[0, 4],
[1, 5]
], To = (n, t, e, i) => {
if (i) {
lt.set(
t[0] + e[0] * 100,
t[1] + e[1] * 100,
t[2] + e[2] * 100
), lt.project(i);
const o = Fi(
Oi([lt.x, lt.y], [n[0], n[1]])
);
let r = Math.atan2(o[1], o[0]);
return isNaN(r) ? 0 : (r < 0 && (r = Je + r), Math.floor((r + Ui) / Qe) % 4);
}
return 0;
}, Ut = 1, ln = Ut * 2, cn = 3, Mo = 5, qe = new X();
let un = 0, zt, Dt, Et, Lt, kt;
const fn = new Yn(), dn = new Yn();
function pn(n, t, e) {
if (!n || !e) return;
const i = n.state.scaleFactor, o = wo[n.state.quadrant], r = _o[o[t || 0]], a = n.state.labelWidht || 0, l = n.state.labelHeight || 0, c = a * i, s = l * i, f = Bi([c, s], r), d = [Math.cos(r), -Math.sin(r)], p = n.layer.labelOffset * i, v = [
(n.state.screenPosition[0] * 0.5 + 0.5) * e[0] + d[0] * p,
(-n.state.screenPosition[1] * 0.5 + 0.5) * e[1] + d[1] * p
];
n.state.anchorPosition = v, n.state.scaledOffset = [
(a - c) / 2,
(l - s) / 2
], n.state.labelPosition = [
v[0] - c / 2 + f[0],
v[1] - s / 2 + f[1]
];
}
function hn(n, t, e, i) {
n.state.visible && (n.state.positionSlot !== t || n.state.prevQuadrant && n.state.prevQuadrant !== n.state.quadrant) && e && (n.state.inTransition = !0, n.state.transitionTime = 0, n.state.prevLabelPosition = e, i && (n.state.prevAnchorPosition = i));
}
function Ao(n, t, e, i) {
const o = e.elapsedTime - un, r = t.fov * Je / 360;
let a = 0;
const l = [];
let c = !1;
return n.forEach((s) => {
const f = s.state.position, d = ko(s.annotation);
if ((d[0] !== f[0] || d[1] !== f[1] || d[2] !== f[2]) && (c = !0, s.state.position = d), s.state.capped = !1, s.state._needsUpdate = !1, s.state.visible || (s.state.health = 0, s.state.prevAnchorPosition = void 0, s.state.prevLabelPosition = void 0), s.state.kill ? s.state.health === 0 ? (s.state.kill = !1, s.state.visible = !1) : s.state.health > 0 && (s.state.health = Math.max(
0,
s.state.health - o * cn
)) : s.state.health < 1 && (s.state.health = Math.min(
1,
Math.max(0, s.state.health + o * cn)
)), s.state.inTransition && (s.state.transitionTime += o * Mo, s.state.transitionTime >= 1 && (s.state.inTransition = !1, s.state.transitionTime = 0, s.state.prevAnchorPosition = void 0, s.state.prevLabelPosition = void 0)), s.state.inViewSpace) {
if (qe.set(...s.state.position), s.state.distance = qe.distanceTo(t.position), s.state.scaleFactor = Math.max(
0.25,
Math.min(
1,
1 / (2 * Math.tan(r) * s.state.distance) * s.layer.distanceFactor
)
), qe.project(t), s.state.screenPosition = [qe.x, qe.y], (!s.layer.minDistance || s.state.distance >= s.layer.minDistance) && (!s.layer.maxDistance || s.state.distance <= s.layer.maxDistance) && a < i) {
a++;
const v = pe(
(s.state.screenPosition[0] ** 2 + s.state.screenPosition[1] ** 2) / 2,
0,
1
), m = Math.min(s.state.distance, 1e3);
s.rank = 1e3, s.rank += s.priority * 1e3 - (v * 100 + m), s.state.visible ? s.rank += 100 : s.rank -= 100, s.state.boost && (s.state.kill = !1, s.state.cooldown = 0, s.state.visible = !0, s.rank += 1e5, s.state.positionSlot = 0, s.state.boost = !1), s.state.prevQuadrant = s.state.quadrant, s.state.quadrant = s.annotation.direction ? To(
s.state.screenPosition,
s.state.position,
s.annotation.direction,
t
) : 0;
} else
s.state.capped = !0;
s.state.capped || l.push(s);
} else
s.state.quadrant = 0, s.state.visible = !1;
s.state.cooldown && s.state.visible === !1 && (s.state.cooldown = Math.max(
0,
s.state.cooldown - o
), s.rank = 0), s.state._visibility !== "hidden" && (!s.state.inViewSpace || s.state.capped) && (s.state.visible = !1, s.state._visibility = "hidden", s.state._needsUpdate = !0);
}), un = e.elapsedTime, l.sort((s, f) => f.rank - s.rank), c && dispatchEvent(new CustomEvent("annotations-position-changed")), l;
}
function Po(n, t) {
fn.clear(), dn.clear(), n.forEach((e) => {
const i = e.state.labelPosition ? [...e.state.labelPosition] : null, o = e.state.anchorPosition ? [...e.state.anchorPosition] : null, r = e.state.positionSlot || 0, a = e.ref?.current;
if (a && (e.state.labelWidht = a.clientWidth, e.state.labelHeight = a.clientHeight), e.state.kill || e.state.occluded)
pn(e, r, t), hn(
e,
r,
i,
o
);
else if (e.state.cooldown)
e.state.visible = !1;
else {
let l = !1;
const c = r === 0 ? [0, 1] : [1, 0], s = e.state.labelWidht, f = e.state.labelHeight, d = s * e.state.scaleFactor, p = f * e.state.scaleFactor;
for (let v = 0; v < c.length; v++) {
pn(e, c[v], t);
const m = {
minX: e.state.labelPosition[0] - Ut,
minY: e.state.labelPosition[1] - Ut,
maxX: e.state.labelPosition[0] + d + ln,
maxY: e.state.labelPosition[1] + p + ln
}, g = e.state.scaleFactor >= 0.5 ? fn : dn;
if (!g.collides(m)) {
g.insert(m), l = !0, hn(
e,
c[v],
i,
o
), e.state.positionSlot = c[v];
break;
}
}
l ? e.state.visible = !0 : (e.state.kill = !0, e.state.cooldown = 2.5);
}
e.state.visible ? (e.state.zIndex = e.state.labelHovered ? 1e6 : e.state.kill ? 0 : Math.round(1 / e.state.distance * 1e5), e.state.opacity = Math.max(0.75, e.state.scaleFactor) * e.state.health, e.state.inTransition && e.state.prevLabelPosition ? [Lt, kt] = jn(
e.state.prevLabelPosition,
e.state.labelPosition,
e.state.transitionTime
) : [Lt, kt] = e.state.labelPosition, e.state.labelX = Lt - e.state.scaledOffset[0], e.state.labelY = kt - e.state.scaledOffset[1], a && (zt = `translate(${e.state.labelX}px,${e.state.labelY}px) scale(${e.state.scaleFactor})`, zt !== e.state._transform && (e.state._transform = zt, e.state._needsUpdate = !0), Dt = `${e.state.opacity}`, Dt !== e.state._opacity && (e.state._opacity = Dt, e.state._needsUpdate = !0), Et = `${e.state.zIndex}`, Et !== e.state._zIndex && (e.state._zIndex = Et, e.state._needsUpdate = !0), e.state._visibility !== "visible" && (e.state._visibility = "visible", e.state._needsUpdate = !0))) : e.state._visibility !== "hidden" && (e.state._visibility = "hidden", e.state._needsUpdate = !0);
});
}
function Co(n) {
n.filter((t) => t.state._needsUpdate).forEach((t) => {
const e = t.ref?.current;
e && (t.state._transform && (e.style.transform = t.state._transform), t.state._opacity && (e.style.opacity = t.state._opacity), t.state._zIndex && (e.style.zIndex = t.state._zIndex), t.state._visibility && (e.style.visibility = t.state._visibility));
});
}
const ce = new de(), So = Math.PI * 2;
let ct, ut, It, Wt;
class Ro {
maxVisible;
camera;
clock;
pointer;
ctx;
overlayTexture = null;
annotationsTexSize = 100;
annotationsRenderTarget;
annotationsBuffer;
annotationsMaterial;
fullscreenRenderer = new Kn();
annotationsData = [];
isBusy = !1;
dataTextureNeedsUpdate = !1;
unsubscribeListeners;
constructor(t, e, i, o = 100) {
this.camera = t, this.clock = e, this.pointer = i, this.maxVisible = o;
const r = hi().getContext("2d");
if (!r) throw Error("Unable to create canvas drawing context!");
this.ctx = r, this.annotationsRenderTarget = new xt(
this.annotationsTexSize,
1,
{
depthBuffer: !1,
stencilBuffer: !1,
format: En,
type: mi,
magFilter: Ze,
minFilter: Ze,
samples: 0,
colorSpace: Dn,
generateMipmaps: !1
}
), this.annotationsBuffer = new Uint8Array(this.annotationsTexSize);
const l = 2 / (Math.log(t.far + 1) / Math.LN2), c = new Fe({
vertexShader: bo,
fragmentShader: yo,
uniforms: {
cameraPosition: new z(new X()),
vMatrix: new z(new et()),
pMatrix: new z(new et()),
depthTexture: new z(null),
dataTexture: new z(null),
logDepthBufFC: new z(l)
},
glslVersion: vi
});
this.annotationsMaterial = c;
const s = he.subscribe(
(d) => d.instances,
(d) => {
this.annotationsData = d, this.dataTextureNeedsUpdate = !0;
}
), f = () => {
this.dataTextureNeedsUpdate = !0;
};
addEventListener("annotations-position-changed", f), this.unsubscribeListeners = () => {
s(), removeEventListener("annotations-position-changed", f);
};
}
updateAnnotationsData(t) {
const { annotationsData: e } = this;
e.forEach((i, o) => {
if (o < t.length) {
const r = (t[o] & 1) == 1, a = (t[o] & 2) == 2;
i.state.inViewSpace = a, !i.state.occluded && r && (i.state.kill = !0), i.state.occluded = r;
}
});
}
updateOverlayTexture(t) {
const { ctx: e, pointer: i } = this;
(this.overlayTexture === null || this.overlayTexture.image.width !== ce.x || this.overlayTexture.image.height !== ce.y) && (e.canvas.width = ce.x, e.canvas.height = ce.y, this.overlayTexture?.dispose(), this.overlayTexture = new Ln(e.canvas));
const o = [
(i.x * 0.5 + 0.5) * ce.x,
(-i.y * 0.5 + 0.5) * ce.y
];
e.clearRect(0, 0, ce.x, ce.y), t.filter((r) => !r.state.occluded && !r.state.capped).sort((r, a) => a.state.distance - r.state.distance).forEach((r) => {
ct = (r.state.screenPosition[0] * 0.5 + 0.5) * ce.x, ut = (-r.state.screenPosition[1] * 0.5 + 0.5) * ce.y;
let a = r.layer.anchorSize * r.state.scaleFactor, l = !1;
if (Math.abs(o[0] - ct) <= a && Math.abs(o[1] - ut) <= a && (r.state.visible ? l = !0 : r.state.boost = !0), (r.state.labelHovered || l) && (a *= 1.5), r.layer.labelOffset > 0 && r.state.visible) {
r.state.inTransition && r.state.prevAnchorPosition ? [It, Wt] = jn(
r.state.prevAnchorPosition,
r.state.anchorPosition,
r.state.transitionTime
) : [It, Wt] = r.state.anchorPosition;
let c = Math.max(
0.1,
r.layer.connectorWidth * r.state.scaleFactor
);
(r.state.labelHovered || l) && (c *= 2), e.globalAlpha = r.state.opacity || 0, e.beginPath(), e.moveTo(ct, ut), e.lineTo(It, Wt), e.strokeStyle = r.layer.connectorColor, e.lineWidth = c, e.stroke();
}
e.beginPath(), e.arc(ct, ut, a, 0, So), e.globalAlpha = r.state.visible ? 1 : 0.5, e.fillStyle = r.layer.anchorColor, e.fill(), e.globalAlpha = r.state.opacity || 0, e.strokeStyle = "black", e.lineWidth = 0.75, e.stroke();
}), this.overlayTexture.needsUpdate = !0;
}
updateDataTexture() {
const t = this.annotationsData;
let e = this.annotationsMaterial.uniforms.dataTexture.value;
if (t.length) {
const i = t.length;
i !== this.annotationsTexSize && (this.annotationsRenderTarget.setSize(i, 1, 0), this.annotationsBuffer = new Uint8Array(i * 2), this.annotationsTexSize = i, e?.dispose(), e = void 0);
const o = new Float32Array(i * 4);
for (let r = 0, a = 0; r < t.length; r++, a += 4) {
const l = t[r];
o[a] = l.state.position[0], o[a + 1] = l.state.position[1], o[a + 2] = l.state.position[2], o[a + 3] = l.layer.anchorOcclusionRadius;
}
this.annotationsMaterial.uniforms.dataTexture.value && this.annotationsMaterial.uniforms.dataTexture.value.dispose(), e ? e.image.data = o : e = new kn(o, i, 1, yt, Nt), e.needsUpdate = !0, this.annotationsMaterial.uniforms.dataTexture.value = e;
}
this.dataTextureNeedsUpdate = !1;
}
dispose() {
this.unsubscribeListeners(), this.annotationsMaterial.dispose(), this.annotationsMaterial.uniforms.dataTexture.value && this.annotationsMaterial.uniforms.dataTexture.value.dispose(), this.annotationsMaterial.dispose(), this.annotationsRenderTarget.dispose();
}
render(t, e) {
t.getSize(ce);
const {
camera: i,
maxVisible: o,
fullscreenRenderer: r,
annotationsMaterial: a,
annotationsData: l,
isBusy: c
} = this;
e?.depthTexture && this.annotationsData.length && !c && (this.dataTextureNeedsUpdate && this.updateDataTexture(), this.isBusy = !0, a.uniforms.cameraPosition.value.copy(i.position), a.uniforms.depthTexture.value = e.depthTexture, a.uniforms.pMatrix.value = i.projectionMatrix, a.uniforms.vMatrix.value = i.matrixWorldInverse, this.fullscreenRenderer.renderMaterial(
t,
this.annotationsRenderTarget,
this.annotationsMaterial
), t.readRenderTargetPixelsAsync(
this.annotationsRenderTarget,
0,
0,
this.annotationsTexSize,
1,
this.annotationsBuffer
).then((f) => this.updateAnnotationsData(f)).finally(() => {
this.isBusy = !1;
}));
const s = Ao(
l,
i,
this.clock,
o
);
Po(s, [ce.x, ce.y]), Co(l), this.updateOverlayTexture(s), this.overlayTexture !== null && r.renderTexture(t, e, this.overlayTexture);
}
}
const zo = 8, mn = 1, Do = ({ maxVisible: n }) => {
const { gl: t, scene: e, camera: i, pointer: o, clock: r } = Ge(), a = Ge((d) => d.size), l = A(() => {
const v = new gi(1, 1);
return new xt(1, 1, {
format: yt,
type: In,
depthBuffer: !0,
depthTexture: v,
generateMipmaps: !1,
samples: zo
});
}, []), c = A(
() => new go(e, i),
[e, i]
), s = A(() => new xo(), []), f = A(
() => new Ro(i, r, o, n),
[i, r, o, n]
);
return R(() => () => {
f && f.dispose();
}, [f]), R(() => {
const d = t.autoClear;
return t.autoClear = !1, () => {
t.autoClear = d;
};
}, [t]), R(() => () => {
l.dispose();
}, [l]), Nn(() => {
const d = t.getPixelRatio();
l.setSize(
a.width * d * mn,
a.height * d * mn
);
}, [l, t, a]), Pe(() => {
c.render(t, l), f.render(t, l), s.render(t, l);
}, 1), null;
}, vn = "annotations_root", Os = ({
maxVisible: n = 100,
autoUpdate: t = !0,
children: e
}) => {
const i = Ge((a) => a.gl), o = he((a) => a.clear), r = A(() => {
const a = i.domElement.parentElement;
if (!a) throw Error("Unable to create root!");
a.querySelector(`#${vn}`)?.remove();
const l = document.createElement("div");
return l.setAttribute("id", vn), l.setAttribute(
"style",
"position:absolute;top:0;left:0;z-index: 1;pointer-events:none;padding:0;width:100%;height:100%;user-select:none"
), a.appendChild(l), Li(l);
}, [i]);
return R(() => (r.render(/* @__PURE__ */ w(po, {})), () => {
r.unmount(), o();
}), [r, o]), /* @__PURE__ */ j(He, { children: [
t && /* @__PURE__ */ w(Do, { maxVisible: n }),
e
] });
}, Eo = ({
id: n,
name: t
}) => /* @__PURE__ */ w(
"div",
{
id: `annotation_${n}`,
style: {
minWidth: "150px",
background: "#33333390",
color: "white",
textAlign: "center",
overflow: "hidden",
borderRadius: "4px",
padding: "1px 6px",
fontFamily: "sans-serif",
fontSize: "12pt"
},
children: /* @__PURE__ */ w("div", { style: { whiteSpace: "nowrap" }, children: t })
},
n
), Us = ({
id: n,
name: t,
priority: e = 0,
visible: i = !0,
distanceFactor: o = 100,
minDistance: r = 10,
maxDistance: a = 5e3,
anchorOcclusionRadius: l = 15,
anchorSize: c = 0.25,
anchorColor: s = "white",
connectorWidth: f = 1,
connectorColor: d = s || "white",
labelOffset: p = 100,
labelComponent: v = Eo,
onClick: m,
children: g
}) => {
const x = he((y) => y.createLayer), b = he((y) => y.updateLayer), u = he((y) => y.layerExist), _ = N(null), h = A(() => ({
id: n,
name: t,
priority: e,
visible: i,
distanceFactor: o,
minDistance: r,
maxDistance: a,
labelOffset: p,
anchorOcclusionRadius: l,
anchorSize: c,
anchorColor: s,
connectorWidth: f,
connectorColor: d,
labelComponent: v,
onClick: m
}), [
n,
t,
e,
i,
l,
c,
s,
f,
d,
r,
a,
p,
o,
v,
m
]);
return Nn(() => {
u(h.id) ? b(h.id, h) : _.current = x(h);
}, [x, b, u, h]), R(() => () => {
_.current && _.current();
}, []), /* @__PURE__ */ w("group", { children: g });
}, Lo = new X(), ko = (n) => n.matrixWorld ? Lo.set(...n.position).applyMatrix4(n.matrixWorld).toArray() : n.position, ft = new X(), Bs = ({
radius: n = 3,
opacity: t = 0.1,
color: e,
fixedX: i,
fixedY: o,
fixedZ: r,
renderOrder: a,
depthTest: l = !1,
depthWrite: c = !1,
children: s
}) => {
const f = Ge((p) => p.controls), d = N(null);
return R(() => {
function p() {
const v = f;
v && d.current ? (v.getTarget(ft), d.current.position.set(
i || ft.x,
o || ft.y,
r || ft.z
), d.current.visible = !0) : d.current && (d.current.visible = !1);
}
return f && f.addEventListener("update", p), p(), () => {
f && f.removeEventListener("update", p);
};
}, [f, i, o, r]), /* @__PURE__ */ j("group", { ref: d, visible: !1, renderOrder: a, children: [
!!s && s,
!s && /* @__PURE__ */ j("mesh", { children: [
/* @__PURE__ */ w("sphereGeometry", { args: [n] }),
/* @__PURE__ */ w(
"meshBasicMaterial",
{
color: e,
transparent: !0,
opacity: t,
depthTest: l,
depthWrite: c
}
)
] })
] });
}, Qn = Ce({
current: 1 / 0
}), Hs = ({
min: n = 0,
max: t,
onDemand: e = !1,
children: i
}) => {
const o = ie(Qn), r = N(null), [a, l] = F(!e);
return Pe(() => {
if (o && r.current) {
const c = o.current >= n && o.current < t;
e && c && !a ? Jt(() => l(!0)) : e && !c && a ? Jt(() => l(!1)) : r.current.visible !== c && (r.current.visible = c);
}
}), /* @__PURE__ */ w("group", { ref: r, visible: !1, children: a && i });
}, Jn = Ce(
null
), _t = () => ie(Jn), se = {
NOT_EMITTER: 29,
// can be used to exclude an object from being used as an event emitter
EMITTER: 30
// used internally to flag an object as an event emitter
};
function je(...n) {
const t = new Wn();
return n.forEach((e) => t.enable(e)), t;
}
var Io = `uniform int side;
varying vec3 vWorldPosition;
flat varying float vEmitterId;
void main() {
if(side == 1 && gl_FrontFacing || side == 0 && !gl_FrontFacing) {
discard;
}
gl_FragColor = vec4(vEmitterId, vWorldPosition);
}`, Wo = `#define PICK_MATERIAL
uniform float emitterId;
varying vec3 vWorldPosition;
flat varying float vEmitterId;
#include <common>
#include <uv_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
void main() {
#include <uv_vertex>
#include <begin_vertex>
#include <project_vertex>
#include <logdepthbuf_vertex>
#include <clipping_planes_vertex>
vec4 worldPosition = vec4(transformed, 1.0);
#ifdef USE_BATCHING
worldPosition = batchingMatrix * worldPosition;
#endif
#ifdef USE_INSTANCING
worldPosition = instanceMatrix * worldPosition;
#endif
vWorldPosition = (modelMatrix * worldPosition).xyz;
vEmitterId = emitterId + float(gl_InstanceID);
}`;
class Go extends Fe {
listeners = /* @__PURE__ */ new Map();
emitters = /* @__PURE__ */ new Map();
currentObjectMap = null;
constructor() {
super({
vertexShader: Wo,
fragmentShader: Io,
uniforms: {
emitterId: new z(0),
side: new z(2)
},
toneMapped: !1,
blending: xi,
side: xe,
forceSinglePass: !0
});
}
dispose() {
super.dispose(), this.listeners.clear(), this.emitters.clear();
}
onBeforeRender(t, e, i, o, r) {
const a = this.emitters.get(r.id);
if (this.currentObjectMap && a) {
const c = this.currentObjectMap.length / 2;
this.currentObjectMap.push(r.id, 0);
for (let s = 1; s < a.instanceCount; s++)
this.currentObjectMap.push(r.id, s);
this.uniforms.emitterId.value = c + 1;
} else
this.uniforms.emitterId.value = 0;
const l = r;
this.uniforms.side.value = l.material.side, this.uniformsNeedUpdate = !0;
}
}
const Fo = {
radius: 3
};
class gn {
_size;
_radius;
_pbo;
_buffer;
_material = new Go();
constructor(t = {}) {
const { radius: e } = { ...Fo, ...t };
this._radius = Math.max(Math.min(10, e), 0), this._size = 2 * this._radius + 1, this._pbo = new xt(this._size, this._size, {
colorSpace: Dn,
samples: 0,
format: yt,
type: Nt,
magFilter: Ze,
minFilter: Ze,
depthBuffer: !0,
stencilBuffer: !1,
generateMipmaps: !1
}), this._buffer = new Float32Array(this._size * this._size * 4);
}
traverseObject = (t, e, i, o) => {
if (t.visible && !t.layers.isEnabled(se.NOT_EMITTER)) {
if (t.type === "Mesh" || t.type === "Line" || t.type === "Points") {
const r = !!t.isInstancedMesh, a = r ? t.count : 1, l = Number.isFinite(o) ? o : t.type === "Mesh" ? 0 : 3;
if (a > 0) {
let c = this._material.emitters.get(t.id);
c ? (c.source = t, c.threshold = l, c.instanced = r, c.instanceCount = a, i < c.depth && (c.depth = i, c.listener = e)) : (c = {
source: t,
depth: i,
listener: e,
threshold: l,
instanced: r,
instanceCount: a
}, this._material.emitters.set(t.id, c)), t.layers.enable(se.EMITTER);
} else
t.layers.disable(se.EMITTER);
}
for (let r = 0; r < t.children.length; r++)
this.traverseObject(t.children[r], e, i + 1, o);
}
};
updateListeners = () => {
this._material.listeners.forEach((t) => {
this.traverseObject(
t.object,
t.object.id,
0,
t.threshold
);
});
};
addListener = (t) => {
this._material.listeners.set(t.object.id, t);
};
getListener = (t) => this._material.listeners.get(t);
removeListener = (t) => {
const e = this._material.listeners.get(t);
if (e) {
const { object: i } = e;
this._material.listeners.delete(t), i.traverse((o) => {
o.layers.disable(se.EMITTER), this._material.emitters.delete(o.id);
});
}
};
async render(t, e, i, o) {
const r = e.domElement.clientWidth, a = e.domElement.clientHeight, l = t.toArray(), c = Hi(l, r, a), s = c[0] - this._radius, f = c[1] - this._radius, d = o.layers.mask, p = i.background, v = e.clearColor, m = e.getClearAlpha();
i.background = null, i.overrideMaterial = this._material, o.setViewOffset(r, a, s, f, this._size, this._size), o.layers.disableAll(), o.layers.set(se.EMITTER);
const g = new Array();
return this._material.currentObjectMap = g, e.setRenderTarget(this._pbo), e.setClearColor(0, 0), e.clear(), e.render(i, o), o.clearViewOffset(), o.layers.mask = d, i.background = p, this._material.currentObjectMap = null, i.overrideMaterial = null, e.clearColor = v, e.setClearAlpha(m), e.setRenderTarget(null), e.readRenderTargetPixelsAsync(
this._pbo,
0,
// x
0,
// y
this._size,
// width
this._size,
// height
this._buffer
).then((x) => this.pick(x, g, l));
}
pick(t, e, i) {
const o = {
object: null,
lsqr: 1 / 0,
bufferIndex: 0
};
let r = 0, a = 0, l = 0, c = 0, s = 0, f = 0, d = 0, p = 0, v;
for (let g = this._size - 1; g >= 0; g--)
for (let x = 0; x < this._size; x++, r += 4)
s = t[r] - 1, s >= 0 && s < e.length - 1 && (f = s * 2, d = e[f], p = e[f + 1], v = this._material.emitters.get(d), v && (a = x - this._radius, l = g - this._radius, c = a ** 2 + l ** 2 - v.threshold ** 2, (!o.object || o.lsqr > c) && (o.object = {
emitter: v,
index: p
}, o.lsqr = c, o.bufferIndex = r)));
return {
match: o.object,
point: i,
position: [
t[o.bufferIndex + 1],
t[o.bufferIndex + 2],
t[o.bufferIndex + 3]
]
};
}
dispose() {
this._pbo.dispose();
const t = Array.from(this._material.listeners.values());
this._material.dispose(), t.forEach(
(e) => e.object.traverse((i) => i.layers.disable(se.EMITTER))
);
}
}
const Oo = 300, xn = 10, Ns = ({
autoUpdate: n = !0,
autoUpdateRenderPriority: t,
threshold: e,
onResult: i,
children: o
}) => {
const { gl: r, camera: a, scene: l, pointer: c } = Ge(), s = A(
() => ({
busy: !1,
currentResult: {
match: null,
point: [0, 0],
position: [0, 0, 0]
},
buttonDown: !1,
keys: {
altKey: !1,
shiftKey: !1,
ctrlKey: !1
},
posX: -1,
posY: -1,
deltaTime: 0,
pickingHelper: Number.isFinite(e) ? new gn({ radius: e }) : new gn()
}),
[e]
), f = re(
(m) => {
const { pickingHelper: g, currentResult: x, keys: b } = s, u = m.match, _ = x.match, h = x.point;
if (_ && (!u || u.emitter.listener !== _.emitter.listener || u.emitter.listener === _.emitter.listener && u.emitter.instanced && u.index !== _.index)) {
const y = g.getListener(_.emitter.listener);
if (y && y.handlers.leave) {
const M = {
target: y.object,
source: _.emitter.source,
ref: y.ref,
instanceIndex: _.index,
pointer: m.point,
position: m.position,
keys: b,
camera: a,
domElement: r.domElement
};
setTimeout(() => y.handlers.leave(M));
}
}
if (u) {
const y = g.getListener(u.emitter.listener);
if (!_ || u.emitter.listener !== _.emitter.listener || u.emitter.listener === _.emitter.listener && u.emitter.instanced && u.index !== _.index) {
const M = g.getListener(u.emitter.listener);
if (M && M.handlers.enter) {
const P = {
target: M.object,
source: u.emitter.source,
ref: M.ref,
instanceIndex: u.index,
pointer: m.point,
position: m.position,
keys: b,
camera: a,
domElement: r.domElement
};
setTimeout(() => M.handlers.enter(P));
}
}
if (y && y.handlers.move && (m.point[0] !== h[0] || m.point[1] !== h[1])) {
const M = {
target: y.object,
source: u.emitter.source,
ref: y.ref,
instanceIndex: u.index,
pointer: m.point,
position: m.position,
keys: b,
camera: a,
domElement: r.domElement
};
setTimeout(() => y.handlers.move(M));
}
}
i && (u !== null || _ !== null) && setTimeout(() => i(m)), s.currentResult = m;
},
[s, r, a, i]
), d = re(() => {
const m = s.currentResult.match;
if (m) {
const g = s.pickingHelper.getListener(
m.emitter.listener
);
g && g.handlers.click && g.handlers.click({
target: g.object,
source: m.emitter.source,
ref: g.ref,
instanceIndex: m.index,
pointer: s.currentResult.point,
position: s.currentResult.position,
keys: s.keys,
camera: a,
domElement: r.domElement
});
}
}, [s, r, a]), p = re(() => {
s?.pickingHelper && !s.busy && !s.buttonDown && (s.busy = !0, s.pickingHelper.updateListeners(), s.pickingHelper.render(c, r, l, a).then(f).finally(() => {
s.busy = !1;
}));
}, [r, a, l, c, s, f]), v = A(() => ({
register: (m) => {
if (!m?.object)
throw Error(
"Unable to register event listener without an object reference!"
);
const g = m.object.id;
return s.pickingHelper.addListener(m), () => {
s.pickingHelper.removeListener(g);
};
},
update: p
}), [s, p]);
return R(() => {
function m(u) {
u.isPrimary && (s.buttonDown = !0, s.posX = u.pageX, s.posY = u.pageY, s.deltaTime = performance.now());
}
function g(u) {
if (u.isPrimary) {
const _ = performance.now(), h = s.buttonDown;
s.buttonDown = !1, h && _ - s.deltaTime < Oo && Math.abs(u.pageX - s.posX) < xn && Math.abs(u.pageY - s.posY) < xn && d();
}
}
function x(u) {
s.keys.shiftKey = u.shiftKey, s.keys.ctrlKey = u.ctrlKey, s.keys.altKey = u.altKey;
}
function b() {
setTimeout(() => {
f({ match: null, point: [0, 0], position: [0, 0, 0] });
}, 60);
}
return addEventListener("keydown", x, { passive: !0, capture: !0 }), addEventListener("keyup", x, { passive: !0, capture: !0 }), r.domElement.addEventListener("pointerdown", m, {
passive: !0,
capture: !0
}), r.domElement.addEventListener("pointerup", g, {
passive: !0,
capture: !0
}), r.domElement.addEventListener("pointerleave", b, {
passive: !0,
capture: !0
}), () => {
removeEventListener("keydown", x), removeEventListener("keyup", x), r.domElement.removeEventListener("pointerdown", m), r.domElement.removeEventListener("pointerup", g), r.domElement.removeEventListener("pointerleave", b), s.pickingHelper.dispose();
};
}, [s, r, d, f]), Pe(() => {
n && p();
}, t), /* @__PURE__ */ w(Jn.Provider, { value: v, children: o });
}, Uo = (n, t, e, i, o) => {
const r = e[0] * 0.5, a = e[1] * 0.5, [l, c] = o, s = n * e[0], f = t * e[1], d = (s - (r + l)) * i[0], p = (f - (a + c)) * i[1];
return [d, p];
}, ve = new X(), Bo = ({
scale: n,
start: t,
size: e,
units: i,
originOffset: o,
axesOffset: r,
axesTickSize: a,
plane: l,
color: c,
side: s,
trimAxesLabels: f = !1,
renderOrder: d
}) => {
const [p, v] = F({ x: 0, y: 0, flipped: !1 });
Pe(({ camera: b }) => {
b.getWorldDirection(ve);
const u = {
x: 0,
y: 0,
flipped: !1
};
l === "xz" ? (u.flipped = ve.y > 0, u.flipped ? (u.x = ve.z < 0 ? Math.PI : 0, u.y = ve.x < 0 ? Math.PI : 0) : (u.x = ve.z > 0 ? Math.PI : 0, u.y = ve.x > 0 ? Math.PI : 0)) : l === "xy" ? (u.flipped = ve.z > 0, u.x = 0, u.y = ve.x > 0 ? Math.PI : 0) : l === "zy" && (u.flipped = ve.x > 0, u.x = 0, u.y = ve.z < 0 ? Math.PI : 0), (p.x !== u.x || p.y !== u.y || p.flipped !== u.flipped) && v(u);
});
const m = A(() => i / 7.5, [i]), g = A(() => new ee(c || "#fff"), [c]), x = A(() => {
const b = [e[0] * 0.5, e[1] * 0.5], u = [
b[0] + o[0],
b[1] + o[1]
], _ = [
u[0] + pe(r[0], -b[0], b[0]),
u[1] + pe(r[1], -b[1], b[1])
], h = [
Math.floor((u[0] - u[0] % i) / i),
Math.floor((u[1] - u[1] % i) / i)
], y = [
-b[0] + u[0] % i,
-b[1] + u[1] % i
], M = [
Math.min(
1e3,
Math.floor((e[0] - u[0] % i) / i) + 1
),
Math.min(
1e3,
Math.floor((e[1] - u[1] % i) / i) + 1
)
], P = [], C = [], S = a * i + m * 0.6, L = [
pe(r[1] + o[1], -b[1], +b[1]),
pe(r[0] + o[0], -b[0], +b[0])
];
for (let k = f ? 1 : 0; k < M[0] - (f ? 1 : 0); k++) {
const T = [
y[0] + k * i,
L[0] + S
], W = Math.round(
10 * (t[0] + (k - h[0]) * i * n[0])
) / 10;
Math.abs(_[0] - (T[0] + b[0])) > i / 4 && P.push({
pos: T,
value: W,
index: `x${k}`
});
}
for (let k = f ? 1 : 0; k < M[1] - (f ? 1 : 0); k++) {
const T = [
L[1] - S,
y[1] + k * i
], W = Math.round(
10 * (t[1] + (k - h[1]) * i * n[1])
) / 10;
Math.abs(_[1] - (T[1] + b[1])) > i / 4 && C.push({
pos: T,
value: W,
index: `y${k}`
});
}
return { xAxis: P, yAxis: C };
}, [
e,
i,
o,
r,
a,
m,
n,
t,
f
]);
return /* @__PURE__ */ j(
"group",
{
renderOrder: d,
position: [0, 0, (p.flipped ? -1 : 1) * (i / 1e3)],
visible: s === "both" || s === "front" && !p.flipped || s === "back" && p.flipped,
children: [
x.xAxis.map((b) => /* @__PURE__ */ w(
nn,
{
renderOrder: d,
characters: "123456789,.0",
position: [...b.pos, 0],
fontSize: m,
textAlign: "center",
anchorX: "center",
anchorY: "middle",
"rotation-z": p.x,
"rotation-y": p.flipped ? Math.PI : 0,
color: g,
"material-depthWrite": !0,
children: b.value
},
b.index
)),
x.yAxis.map((b) => /* @__PURE__ */ w(
nn,
{
renderOrder: d,
characters: "123456789,.0",
position: [...b.pos, 0],
fontSize: m,
textAlign: "center",
anchorX: "center",
anchorY: "middle",
"rotation-z": p.y + Math.PI / 2,
"rotation-x": p.flipped ? Math.PI : 0,
color: g,
"material-depthWrite": !0,
children: b.value
},
b.index
))
]
}
);
};
var Ho = `precision highp float;
#include <common>
#include <logdepthbuf_pars_fragment>
uniform vec3 uBackground;
uniform float uBackgroundOpacity;
uniform vec2 uSize;
uniform float uCellSize;
uniform float uSubDivisions;
uniform float uOpacity;
uniform vec3 uGridColorMajor;
uniform vec3 uGridColorMinor;
uniform float uGridLineWidth;
uniform vec2 uAxesOffset;
uniform vec3 uAxesColor;
uniform float uAxesLineWidth;
uniform float uAxesTickSize;
uniform vec2 uOriginOffset;
uniform vec2 uCursorPosition;
uniform vec3 uRulerColor;
uniform float uRulerLineWidth;
uniform float uRulerOpacity;
uniform sampler2D uProjectionTexture;
uniform vec3 uProjectionColor;
uniform sampler2D uTexture;
uniform float uTextureMix;
varying vec2 vUv;
float pristineGrid(vec2 uv, vec2 lineWidth) {
vec2 uvDeriv = fwidth(uv * 2.0);
vec2 drawWidth = clamp(lineWidth, uvDeriv, vec2(0.5));
vec2 lineAA = uvDeriv * 1.5;
vec2 gridUV = 1.0 - abs(fract(uv) * 2.0 - 1.0);
vec2 grid2 = smoothstep(drawWidth + lineAA, drawWidth - lineAA, gridUV);
grid2 *= saturate(lineWidth / drawWidth);
grid2 = mix(grid2, lineWidth, clamp(uvDeriv * 2.0 - 1.0, 0.0, 1.0));
return max(grid2.x, grid2.y);
}
float pristineRadialGrid(vec2 _uv, vec2 _lineWidth, float _segments, float _cutoff) {
float angle = atan(_uv.y, _uv.x) / PI2;
float angleFrac = fract(angle);
float ddAngle = fwidth(angle * 2.0);
float ddAngleFrac = fwidth(angleFrac * 2.0);
ddAngle = ddAngle - 0.00001 < ddAngleFrac ? ddAngle : ddAngleFrac;
float dist = length(_uv);
#ifdef DYNAMICSEGMENTS
float logDist = log2(dist);
float segments = pow(2.0, max(2.0, ceil(logDist) + 2.0));
#else
float segments = max(1.0, round(_segments));
#endif
vec2 lineWidth = vec2(_lineWidth.x * segments / (dist * PI2), _lineWidth.y);
vec2 uv = vec2(angle * segments, dist);
vec2 uvDeriv = vec2(ddAngle * segments, fwidth(dist * 2.0));
vec2 drawWidth = clamp(lineWidth, uvDeriv, vec2(0.5));
vec2 lineAA = uvDeriv * 1.5;
vec2 gridUV = 1.0 - abs(fract(uv) * 2.0 - 1.0);
vec2 grid2 = smoothstep(drawWidth + lineAA, drawWidth - lineAA, gridUV);
#ifdef SATURATE
grid2 *= saturate(lineWidth / drawWidth);
#endif
grid2 *= step(_cutoff, dist);
return max(grid2.x, grid2.y);
}
float lines(vec2 uv, vec2 lineWidth) {
vec2 uvDeriv = fwidth(uv * 2.0);
vec2 drawWidth = clamp(lineWidth * uvDeriv, uvDeriv, vec2(0.5));
vec2 lineAA = uvDeriv * 1.5;
vec2 axisLine2 = smoothstep(drawWidth + lineAA, drawWidth - lineAA, abs(uv * 2.0));
axisLine2 *= saturate(lineWidth / drawWidth);
return max(axisLine2.x, axisLine2.y);
}
float ticklines(vec2 uv, vec2 offset, vec2 lineWidth, float tickSize) {
vec2 uvDeriv = fwidth(uv * 2.0);
vec2 drawWidth = clamp(lineWidth * uvDeriv, uvDeriv, vec2(0.5));
vec2 lineAA = uvDeriv * 1.5;
vec2 tickUV = 1.0 - abs(fract(uv) * 2.0 - 1.0);
vec2 tickLine2 = smoothstep(drawWidth + lineAA, drawWidth - lineAA, tickUV);
tickLine2 *= saturate(lineWidth / drawWidth);
tickLine2 *= 1.0 - step( tickSize, abs( uv.yx - offset.yx));
return max(tickLine2.x, tickLine2.y);
}
vec4 drawGrid(vec4 color, vec2 uv, vec3 lineColor, vec2 lineWidth) {
float grid = pristineGrid(uv, lineWidth);
color = mix(color, vec4(lineColor, uOpacity), grid);
return color;
}
vec4 drawRadialGrid(vec4 color, vec2 uv, vec3 lineColor, vec2 lineWidth, float segments, float cutoff) {
float grid = pristineRadialGrid(uv, lineWidth, segments, cutoff);
color = mix(color, vec4(lineColor, uOpacity), grid);
return color;
}
vec4 drawAxisLines(vec4 color, vec2 uv, vec2 originOffset, vec2 axesOffset, vec3 lineColor, vec2 lineWidth, float tickSize) {
vec2 tickOffset = axesOffset;
float axesLines = lines(uv - originOffset - axesOffset, lineWidth);
vec2 tickLineWidth = lineWidth;
float majorTicks = ticklines(uv - originOffset, tickOffset, tickLineWidth, tickSize);
float minorTicks = ticklines((uv - originOffset) * uSubDivisions, tickOffset * uSubDivisions, tickLineWidth * uSubDivisions * 0.5, tickSize * uSubDivisions * 0.5);
float lines = max(axesLines, max(minorTicks, majorTicks));
color = mix(color, vec4(lineColor, uOpacity), lines);
return color;
}
vec4 drawRulerLines(vec4 color, vec2 uv, vec3 lineColor, vec2 lineWidth, float opacity) {
float rulerLines = lines(uv, lineWidth) * opacity;
color = mix(color, vec4(lineColor, uOpacity), rulerLines);
return color;
}
void main() {
#include <logdepthbuf_fragment>
vec2 originOffset = clamp(uOriginOffset, -uSize / 2.0, uSize / 2.0) / uCellSize;
vec2 axesOffset = uAxesOffset / uCellSize;
vec2 uv = (vUv.xy - 0.5) * (uSize / uCellSize);
vec2 uvMaj = uv - originOffset;
vec2 uvMin = uvMaj * uSubDivisions;