@realsee/dnalogel
Version:
307 lines (306 loc) • 15.6 kB
JavaScript
var C = Object.defineProperty;
var S = (v, m, e) => m in v ? C(v, m, { enumerable: !0, configurable: !0, writable: !0, value: e }) : v[m] = e;
var g = (v, m, e) => (S(v, typeof m != "symbol" ? m + "" : m, e), e);
import { BaseController as G } from "../Base/BaseController.js";
import { RENDER_ORDER as E } from "../Constants/RenderOrder.js";
import * as c from "three";
import { getMouseRaycaster as A } from "../utils/getMouseRaycaster.js";
import { SolidGuideLine as k } from "../utils/solidGuide.js";
import { rayOnLine as P } from "../../../Sculpt/utils/three/rayOnLine.js";
import { worldBoundingBox as I } from "../../three/boundingBox.js";
import { getNormal as H } from "../../three/getNormal.js";
var L, w;
class Y extends G {
constructor(...e) {
super(...e);
g(this, "name", "ScaleController");
/** 拖拽时用于显示方向参考的直线(使用 Line 实现无限长穿透整个平面的直线) */
g(this, "dragGuideLine");
/** 当前拖拽时显示的面片 */
g(this, "currentFacePatch");
g(this, "solidGuide", new k(this.camera, this.container, (w = (L = this.config) == null ? void 0 : L.solidGuide) != null ? w : {}));
g(this, "startInfo");
g(this, "dragStart", (e) => {
if (this.isDragging)
return;
this.isDragging = !0;
const { intersect: t } = e, r = this.camera.position, s = new Set(this.helperObject3D.scaleMeshes.map((u) => u.uuid));
let i = t.object;
for (; i.parent && !s.has(i.uuid); )
i = i.parent;
const n = i, o = n.scalePosition.basePosition.clone(), h = n.scalePosition.handlePosition.clone(), l = h.clone().sub(o).clone(), a = new c.Line3(o, o.clone().add(l.normalize().multiplyScalar(5)));
a.applyMatrix4(this.helperObject3D.matrixWorld);
const d = new c.Raycaster(r, t.point.clone().sub(r)), p = P({ raycaster: d, line: a });
this.startInfo = { line: a, scaleStartPoint: p, draggingObject: n }, this.helperObject3D.scaleMeshes.forEach((u) => {
u.uuid !== n.uuid && (u.visible = !1);
}), "hideLineConnections" in this.helperObject3D && typeof this.helperObject3D.hideLineConnections == "function" && this.helperObject3D.hideLineConnections();
try {
this.originObject3D.updateMatrixWorld(!0), this.helperObject3D.updateMatrixWorld(!0);
const u = this.getObjectWorldCenter(), M = h.clone().applyMatrix4(this.helperObject3D.matrixWorld).clone().sub(u).normalize(), b = u, y = this.solidGuide.createSolidLine(b, M, { color: 16777215, renderOrder: E.DRAG_GUIDE_LINE });
this.isRectangleWithEdgeMesh() || this.scene.add(y), this.dragGuideLine = y, this.addFaceMesh();
} catch (u) {
}
n.visible = !0, this.hooks.emit("scaleStart");
});
g(this, "dragging", (e) => {
const t = "touches" in e ? e.touches[0].clientX : e.x, r = "touches" in e ? e.touches[0].clientY : e.y, s = A(this.camera, { x: t, y: r }, this.container), i = this.isMoveControllerDragging();
if (!this.isDragging && !i && s) {
this.addHoverFaceMesh(s);
return;
}
if (this.startInfo)
return s ? (this.scale(s), this.updateDragGuideLineDirection(), this.removeFaceMesh(), this.addFaceMesh(), !1) : this.dragEnd();
});
g(this, "isRectangleWithEdgeMesh", () => this.originObject3D.name === "RectangleWithEdgeMesh" && ["topleft", "topright", "bottomleft", "bottomright"].includes(this.startInfo.draggingObject.scalePosition.id));
g(this, "scale", (e) => {
var o, h, l, a;
if (!this.startInfo)
return;
const { line: t, scaleStartPoint: r, draggingObject: s } = this.startInfo, { scalePosition: i } = s;
if (this.isRectangleWithEdgeMesh()) {
const d = this.originObject3D, u = d.geometry.attributes.position;
if (!u)
return [];
const D = [], M = u.count;
for (let f = 0; f < M; f++) {
const j = new c.Vector3().fromBufferAttribute(u, f);
d.updateMatrixWorld(!0), j.applyMatrix4(d.matrixWorld), D.push(j);
}
const b = D.slice(0, 4);
if (!b || b.length < 4)
return;
const y = H(b).normalize(), x = new c.Plane().setFromNormalAndCoplanarPoint(y, b[0]), F = Math.abs(e.ray.direction.dot(y)) < 0.1;
let O;
if (F)
O = P({ raycaster: e, line: t, clampToLine: !1 }).applyMatrix4(new c.Matrix4().getInverse(this.helperObject3D.matrixWorld.clone()));
else {
const f = new c.Vector3();
if (e.ray.intersectPlane(x, f), !f)
return;
const j = new c.Matrix4().getInverse(this.helperObject3D.matrixWorld);
O = f.clone().applyMatrix4(j);
}
const W = r.clone().applyMatrix4(new c.Matrix4().getInverse(this.helperObject3D.matrixWorld));
(h = (o = this.config) == null ? void 0 : o.scaleCallback) == null || h.call(o, {
intersectPoint: O,
scalePosition: i,
offset: O.clone().sub(W)
}), s.position.copy(O), this.hooks.emit("scale", new c.Vector3(1, 1, 1));
return;
}
const n = P({ raycaster: e, line: t, clampToLine: !1 });
n.applyMatrix4(new c.Matrix4().getInverse(this.helperObject3D.matrixWorld.clone())), (a = (l = this.config) == null ? void 0 : l.scaleCallback) == null || a.call(l, {
intersectPoint: n,
scalePosition: i,
offset: n.clone().sub(r)
}), s.position.copy(n.clone()), this.hooks.emit("scale", new c.Vector3(1, 1, 1));
});
g(this, "dragEnd", () => {
if (this.isDragging) {
if (this.startInfo = void 0, this.isDragging = !1, this.helperObject3D.scaleMeshes.forEach((e) => {
e.visible = !0;
}), "showLineConnections" in this.helperObject3D && typeof this.helperObject3D.showLineConnections == "function" && this.helperObject3D.showLineConnections(), this.dragGuideLine) {
this.scene.remove(this.dragGuideLine), this.dragGuideLine.geometry.dispose();
const e = this.dragGuideLine.material;
Array.isArray(e) ? e.forEach((t) => t.dispose()) : e.dispose(), this.dragGuideLine = void 0;
}
this.removeFaceMesh(), this.internalHooks.emit("initialHelperPosition"), this.hooks.emit("scaleEnd");
}
});
const t = this.show.bind(this), r = this.hide.bind(this), s = () => {
"update" in this.helperObject3D && typeof this.helperObject3D.update == "function" && this.helperObject3D.update(this.camera), this.isDragging && this.updateDragGuideLineScale(), this.render();
};
s(), this.domEvents.addEventListener(this.helperObject3D, "mousedown", this.dragStart), document.addEventListener("mousemove", this.dragging), document.addEventListener("mouseup", this.dragEnd), this.domEvents.addEventListener(this.helperObject3D, "touchstart", this.dragStart), document.addEventListener("touchmove", this.dragging), document.addEventListener("touchend", this.dragEnd), this.hooks.on("rotateStart", r), this.hooks.on("rotateEnd", t), this.hooks.on("moveStart", () => {
r(), this.removeFaceMesh();
}), this.hooks.on("moveEnd", t), this.hooks.on("moveByMouseEnable", r), this.hooks.on("moveByMouseDisable", t), this.hooks.on("updateOtherHelpers", (i) => {
"update" in this.helperObject3D && typeof this.helperObject3D.update == "function" && this.helperObject3D.update(i.camera), this.isDragging && this.updateDragGuideLineScale(), this.render();
}), this.cameraHooks.on("cameraUpdate", s), this.disposers.push(() => {
if (this.domEvents.removeEventListener(this.helperObject3D, "mousedown", this.dragStart), document.removeEventListener("mousemove", this.dragging), document.removeEventListener("mouseup", this.dragEnd), this.domEvents.removeEventListener(this.helperObject3D, "touchstart", this.dragStart), document.removeEventListener("touchmove", this.dragging), document.removeEventListener("touchend", this.dragEnd), this.hooks.off("rotateStart", r), this.hooks.off("rotateEnd", t), this.hooks.off("moveStart", r), this.hooks.off("moveEnd", t), this.hooks.off("moveByMouseEnable", r), this.hooks.off("moveByMouseDisable", t), this.hooks.off("updateOtherHelpers"), this.cameraHooks.off("cameraUpdate", s), this.currentFacePatch) {
this.scene.remove(this.currentFacePatch), this.currentFacePatch.geometry.dispose();
const i = this.currentFacePatch.material;
Array.isArray(i) ? i.forEach((n) => n.dispose()) : i.dispose(), this.currentFacePatch = void 0;
}
});
}
initialHelperPosition() {
this.helperObject3D.initialPosition();
const e = this;
e.offHoverListener && e.offHoverListener(), e.offHoverListener = this.hoverListener(this.helperObject3D.scaleMeshes);
}
/**
* 获取物体的真正几何中心(世界坐标)
* 优先使用 worldCenter 属性(对于 PrismMesh 等),否则使用包围盒中心
*/
getObjectWorldCenter() {
if ("worldCenter" in this.originObject3D && typeof this.originObject3D.worldCenter == "object")
return this.originObject3D.worldCenter.clone();
const e = I(this.originObject3D);
return e ? e.getCenter(new c.Vector3()) : this.originObject3D.getWorldPosition(new c.Vector3());
}
getMoveMeshes() {
var r, s, i, n;
const e = [], t = (n = (i = (s = (r = this.helperObject3D.parent) == null ? void 0 : r.children) == null ? void 0 : s.find((o) => o.name === "MoveHelper")) == null ? void 0 : i.children) != null ? n : [];
for (const o of t)
o.name === "ArrowGroup" ? e.push(...o.children) : o.name === "center-plane-handle" && e.push(o);
return e;
}
addHoverFaceMesh(e) {
var o, h;
if (this.isMoveControllerDragging() || (this.removeFaceMesh(), !((o = this.originObject3D.parent) != null && o.getWorldFacesGeometry)))
return;
const t = (h = this.originObject3D.parent) == null ? void 0 : h.getWorldFacesGeometry(), r = this.helperObject3D.scaleMeshes, s = this.getMoveMeshes();
if (e.intersectObjects(s, !1), e.intersectObjects(s, !1).length > 0)
return;
let i = null, n = null;
for (const l of r) {
if (!l.visible)
continue;
const a = e.intersectObject(l, !1);
if (a && a.length > 0) {
i = l, n = a[0].point.clone();
break;
}
}
if (i && n && Array.isArray(t) && t.length > 0) {
let l = null, a = 1 / 0;
for (const d of t) {
if (!d.vertices || d.vertices.length < 3)
continue;
const p = new c.Vector3();
for (const D of d.vertices)
p.add(D);
p.divideScalar(d.vertices.length);
const u = n.distanceTo(p);
u < a && (a = u, l = d);
}
l && this.addFacePatch(l);
}
}
addFaceMesh() {
var r, s, i;
if (this.isMoveControllerDragging())
return;
const e = (r = this.startInfo) == null ? void 0 : r.draggingObject;
if (!e || !((s = this.originObject3D.parent) != null && s.getWorldFacesGeometry))
return;
const t = (i = this.originObject3D.parent) == null ? void 0 : i.getWorldFacesGeometry();
if (t && t.length > 0) {
const n = e.getWorldPosition(new c.Vector3());
let o = 1 / 0, h = t[0];
for (const l of t) {
const a = l.center;
let d;
if (a instanceof c.Vector3)
d = a;
else if (Array.isArray(a) && a.length === 3)
d = new c.Vector3(a[0], a[1], a[2]);
else
continue;
const p = d.distanceTo(n);
p < o && (o = p, h = l);
}
this.addFacePatch(h);
}
}
removeFaceMesh() {
if (this.currentFacePatch) {
this.scene.remove(this.currentFacePatch), this.currentFacePatch.geometry.dispose();
const e = this.currentFacePatch.material;
Array.isArray(e) ? e.forEach((t) => t.dispose()) : e.dispose(), this.currentFacePatch = void 0;
}
}
setScale(e) {
var o, h, l;
let t = 1, r = 1, s = 1;
typeof e == "number" ? (t = e, r = e, s = e) : typeof e == "object" && (t = (o = e.x) != null ? o : 1, r = (h = e.y) != null ? h : 1, s = (l = e.z) != null ? l : 1);
const i = new c.Vector3(t, r, s);
this.hooks.emit("wantToScale", i) || (this.originObject3D.scale.copy(i), this.internalHooks.emit("setObjectScale", i), this.hooks.emit("scale", i), this.render());
}
/**
* 更新直线方向,保持直线始终经过物体中心和当前拉伸球位置
*/
updateDragGuideLineDirection() {
if (!this.dragGuideLine || !this.startInfo)
return;
const { draggingObject: e } = this.startInfo;
this.originObject3D.updateMatrixWorld(!0), this.helperObject3D.updateMatrixWorld(!0);
const t = this.getObjectWorldCenter(), s = e.position.clone().applyMatrix4(this.helperObject3D.matrixWorld).clone().sub(t).normalize(), i = t;
this.solidGuide.updateSolidLine(this.dragGuideLine, i, s);
}
/**
* 更新直线缩放,保持视觉上的恒定大小
*/
updateDragGuideLineScale() {
if (!this.dragGuideLine || !this.startInfo)
return;
const { scalePosition: e } = this.startInfo.draggingObject, t = e.handlePosition.clone().applyMatrix4(this.helperObject3D.matrixWorld), r = e.basePosition.clone().applyMatrix4(this.helperObject3D.matrixWorld), s = t, i = t.clone().sub(r).normalize();
this.solidGuide.updateSolidLine(this.dragGuideLine, s, i);
}
/**
* 检查 MoveController 是否正在拖拽中
*/
isMoveControllerDragging() {
var e, t, r;
return (r = (t = (e = this.otherControllers) == null ? void 0 : e.moveController) == null ? void 0 : t.getIsDragging()) != null ? r : !1;
}
/**
* 向场景添加一个表示 face 的面片,红色,位置与 face.center 一致,使用两个三角形拼成矩形
*/
addFacePatch(e) {
if (this.currentFacePatch) {
this.scene.remove(this.currentFacePatch), this.currentFacePatch.geometry.dispose();
const i = this.currentFacePatch.material;
Array.isArray(i) ? i.forEach((n) => n.dispose()) : i.dispose(), this.currentFacePatch = void 0;
}
if (!e.vertices || e.vertices.length < 3) {
console.warn("Face has no vertices, cannot create patch");
return;
}
const t = new c.BufferGeometry();
if (e.vertices.length === 4) {
const i = new Float32Array([
// 第一个三角形
e.vertices[0].x,
e.vertices[0].y,
e.vertices[0].z,
e.vertices[1].x,
e.vertices[1].y,
e.vertices[1].z,
e.vertices[2].x,
e.vertices[2].y,
e.vertices[2].z,
// 第二个三角形
e.vertices[0].x,
e.vertices[0].y,
e.vertices[0].z,
e.vertices[2].x,
e.vertices[2].y,
e.vertices[2].z,
e.vertices[3].x,
e.vertices[3].y,
e.vertices[3].z
]);
t.setAttribute("position", new c.BufferAttribute(i, 3)), t.setIndex([0, 1, 2, 3, 4, 5]);
} else {
const i = new Float32Array(e.vertices.length * 3), n = new Uint16Array(e.vertices.length);
e.vertices.forEach((o, h) => {
i[h * 3] = o.x, i[h * 3 + 1] = o.y, i[h * 3 + 2] = o.z, n[h] = h;
}), t.setAttribute("position", new c.BufferAttribute(i, 3)), t.setIndex(new c.BufferAttribute(n, 1));
}
t.computeVertexNormals();
const r = new c.MeshBasicMaterial({
color: 16777215,
side: c.DoubleSide,
depthTest: !1,
// 禁用深度测试,避免被其他对象遮挡
depthWrite: !1,
// 禁用深度写入
transparent: !0,
opacity: 0.45
}), s = new c.Mesh(t, r);
return s.renderOrder = E.DRAG_FACE_PATCH, this.scene.add(s), this.currentFacePatch = s, this.render(), s;
}
}
export {
Y as ScaleController
};