You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
476 lines
12 KiB
476 lines
12 KiB
4 years ago
|
import { EventState, Mesh, MeshBuilder, PickingInfo, PointerEventTypes, PointerInfo, Scene, Vector3 } from "@babylonjs/core";
|
||
|
import { Button, Container, Control, Ellipse, MultiLine, Rectangle, TextBlock } from "@babylonjs/gui";
|
||
|
import { UIManager } from "../controller/ui-manager";
|
||
|
import { BabylonUIStyleTool } from "./babylon-ui-style-tool";
|
||
|
|
||
|
/**
|
||
|
* 测量工具
|
||
|
*/
|
||
|
export class MeasureTool {
|
||
|
|
||
|
|
||
|
static instance: MeasureTool;
|
||
|
|
||
|
/**
|
||
|
* 当前编辑的测量
|
||
|
*/
|
||
|
currentMeasureInfo: MeasureInfo;
|
||
|
|
||
|
/**
|
||
|
* 当前所处的测量状态
|
||
|
*/
|
||
|
currentMeasureType: MeasureType = MeasureType.None;
|
||
|
|
||
|
scene: Scene;
|
||
|
|
||
|
constructor(scene: Scene) {
|
||
|
this.scene = scene;
|
||
|
MeasureTool.instance = this;
|
||
|
scene.onPointerObservable.add(
|
||
|
MeasureTool.instance.onPointerObservable
|
||
|
);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 新建测量
|
||
|
* @param start
|
||
|
* @param type
|
||
|
*/
|
||
|
createMeasureInfo(start: Vector3) {
|
||
|
let type: MeasureType = this.currentMeasureType;
|
||
|
if (type != MeasureType.None) {
|
||
|
this.currentMeasureInfo = new MeasureInfo(start, type);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 中断测量
|
||
|
*/
|
||
|
breakMeasure() {
|
||
|
this.currentMeasureInfo = null;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 改变测量类型
|
||
|
* @param type MeasureType.None,表示结束测量
|
||
|
*/
|
||
|
changeMeasureType(type: MeasureType) {
|
||
|
|
||
|
|
||
|
this.currentMeasureType = type;
|
||
|
switch (type) {
|
||
|
case MeasureType.None:
|
||
|
this.breakMeasure();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//鼠标交互监听
|
||
|
onPointerObservable(eventData: PointerInfo, eventState: EventState) {
|
||
|
let instance = MeasureTool.instance;
|
||
|
if (instance.currentMeasureType == MeasureType.None) {
|
||
|
return; //非测量状态
|
||
|
}
|
||
|
|
||
|
switch (eventData.type) {
|
||
|
case PointerEventTypes.POINTERPICK:
|
||
|
if (eventData.event.button == 0 && eventData.pickInfo.hit) {
|
||
|
if (!instance.isPickTooFar(eventData.pickInfo)) {
|
||
|
if (instance.currentMeasureInfo == null) {
|
||
|
instance.createMeasureInfo(eventData.pickInfo.pickedPoint);
|
||
|
}
|
||
|
else {
|
||
|
instance.addMeasurePoint(eventData.pickInfo);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else if (eventData.event.button == 2) //右键,中断
|
||
|
{
|
||
|
instance.breakMeasure();
|
||
|
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 添加测量点
|
||
|
*/
|
||
|
addMeasurePoint(pickInfo: PickingInfo) {
|
||
|
console.log("测量", pickInfo);
|
||
|
if (this.currentMeasureInfo != null) {
|
||
|
this.currentMeasureInfo.addPoint(pickInfo.pickedPoint);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 点击太远了(点在天空盒上)
|
||
|
* @param point
|
||
|
*/
|
||
|
isPickTooFar(point: PickingInfo) {
|
||
|
|
||
|
if (point.pickedMesh != null && point.pickedMesh.name == "skyBox") {
|
||
|
return true;
|
||
|
}
|
||
|
else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 测量状态
|
||
|
*/
|
||
|
export enum MeasureType {
|
||
|
/**
|
||
|
* 未测量
|
||
|
*/
|
||
|
None,
|
||
|
|
||
|
/**
|
||
|
* 直线距离
|
||
|
*/
|
||
|
Distance,
|
||
|
|
||
|
/**
|
||
|
* 高度
|
||
|
*/
|
||
|
Height,
|
||
|
|
||
|
/**
|
||
|
* 面积
|
||
|
*/
|
||
|
Area,
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 测量信息
|
||
|
*/
|
||
|
export class MeasureInfo {
|
||
|
/**
|
||
|
* 所有点
|
||
|
*/
|
||
|
points: MeasurePoint[];
|
||
|
|
||
|
type: MeasureType;
|
||
|
|
||
|
/**
|
||
|
* ui根节点
|
||
|
*/
|
||
|
uiRoot: Container;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 圆形起点
|
||
|
*/
|
||
|
ell_start: Ellipse;
|
||
|
|
||
|
/**
|
||
|
* 释放按钮
|
||
|
*/
|
||
|
btn_dispose: Button;
|
||
|
|
||
|
/**
|
||
|
* 是信息
|
||
|
*/
|
||
|
txt_info: TextBlock;
|
||
|
|
||
|
/**
|
||
|
* 信息背景
|
||
|
*/
|
||
|
txtbg: Rectangle;
|
||
|
|
||
|
mulLine: MultiLine;
|
||
|
|
||
|
constructor(start: Vector3, type: MeasureType) {
|
||
|
this.points = [];
|
||
|
this.type = type;
|
||
|
|
||
|
this.mulLine = new MultiLine("mulLine" + type);
|
||
|
this.mulLine.lineWidth = 1.5;
|
||
|
this.mulLine.color = BabylonUIStyleTool.c_color_3d_blue;
|
||
|
this.mulLine.shadowColor = BabylonUIStyleTool.c_color_3d_blue;
|
||
|
this.mulLine.shadowBlur = 5;
|
||
|
UIManager.Instance.uiRoot.addControl(this.mulLine);
|
||
|
this.addPoint(start);
|
||
|
|
||
|
this.updateUI();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 新增节点
|
||
|
*/
|
||
|
addPoint(pos: Vector3) {
|
||
|
if (this.points.length > 0) {
|
||
|
for (let i = 0; i < this.points.length; i++) {
|
||
|
this.points[i].changeEnd(false);
|
||
|
}
|
||
|
if (this.type == MeasureType.Height) {
|
||
|
pos.x = this.points[0].pos.x;
|
||
|
pos.z = this.points[0].pos.z;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
let point = new MeasurePoint(pos, true, this);
|
||
|
this.points.push(point);
|
||
|
|
||
|
if (this.points.length > 3 && this.type == MeasureType.Area) {
|
||
|
let lastPoint = this.mulLine.getAt(this.points.length - 1);
|
||
|
this.mulLine.remove(lastPoint);
|
||
|
this.mulLine.add(point.mesh);
|
||
|
this.mulLine.add(this.points[0].mesh);
|
||
|
}
|
||
|
else {
|
||
|
this.mulLine.add(point.mesh);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
if (this.points.length > 1 && this.type == MeasureType.Height) { //高度只能放两个点
|
||
|
MeasureTool.instance.breakMeasure();
|
||
|
}
|
||
|
|
||
|
if (this.points.length == 3 && this.type == MeasureType.Area) {
|
||
|
this.mulLine.add(this.points[0].mesh);
|
||
|
}
|
||
|
|
||
|
//更新显示
|
||
|
this.updateUI();
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 更新UI
|
||
|
*/
|
||
|
updateUI() {
|
||
|
if (this.uiRoot == null) {
|
||
|
this.ell_start = new Ellipse("MeasureStart")
|
||
|
UIManager.Instance.uiRoot.addControl(this.ell_start);
|
||
|
BabylonUIStyleTool.setStyle_size(this.ell_start, "10px", "10px");
|
||
|
this.ell_start.thickness = 2;
|
||
|
this.ell_start.color = BabylonUIStyleTool.c_color_3d_blue;
|
||
|
this.ell_start.background = BabylonUIStyleTool.c_color_3d_blueBg;
|
||
|
this.ell_start.linkWithMesh(this.points[0].mesh);
|
||
|
|
||
|
this.uiRoot = new Container("Measure");
|
||
|
this.uiRoot.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
|
||
|
UIManager.Instance.uiRoot.addControl(this.uiRoot);
|
||
|
BabylonUIStyleTool.setStyle_size(this.uiRoot, "100px", "40px");
|
||
|
|
||
|
this.btn_dispose = Button.CreateSimpleButton("dispose", "x");
|
||
|
this.uiRoot.addControl(this.btn_dispose);
|
||
|
this.btn_dispose.leftInPixels = 10;
|
||
|
BabylonUIStyleTool.setStyle_size(this.btn_dispose, "15px", "15px");
|
||
|
this.btn_dispose.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
|
||
|
this.btn_dispose.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
|
||
|
let instance = this;
|
||
|
this.btn_dispose.onPointerClickObservable.add(() => {
|
||
|
instance.dispose();
|
||
|
});
|
||
|
this.btn_dispose.background = BabylonUIStyleTool.c_color_3d_blueBg;
|
||
|
this.btn_dispose.color = BabylonUIStyleTool.c_color_3d_blue;
|
||
|
|
||
|
this.txtbg = new Rectangle("txtBG");
|
||
|
this.uiRoot.addControl(this.txtbg);
|
||
|
BabylonUIStyleTool.setStyle_size(this.txtbg, "70px", "25px");
|
||
|
this.txtbg.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
|
||
|
this.txtbg.verticalAlignment = Control.VERTICAL_ALIGNMENT_BOTTOM;
|
||
|
this.txtbg.background = BabylonUIStyleTool.c_color_3d_blueBg;
|
||
|
this.txtbg.color = BabylonUIStyleTool.c_color_3d_blue;
|
||
|
|
||
|
|
||
|
this.txt_info = new TextBlock("txt");
|
||
|
this.uiRoot.addControl(this.txt_info);
|
||
|
BabylonUIStyleTool.setStyle_size(this.txt_info, "70px", "25px");
|
||
|
this.txt_info.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
|
||
|
this.txt_info.verticalAlignment = Control.VERTICAL_ALIGNMENT_BOTTOM;
|
||
|
this.txt_info.textHorizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
|
||
|
this.txt_info.color = BabylonUIStyleTool.c_color_3d_blue;
|
||
|
this.txt_info.resizeToFit = true;
|
||
|
|
||
|
let ell_end = new Ellipse("end")
|
||
|
this.uiRoot.addControl(ell_end);
|
||
|
BabylonUIStyleTool.setStyle_size(ell_end, "10px", "10px");
|
||
|
ell_end.thickness = 2;
|
||
|
ell_end.color = BabylonUIStyleTool.c_color_3d_blue;
|
||
|
ell_end.background = BabylonUIStyleTool.c_color_3d_blueBg;
|
||
|
ell_end.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
|
||
|
ell_end.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
let info: string = "";
|
||
|
switch (this.type) {
|
||
|
case MeasureType.Distance:
|
||
|
info = " 长度:" + this.getPointsDistance(this.points).toFixed(1) + "米";
|
||
|
break;
|
||
|
case MeasureType.Height:
|
||
|
info = " 高度:" + this.getPointsHeight(this.points).toFixed(1) + "米";
|
||
|
break;
|
||
|
case MeasureType.Area:
|
||
|
info = " 面积:" + this.getPointsArea(this.points).toFixed(1) + "平方米";
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
this.txt_info.text = info;
|
||
|
|
||
|
let instance = this;
|
||
|
setTimeout(() => {
|
||
|
|
||
|
instance.txtbg.widthInPixels = instance.txt_info.widthInPixels + 3;
|
||
|
instance.uiRoot.widthInPixels = instance.txt_info.widthInPixels + 3;
|
||
|
|
||
|
let lastPoint = instance.points[instance.points.length - 1];
|
||
|
instance.uiRoot.linkWithMesh(lastPoint.mesh);
|
||
|
instance.uiRoot.linkOffsetYInPixels = 20 - 5;
|
||
|
instance.uiRoot.linkOffsetXInPixels = instance.uiRoot.widthInPixels * 0.5 - 5;
|
||
|
|
||
|
}, (30));
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 获取多个点的长度
|
||
|
* @param points
|
||
|
*/
|
||
|
getPointsDistance(points: MeasurePoint[]) {
|
||
|
let result = 0;
|
||
|
|
||
|
if (points != null && points.length > 1) {
|
||
|
for (let i = 1; i < points.length; i++) {
|
||
|
result += Vector3.Distance(points[i - 1].pos, points[i].pos);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
return result;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 获取高度
|
||
|
* @param points
|
||
|
*/
|
||
|
getPointsHeight(points: MeasurePoint[]) {
|
||
|
let result = 0;
|
||
|
|
||
|
if (points != null && points.length > 1) {
|
||
|
result = points[points.length - 1].pos.y - points[0].pos.y;
|
||
|
result = Math.abs(result);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 获取面积(在地面的投影面积, xz平面内)
|
||
|
* @param points
|
||
|
*/
|
||
|
getPointsArea(points: MeasurePoint[]) {
|
||
|
let result = 0;
|
||
|
|
||
|
if (points != null && points.length > 2) {
|
||
|
|
||
|
for (let i = 0; i < points.length - 1; i++) {
|
||
|
result += (points[i].pos.x * points[i + 1].pos.z - points[i + 1].pos.x * points[i].pos.z);
|
||
|
}
|
||
|
|
||
|
let num = points.length - 1;
|
||
|
|
||
|
result = 0.5 * (result + points[num].pos.x * points[0].pos.z - points[0].pos.x * points[num].pos.z);
|
||
|
result = Math.abs(result);
|
||
|
}
|
||
|
|
||
|
|
||
|
return result;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 释放
|
||
|
*/
|
||
|
dispose() {
|
||
|
this.ell_start.dispose();
|
||
|
|
||
|
this.uiRoot.dispose();
|
||
|
this.uiRoot = null;
|
||
|
this.btn_dispose = null;
|
||
|
this.txt_info = null;
|
||
|
this.mulLine.dispose();
|
||
|
this.mulLine = null;
|
||
|
|
||
|
for (let i = 0; i < this.points.length; i++) {
|
||
|
this.points[i].dispose();
|
||
|
}
|
||
|
this.points = [];
|
||
|
|
||
|
MeasureTool.instance.breakMeasure();
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 测量点
|
||
|
*/
|
||
|
export class MeasurePoint {
|
||
|
|
||
|
/**
|
||
|
* 终点
|
||
|
*/
|
||
|
isEnd: boolean;
|
||
|
|
||
|
pos: Vector3;
|
||
|
|
||
|
/**
|
||
|
* 所属测量信息
|
||
|
*/
|
||
|
belongTo: MeasureInfo;
|
||
|
|
||
|
/**
|
||
|
* 模型网格
|
||
|
*/
|
||
|
mesh: Mesh;
|
||
|
|
||
|
|
||
|
|
||
|
constructor(pos: Vector3, isEnd: boolean, belongTo: MeasureInfo) {
|
||
|
this.pos = pos;
|
||
|
this.isEnd = isEnd;
|
||
|
this.belongTo = belongTo;
|
||
|
|
||
|
this.mesh = MeshBuilder.CreateSphere(belongTo.type.toString(), { segments: 1, diameter: 0.01 }, MeasureTool.instance.scene);
|
||
|
this.mesh.position = pos;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 更新终点的显示
|
||
|
* @param isEnd
|
||
|
*/
|
||
|
changeEnd(isEnd: boolean) {
|
||
|
this.isEnd = isEnd;
|
||
|
//更新终点显示
|
||
|
|
||
|
}
|
||
|
|
||
|
dispose() {
|
||
|
if (this.mesh != null) {
|
||
|
this.mesh.dispose();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|