123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610 |
- import * as THREE from 'three';
- // import { setModalCenter } from '/@/utils/threejs/util';
- import Smoke from '../../comment/threejs/Smoke';
- import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer';
- import { get } from 'lodash-es';
- import { getTextCanvas } from '/@/utils/threejs/util';
- // import * as dat from 'dat.gui';
- // const gui = new dat.GUI();
- // gui.domElement.style = 'position:absolute;top:100px;left:10px;z-index:99999999999999';
- class ModelContext {
- model;
- // modelName = 'jbfj-hd';
- modelName = 'jbfj-dual';
- /** 本模型的根3D对象 */
- group?: THREE.Object3D;
- /** 本模型所包含的所有元素合集 */
- private elements: unknown[] = [];
- /** 本模型支持的 Object3DGroup 模块 */
- private modules: {
- /** 模块名称 */
- name: string;
- /** 控制该模块所用的上下文 */
- context: THREE.Object3D;
- /** 控制时行为声明 */
- behavior: (context: THREE.Object3D) => void;
- }[] = [];
- constructor(model) {
- this.model = model;
- }
- addLight() {
- // optional implementation
- }
- /**
- * 设置模型类型并切换,不同的类型通常对应不同的具体模型,在模型总控制器下的具体模型会根据传入的参数彼此交互、切换
- *
- * 本模型分为外侧(右侧)风机、内侧(左侧)风机,用户选择一个风机后,详情参数的框需要高亮,风机之间需要联动形成风流
- *
- * @param data 风机数据,第一项应为外侧(右侧)风机,第二项为内侧(左侧)
- */
- setModelType(modelType: 'inner' | 'outer' | string, data: any[]) {
- const fanOuter1Run = get<string>(data[0], 'Fan1StartStatus', '0') == '1';
- const fanInner1Run = get<string>(data[1], 'Fan1StartStatus', '0') == '1';
- if (modelType === 'inner') {
- this.execute('fanLeftStrong');
- }
- if (modelType === 'outer') {
- this.execute('fanRightStrong');
- }
- if (fanOuter1Run && fanInner1Run) {
- this.execute('fan1RightOpen&fan1LeftOpen');
- }
- if (fanOuter1Run && !fanInner1Run) {
- this.execute('fan1RightOpen&fan2LeftOpen');
- }
- if (!fanOuter1Run && fanInner1Run) {
- this.execute('fan2RightOpen&fan1LeftOpen');
- }
- if (!fanOuter1Run && !fanInner1Run) {
- this.execute('fan2RightOpen&fan2LeftOpen');
- }
- }
- private execute(cmdname: string) {
- this.modules.forEach(({ name, context, behavior }) => {
- if (name === cmdname) {
- behavior(context);
- }
- });
- }
- mountedThree() {
- return new Promise((resolve) => {
- this.model.setGLTFModel([this.modelName]).then(async (gltf) => {
- this.group = gltf[0];
- if (this.group) {
- // 将管道由黑色不透光的材质修改为半透明材质
- const material = new THREE.MeshBasicMaterial({
- color: '#000',
- transparent: true,
- opacity: 0.3,
- side: THREE.DoubleSide, // 这里是双面渲染的意思
- });
- [
- this.group.getObjectByName('Cylinder1054'),
- this.group.getObjectByName('BuErTaiJuBuFengJi_shupailie_baisezitiCylinder1054'),
- this.group.getObjectByName('pCylinder1'),
- ].forEach((e: THREE.Mesh) => {
- e.material = material;
- // e.renderOrder = 300;
- });
- // this.group.scale.set(2, 2, 2);
- // setModalCenter(this.group);
- this.addLight();
- this.setModelPosition();
- this.initModules().then(resolve);
- }
- });
- });
- }
- destroy() {
- if (!this.model) return;
- this.elements.forEach((element) => {
- this.model.clearGroup(element);
- });
- }
- // 设置模型位置
- setModelPosition() {
- if (!this.group) return;
- this.group.scale.set(0.6, 0.6, 0.6);
- // const ff = gui.addFolder(`位置调整`);
- // ff.add(this.group.position, 'x', -100, 100);
- // ff.add(this.group.position, 'y', -100, 100);
- // ff.add(this.group.position, 'z', -100, 100);
- this.group.position.set(0, 0, -60);
- this.group.rotation.y = Math.PI / 2;
- }
- // hideElements(eles: THREE.Object3D[]) {
- // eles.forEach((g) => {
- // g.visible = false;
- // });
- // }
- // showElements(eles: THREE.Object3D[]) {
- // eles.forEach((g) => {
- // g.visible = true;
- // });
- // }
- weakElements(eles: unknown[]) {
- eles.forEach((g) => {
- if (g instanceof Smoke) {
- g.oldOpacityFactor = 0.4;
- }
- if (g instanceof CSS3DObject) {
- g.element.style.setProperty('opacity', '0.3');
- }
- });
- }
- strongElements(eles: unknown[]) {
- eles.forEach((g) => {
- if (g instanceof Smoke) {
- g.oldOpacityFactor = 0.75;
- }
- if (g instanceof CSS3DObject) {
- g.element.style.setProperty('opacity', '1');
- }
- });
- }
- startAnimation(eles: unknown[]) {
- eles.forEach((g) => {
- if (g instanceof Smoke) {
- g.startSmoke();
- }
- });
- }
- stopAnimation(eles: unknown[]) {
- const smokes = eles.filter((g) => {
- return g instanceof Smoke;
- });
- return Promise.all(smokes.map((e) => e.stopSmoke()));
- }
- /** 核心方法,初始化本模型的各个模块,这些模块可以实现特定场景的展示、控制等功能 */
- async initModules() {
- if (this.elements.length > 0) return;
- // 右侧风机-主风机进风
- const curveFan1Right = this.generateSmokePath(
- [
- new THREE.Vector3(-85.69, 2.18, 51.89),
- new THREE.Vector3(-85.69, 2.18, 41.89),
- new THREE.Vector3(-85.69, 2.18, 35.32),
- new THREE.Vector3(-85.69, 0.78, 33.08),
- new THREE.Vector3(-85.69, 0.78, 27.84),
- new THREE.Vector3(-85.69, 4.72, 21.56),
- new THREE.Vector3(-85.69, 4.72, -13),
- new THREE.Vector3(-26.2, 4.72, -13.24),
- new THREE.Vector3(-25.61, 4.72, -47.03),
- new THREE.Vector3(80.03, 4.72, -47.03),
- ],
- true
- );
- // 右侧风机-备风机进风
- const curveFan2Right = this.generateSmokePath(
- [
- new THREE.Vector3(-85.69, -0.53, 51.89),
- new THREE.Vector3(-85.69, -0.53, 41.89),
- new THREE.Vector3(-85.69, -0.51, 35.32),
- new THREE.Vector3(-85.69, 0.78, 33.08),
- new THREE.Vector3(-85.69, 0.78, 27.84),
- new THREE.Vector3(-85.69, 4.72, 21.56),
- new THREE.Vector3(-85.69, 4.72, -13),
- new THREE.Vector3(-26.2, 4.72, -13.24),
- new THREE.Vector3(-25.61, 4.72, -47.03),
- new THREE.Vector3(80.03, 4.72, -47.03),
- ],
- true
- );
- // 左侧风机-主风机进风
- const curveFan1Left = this.generateSmokePath(
- [
- new THREE.Vector3(-85.69, 2.18, 12.72),
- new THREE.Vector3(-85.69, 2.18, 2.72),
- new THREE.Vector3(-85.69, 2.18, -3.85),
- new THREE.Vector3(-85.69, 0.78, -6.09),
- new THREE.Vector3(-85.69, 0.78, -12.92),
- new THREE.Vector3(80.25, 0.78, -12.92),
- ],
- true
- );
- // 左侧风机-备风机进风
- const curveFan2Left = this.generateSmokePath(
- [
- new THREE.Vector3(-85.69, -0.53, 12.72),
- new THREE.Vector3(-85.69, -0.53, 2.72),
- new THREE.Vector3(-85.69, -0.51, -3.85),
- new THREE.Vector3(-85.69, 0.78, -6.09),
- new THREE.Vector3(-85.69, 0.78, -12.92),
- new THREE.Vector3(80.25, 0.78, -12.92),
- ],
- true
- );
- // 右侧巷道-回风前段
- const curveTunnelRight = this.generateSmokePath([
- new THREE.Vector3(86.67, 0.78, -16.57),
- new THREE.Vector3(30.11, 0.78, -16.57),
- new THREE.Vector3(30.11, 0.78, -50.39),
- ]);
- // 左侧巷道-回风前段
- const curveTunnelLeft = this.generateSmokePath([new THREE.Vector3(30.11, 0.78, -50.39), new THREE.Vector3(-72.58, 0.78, -50.17)]);
- // 左侧巷道-回风全长
- const curveTunnelMajor = this.generateSmokePath([new THREE.Vector3(86.55, 0.78, -50.39), new THREE.Vector3(-72.58, 0.78, -50.17)]);
- const group1 = new THREE.Group();
- const smokeFan1Right = new Smoke('/model/img/texture-smoke.png', '#ffffff', 1, 0.75, 0.5, 600);
- smokeFan1Right.setPath(curveFan1Right);
- this.elements.push(smokeFan1Right);
- const smokeFan2Right = new Smoke('/model/img/texture-smoke.png', '#ffffff', 10, 0.75, 0.5, 400);
- smokeFan2Right.setPath(curveFan2Right);
- this.elements.push(smokeFan2Right);
- const smokeFan1Left = new Smoke('/model/img/texture-smoke.png', '#ffffff', 10, 0.75, 0.5, 600);
- smokeFan1Left.setPath(curveFan1Left);
- this.elements.push(smokeFan1Left);
- const smokeFan2Left = new Smoke('/model/img/texture-smoke.png', '#ffffff', 10, 0.75, 0.5, 400);
- smokeFan2Left.setPath(curveFan2Left);
- this.elements.push(smokeFan2Left);
- const smokeTunnelRight = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.35, 1.5, 150);
- smokeTunnelRight.setPath(curveTunnelRight);
- this.elements.push(smokeTunnelRight);
- const smokeTunnelLeft = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.35, 1.5, 150);
- smokeTunnelLeft.setPath(curveTunnelLeft);
- this.elements.push(smokeTunnelLeft);
- const smokeTunnelMajor = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.35, 1.5, 150);
- smokeTunnelMajor.setPath(curveTunnelMajor);
- this.elements.push(smokeTunnelMajor);
- await smokeFan1Right.setPoints();
- this.group?.add(smokeFan1Right.points);
- await smokeFan2Right.setPoints();
- this.group?.add(smokeFan2Right.points);
- await smokeFan1Left.setPoints();
- this.group?.add(smokeFan1Left.points);
- await smokeFan2Left.setPoints();
- this.group?.add(smokeFan2Left.points);
- await smokeTunnelRight.setPoints();
- this.group?.add(smokeTunnelRight.points);
- await smokeTunnelLeft.setPoints();
- this.group?.add(smokeTunnelLeft.points);
- await smokeTunnelMajor.setPoints();
- this.group?.add(smokeTunnelMajor.points);
- const fanLeftSelectors = [
- {
- query: '#inputBox2',
- position: [-85, 8, -16],
- scale: 0.1,
- },
- {
- query: '#T1_1',
- position: [93, 18, -65],
- scale: 0.2,
- },
- {
- query: '#T1_2',
- position: [35, 16, -59],
- scale: 0.175,
- },
- ];
- const fanRightSelectors = [
- {
- query: '#inputBox3',
- position: [-85, 8, 24],
- scale: 0.1,
- },
- {
- query: '#T2_1',
- position: [93, 18, -98],
- scale: 0.2,
- },
- {
- query: '#T2_2',
- position: [35, 16, -92],
- scale: 0.175,
- },
- ];
- const commonSelectors = [
- {
- query: '#T3',
- position: [-26, 14, -86],
- scale: 0.15,
- },
- ];
- const fanLeftSprites = this.initCssElement(fanLeftSelectors);
- const fanRightSprites = this.initCssElement(fanRightSelectors);
- const commonSprites = this.initCssElement(commonSelectors);
- // 双巷道的通风机都开启有4种情况
- this.modules.push({
- name: 'fan1RightOpen&fan1LeftOpen',
- context: group1,
- behavior: () => {
- // this.weakElements(this.elements);
- this.stopAnimation(this.elements).then(() => {
- // this.strongElements();
- this.startAnimation([smokeFan1Right, smokeFan1Left, smokeTunnelRight, smokeTunnelMajor]);
- });
- },
- });
- this.modules.push({
- name: 'fan2RightOpen&fan1LeftOpen',
- context: group1,
- behavior: () => {
- this.stopAnimation(this.elements).then(() => {
- this.startAnimation([smokeFan2Right, smokeFan1Left, smokeTunnelRight, smokeTunnelMajor]);
- });
- },
- });
- this.modules.push({
- name: 'fan1RightOpen&fan2LeftOpen',
- context: group1,
- behavior: () => {
- this.stopAnimation(this.elements).then(() => {
- this.startAnimation([smokeFan1Right, smokeFan2Left, smokeTunnelRight, smokeTunnelMajor]);
- });
- },
- });
- this.modules.push({
- name: 'fan2RightOpen&fan2LeftOpen',
- context: group1,
- behavior: () => {
- this.stopAnimation(this.elements).then(() => {
- this.startAnimation([smokeFan2Right, smokeFan2Left, smokeTunnelRight, smokeTunnelMajor]);
- });
- },
- });
- // 只有一个风机启动有4种情况
- this.modules.push({
- name: 'fan1RightOpen',
- context: group1,
- behavior: () => {
- this.stopAnimation(this.elements).then(() => {
- this.startAnimation([smokeFan1Right, smokeTunnelMajor]);
- });
- },
- });
- this.modules.push({
- name: 'fan2RightOpen',
- context: group1,
- behavior: () => {
- this.stopAnimation(this.elements).then(() => {
- this.startAnimation([smokeFan2Right, smokeTunnelMajor]);
- });
- },
- });
- this.modules.push({
- name: 'fan1LeftOpen',
- context: group1,
- behavior: () => {
- this.stopAnimation(this.elements).then(() => {
- this.startAnimation([smokeFan1Left, smokeTunnelRight, smokeTunnelLeft]);
- });
- },
- });
- this.modules.push({
- name: 'fan2LeftOpen',
- context: group1,
- behavior: () => {
- this.stopAnimation(this.elements).then(() => {
- this.startAnimation([smokeFan2Left, smokeTunnelRight, smokeTunnelLeft]);
- });
- },
- });
- // 只有一个风机启动有2种告示牌情况
- this.modules.push({
- name: 'fanLeftStrong',
- context: group1,
- behavior: () => {
- this.weakElements(this.elements);
- this.strongElements([...fanLeftSprites, ...commonSprites]);
- },
- });
- this.modules.push({
- name: 'fanRightStrong',
- context: group1,
- behavior: () => {
- this.weakElements(this.elements);
- this.strongElements([...fanRightSprites, ...commonSprites]);
- },
- });
- }
- /** 初始化css元素,将css元素选择器传入,该方法会将这些元素按顺序放入传入的锚点中 */
- initCssElement(selectors: { query: string; position: number[]; scale: number }[]) {
- const arr: CSS3DObject[] = [];
- selectors.forEach(({ query, position, scale }) => {
- const element = document.querySelector(query) as HTMLElement;
- if (element) {
- const css3D = new CSS3DObject(element);
- this.elements.push(css3D);
- arr.push(css3D);
- css3D.name = query;
- css3D.scale.set(scale, scale, scale);
- css3D.rotation.y = -Math.PI / 2;
- css3D.position.set(position[0], position[1], position[2]);
- this.group?.add(css3D);
- }
- });
- return arr;
- }
- /** 生成适用于 Smoke 的曲线数据,输入途径点,输出路径,如果是进风类型,首个线段将有扩散效果,出风则是末尾线段有扩散效果 */
- generateSmokePath(points: THREE.Vector3[], airIn?: boolean, airOut?: boolean) {
- if (!this.group) return;
- const result: any[] = [];
- for (let index = 1; index < points.length; index++) {
- const path0 = points[index - 1];
- const path1 = points[index];
- const path = {
- path0,
- path1,
- isSpread: false,
- spreadDirection: 0,
- // spreadRang: -10,
- };
- if (airIn && index === 1) {
- // 首个线段需要扩散,由大变小
- path.isSpread = true;
- path.spreadDirection = -1;
- }
- if (airOut && index === points.length - 1) {
- // 末个线段需要扩散,由小变大
- path.isSpread = false;
- path.spreadDirection = 1;
- }
- if (!airIn && !airOut) {
- path.isSpread = false;
- path.spreadDirection = 2;
- }
- result.push(path);
- }
- return result;
- }
- /** 添加风机描述,右侧风机是arr的第一项,左侧风机是第二项 */
- addText(arr) {
- const positions = [
- [-84.79, 0.82, 20.3],
- [-84.79, 0.82, 7.6],
- ];
- arr.forEach((e, i) => {
- this.addTextByData(e, positions[i], `text${i}`);
- });
- }
- // 从 .fanLocal.threejs.base 复制
- addTextByData(selectData, position, name) {
- if (!this.group) {
- return;
- }
- // @ts-ignore
- const screenDownText = get(VENT_PARAM, 'modalText', History_Type['type'] == 'remote' ? '国能神东煤炭集团监制' : '煤炭科学技术研究院有限公司研制');
- const screenDownTextX = 80 - (screenDownText.length - 10) * 6;
- const textArr = [
- {
- text: `智能局部通风机监测与控制系统`,
- font: 'normal 30px Arial',
- color: '#009900',
- strokeStyle: '#002200',
- x: 20,
- y: 108,
- },
- {
- text: `供风距离(m):`,
- font: 'normal 30px Arial',
- color: '#009900',
- strokeStyle: '#002200',
- x: 0,
- y: 152,
- },
- {
- text: `${
- selectData.airSupplyDistence_merge
- ? selectData.airSupplyDistence_merge
- : selectData.fchimenylength
- ? selectData.fchimenylength
- : selectData.airSupplyDistence_merge
- ? selectData.airSupplyDistence_merge
- : '-'
- }`,
- font: 'normal 30px Arial',
- color: '#009900',
- strokeStyle: '#002200',
- x: 228,
- y: 152,
- },
- {
- text: `风筒直径(mm): `,
- font: 'normal 30px Arial',
- color: '#009900',
- strokeStyle: '#002200',
- x: 0,
- y: 200,
- },
- {
- text: ` ${selectData.fchimenydiamlimit ? selectData.fchimenydiamlimit : selectData.ductDiameter_merge ? selectData.ductDiameter_merge : '-'}`,
- font: 'normal 30px Arial',
- color: '#009900',
- strokeStyle: '#002200',
- x: 220,
- y: 200,
- },
- {
- text: `故障诊断:`,
- font: 'normal 30px Arial',
- color: '#009900',
- strokeStyle: '#002200',
- x: 0,
- y: 245,
- },
- {
- text: `${selectData.warnLevel_str ? selectData.warnLevel_str : '-'}`,
- font: 'normal 30px Arial',
- color: '#009900',
- strokeStyle: '#002200',
- x: 220,
- y: 245,
- },
- {
- text: `型号功率:`,
- font: 'normal 30px Arial',
- color: '#009900',
- strokeStyle: '#002200',
- x: 0,
- y: 285,
- },
- {
- text: `${selectData.model_Power_merge ? selectData.model_Power_merge : '-'}`,
- font: 'normal 26px Arial',
- color: '#009900',
- strokeStyle: '#002200',
- x: 220,
- y: 285,
- },
- {
- text: screenDownText,
- font: 'normal 28px Arial',
- color: '#009900',
- strokeStyle: '#002200',
- x: screenDownTextX,
- y: 325,
- },
- ];
- getTextCanvas(526, 346, textArr, '').then((canvas: HTMLCanvasElement) => {
- const textMap = new THREE.CanvasTexture(canvas); // 关键一步
- const textMaterial = new THREE.MeshBasicMaterial({
- // 关于材质并未讲解 实操即可熟悉 这里是漫反射类似纸张的材质,对应的就有高光类似金属的材质.
- map: textMap, // 设置纹理贴图
- transparent: true,
- side: THREE.FrontSide, // 这里是双面渲染的意思
- });
- textMaterial.blending = THREE.CustomBlending;
- const monitorPlane = this.group?.getObjectByName(name);
- if (monitorPlane) {
- // @ts-ignore-next-line
- monitorPlane.material = textMaterial;
- } else {
- const planeGeometry = new THREE.PlaneGeometry(526, 346); // 平面3维几何体PlaneGeometry
- const planeMesh = new THREE.Mesh(planeGeometry, textMaterial);
- planeMesh.name = name;
- planeMesh.scale.set(0.0135, 0.0135, 0.0135);
- planeMesh.rotation.y = -Math.PI / 2;
- planeMesh.position.set(position[0], position[1], position[2]);
- this.group?.add(planeMesh);
- }
- });
- }
- }
- export default ModelContext;
|