UNPKG

@realsee/dnalogel

Version:
306 lines (300 loc) 11.9 kB
var I = Object.defineProperty; var b = Object.getOwnPropertySymbols; var S = Object.prototype.hasOwnProperty, V = Object.prototype.propertyIsEnumerable; var M = (c, s, e) => s in c ? I(c, s, { enumerable: !0, configurable: !0, writable: !0, value: e }) : c[s] = e, x = (c, s) => { for (var e in s || (s = {})) S.call(s, e) && M(c, e, s[e]); if (b) for (var e of b(s)) V.call(s, e) && M(c, e, s[e]); return c; }; var l = (c, s, e) => (M(c, typeof s != "symbol" ? s + "" : s, e), e); import { IObject3D as E } from "../../shared-utils/three/IObject3D.js"; import { anyPositionToVector3 as w } from "../../shared-utils/positionToVector3.js"; import * as o from "three"; import { intersectWithoutLine as C } from "../../shared-utils/three/core/Raycaster.js"; import { ColoredMesh as W } from "../utils/three/ColoredMesh.js"; import { PrismGeometry as F } from "../../shared-utils/three/core/PrismGeometry.js"; import { PrismAnimationGeometry as U } from "../../shared-utils/three/core/PrismAnimationGeometry.js"; import { triangleArea as _, triangleCenter as T } from "../../shared-utils/three/geometryUtil.js"; import { LineMesh as O } from "./Line.js"; import { LineGeometry as R } from "../../shared-utils/three/core/LineGeometry.js"; import { AnimationFrameLoop as z } from "@realsee/five"; class Q extends E { constructor(e) { super(); l(this, "name", "PrismMesh"); l(this, "five"); l(this, "animatedBoxMesh"); l(this, "activeAnimations", /* @__PURE__ */ new Map()); l(this, "activeEaseInAnimations", /* @__PURE__ */ new Map()); l(this, "_geometryInfoCache"); l(this, "geometryInfoNeedUpdate", !0); l(this, "edgeMesh", new O()); l(this, "prismMesh", new W()); l(this, "paramStyle"); this.five = e == null ? void 0 : e.five, this.prismMesh.name = "PrismMesh", this.prismMesh.geometry = new F(), this.edgeMesh.name = "EdgeMesh", this.addIfNotExists(this.prismMesh, this.edgeMesh), e && Object.keys(e).some((i) => i !== "five") && this.setPoints(e), this.setStyle(e); } get topPosition() { return new o.Vector3().fromArray(this.prismMesh.geometry.topPosition); } get bottomPositions() { return this.prismMesh.geometry.bottomPositions.map((e) => new o.Vector3().fromArray(e)); } get topPositions() { const e = this.bottomPositions[0].clone().sub(this.topPosition); return this.bottomPositions.map((i) => i.clone().sub(e)); } get style() { return { color: this.color, lineColor: this.lineColor, lineWidth: this.lineWidth, opacity: this.opacity, occlusionVisibility: this.occlusionVisibility, occlusionMode: this.occlusionMode }; } get opacity() { return this.prismMesh.opacity; } get occlusionVisibility() { return this.prismMesh.occlusionVisibility; } get occlusionMode() { return this.prismMesh.occlusionMode; } /** * @deprecated notice: please use specified center instead, such as `localCenter` or `worldCenter` */ get center() { return this.localCenter.clone(); } get localCenter() { var e; return (e = this.geometryInfo.center.clone()) != null ? e : new o.Vector3(9999, 9999, 9999); } get geometryInfo() { if (this.geometryInfoNeedUpdate) { this.geometryInfoNeedUpdate = !1; const e = this.prismMesh.geometry.bottomPositions, i = this.prismMesh.geometry.topPosition; if (!e || e.length < 3 || !i) { this._geometryInfoCache = void 0; return; } const t = e.map((d, f) => f >= e.length - 2 ? null : [ new o.Vector3().fromArray(e[0]), new o.Vector3().fromArray(e[f + 1]), new o.Vector3().fromArray(e[f + 2]) ]).filter(Boolean); if (t.length === 0) { this._geometryInfoCache = void 0; return; } let n = 0, r = new o.Vector3(); for (const [d, f, m] of t) { const a = _(d, f, m), u = T(d, f, m, a); n += a, r.add(u); } r = r.divideScalar(n); const y = new o.Vector3().fromArray(i).sub(new o.Vector3().fromArray(e[0])), p = r.clone().add(y.divideScalar(2)); this._geometryInfoCache = { center: p }; } return this._geometryInfoCache; } get worldCenter() { return this.updateMatrixWorld(), this.localToWorld(this.localCenter); } get color() { return this.prismMesh.color; } get lineWidth() { return this.edgeMesh.style.lineWidth; } get lineColor() { return this.edgeMesh.color; } setStyle(e = {}) { this.paramStyle = x(x({}, this.paramStyle), e), this.prismMesh.setStyle(this.paramStyle), this.edgeMesh.setStyle(this.paramStyle); } /** * Check if the prism is a box (has 4 bottom positions) */ isBox() { const e = this.prismMesh.geometry.bottomPositions; return e && e.length === 4; } setPoints(e) { var i; this.prismMesh.geometry.setPosition({ bottomPositions: (i = e.points) == null ? void 0 : i.map(w).map((t) => t.toArray()), topPosition: e.heightPoint ? w(e.heightPoint).toArray() : void 0 }), this.edgeMesh.geometry = new R().fromEdgesGeometry(new o.EdgesGeometry(this.prismMesh.geometry)), this.geometryInfoNeedUpdate = !0; } highlight() { this.prismMesh.highlight(); } unhighlight() { this.prismMesh.unhighlight(); } raycast(e, i) { return this.children.forEach((t) => C(t, e, i, !0)), !1; } /** * 设置Five实例 * @param five Five实例 */ setFive(e) { this.five = e; } /** * 播放盒子上下动画 * @param id 盒子项目ID * @param color 动画颜色,默认为浅蓝色 (#4DF0FF) * @returns Promise,动画完成时解析 */ playBoxAnimation(e, i = "#4DF0FF") { return this.five ? (this.activeAnimations.has(e) && this.forceFinishBoxAnimation(e), new Promise((t) => { this.prismMesh.visible = !1, this.edgeMesh.visible = !1; const n = this.createOrUpdateAnimatedBoxMesh(i), r = n.material, y = 3e3; let p = 0; const d = z.shared.add((f, m) => { p += m; let a = p / y; a > 1 && (a = 1); let u = a; a <= 0.5 ? u = a * 2 : u = 2 - a * 2, r.forEach((g) => { g.uniforms.progress.value = u; }), a === 1 && (d(), n.visible = !1, this.playEaseInAnimation(e, () => { this.activeAnimations.delete(e), t(); })), this.five.needsRender = !0; }); this.activeAnimations.set(e, { animatedBoxMesh: n, remove: d }), n.visible = !0; })) : (console.warn("Five instance not available for box animation"), Promise.resolve()); } /** * 强制立即完成所有活动的盒子动画 * @param id 可选的盒子项目ID。如果未提供,完成所有活动的盒子动画。 */ forceFinishBoxAnimation(e) { if (!this.five) { console.warn("Five instance not available for finishing box animation"); return; } (e ? [e] : Array.from(this.activeAnimations.keys())).forEach((t) => { const n = this.activeAnimations.get(t); n && (n.remove(), this.animatedBoxMesh && (this.animatedBoxMesh.visible = !1), this.activeAnimations.delete(t)); const r = this.activeEaseInAnimations.get(t); r && (clearInterval(r.interval), this.activeEaseInAnimations.delete(t)); }), this.showBoxFinalState(), this.five.needsRender = !0; } playEaseInAnimation(e, i) { if (!this.five) return; this.prismMesh.visible = !0, this.edgeMesh.visible = !0; let t = 0; const n = this.prismMesh.style.opacity; this.prismMesh.setStyle({ opacity: 0 }), this.edgeMesh.setStyle({ lineOpacity: 0 }); const r = setInterval(() => { t === 61 && (clearInterval(r), this.activeEaseInAnimations.delete(e), this.showBoxFinalState(), i()), this.edgeMesh.setStyle({ lineOpacity: t / 60 }), this.prismMesh.setStyle({ opacity: t / 60 * n }), this.five.needsRender = !0, t++; }, 16); this.activeEaseInAnimations.set(e, { interval: r, model: this }); } /** * 创建或更新动画盒子网格 * @param color 动画颜色 */ createOrUpdateAnimatedBoxMesh(e) { if (!this.five) throw new Error("Five instance not available"); const i = new o.Vector3(1 / 0, 1 / 0, 1 / 0), t = new o.Vector3(-1 / 0, -1 / 0, -1 / 0), n = this.bottomPositions; for (const h of n) i.min(h), t.max(h); const r = this.topPositions; for (const h of r) i.min(h), t.max(h); const y = t.clone().sub(i), p = this.prismMesh.geometry, d = p.bottomPositions, f = p.topPosition; if (this.animatedBoxMesh) return this.matrixWorld.decompose(this.animatedBoxMesh.position, this.animatedBoxMesh.quaternion, this.animatedBoxMesh.scale), this.animatedBoxMesh.updateMatrixWorld(), this.animatedBoxMesh.matrixAutoUpdate = !1, this.animatedBoxMesh; const m = new U({ bottomPositions: d, topPosition: f, type: "Concave" }), a = ` varying vec2 vUV; varying vec3 vWorldPosition; #include <common> void main() { vUV = uv; vec4 transformed = vec4(position, 1.0); vWorldPosition = (modelMatrix * vec4( position, 1.0)).xyz; gl_Position = projectionMatrix * modelViewMatrix * transformed; } `, u = ` #include <common> uniform vec3 color; uniform vec3 size; uniform vec3 minPos; uniform vec2 faceSize; uniform float opacity; uniform float progress; uniform float spread; uniform float faceIndex; varying vec2 vUV; varying vec3 vWorldPosition; void main() { float u = vUV.x; float v = vUV.y; float r = 0.1 + sqrt(u * u + v * v) * 0.3; // 边缘线效果 - 基于UV坐标 float edgeThickness = 0.003; if (u < edgeThickness || u > 1.0 - edgeThickness || v < edgeThickness || v > 1.0 - edgeThickness) { r += 1.0 - min(abs(u - 0.5), abs(v - 0.5)) * 2.0; } // 扫光动画 - 根据面类型使用不同效果 float sweepEffect = 1.0; vec3 p = (vWorldPosition - minPos) / size; sweepEffect = pow(clamp(1.0 - abs(p.y - progress) / spread, 0.0, 1.0), 2.0); r = r * sweepEffect; gl_FragColor = vec4(color, clamp(r * opacity, 0.0, 1.0)); } `, g = [], A = new o.Color(e); for (let h = 0; h < m.faceCount; h++) { const v = new o.ShaderMaterial({ vertexShader: a, fragmentShader: u, uniforms: { color: { value: A }, size: { value: y }, minPos: { value: i }, faceSize: { value: new o.Vector2(1, 1) }, opacity: { value: 1.5 }, progress: { value: 0.5 }, spread: { value: 0.3 }, faceIndex: { value: h } }, depthTest: !1, side: o.DoubleSide, transparent: !0 }); v.polygonOffset = !0, v.polygonOffsetFactor = 1, v.polygonOffsetUnits = 1, g.push(v); } return this.animatedBoxMesh = new o.Mesh(m, g), this.matrixWorld.decompose(this.animatedBoxMesh.position, this.animatedBoxMesh.quaternion, this.animatedBoxMesh.scale), this.animatedBoxMesh.updateMatrixWorld(), this.animatedBoxMesh.matrixAutoUpdate = !1, this.animatedBoxMesh.onBeforeRender = () => { m.boundingBox || m.computeBoundingBox(); const h = m.boundingBox.min.clone().applyMatrix4(this.animatedBoxMesh.matrixWorld), v = m.boundingBox.max.clone().applyMatrix4(this.animatedBoxMesh.matrixWorld), B = new o.Vector3().subVectors(v, h); g.forEach((P) => { P.uniforms.minPos.value.copy(h), P.uniforms.size.value.copy(B); }), this.five.needsRender = !0; }, this.five.scene.add(this.animatedBoxMesh), this.animatedBoxMesh; } /** * 显示盒子模型的最终状态(完全可见且不透明度完整) */ showBoxFinalState() { this.prismMesh.visible = !0, this.edgeMesh.visible = !0, this.prismMesh.setStyle({ opacity: this.prismMesh.style.opacity }), this.edgeMesh.setStyle({ lineOpacity: 1 }); } } export { Q as PrismMesh };