mirror of https://gitee.com/cf-fz/WebCAD.git
!1669 功能:添加相机控制操作面板(仅在透视相机下显示),模型控制面板
parent
93717dcf02
commit
cb1f591b67
@ -0,0 +1,63 @@
|
||||
import { app } from "../ApplicationServices/Application";
|
||||
import { GetCurrentViewPreViewImage } from "../Common/SerializeMaterial";
|
||||
import { CameraSnapshootRecord } from "../DatabaseServices/CameraSnapshoot/CameraSnapshootRecord";
|
||||
import { RestoreCameraSnapshootRecord, SaveCameraSnapshootRecord } from "../DatabaseServices/CameraSnapshoot/CameraSnapshootRecordUtil";
|
||||
import { Command } from "../Editor/CommandMachine";
|
||||
import { CameraSnapshootStore } from "../UI/Components/CameraControlButton/CameraState/CameraSnapshootPanel";
|
||||
|
||||
|
||||
//保存相机状态
|
||||
export class Command_CameraSnapshootSave implements Command
|
||||
{
|
||||
Transparency = true;
|
||||
async exec()
|
||||
{
|
||||
const snapshot = GetCurrentViewPreViewImage(true, true) as string;
|
||||
let record = new CameraSnapshootRecord();
|
||||
SaveCameraSnapshootRecord(record);
|
||||
app.Database.CameraSnapshoots.push(record);
|
||||
CameraSnapshootStore.GetInstance().cameraSnapshootRecords.push({ snapshot, record });
|
||||
app.Saved = false;
|
||||
}
|
||||
}
|
||||
|
||||
//保存相机状态(存放到指定的位置)
|
||||
export class Command_CameraSnapshootSaveIndex implements Command
|
||||
{
|
||||
constructor(private _SaveIndex: number) { }
|
||||
Transparency = true;
|
||||
async exec()
|
||||
{
|
||||
const snapshot = GetCurrentViewPreViewImage(true, true) as string;
|
||||
let record = new CameraSnapshootRecord();
|
||||
SaveCameraSnapshootRecord(record);
|
||||
|
||||
let store = CameraSnapshootStore.GetInstance();
|
||||
|
||||
if (app.Database.CameraSnapshoots.length < this._SaveIndex)
|
||||
for (let i = app.Database.CameraSnapshoots.length; i < this._SaveIndex; i++)
|
||||
{
|
||||
app.Database.CameraSnapshoots.push(record);
|
||||
CameraSnapshootStore.GetInstance().cameraSnapshootRecords.push({ snapshot, record });
|
||||
}
|
||||
else
|
||||
{
|
||||
app.Database.CameraSnapshoots[this._SaveIndex] = record;
|
||||
store.cameraSnapshootRecords[this._SaveIndex] = { snapshot, record };
|
||||
}
|
||||
|
||||
app.Saved = false;
|
||||
}
|
||||
}
|
||||
|
||||
//还原相机状态
|
||||
export class Command_CameraSnapshootRestore implements Command
|
||||
{
|
||||
constructor(private _Index: number) { }
|
||||
async exec()
|
||||
{
|
||||
let record = app.Database.CameraSnapshoots[this._Index];
|
||||
if (record)
|
||||
RestoreCameraSnapshootRecord(record);
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
import { RenderType } from "../../GraphicsSystem/RenderType";
|
||||
import { Factory } from "../CADFactory";
|
||||
import { CADFiler } from "../CADFiler";
|
||||
import { CADObject } from "../CADObject";
|
||||
|
||||
@Factory
|
||||
export class CameraSnapshootRecord extends CADObject
|
||||
{
|
||||
Name: string = "";
|
||||
_CameraData = new CADFiler;
|
||||
|
||||
get RenderType(): RenderType
|
||||
{
|
||||
return this._CameraData.Data[7];
|
||||
}
|
||||
|
||||
//#region -------------------------File-------------------------
|
||||
|
||||
//对象从文件中读取数据,初始化自身
|
||||
ReadFile(file: CADFiler)
|
||||
{
|
||||
let ver = file.Read();
|
||||
this._CameraData.Data = file.Read();
|
||||
this.Name = file.Read();
|
||||
}
|
||||
//对象将自身数据写入到文件.
|
||||
WriteFile(file: CADFiler)
|
||||
{
|
||||
file.Write(1);
|
||||
file.Write(this._CameraData.Data);
|
||||
file.Write(this.Name);
|
||||
}
|
||||
//#endregion
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { app } from "../../ApplicationServices/Application";
|
||||
import { userConfig } from "../../Editor/UserConfig";
|
||||
import { CameraSnapshootRecord } from "./CameraSnapshootRecord";
|
||||
|
||||
export function SaveCameraSnapshootRecord(record: CameraSnapshootRecord, name: string = new Date().toLocaleString("chinese", { hour12: false }))
|
||||
{
|
||||
record.WriteAllObjectRecord();
|
||||
record._CameraData.Clear();
|
||||
record._CameraData.Write(1);
|
||||
app.Viewer.CameraCtrl.WriteFile(record._CameraData);
|
||||
record._CameraData.Write(app.Editor.UCSMatrix.toArray());
|
||||
record._CameraData.Write(userConfig._renderType);
|
||||
record.Name = name;
|
||||
}
|
||||
|
||||
export function RestoreCameraSnapshootRecord(record: CameraSnapshootRecord)
|
||||
{
|
||||
record._CameraData.Reset();
|
||||
let ver = record._CameraData.Read();
|
||||
app.Viewer.CameraCtrl.ReadFile(record._CameraData);
|
||||
app.Editor.UCSMatrix.fromArray(record._CameraData.Read());
|
||||
app.Editor.UCSMatrix = app.Editor.UCSMatrix;
|
||||
userConfig.RenderType = record._CameraData.Read();
|
||||
app.Viewer.UpdateRender();
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
import { Box3, BufferAttribute, BufferGeometry, Material, Matrix4, Mesh, Vector3 } from "three";
|
||||
import { USE_WORLD_UV, U_WORLD_MOVE, U_WORLD_REP, V_WORLD_MOVE, V_WORLD_REP } from "../../Add-on/testEntity/ParseMaterialImage";
|
||||
import { XAxis, XAxisN, YAxis, YAxisN, ZAxis, ZAxisN } from "../../Geometry/GeUtils";
|
||||
import { Orbit } from "../../Geometry/Orbit";
|
||||
|
||||
const DIRS = [XAxis, YAxis, ZAxis, XAxisN, YAxisN, ZAxisN];
|
||||
function GetFaceDir(direction: Vector3): Vector3
|
||||
{
|
||||
let absx = Math.abs(direction.x);
|
||||
let absy = Math.abs(direction.y);
|
||||
let absz = Math.abs(direction.z);
|
||||
|
||||
let face = - 1.0;
|
||||
if (absx > absz)
|
||||
{
|
||||
if (absx > absy)
|
||||
face = direction.x > 0 ? 0 : 3;
|
||||
else
|
||||
face = direction.y > 0 ? 1 : 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (absz > absy)
|
||||
face = direction.z > 0 ? 2 : 5;
|
||||
else
|
||||
face = direction.y > 0 ? 1 : 4;
|
||||
}
|
||||
return DIRS[face];
|
||||
}
|
||||
|
||||
export class GenUVForWorld
|
||||
{
|
||||
InvMtxMap = new Map<Vector3, Matrix4>();
|
||||
|
||||
private _Z = new Vector3;
|
||||
private _X = new Vector3;
|
||||
private _Y = new Vector3;
|
||||
private _Box = new Box3;
|
||||
private _Box2 = new Box3;
|
||||
GetMtxInv(normalX: number, normalY: number, normalZ: number)
|
||||
{
|
||||
this._Z.set(normalX, normalY, normalZ);
|
||||
let n = GetFaceDir(this._Z);
|
||||
let mtx = this.InvMtxMap.get(n);
|
||||
if (mtx)
|
||||
return mtx;
|
||||
|
||||
this._Z.copy(n);
|
||||
Orbit.ComputUpDirection(this._Z, this._Y, this._X);
|
||||
mtx = new Matrix4().makeBasis(this._X, this._Y, this._Z);
|
||||
mtx.getInverse(mtx);
|
||||
|
||||
this.InvMtxMap.set(n, mtx);
|
||||
return mtx;
|
||||
}
|
||||
|
||||
GenUV(mesh: Mesh<BufferGeometry, Material[]>)
|
||||
{
|
||||
if (Array.isArray(mesh.material))
|
||||
{
|
||||
let geo = mesh.geometry;
|
||||
if (!geo.boundingBox)
|
||||
geo.computeBoundingBox();
|
||||
let normals = geo.getAttribute("normal") as BufferAttribute;
|
||||
let pos = geo.getAttribute("position") as BufferAttribute;
|
||||
let uvs = geo.getAttribute("uv") as BufferAttribute;
|
||||
for (let i = 0; i < mesh.material.length; i++)
|
||||
{
|
||||
let mtl = mesh.material[i];
|
||||
if (mtl[USE_WORLD_UV])
|
||||
{
|
||||
this._Box.makeEmpty();
|
||||
let g = mesh.geometry.groups[i];
|
||||
for (let y = 0; y < g.count; y++)
|
||||
{
|
||||
let index = (y + g.start) * 3;
|
||||
this._X.set(pos.array[index], pos.array[index + 1], pos.array[index + 2]);
|
||||
this._Box.expandByPoint(this._X);
|
||||
}
|
||||
|
||||
for (let y = 0; y < g.count; y++)
|
||||
{
|
||||
let index = (y + g.start) * 3;
|
||||
let mtx = this.GetMtxInv(normals.array[index], normals.array[index + 1], normals.array[index + 2]);
|
||||
|
||||
this._X.set(pos.array[index], pos.array[index + 1], pos.array[index + 2]);
|
||||
|
||||
this._X.applyMatrix4(mtx);
|
||||
|
||||
this._Box2.copy(this._Box).applyMatrix4(mtx);
|
||||
|
||||
//@ts-ignore
|
||||
uvs.array[(y + g.start) * 2] = (((this._X.x - (this._Box2.min.x + this._Box2.max.x) * 0.5)) * 1e-2 + mtl[U_WORLD_MOVE]) * mtl[U_WORLD_REP] + 0.5;
|
||||
//@ts-ignore
|
||||
uvs.array[(y + g.start) * 2 + 1] = (((this._X.y - (this._Box2.min.y + this._Box2.max.y) * 0.5)) * 1e-2 - mtl[V_WORLD_MOVE]) * mtl[V_WORLD_REP] + 0.5;
|
||||
}
|
||||
uvs.needsUpdate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
let mtl = mesh.material;
|
||||
if (mtl[USE_WORLD_UV])
|
||||
{
|
||||
let geo = mesh.geometry;
|
||||
if (!geo.boundingBox)
|
||||
geo.computeBoundingBox();
|
||||
|
||||
let normals = geo.getAttribute("normal") as BufferAttribute;
|
||||
let pos = geo.getAttribute("position") as BufferAttribute;
|
||||
let uvs = geo.getAttribute("uv") as BufferAttribute;
|
||||
for (let y = 0; y < pos.count; y++)
|
||||
{
|
||||
let index = y * 3;
|
||||
let mtx = this.GetMtxInv(normals.array[index], normals.array[index + 1], normals.array[index + 2]);
|
||||
|
||||
this._X.set(pos.array[index], pos.array[index + 1], pos.array[index + 2]);
|
||||
|
||||
this._X.applyMatrix4(mtx);
|
||||
|
||||
this._Box.copy(geo.boundingBox);
|
||||
this._Box.applyMatrix4(mtx);
|
||||
|
||||
//@ts-ignore
|
||||
uvs.array[y * 2] = (((this._X.x - (this._Box.min.x + this._Box.max.x) * 0.5)) * 1e-2 + mtl[U_WORLD_MOVE]) * mtl[U_WORLD_REP] + 0.5;
|
||||
//@ts-ignore
|
||||
uvs.array[y * 2 + 1] = (((this._X.y - (this._Box.min.y + this._Box.max.y) * 0.5)) * 1e-2 + mtl[V_WORLD_MOVE]) * mtl[V_WORLD_REP] + 0.5;
|
||||
}
|
||||
uvs.needsUpdate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,183 @@
|
||||
import { Button, Icon, Label, NumericInput, Popover, Position, Radio, RadioGroup, Slider, Switch, Tooltip } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
import { app, CameraRoamType } from '../../../../ApplicationServices/Application';
|
||||
import { CameraType } from '../../../../GraphicsSystem/CameraUpdate';
|
||||
import CameraSettingStore, { CameraMode } from './CameraSettingStore';
|
||||
import { CameraSettingIcon } from './CameraState';
|
||||
|
||||
@observer
|
||||
export default class CameraSetting extends Component
|
||||
{
|
||||
CameraSettingStore: CameraSettingStore = CameraSettingStore.GetSingleInstance();
|
||||
AlterCameraVision = (deg: number): void =>
|
||||
{
|
||||
this.CameraSettingStore.cameraFov = deg;
|
||||
app.Viewer.CameraControl.Fov = deg;
|
||||
app.Editor.UpdateScreen();
|
||||
};
|
||||
render()
|
||||
{
|
||||
const { CameraSettingStore } = this;
|
||||
return (
|
||||
<Popover
|
||||
onOpening={() =>
|
||||
{
|
||||
CameraSettingStore.cameraType = app.Viewer.CameraControl.CameraType;
|
||||
}}
|
||||
interactionKind='click'
|
||||
placement={Position.TOP}
|
||||
minimal
|
||||
content={
|
||||
<div className='CameraSettingPanel'>
|
||||
<div className='CameraSettingItem'>
|
||||
<Label>锁定相机旋转</Label>
|
||||
<Switch size={30} disabled={app.Viewer.CameraCtrl.CameraType === CameraType.OrthographicCamera}
|
||||
onChange={(): void =>
|
||||
{
|
||||
CameraSettingStore.isCameraRotateLock = !CameraSettingStore.isCameraRotateLock;
|
||||
app.Viewer.CameraControl.DisableRotate = CameraSettingStore.isCameraRotateLock;
|
||||
}} />
|
||||
</div>
|
||||
<div className='CameraSettingItem'>
|
||||
<Label>相机类型</Label>
|
||||
<RadioGroup selectedValue={CameraSettingStore.cameraType} inline
|
||||
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
||||
{
|
||||
CameraSettingStore.cameraType = (parseInt((e.target as HTMLInputElement).value));
|
||||
app.Viewer.CameraCtrl.CameraType = CameraSettingStore.cameraType;
|
||||
}} >
|
||||
<Radio value={CameraType.PerspectiveCamera} label='透视' />
|
||||
<Radio value={CameraType.OrthographicCamera} label='正交' />
|
||||
</RadioGroup>
|
||||
</div>
|
||||
<div className='CameraSettingItem'>
|
||||
<Label>相机模式</Label>
|
||||
<RadioGroup selectedValue={CameraSettingStore.cameraMode} inline
|
||||
disabled={app.Viewer.CameraCtrl.CameraType === CameraType.OrthographicCamera}
|
||||
onChange={(e: React.FormEvent<HTMLInputElement>) =>
|
||||
{
|
||||
CameraSettingStore.cameraMode = (e.target as HTMLInputElement).value as CameraMode;
|
||||
app.CameraRoamType = CameraSettingStore.cameraMode === "飞行" ? CameraRoamType.Fly : CameraRoamType.Walk;
|
||||
}} >
|
||||
<Radio value={CameraMode.walk} label='行走' />
|
||||
<Radio value={CameraMode.fly} label='飞行' />
|
||||
</RadioGroup>
|
||||
</div>
|
||||
<div className='CameraSettingItem'>
|
||||
<Label>高度</Label>
|
||||
<div>
|
||||
<Slider
|
||||
disabled={app.Viewer.CameraCtrl.CameraType === CameraType.OrthographicCamera}
|
||||
max={5000}
|
||||
min={0}
|
||||
value={CameraSettingStore.cameraAltitude > 5000 ? 5000 : CameraSettingStore.cameraAltitude}
|
||||
onChange={(e) =>
|
||||
{
|
||||
CameraSettingStore.cameraAltitude = e;
|
||||
|
||||
let dif = app.Viewer.Camera.position.z - e;
|
||||
app.Viewer.Camera.position.z = e;
|
||||
app.Viewer.CameraControl.Target.z -= dif;
|
||||
app.Viewer.CameraControl.UpdateCameraMatrix();
|
||||
app.Editor.UpdateScreen();
|
||||
}} />
|
||||
<NumericInput style={{ fontSize: '12px' }} value={CameraSettingStore.cameraAltitude}
|
||||
disabled={app.Viewer.CameraCtrl.CameraType === CameraType.OrthographicCamera}
|
||||
onValueChange={(e) =>
|
||||
{
|
||||
if (e < 0) return;
|
||||
CameraSettingStore.cameraAltitude = e;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='CameraSettingItem'>
|
||||
<Label>视野</Label>
|
||||
<div>
|
||||
<div className='CameraVison'>
|
||||
<Slider
|
||||
disabled={app.Viewer.CameraCtrl.CameraType === CameraType.OrthographicCamera}
|
||||
value={CameraSettingStore.cameraFov < 30 ? 30 : CameraSettingStore.cameraFov}
|
||||
max={120}
|
||||
min={30}
|
||||
onChange={(e: number) =>
|
||||
{
|
||||
if (e <= 35) e = 30;
|
||||
if (e >= 55 && e <= 65) e = 60;
|
||||
if (e >= 115) e = 120;
|
||||
this.AlterCameraVision(e);
|
||||
}}
|
||||
/>
|
||||
<div className='VisionMarker'
|
||||
onClick={() => { this.AlterCameraVision(30); }}
|
||||
>
|
||||
<Icon icon='caret-up' />
|
||||
人眼
|
||||
</div>
|
||||
<div className='VisionMarker'
|
||||
onClick={() => { this.AlterCameraVision(60); }}
|
||||
>
|
||||
<Icon icon='caret-up' />
|
||||
标准
|
||||
</div>
|
||||
<div className='VisionMarker'
|
||||
onClick={() => { this.AlterCameraVision(120); }}
|
||||
>
|
||||
<Icon icon='caret-up' />
|
||||
广角
|
||||
</div>
|
||||
</div>
|
||||
<NumericInput allowNumericCharactersOnly={false}
|
||||
value={CameraSettingStore.cameraFov}
|
||||
min={0}
|
||||
disabled={app.Viewer.CameraCtrl.CameraType === CameraType.OrthographicCamera}
|
||||
onValueChange={(e) =>
|
||||
{
|
||||
if (e === NaN) return;
|
||||
if (e > 120) return;
|
||||
this.AlterCameraVision(e);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='CameraSettingItem'>
|
||||
<Label>行走速度</Label>
|
||||
<div>
|
||||
<Slider
|
||||
disabled={app.Viewer.CameraCtrl.CameraType === CameraType.OrthographicCamera}
|
||||
max={5}
|
||||
min={1}
|
||||
value={CameraSettingStore.walkSpeed}
|
||||
onChange={(e) =>
|
||||
{
|
||||
CameraSettingStore.walkSpeed = e;
|
||||
app.CameraFlySpeed = e;
|
||||
}} />
|
||||
<NumericInput
|
||||
max={5}
|
||||
min={1}
|
||||
allowNumericCharactersOnly={true}
|
||||
disabled={app.Viewer.CameraCtrl.CameraType === CameraType.OrthographicCamera}
|
||||
value={CameraSettingStore.walkSpeed} onValueChange={(e) =>
|
||||
{
|
||||
if (e > 5 || e < 1) return;
|
||||
CameraSettingStore.walkSpeed = e;
|
||||
app.CameraFlySpeed = e;
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Tooltip content='相机设置' minimal>
|
||||
<Button
|
||||
icon={<CameraSettingIcon subIcon='cog' />}
|
||||
minimal
|
||||
small
|
||||
/>
|
||||
</Tooltip>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
import { observable } from "mobx";
|
||||
import { end } from "xaop";
|
||||
import { app, CameraRoamType } from "../../../../ApplicationServices/Application";
|
||||
import { CameraType } from "../../../../GraphicsSystem/CameraUpdate";
|
||||
|
||||
export enum CameraView
|
||||
{
|
||||
orthographic = '正交相机',
|
||||
perspective = '透视相机',
|
||||
}
|
||||
export enum CameraMode
|
||||
{
|
||||
fly = '飞行',
|
||||
walk = '行走',
|
||||
}
|
||||
export default class CameraSettingStore
|
||||
{
|
||||
/**
|
||||
* @description 锁定相机视角
|
||||
*/
|
||||
@observable isCameraRotateLock: boolean = false;
|
||||
/**
|
||||
* @description 相机高度
|
||||
*/
|
||||
@observable cameraAltitude: number = 0;
|
||||
/**
|
||||
* @description 相机视角
|
||||
*/
|
||||
@observable cameraFov: number = 60;
|
||||
/**
|
||||
* @description 相机类型
|
||||
*/
|
||||
@observable cameraType: CameraType = CameraType.PerspectiveCamera;
|
||||
/**
|
||||
* @description 相机模式
|
||||
*/
|
||||
@observable cameraMode: CameraMode = CameraMode.walk;
|
||||
/**
|
||||
* @description 行走速度
|
||||
*/
|
||||
@observable walkSpeed: number = 3;
|
||||
|
||||
private static _SingleInstance: CameraSettingStore;
|
||||
static GetSingleInstance = (): CameraSettingStore =>
|
||||
{
|
||||
if (this._SingleInstance) return this._SingleInstance;
|
||||
this._SingleInstance = new CameraSettingStore();
|
||||
return this._SingleInstance;
|
||||
};
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this.cameraMode = app.CameraRoamType === CameraRoamType.Fly ? CameraMode.fly : CameraMode.walk;
|
||||
|
||||
this.cameraType = app.Viewer.CameraControl.CameraType;
|
||||
end(app.Viewer.CameraControl, app.Viewer.CameraControl.SwitchCamera, () =>
|
||||
{
|
||||
this.cameraType = app.Viewer.CameraControl.CameraType;
|
||||
});
|
||||
|
||||
end(app.Viewer.CameraControl, app.Viewer.CameraControl.UpdateCameraMatrix, () =>
|
||||
{
|
||||
this.cameraAltitude = app.Viewer.Camera.position.z;
|
||||
});
|
||||
|
||||
end(app, app.OpenFile, () =>
|
||||
{
|
||||
this.cameraAltitude = app.Viewer.Camera.position.z;
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,259 @@
|
||||
.CameraState
|
||||
{
|
||||
// width : 200px;
|
||||
height : 40px;
|
||||
position : absolute;
|
||||
background-color: rgba(245, 248, 250, .9);
|
||||
bottom : 10px;
|
||||
right : 10px;
|
||||
box-sizing : border-box;
|
||||
padding : 10px;
|
||||
display : flex;
|
||||
justify-content : space-around;
|
||||
align-items : center;
|
||||
border-radius : 5px;
|
||||
.BtnContainer
|
||||
{
|
||||
position : relative;
|
||||
width : 30px;
|
||||
height : 24px;
|
||||
display : flex;
|
||||
align-items : center;
|
||||
justify-content: center;
|
||||
flex-direction : column
|
||||
}
|
||||
.CameraSettingIcon
|
||||
{
|
||||
.BtnContainer();
|
||||
>span
|
||||
{
|
||||
position: absolute;
|
||||
}
|
||||
>span:nth-child(1)
|
||||
{
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
>span:nth-child(2)
|
||||
{
|
||||
left : 15px;
|
||||
top : 10px;
|
||||
// color: #CED9E0;
|
||||
color: #293743;
|
||||
}
|
||||
}
|
||||
.ResetCamera
|
||||
{
|
||||
width : 44px;
|
||||
height: 24px;
|
||||
>span
|
||||
{
|
||||
margin-top: 2px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.CameraSettingPanel
|
||||
{
|
||||
width : 300px;
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
box-sizing : border-box;
|
||||
padding : 25px;
|
||||
.CameraSettingItem
|
||||
{
|
||||
display : flex;
|
||||
justify-content: space-between;
|
||||
align-items : center;
|
||||
.bp3-label{
|
||||
font-size : 14px;
|
||||
font-weight: bold;
|
||||
color : #4e5257;
|
||||
}
|
||||
.bp3-radio
|
||||
{
|
||||
font-weight : bold;
|
||||
color : #4e5257;
|
||||
margin-right: 10px;
|
||||
}
|
||||
&:nth-child(4),&:nth-child(5),&:nth-child(6)
|
||||
{
|
||||
flex-direction: column;
|
||||
align-items : flex-start;
|
||||
|
||||
>div
|
||||
{
|
||||
width : 100%;
|
||||
box-sizing : border-box;
|
||||
display : flex;
|
||||
justify-content: space-between;
|
||||
.bp3-input
|
||||
{
|
||||
width: 45px;
|
||||
}
|
||||
.bp3-button-group
|
||||
{
|
||||
height: 30px;
|
||||
>button
|
||||
{
|
||||
width : 15px !important;
|
||||
min-width: 10px;
|
||||
}
|
||||
}
|
||||
.bp3-slider
|
||||
{
|
||||
min-width: 180px;
|
||||
width : 10px;
|
||||
.bp3-slider-handle
|
||||
{
|
||||
transform : scale(80%);
|
||||
border-radius: 25px;
|
||||
}
|
||||
.bp3-slider-axis
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
.bp3-slider-label
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&:nth-child(4)
|
||||
{
|
||||
.bp3-slider
|
||||
{
|
||||
margin-top: 8px;
|
||||
}
|
||||
.bp3-label
|
||||
{
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
&:nth-child(6)
|
||||
{
|
||||
margin-top: 30px;
|
||||
.bp3-slider
|
||||
{
|
||||
margin-top: 8px;
|
||||
}
|
||||
.bp3-label
|
||||
{
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
.CameraVison
|
||||
{
|
||||
position: relative;
|
||||
.VisionMarker
|
||||
{
|
||||
position : absolute;
|
||||
display : flex;
|
||||
flex-direction: column;
|
||||
top : 20px;
|
||||
user-select : none;
|
||||
color : #4e5257;
|
||||
font-weight : bold;
|
||||
cursor : pointer;
|
||||
&:hover
|
||||
{
|
||||
color: #1374bd;
|
||||
}
|
||||
&:nth-child(2)
|
||||
{
|
||||
left: -5px;
|
||||
}
|
||||
&:nth-child(3)
|
||||
{
|
||||
left: 54px;
|
||||
}
|
||||
&:nth-child(4)
|
||||
{
|
||||
width : 24px;
|
||||
height: 31px;
|
||||
top : 19px;
|
||||
left : 170px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.SaveCameraVision
|
||||
{
|
||||
box-sizing: border-box;
|
||||
padding : 10px;
|
||||
display : flex;
|
||||
flex-wrap : wrap;
|
||||
>button
|
||||
{
|
||||
align-self : flex-start;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.SaveCamera
|
||||
{
|
||||
box-sizing : border-box;
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
justify-content: center;
|
||||
align-items : center;
|
||||
padding : 30px;
|
||||
border : 1px dashed lightgray;
|
||||
margin-right : 5px;
|
||||
border-radius : 5px;
|
||||
user-select : none;
|
||||
cursor : pointer;
|
||||
}
|
||||
.CameraVison
|
||||
{
|
||||
position: relative;
|
||||
>img
|
||||
{
|
||||
border : 1px solid #1374bd;
|
||||
border-radius: 5px;
|
||||
margin-right : 5px;
|
||||
width : 134px;
|
||||
height : 93px;
|
||||
}
|
||||
& >button:nth-child(2)
|
||||
{
|
||||
position: absolute;
|
||||
top : 5px;
|
||||
right : 10px;
|
||||
}
|
||||
.VisionName
|
||||
{
|
||||
position : absolute;
|
||||
bottom : 5px;
|
||||
left : 0;
|
||||
width : 134px;
|
||||
display : flex;
|
||||
justify-content: center;
|
||||
box-shadow : 0 -10px 5px rgba(1, 1, 1, .5) inset;
|
||||
border-radius : 0 0 5px 5px;
|
||||
.bp3-editable-text
|
||||
{
|
||||
text-align : center;
|
||||
color : white;
|
||||
font-weight : bold;
|
||||
input
|
||||
{
|
||||
color : black;
|
||||
}
|
||||
}
|
||||
}
|
||||
.VisionIndex
|
||||
{
|
||||
position : absolute;
|
||||
width : 20px;
|
||||
height : 20px;
|
||||
top : 5px;
|
||||
left : 5px;
|
||||
color : white;
|
||||
font-weight : bold;
|
||||
font-size : 14px;
|
||||
// box-shadow : 0 0 10px rgba(255, 255, 255, .7) inset;
|
||||
border-radius: 100%;
|
||||
text-align : center;
|
||||
line-height : 20px;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
import { Button, FocusStyleManager, Icon, IconName, Tooltip } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
import { app } from '../../../../ApplicationServices/Application';
|
||||
import { GetBox } from '../../../../Geometry/GeUtils';
|
||||
import { CameraType } from '../../../../GraphicsSystem/CameraUpdate';
|
||||
import ModifyModelStore from '../../ToolBar/ModifyModel/ModifyModelStore';
|
||||
import CameraSetting from './CameraSetting';
|
||||
import CameraSnapshootPanel from './CameraSnapshootPanel';
|
||||
import './CameraState.less';
|
||||
@observer
|
||||
export default class CameraState extends Component
|
||||
{
|
||||
ModifyModelStore: ModifyModelStore = ModifyModelStore.GetSingleInstance();
|
||||
ResetCameraVisoin = async () =>
|
||||
{
|
||||
//重置视图
|
||||
if (app.Viewer.CameraCtrl.CameraType === CameraType.OrthographicCamera)
|
||||
app.Viewer.ZoomAll();
|
||||
else
|
||||
{
|
||||
//尝试移动到中心
|
||||
let box = GetBox(app.Viewer.Scene, true);
|
||||
if (!box.isEmpty())
|
||||
{
|
||||
app.Viewer.CameraControl.Target.sub(app.Viewer.Camera.position);
|
||||
box.getCenter(app.Viewer.Camera.position);
|
||||
app.Viewer.CameraControl.Target.add(app.Viewer.Camera.position);
|
||||
app.Viewer.CameraControl.UpdateCameraMatrix();
|
||||
}
|
||||
}
|
||||
app.Viewer.UpdateRender();
|
||||
};
|
||||
|
||||
componentDidMount()
|
||||
{
|
||||
FocusStyleManager.onlyShowFocusOnTabs();
|
||||
}
|
||||
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<div className='CameraState'>
|
||||
<CameraSnapshootPanel />
|
||||
<CameraSetting />
|
||||
<Tooltip content='重置视图' minimal>
|
||||
<Button
|
||||
className='ResetCamera'
|
||||
icon={<Icon icon='fullscreen' size={14} />}
|
||||
minimal
|
||||
small
|
||||
onClick={() =>
|
||||
{
|
||||
this.ResetCameraVisoin();
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function CameraSettingIcon(props: { subIcon: IconName; }): JSX.Element
|
||||
{
|
||||
return (
|
||||
<div className='CameraSettingIcon'>
|
||||
<Icon icon='mobile-video' size={20} />
|
||||
<Icon icon={props.subIcon} size={12} />
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { ZINDEX } from '../../../../Common/ZIndex';
|
||||
import CameraState from './CameraState';
|
||||
|
||||
export default class CameraStateManage
|
||||
{
|
||||
private _CameraStateContainer: HTMLElement;
|
||||
constructor()
|
||||
{
|
||||
this._CameraStateContainer = document.createElement('div');
|
||||
this._CameraStateContainer.id = 'CameraState';
|
||||
this._CameraStateContainer.style.zIndex = ZINDEX.CameraState;
|
||||
document.getElementById('Webgl').appendChild(this._CameraStateContainer);
|
||||
}
|
||||
|
||||
RenderCameraState()
|
||||
{
|
||||
ReactDOM.render(<CameraState />, this._CameraStateContainer);
|
||||
}
|
||||
}
|
@ -0,0 +1,242 @@
|
||||
.ModifyModelPanel
|
||||
{
|
||||
width : 100%;
|
||||
min-height : 50px;
|
||||
max-height : 700px;
|
||||
overflow : auto;
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
box-sizing : border-box;
|
||||
// padding : 0 10px;
|
||||
cursor : default;
|
||||
.ModifyModelPanelWrapper
|
||||
{
|
||||
width : 100%;
|
||||
height : 100%;
|
||||
position: relative;
|
||||
.ResizeBar
|
||||
{
|
||||
width : 100%;
|
||||
height : 10px;
|
||||
position: absolute;
|
||||
cursor : s-resize;
|
||||
&.AtTop
|
||||
{
|
||||
top: 0;
|
||||
}
|
||||
&.AtBottom
|
||||
{
|
||||
bottom : 0;
|
||||
display : flex;
|
||||
justify-content: center;
|
||||
z-index : 99;
|
||||
}
|
||||
}
|
||||
.ModifyModulePanelTitle
|
||||
{
|
||||
width : 100%;
|
||||
display : flex;
|
||||
flex-direction: column;
|
||||
align-items : center;
|
||||
.TitleInfo
|
||||
{
|
||||
display : flex;
|
||||
width : 100%;
|
||||
justify-content: space-between;
|
||||
align-items : center;
|
||||
font-size : 16px;
|
||||
}
|
||||
}
|
||||
.ModuleBaseParams
|
||||
{
|
||||
width : 100%;
|
||||
display : flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 10px;
|
||||
.CollapseOpenTitle
|
||||
{
|
||||
width : 100%;
|
||||
display : flex;
|
||||
justify-content: space-between;
|
||||
align-items : center;
|
||||
margin-bottom : 10px;
|
||||
font-weight : bold;
|
||||
cursor : pointer;
|
||||
}
|
||||
.CollapseContent
|
||||
{
|
||||
display : flex;
|
||||
flex-direction: column;
|
||||
box-sizing : border-box;
|
||||
padding : 0 10px;
|
||||
.ParamsItem
|
||||
{
|
||||
display : flex;
|
||||
flex-direction: column;
|
||||
.bp3-button-group{
|
||||
display: none;
|
||||
}
|
||||
.ParamsSlider
|
||||
{
|
||||
width : 100%;
|
||||
height : 35px;
|
||||
display : flex;
|
||||
flex-direction : row;
|
||||
justify-content: space-between;
|
||||
>span
|
||||
{
|
||||
line-height: 35px;
|
||||
}
|
||||
.bp3-input
|
||||
{
|
||||
width : 50px;
|
||||
font-size: 12px;
|
||||
}
|
||||
// .bp3-button-group
|
||||
// {
|
||||
// height: 30px;
|
||||
// >button
|
||||
// {
|
||||
// width : 15px;
|
||||
// min-width: 1px;
|
||||
// }
|
||||
// }
|
||||
.bp3-slider
|
||||
{
|
||||
min-width : 10px;
|
||||
width : 140px;
|
||||
margin-top: 8px;
|
||||
.bp3-slider-axis
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
.bp3-slider-handle
|
||||
{
|
||||
transform : scale(80%);
|
||||
border-radius: 50px;
|
||||
}
|
||||
.bp3-slider-label
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.bp3-checkbox
|
||||
{
|
||||
align-self: flex-end;
|
||||
margin : 10px 0 0 0;
|
||||
}
|
||||
.ParamsRotate
|
||||
{
|
||||
display : flex;
|
||||
flex-direction : row;
|
||||
justify-content: space-between;
|
||||
align-items : center;
|
||||
margin-top : 10px;
|
||||
>div
|
||||
{
|
||||
display : flex;
|
||||
flex-direction: column;
|
||||
align-items : center;
|
||||
>div
|
||||
{
|
||||
padding : 2px;
|
||||
display : flex;
|
||||
flex-direction: row;
|
||||
align-items : center;
|
||||
border: 1px solid lightgray;
|
||||
.RotateBox
|
||||
{
|
||||
width : 20px;
|
||||
height : 20px;
|
||||
box-sizing : border-box;
|
||||
border : 1px solid lightgray;
|
||||
border-radius : 100%;
|
||||
position : relative;
|
||||
transform : rotate(135deg);
|
||||
background-color: rgba(92, 112, 128, 0.2);
|
||||
>span
|
||||
{
|
||||
position : absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
.bp3-input
|
||||
{
|
||||
min-width : 0px;
|
||||
width : 30px;
|
||||
font-size : 8px;
|
||||
padding : 0;
|
||||
margin-left: 6px;
|
||||
//输入框边框不是border是box-shadow
|
||||
box-shadow: none;
|
||||
}
|
||||
// .bp3-button-group
|
||||
// {
|
||||
// height : 20px;
|
||||
// margin-left: -14px;
|
||||
// z-index : 20;
|
||||
// >button
|
||||
// {
|
||||
// min-width : 0px;
|
||||
// width : 15px;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
.ParamsPosition
|
||||
{
|
||||
display : flex;
|
||||
flex-direction : row;
|
||||
justify-content: space-between;
|
||||
margin-top : 10px;
|
||||
align-items : center;
|
||||
>div
|
||||
{
|
||||
display : flex;
|
||||
flex-direction: column;
|
||||
align-items : center;
|
||||
&:nth-child(2)
|
||||
{
|
||||
.bp3-input-group::before{
|
||||
background-color: red;
|
||||
}
|
||||
}
|
||||
&:nth-child(3)
|
||||
{
|
||||
.bp3-input-group::before{
|
||||
background-color: #00ff00;
|
||||
}
|
||||
}
|
||||
&:nth-child(4)
|
||||
{
|
||||
.bp3-input-group::before{
|
||||
background-color: blue;
|
||||
}
|
||||
}
|
||||
.bp3-input-group
|
||||
{
|
||||
display : flex;
|
||||
position: relative;
|
||||
&::before{
|
||||
height : 100%;
|
||||
width : 2px;
|
||||
content : '';
|
||||
position : absolute;
|
||||
z-index : 23;
|
||||
}
|
||||
}
|
||||
.bp3-input
|
||||
{
|
||||
min-width: 0px;
|
||||
width : 59px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { ZINDEX } from '../../../../Common/ZIndex';
|
||||
import ModifyModelPanel from './ModifyModelPanel';
|
||||
|
||||
export default class ModifyModelManage
|
||||
{
|
||||
private _ModifyPanel: HTMLElement;
|
||||
constructor()
|
||||
{
|
||||
this._ModifyPanel = document.createElement("div");
|
||||
this._ModifyPanel.id = "ModifyModel";
|
||||
this._ModifyPanel.style.zIndex = ZINDEX.CommandInput;
|
||||
document.getElementById("Webgl").appendChild(this._ModifyPanel);
|
||||
}
|
||||
RenderModifyModelPanel()
|
||||
{
|
||||
ReactDOM.render(<ModifyModelPanel />, this._ModifyPanel);
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
import { Divider } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component, RefObject } from 'react';
|
||||
import './ModifyModel.less';
|
||||
import ModifyModelStore from './ModifyModelStore';
|
||||
import ModuleBaseParams from './ModuleBaseParams';
|
||||
|
||||
@observer
|
||||
export default class ModifyModelPanel extends Component
|
||||
{
|
||||
ModifyModelPanelRef: RefObject<HTMLDivElement> = React.createRef<HTMLDivElement>();
|
||||
ModifyModelStore: ModifyModelStore = ModifyModelStore.GetSingleInstance();
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<div className='ModifyModelPanel' id='ModifyModelPanel' ref={this.ModifyModelPanelRef}>
|
||||
<main className='ModifyModelPanelWrapper'>
|
||||
<div className='ModifyModulePanelTitle'>
|
||||
<div className='TitleInfo'>
|
||||
<div>
|
||||
{this.ModifyModelStore.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Divider />
|
||||
<ModuleBaseParams />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,279 @@
|
||||
import { Intent } from "@blueprintjs/core";
|
||||
import { action, observable } from "mobx";
|
||||
import { Box3, Euler, MathUtils, Matrix4, Vector3 } from "three";
|
||||
import { end } from "xaop";
|
||||
import { app } from "../../../../ApplicationServices/Application";
|
||||
import { UpdateDraw } from "../../../../Common/Status";
|
||||
import { CommandHistoryRecord } from "../../../../DatabaseServices/CommandHistoryRecord";
|
||||
import { CreateObjectData } from "../../../../DatabaseServices/CreateObjectData";
|
||||
import { Board } from "../../../../DatabaseServices/Entity/Board";
|
||||
import { Entity } from "../../../../DatabaseServices/Entity/Entity";
|
||||
import { EntityRef } from "../../../../DatabaseServices/Entity/EntityRef";
|
||||
import { commandMachine } from "../../../../Editor/CommandMachine";
|
||||
import { CommandState } from "../../../../Editor/CommandState";
|
||||
import { SelectSetBase } from "../../../../Editor/SelectBase";
|
||||
import { AppToaster } from "../../Toaster";
|
||||
import { ModuleAxis } from "./ModuleBaseParams";
|
||||
|
||||
export default class ModifyModelStore
|
||||
{
|
||||
@observable isCollapseOpened: boolean = true;
|
||||
@observable isProportionalZoom: boolean = true;
|
||||
@observable module_Length: number = 0;
|
||||
@observable module_Width: number = 0;
|
||||
@observable module_Height: number = 0;
|
||||
@observable offGround: number = 0;//离地
|
||||
@observable rotateX: number = 0;
|
||||
@observable rotateY: number = 0;
|
||||
@observable rotateZ: number = 0;
|
||||
@observable positionX: number = 0;
|
||||
@observable positionY: number = 0;
|
||||
@observable positionZ: number = 0;
|
||||
@observable name: string = "";
|
||||
sliderMax: number = 5000;
|
||||
@observable _EntityIds: number[] = [];
|
||||
|
||||
private constructor()
|
||||
{
|
||||
let selectCtrl = app.Editor.SelectCtrl;
|
||||
end(selectCtrl, selectCtrl.AddSelect, (ss: SelectSetBase) =>
|
||||
{
|
||||
if (selectCtrl.SelectSet.SelectObjectCount > 50)
|
||||
observable(this._EntityIds).replace([]);
|
||||
else
|
||||
observable(this._EntityIds).replace(selectCtrl.SelectSet.SelectEntityList.map(e => e.Id.Index));
|
||||
|
||||
this.UpdateUIData();
|
||||
});
|
||||
|
||||
end(app.Database.hm, app.Database.hm.RedoEvent, (cmdName: string, historyRec: CommandHistoryRecord) =>
|
||||
{
|
||||
for (let [h, hs] of historyRec.HistoryList)
|
||||
{
|
||||
if (h === app.Database.ModelSpace.Id)
|
||||
{
|
||||
for (let h of hs)
|
||||
{
|
||||
if (h.redoData instanceof CreateObjectData)
|
||||
{
|
||||
if (this._EntityIds.includes(h.redoData.Object.Id.Index))
|
||||
{
|
||||
this.UpdateUIData();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (this._EntityIds.includes(h.Index))
|
||||
this.UpdateUIData();
|
||||
}
|
||||
});
|
||||
|
||||
end(app.Database.hm, app.Database.hm.UndoEvent, (cmdName: string, historyRec: CommandHistoryRecord) =>
|
||||
{
|
||||
//撤销时不会出现CreateObjectData.(因为我们使用标记删除的方式,所以不可能)
|
||||
for (let [h, hs] of historyRec.HistoryList)
|
||||
{
|
||||
if (this._EntityIds.includes(h.Index))
|
||||
this.UpdateUIData();
|
||||
}
|
||||
});
|
||||
|
||||
app.CommandReactor.OnCommandEnd((cmdName, changeObjects, createObjects) =>
|
||||
{
|
||||
let set = new Set(this._EntityIds);
|
||||
for (let e of changeObjects)
|
||||
{
|
||||
if (set.has(e.Id.Index))
|
||||
{
|
||||
this.UpdateUIData();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
end(app.Database, app.Database.FileRead, () =>
|
||||
{
|
||||
observable(this._EntityIds).replace([]);
|
||||
this.UpdateUIData();
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
UpdateUIData()
|
||||
{
|
||||
let ents: Entity[] = this._EntityIds.map(id => app.Database.GetObjectId(id).Object).filter(en => en && en instanceof Entity) as Entity[];
|
||||
if (!ents.length) return;
|
||||
|
||||
this.InitBox(ents);
|
||||
|
||||
this.offGround = this.__Box.min.z;
|
||||
|
||||
if (this._EntityIds.length === 1)
|
||||
{
|
||||
let ent = app.Database.GetObjectId(this._EntityIds[0])?.Object as Entity;
|
||||
if (!ent) return;
|
||||
|
||||
if (ent instanceof Board)
|
||||
{
|
||||
this.name = "[板]" + ent.Name;
|
||||
this.module_Length = ent.Height;
|
||||
this.module_Width = ent.Width;
|
||||
this.module_Height = ent.Thickness;
|
||||
}
|
||||
else if (ent instanceof EntityRef)
|
||||
{
|
||||
this.name = "[模型]" + ent.Url;
|
||||
let size = ent.CurSize;
|
||||
this.module_Length = size.x;
|
||||
this.module_Width = size.y;
|
||||
this.module_Height = size.z;
|
||||
if (size.x > 2000 || size.y > 2000 || size.z > 2000)
|
||||
this.sliderMax = 5000;
|
||||
else if (size.x < 500 && size.y < 500 && size.z < 500)
|
||||
this.sliderMax = 1000;
|
||||
else
|
||||
this.sliderMax = 2000;
|
||||
}
|
||||
|
||||
let p = ent.Position;
|
||||
this.positionX = p.x;
|
||||
this.positionY = p.y;
|
||||
this.positionZ = p.z;
|
||||
|
||||
let ocs = ent.OCS.setPosition(0, 0, 0);
|
||||
let eu = new Euler().setFromRotationMatrix(ocs, "ZYX");
|
||||
this.rotateX = eu.x * MathUtils.RAD2DEG;
|
||||
this.rotateY = eu.y * MathUtils.RAD2DEG;
|
||||
this.rotateZ = eu.z * MathUtils.RAD2DEG;
|
||||
}
|
||||
else
|
||||
{
|
||||
let size = this.__Box.getSize(new Vector3);
|
||||
this.module_Length = size.x;
|
||||
this.module_Width = size.y;
|
||||
this.module_Height = size.z;
|
||||
|
||||
this.positionX = this.__Box.min.x;
|
||||
this.positionY = this.__Box.min.y;
|
||||
this.positionZ = this.__Box.min.z;
|
||||
|
||||
this.rotateX = 0;
|
||||
this.rotateY = 0;
|
||||
this.rotateZ = 0;
|
||||
|
||||
this.name = `[${ents.length}个模型]`;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateModulePosition = async (ent: Entity, position: number, axis: ModuleAxis) =>
|
||||
{
|
||||
if (!ent) return;
|
||||
let p = ent.Position;
|
||||
switch (axis)
|
||||
{
|
||||
case (ModuleAxis.x):
|
||||
{
|
||||
p.x = position;
|
||||
break;
|
||||
}
|
||||
case (ModuleAxis.y):
|
||||
{
|
||||
p.y = position;
|
||||
break;
|
||||
}
|
||||
case (ModuleAxis.z):
|
||||
{
|
||||
p.z = position;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
AppToaster.show({
|
||||
message: "出错啦!",
|
||||
timeout: 5000,
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
break;
|
||||
}
|
||||
ent.Position = p;
|
||||
app.Viewer.UpdateRender();
|
||||
};
|
||||
UpdateModuleRotate = (ent: Entity, x: number, y: number, z: number) =>
|
||||
{
|
||||
let eu = new Euler(x * MathUtils.DEG2RAD, y * MathUtils.DEG2RAD, z * MathUtils.DEG2RAD, 'ZYX');
|
||||
ent.WriteAllObjectRecord();
|
||||
ent.OCS = ent.OCS.makeRotationFromEuler(eu).setPosition(ent.Position);
|
||||
ent.Update(UpdateDraw.Matrix);
|
||||
app.Viewer.UpdateRender();
|
||||
};
|
||||
|
||||
private __Box: Box3;
|
||||
private InitBox(ens: Entity[])
|
||||
{
|
||||
this.__Box = new Box3;
|
||||
for (let e of ens)
|
||||
this.__Box.union(e.BoundingBox);
|
||||
}
|
||||
|
||||
UpdateScaleModule = async (length: number, width: number, height: number) =>
|
||||
{
|
||||
let ents: Entity[] = this._EntityIds.map(id => app.Database.GetObjectId(id).Object).filter(en => en && en instanceof Entity) as Entity[];
|
||||
if (ents.length === 0) return;
|
||||
|
||||
const Command_KEY = '修改模型尺寸';
|
||||
if (CommandState.CommandIng)
|
||||
{
|
||||
if (app.Database.hm.UndoData.CommandName !== Command_KEY)
|
||||
{
|
||||
await app.Editor.ModalManage.EndExecingCmd();
|
||||
if (CommandState.CommandIng)
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "命令正在执行中!无法修改模型尺寸!",
|
||||
timeout: 5000,
|
||||
intent: Intent.DANGER
|
||||
});
|
||||
return;
|
||||
}
|
||||
commandMachine.CommandStart(Command_KEY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
commandMachine.CommandStart(Command_KEY);
|
||||
}
|
||||
|
||||
if (ents.length === 1)
|
||||
{
|
||||
let ent = ents[0];
|
||||
if (ent instanceof EntityRef)
|
||||
ent.ScaleSize = new Vector3(length, width, height);
|
||||
else if (ent instanceof Board)
|
||||
{
|
||||
ent.Height = length;
|
||||
ent.Width = width;
|
||||
ent.Thickness = height;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
let c = this.__Box.getCenter(new Vector3);
|
||||
let size = this.__Box.getSize(new Vector3);
|
||||
//不等比例缩放
|
||||
let mtx = new Matrix4().makeScale(length / size.x, width / size.y, height / size.z).setPosition(c.x * (1 - (length / size.x)), c.y * ((1 - width / size.y)), c.z * (1 - height / size.z));
|
||||
|
||||
for (let e of ents)
|
||||
e.ApplyMatrix(mtx);
|
||||
|
||||
this.__Box.applyMatrix4(mtx);
|
||||
}
|
||||
app.Viewer.UpdateRender();
|
||||
};
|
||||
private static _SingleInstance: ModifyModelStore;
|
||||
static GetSingleInstance = (): ModifyModelStore =>
|
||||
{
|
||||
if (this._SingleInstance) return this._SingleInstance;
|
||||
this._SingleInstance = new ModifyModelStore();
|
||||
return this._SingleInstance;
|
||||
};
|
||||
}
|
@ -0,0 +1,677 @@
|
||||
import { Button, Checkbox, Collapse, Divider, Icon, InputGroup, Intent, NumericInput, Slider, Tooltip } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
import { MathUtils } from 'three';
|
||||
import { app } from '../../../../ApplicationServices/Application';
|
||||
import { FixedNotZero } from '../../../../Common/Utils';
|
||||
import { Entity } from '../../../../DatabaseServices/Entity/Entity';
|
||||
import { EntityRef } from '../../../../DatabaseServices/Entity/EntityRef';
|
||||
import { commandMachine } from '../../../../Editor/CommandMachine';
|
||||
import { CommandState } from '../../../../Editor/CommandState';
|
||||
import { ZeroVec } from '../../../../Geometry/GeUtils';
|
||||
import { AppToaster } from '../../Toaster';
|
||||
import ModifyModelStore from './ModifyModelStore';
|
||||
|
||||
export enum ModuleAxis
|
||||
{
|
||||
x = 'x',
|
||||
y = 'y',
|
||||
z = 'z',
|
||||
}
|
||||
enum ModuleScale
|
||||
{
|
||||
LENGTH,
|
||||
WIDTH,
|
||||
HEIGHT,
|
||||
}
|
||||
const iClamp = (value: number, min: number, max: number) =>
|
||||
{
|
||||
if (value <= min) return min;
|
||||
return Math.max(min, Math.min(max, value));
|
||||
};
|
||||
@observer
|
||||
export default class ModuleBaseParams extends Component<{}, {}>
|
||||
{
|
||||
ModifyModelStore: ModifyModelStore = ModifyModelStore.GetSingleInstance();
|
||||
tempNumberInputValue: number = 0;
|
||||
LetsRotate = async (e: React.PointerEvent<HTMLDivElement>, axis: ModuleAxis) =>
|
||||
{
|
||||
if (!app.Editor.SelectCtrl.SelectSet.SelectEntityList.length) return;
|
||||
|
||||
if (CommandState.CommandIng)
|
||||
{
|
||||
await app.Editor.ModalManage.EndExecingCmd();
|
||||
if (CommandState.CommandIng)
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "命令正在执行中!无法修改模型角度!",
|
||||
timeout: 5000,
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
const Command_KEY = '修改模型角度';
|
||||
commandMachine.CommandStart(Command_KEY);
|
||||
|
||||
|
||||
const { ModifyModelStore } = this;
|
||||
const targetEl = e.target as HTMLDivElement;
|
||||
targetEl.setPointerCapture(e.pointerId);
|
||||
|
||||
const boxRect = targetEl.getBoundingClientRect();
|
||||
const boxCenter = [boxRect.right - (boxRect.width / 2), boxRect.bottom - (boxRect.height / 2)];
|
||||
targetEl.onmousemove = (ev: MouseEvent) =>
|
||||
{
|
||||
const angle = Math.atan2(-(ev.clientY - boxCenter[1]), ev.clientX - boxCenter[0]) * MathUtils.RAD2DEG;
|
||||
switch (axis)
|
||||
{
|
||||
case ModuleAxis.x: {
|
||||
ModifyModelStore.rotateX = Math.round(angle);
|
||||
if (ModifyModelStore.rotateX < 0) ModifyModelStore.rotateX += 360;
|
||||
ModifyModelStore.rotateX = this.StickyRotateDeg(ModifyModelStore.rotateX);
|
||||
targetEl.style.transform = `rotate(${-ModifyModelStore.rotateX + 135}deg)`;
|
||||
break;
|
||||
}
|
||||
case ModuleAxis.y: {
|
||||
ModifyModelStore.rotateY = Math.round(angle);
|
||||
if (ModifyModelStore.rotateY < 0) ModifyModelStore.rotateY += 360;
|
||||
ModifyModelStore.rotateY = this.StickyRotateDeg(ModifyModelStore.rotateY);
|
||||
targetEl.style.transform = `rotate(${-ModifyModelStore.rotateY + 135}deg)`;
|
||||
break;
|
||||
}
|
||||
case ModuleAxis.z: {
|
||||
ModifyModelStore.rotateZ = Math.round(angle);
|
||||
if (ModifyModelStore.rotateZ < 0) ModifyModelStore.rotateZ += 360;
|
||||
ModifyModelStore.rotateZ = this.StickyRotateDeg(ModifyModelStore.rotateZ);
|
||||
targetEl.style.transform = `rotate(${-ModifyModelStore.rotateZ + 135}deg)`;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
for (let e of app.Editor.SelectCtrl.SelectSet.SelectEntityList)
|
||||
{
|
||||
ModifyModelStore.UpdateModuleRotate(e,
|
||||
ModifyModelStore.rotateX,
|
||||
ModifyModelStore.rotateY,
|
||||
ModifyModelStore.rotateZ
|
||||
);
|
||||
}
|
||||
};
|
||||
targetEl.onmouseup = () =>
|
||||
{
|
||||
if (CommandState.CommandIng && app.Database.hm.UndoData.CommandName === Command_KEY)
|
||||
{
|
||||
commandMachine.CommandEnd();
|
||||
}
|
||||
targetEl.onmouseup = null;
|
||||
targetEl.onmousemove = null;
|
||||
};
|
||||
};
|
||||
LetsMove = async (e: React.PointerEvent<HTMLElement>, axis: ModuleAxis) =>
|
||||
{
|
||||
if (!app.Editor.SelectCtrl.SelectSet.SelectEntityList.length) return;
|
||||
const Command_KEY = '修改模型位置';
|
||||
if (CommandState.CommandIng)
|
||||
{
|
||||
await app.Editor.ModalManage.EndExecingCmd();
|
||||
if (CommandState.CommandIng)
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "命令正在执行中!无法修改模型位置!",
|
||||
timeout: 5000,
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
return;
|
||||
}
|
||||
commandMachine.CommandStart(Command_KEY);
|
||||
} else
|
||||
{
|
||||
commandMachine.CommandStart(Command_KEY);
|
||||
}
|
||||
|
||||
const { ModifyModelStore } = this;
|
||||
const targetEl = e.target as HTMLInputElement;
|
||||
targetEl.setPointerCapture(e.pointerId);
|
||||
const downX = e.clientX;
|
||||
let currPosition: number = 0;
|
||||
let startPoint: number = Date.now();
|
||||
const test: number[] = [];
|
||||
for (let e of app.Editor.SelectCtrl.SelectSet.SelectEntityList)
|
||||
{
|
||||
switch (axis)
|
||||
{
|
||||
case ModuleAxis.x: {
|
||||
test.push(e.Position.x);
|
||||
break;
|
||||
}
|
||||
case ModuleAxis.y: {
|
||||
test.push(e.Position.y);
|
||||
break;
|
||||
}
|
||||
case ModuleAxis.z: {
|
||||
test.push(e.Position.z);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (axis)
|
||||
{
|
||||
case ModuleAxis.x: {
|
||||
currPosition = ModifyModelStore.positionX;
|
||||
break;
|
||||
}
|
||||
case ModuleAxis.y: {
|
||||
currPosition = ModifyModelStore.positionY;
|
||||
break;
|
||||
}
|
||||
case ModuleAxis.z: {
|
||||
currPosition = ModifyModelStore.positionZ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
targetEl.onmousemove = (ev: MouseEvent) =>
|
||||
{
|
||||
ev.preventDefault();
|
||||
const endPoint = Date.now();
|
||||
const move = ev.clientX - downX;
|
||||
if (Math.abs(startPoint - endPoint) < 400) return;
|
||||
for (let [e, index] of new Map(app.Editor.SelectCtrl.SelectSet.SelectEntityList.map((e: Entity, index) => [e, index])))
|
||||
{
|
||||
switch (axis)
|
||||
{
|
||||
case ModuleAxis.x: {
|
||||
ModifyModelStore.positionX = currPosition + (-move);
|
||||
ModifyModelStore.UpdateModulePosition(e, (test[index] + (-move)), ModuleAxis.x);
|
||||
break;
|
||||
}
|
||||
case ModuleAxis.y: {
|
||||
ModifyModelStore.positionY = currPosition + (-move);
|
||||
ModifyModelStore.UpdateModulePosition(e, (test[index] + (-move)), ModuleAxis.y);
|
||||
break;
|
||||
}
|
||||
case ModuleAxis.z: {
|
||||
ModifyModelStore.positionZ = currPosition + (-move);
|
||||
ModifyModelStore.UpdateModulePosition(e, (test[index] + (-move)), ModuleAxis.z);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
targetEl.onmouseup = () =>
|
||||
{
|
||||
targetEl.select();
|
||||
if (CommandState.CommandIng && app.Database.hm.UndoData.CommandName === Command_KEY)
|
||||
{
|
||||
commandMachine.CommandEnd();
|
||||
}
|
||||
targetEl.onmouseup = null;
|
||||
targetEl.onmousemove = null;
|
||||
};
|
||||
};
|
||||
ChangePosition = async (e: string, axis: ModuleAxis) =>
|
||||
{
|
||||
if (!app.Editor.SelectCtrl.SelectSet.SelectEntityList.length) return;
|
||||
const ent = app.Editor.SelectCtrl.SelectSet.SelectEntityList[0];
|
||||
const Command_KEY = '修改模型位置';
|
||||
if (CommandState.CommandIng)
|
||||
{
|
||||
await app.Editor.ModalManage.EndExecingCmd();
|
||||
if (CommandState.CommandIng)
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "命令正在执行中!无法修改模型位置!",
|
||||
timeout: 5000,
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
return;
|
||||
}
|
||||
commandMachine.CommandStart(Command_KEY);
|
||||
} else
|
||||
{
|
||||
commandMachine.CommandStart(Command_KEY);
|
||||
}
|
||||
|
||||
const { ModifyModelStore } = this;
|
||||
let v = parseInt(e);
|
||||
if (isNaN(v))
|
||||
{
|
||||
v = 0;
|
||||
}
|
||||
switch (axis)
|
||||
{
|
||||
case ModuleAxis.x: {
|
||||
ModifyModelStore.positionX = v;
|
||||
ModifyModelStore.UpdateModulePosition(ent, ModifyModelStore.positionX, ModuleAxis.x);
|
||||
break;
|
||||
}
|
||||
case ModuleAxis.y: {
|
||||
ModifyModelStore.positionY = v;
|
||||
ModifyModelStore.UpdateModulePosition(ent, ModifyModelStore.positionY, ModuleAxis.y);
|
||||
break;
|
||||
}
|
||||
case ModuleAxis.z: {
|
||||
ModifyModelStore.positionZ = v;
|
||||
ModifyModelStore.UpdateModulePosition(ent, ModifyModelStore.positionZ, ModuleAxis.z);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (CommandState.CommandIng && app.Database.hm.UndoData.CommandName === Command_KEY)
|
||||
{
|
||||
commandMachine.CommandEnd();
|
||||
}
|
||||
};
|
||||
StickyRotateDeg = (value: number): number =>
|
||||
{
|
||||
if (value <= 5) value = 0;
|
||||
if (value >= 355) value = 0;
|
||||
if (value >= 85 && value <= 95)
|
||||
value = 90;
|
||||
if (value >= 175 && value <= 185)
|
||||
value = 180;
|
||||
if (value >= 265 && value <= 275)
|
||||
value = 270;
|
||||
return value;
|
||||
};
|
||||
ChangeModuelScala = (e: React.KeyboardEvent, scale: ModuleScale) =>
|
||||
{
|
||||
const { ModifyModelStore } = this;
|
||||
let percentage: number = 0;
|
||||
if (e.key === 'Enter')
|
||||
{
|
||||
if (ModifyModelStore.isProportionalZoom)
|
||||
{
|
||||
switch (scale)
|
||||
{
|
||||
case ModuleScale.LENGTH:
|
||||
percentage = ModifyModelStore.module_Length / this.tempNumberInputValue;
|
||||
ModifyModelStore.module_Width = ModifyModelStore.module_Width * percentage;
|
||||
ModifyModelStore.module_Height = ModifyModelStore.module_Height * percentage;
|
||||
break;
|
||||
case ModuleScale.WIDTH:
|
||||
percentage = ModifyModelStore.module_Width / this.tempNumberInputValue;
|
||||
ModifyModelStore.module_Length = ModifyModelStore.module_Length * percentage;
|
||||
ModifyModelStore.module_Height = ModifyModelStore.module_Height * percentage;
|
||||
break;
|
||||
case ModuleScale.HEIGHT:
|
||||
percentage = ModifyModelStore.module_Height / this.tempNumberInputValue;
|
||||
ModifyModelStore.module_Width = ModifyModelStore.module_Width * percentage;
|
||||
ModifyModelStore.module_Length = ModifyModelStore.module_Length * percentage;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
ModifyModelStore.UpdateScaleModule(
|
||||
ModifyModelStore.module_Length,
|
||||
ModifyModelStore.module_Width,
|
||||
ModifyModelStore.module_Height,
|
||||
);
|
||||
switch (scale)
|
||||
{
|
||||
case ModuleScale.LENGTH:
|
||||
this.tempNumberInputValue = ModifyModelStore.module_Length;
|
||||
break;
|
||||
case ModuleScale.WIDTH:
|
||||
this.tempNumberInputValue = ModifyModelStore.module_Width;
|
||||
break;
|
||||
case ModuleScale.HEIGHT:
|
||||
this.tempNumberInputValue = ModifyModelStore.module_Height;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
const Command_KEY = '修改模型尺寸';
|
||||
if (CommandState.CommandIng && app.Database.hm.UndoData.CommandName === Command_KEY)
|
||||
commandMachine.CommandEnd();
|
||||
}
|
||||
if (e.key === 'Escape')
|
||||
{
|
||||
switch (scale)
|
||||
{
|
||||
case ModuleScale.LENGTH:
|
||||
ModifyModelStore.module_Length = this.tempNumberInputValue;
|
||||
break;
|
||||
case ModuleScale.WIDTH:
|
||||
ModifyModelStore.module_Width = this.tempNumberInputValue;
|
||||
break;
|
||||
case ModuleScale.HEIGHT:
|
||||
ModifyModelStore.module_Height = this.tempNumberInputValue;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (e.ctrlKey && e.key === 'z')
|
||||
{
|
||||
app.Database.hm.Undo();
|
||||
}
|
||||
};
|
||||
render()
|
||||
{
|
||||
const [min, max] = [0, 360];
|
||||
const { ModifyModelStore } = this;
|
||||
return (
|
||||
<div className='ModuleBaseParams'>
|
||||
<div className='CollapseOpenTitle'
|
||||
style={{ display: ModifyModelStore._EntityIds.length < 1 ? 'none' : "block" }}
|
||||
onClick={() =>
|
||||
{
|
||||
ModifyModelStore.isCollapseOpened = !ModifyModelStore.isCollapseOpened;
|
||||
}}
|
||||
>
|
||||
<div >
|
||||
<Icon icon={ModifyModelStore.isCollapseOpened ? 'chevron-up' : "chevron-down"} />
|
||||
基础参数
|
||||
</div>
|
||||
<Button small minimal intent='primary' text='恢复默认' style={{ fontSize: '12px' }}
|
||||
onClick={async (e: React.MouseEvent) =>
|
||||
{
|
||||
e.stopPropagation();
|
||||
if (!app.Editor.SelectCtrl.SelectSet.SelectEntityList.length) return;
|
||||
if (CommandState.CommandIng)
|
||||
{
|
||||
await app.Editor.ModalManage.EndExecingCmd();
|
||||
if (CommandState.CommandIng)
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "命令正在执行中!无法修改模型尺寸!",
|
||||
timeout: 5000,
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
const Command_KEY = '修改模型尺寸';
|
||||
commandMachine.CommandStart(Command_KEY);
|
||||
for (let ent of app.Editor.SelectCtrl.SelectSet.SelectEntityList as EntityRef[])
|
||||
{
|
||||
ent.ScaleSize = ZeroVec;
|
||||
app.Viewer.UpdateRender();
|
||||
}
|
||||
commandMachine.CommandEnd();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Collapse isOpen={ModifyModelStore.isCollapseOpened}>
|
||||
<div className='CollapseContent' style={{ display: ModifyModelStore._EntityIds.length < 1 ? 'none' : "block" }}>
|
||||
<div >
|
||||
<div className='ParamsItem'>
|
||||
<div className='ParamsSlider'>
|
||||
<span>长度</span>
|
||||
<Slider
|
||||
min={1}
|
||||
max={ModifyModelStore.sliderMax}
|
||||
value={
|
||||
iClamp(ModifyModelStore.module_Length, 0, ModifyModelStore.sliderMax)
|
||||
}
|
||||
onChange={(e) =>
|
||||
{
|
||||
const percentage = e / ModifyModelStore.module_Length;
|
||||
ModifyModelStore.module_Length = e;
|
||||
if (ModifyModelStore.isProportionalZoom)
|
||||
{
|
||||
ModifyModelStore.module_Width = ModifyModelStore.module_Width * percentage;
|
||||
ModifyModelStore.module_Height = ModifyModelStore.module_Height * percentage;
|
||||
}
|
||||
ModifyModelStore.UpdateScaleModule(
|
||||
ModifyModelStore.module_Length,
|
||||
ModifyModelStore.module_Width,
|
||||
ModifyModelStore.module_Height,
|
||||
);
|
||||
}}
|
||||
onRelease={() =>
|
||||
{
|
||||
const Command_KEY = '修改模型尺寸';
|
||||
if (CommandState.CommandIng && app.Database.hm.UndoData.CommandName === Command_KEY)
|
||||
commandMachine.CommandEnd();
|
||||
}}
|
||||
/>
|
||||
<NumericInput
|
||||
value={iClamp(ModifyModelStore.module_Length, 0, Number.MAX_VALUE).toFixed(0)}
|
||||
onValueChange={(e) =>
|
||||
{
|
||||
if (e === 0) return;
|
||||
ModifyModelStore.module_Length = e;
|
||||
}}
|
||||
onFocus={(e) =>
|
||||
{
|
||||
this.tempNumberInputValue = parseInt(e.target.value);
|
||||
}}
|
||||
onKeyDown={(e) =>
|
||||
{
|
||||
e.stopPropagation();
|
||||
this.ChangeModuelScala(e, ModuleScale.LENGTH);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='ParamsItem'>
|
||||
<div className='ParamsSlider'>
|
||||
<span>宽度</span>
|
||||
<Slider
|
||||
min={1}
|
||||
max={ModifyModelStore.sliderMax}
|
||||
value={iClamp(ModifyModelStore.module_Width, 0, ModifyModelStore.sliderMax)}
|
||||
onChange={(e) =>
|
||||
{
|
||||
const percentage = e / ModifyModelStore.module_Width;
|
||||
ModifyModelStore.module_Width = e;
|
||||
if (ModifyModelStore.isProportionalZoom)
|
||||
{
|
||||
ModifyModelStore.module_Length = ModifyModelStore.module_Length * percentage;
|
||||
ModifyModelStore.module_Height = ModifyModelStore.module_Height * percentage;
|
||||
}
|
||||
ModifyModelStore.UpdateScaleModule(
|
||||
ModifyModelStore.module_Length,
|
||||
ModifyModelStore.module_Width,
|
||||
ModifyModelStore.module_Height,
|
||||
);
|
||||
}}
|
||||
onRelease={() =>
|
||||
{
|
||||
const Command_KEY = '修改模型尺寸';
|
||||
if (CommandState.CommandIng && app.Database.hm.UndoData.CommandName === Command_KEY)
|
||||
commandMachine.CommandEnd();
|
||||
}} />
|
||||
<NumericInput
|
||||
value={iClamp(ModifyModelStore.module_Width, 0, Number.MAX_VALUE).toFixed(0)}
|
||||
onValueChange={(e) =>
|
||||
{
|
||||
if (e === 0) return;
|
||||
ModifyModelStore.module_Width = e;
|
||||
}}
|
||||
onFocus={(e) =>
|
||||
{
|
||||
this.tempNumberInputValue = parseInt(e.target.value);
|
||||
}}
|
||||
onKeyDown={(e) =>
|
||||
{
|
||||
e.stopPropagation();
|
||||
this.ChangeModuelScala(e, ModuleScale.WIDTH);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='ParamsItem'>
|
||||
<div className='ParamsSlider'>
|
||||
<span>高度</span>
|
||||
<Slider
|
||||
min={1}
|
||||
max={ModifyModelStore.sliderMax}
|
||||
value={iClamp(ModifyModelStore.module_Height, 0, ModifyModelStore.sliderMax)}
|
||||
onChange={(e: number) =>
|
||||
{
|
||||
const percentage = e / ModifyModelStore.module_Height;
|
||||
ModifyModelStore.module_Height = e;
|
||||
if (ModifyModelStore.isProportionalZoom)
|
||||
{
|
||||
ModifyModelStore.module_Length = ModifyModelStore.module_Length * percentage;;
|
||||
ModifyModelStore.module_Width = ModifyModelStore.module_Width * percentage;
|
||||
}
|
||||
ModifyModelStore.UpdateScaleModule(
|
||||
ModifyModelStore.module_Length,
|
||||
ModifyModelStore.module_Width,
|
||||
ModifyModelStore.module_Height,
|
||||
);
|
||||
}}
|
||||
onRelease={() =>
|
||||
{
|
||||
const Command_KEY = '修改模型尺寸';
|
||||
if (CommandState.CommandIng && app.Database.hm.UndoData.CommandName === Command_KEY)
|
||||
commandMachine.CommandEnd();
|
||||
}} />
|
||||
<NumericInput
|
||||
value={iClamp(ModifyModelStore.module_Height, 0, Number.MAX_VALUE).toFixed(0)}
|
||||
onValueChange={(e) =>
|
||||
{
|
||||
if (e === 0) return;
|
||||
ModifyModelStore.module_Height = e;
|
||||
}}
|
||||
onFocus={(e) =>
|
||||
{
|
||||
this.tempNumberInputValue = parseInt(e.target.value);
|
||||
}}
|
||||
onKeyDown={(e) =>
|
||||
{
|
||||
e.stopPropagation();
|
||||
this.ChangeModuelScala(e, ModuleScale.HEIGHT);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className='ParamsItem'>
|
||||
<div className='ParamsSlider'>
|
||||
<span>离地</span>
|
||||
<Slider
|
||||
min={0}
|
||||
max={5000}
|
||||
value={MathUtils.clamp(ModifyModelStore.offGround, 0, 5000)}
|
||||
onChange={(e) =>
|
||||
{
|
||||
ModifyModelStore.offGround = e;
|
||||
for (let ent of app.Editor.SelectCtrl.SelectSet.SelectEntityList)
|
||||
{
|
||||
|
||||
}
|
||||
}} />
|
||||
<NumericInput value={ModifyModelStore.offGround} />
|
||||
</div>
|
||||
</div> */}
|
||||
<div className='ParamsItem'>
|
||||
<Checkbox label='等比缩放' checked={ModifyModelStore.isProportionalZoom} onChange={() =>
|
||||
{
|
||||
ModifyModelStore.isProportionalZoom = !ModifyModelStore.isProportionalZoom;
|
||||
}} />
|
||||
</div>
|
||||
<div className='ParamsItem'>
|
||||
<div className='ParamsPosition'>
|
||||
<span style={{ lineHeight: "24px" }}>位置</span>
|
||||
<div>
|
||||
<InputGroup
|
||||
value={FixedNotZero(ModifyModelStore.positionX.toString())}
|
||||
onChange={(e) =>
|
||||
{
|
||||
this.ChangePosition(e.target.value, ModuleAxis.x);
|
||||
}}
|
||||
onPointerDown={(e) => { this.LetsMove(e, ModuleAxis.x); }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<InputGroup
|
||||
value={FixedNotZero(ModifyModelStore.positionY.toString())}
|
||||
onChange={(e) =>
|
||||
{
|
||||
this.ChangePosition(e.target.value, ModuleAxis.y);
|
||||
}}
|
||||
onPointerDown={(e) => { this.LetsMove(e, ModuleAxis.y); }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<InputGroup
|
||||
value={FixedNotZero(ModifyModelStore.positionZ.toString())}
|
||||
onChange={(e) =>
|
||||
{
|
||||
this.ChangePosition(e.target.value, ModuleAxis.z);
|
||||
}}
|
||||
onPointerDown={(e) => { this.LetsMove(e, ModuleAxis.z); }}
|
||||
/>
|
||||
</div>
|
||||
<Tooltip minimal placement='top' content='重置'>
|
||||
<Button minimal small intent={Intent.PRIMARY} icon='redo' />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className='ParamsItem' style={{ marginTop: "10px" }}>
|
||||
<div className='ParamsRotate'>
|
||||
<span style={{ lineHeight: "24px" }}>旋转</span>
|
||||
<div className='RotateX'>
|
||||
<div>
|
||||
<div
|
||||
className='RotateBox'
|
||||
onPointerDown={(e) => { this.LetsRotate(e, ModuleAxis.x); }}
|
||||
style={{ transform: `rotate(${-ModifyModelStore.rotateX + 135}deg)` }}
|
||||
>
|
||||
<Icon icon='record' size={8} style={{ color: "#FF0000" }} />
|
||||
</div>
|
||||
<NumericInput
|
||||
min={min}
|
||||
max={max}
|
||||
value={ModifyModelStore.rotateX.toFixed(0)}
|
||||
onValueChange={(e: number) =>
|
||||
{
|
||||
ModifyModelStore.rotateX = e;
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
<div className='RotateY'>
|
||||
<div>
|
||||
<div
|
||||
className='RotateBox'
|
||||
onPointerDown={(e) => { this.LetsRotate(e, ModuleAxis.y); }}
|
||||
style={{ transform: `rotate(${-ModifyModelStore.rotateY + 135}deg)` }}
|
||||
>
|
||||
<Icon icon='record' size={8} style={{ color: "#00FF00" }} />
|
||||
</div>
|
||||
<NumericInput
|
||||
min={min}
|
||||
max={max}
|
||||
value={ModifyModelStore.rotateY.toFixed(0)}
|
||||
onValueChange={(e: number) =>
|
||||
{
|
||||
ModifyModelStore.rotateY = e;
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
<div className='RotateZ'>
|
||||
<div>
|
||||
<div
|
||||
className='RotateBox'
|
||||
onPointerDown={(e) => { this.LetsRotate(e, ModuleAxis.z); }}
|
||||
style={{ transform: `rotate(${-ModifyModelStore.rotateZ + 135}deg)` }}
|
||||
>
|
||||
<Icon icon='record' size={8} style={{ color: "#0000FF" }} />
|
||||
</div>
|
||||
<NumericInput
|
||||
min={min}
|
||||
max={max}
|
||||
value={ModifyModelStore.rotateZ.toFixed(0)}
|
||||
onValueChange={(e: number) =>
|
||||
{
|
||||
ModifyModelStore.rotateZ = e;
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
<Tooltip minimal placement='top' content='重置'>
|
||||
<Button minimal small intent={Intent.PRIMARY} icon='redo' />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Collapse>
|
||||
<Divider />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
Loading…
Reference in new issue