很多bim轻量化平台应该都有测量这个功能,什么距离啊,角度测量呀,里面比较复杂的还是面积测量这块,其他就比较简单了;

首先面积测量你得考虑是否共面,还得兼容凹凸多边形

先看看效果吧

逻辑很简单:1.只有模型才可以点击,空白的地方不能测量的(其实就是碰撞检测)2.鼠标抬起是确定一个点,鼠标移动是你绘制线 3.三个点确定一个平面 4.把任意多变形拆分成三角形来计算面积

实现逻辑都在这里,画线是用three的MeshLine画的,其实主要看面积测量逻辑就ok了,语言是ts写的,具体细节交流私下

@Injectable()
export class MeasureAreaService {private sprite = null;private line_measure = null;private currentPoint = null;private currentCount = 0;private firstPoint = null;private origin = null;private downTime = 0;private upTime = 0;private onmousemoveSubscription = null;private onmousedownSubscription = null;private onmouseupSubscription = null;private mousewheelSubscription = null;private needHighLightSubscription = null;private pointList = new Array < THREE.Vector3 > ();private keydownEvent = null;private aParameter = 0;private bParameter = 0;private cParameter = 0;private dParameter = 0;private measureItems: any[] = [];private dashedLine;public close() {if (this.onmousemoveSubscription) {this.onmousemoveSubscription.unsubscribe();}if (this.onmousedownSubscription) {this.onmousedownSubscription.unsubscribe();}if (this.onmouseupSubscription) {this.onmouseupSubscription.unsubscribe();}if (this.mousewheelSubscription) {this.mousewheelSubscription.unsubscribe();}if (this.needHighLightSubscription) {this.needHighLightSubscription.unsubscribe();}if (this.sprite) {this.viewerService.highlightScene.remove(this.sprite);}if (this.keydownEvent) {(window as any).removeEventListener('keydown', this.keydownEvent, false);}//this.state = OptState.Idle;this.clear();//this.rendererService.needHighLightRender.emit(true);this.measureItemsManagerService.close();}public open() {this.keydownEvent = (event) => {if (event.keyCode === 27) {if (this.currentCount >= 3) {if (this.currentPoint.x == this.firstPoint.x &&this.currentPoint.y == this.firstPoint.y &&this.currentPoint.z == this.firstPoint.z) {//防止闪烁if (this.measureItemsManagerService.getCurrentMeasureItem()) {this.measureItemsManagerService.deactiveCurrentMeasureLine();}//实例化线条this.measureItemsManagerService.getCurrentMeasureItem().active = true;this.measureItemsManagerService.createMeasureItem().active = true;} else {this.viewerService.highlightScene.remove(this.measureItemsManagerService.getCurrentMeasureItem().highlightLine.getHighlightLine());let highlightLine = new HighLightLine(this.measureItemsManagerService.getHighlightMaterial());let measureItem = this.measureItemsManagerService.createMeasureItem();measureItem.setHighlightLine(highlightLine);measureItem.active = true;this.viewerService.highlightScene.add(measureItem.highlightLine.getHighlightLine());this.measureItemsManagerService.getCurrentMeasureItem().highlightLine.setFirstVertice(this.firstPoint);this.measureItemsManagerService.getCurrentMeasureItem().highlightLine.setSecondVertice(this.origin);            this.measureItems[2].highlightLabel.setPosition(this.geometricCenter());this.measureItems[2].highlightLabel.setLabelText((this.areaPolygon()).toFixed(1) + '㎡');this.measureItems[2].highlightLabel.updatePosition(this.viewerService.activeCamera);this.measureItemsManagerService.deactiveCurrentMeasureLine();this.measureItemsManagerService.getCurrentMeasureItem().active = true;this.measureItemsManagerService.createMeasureItem().active = true;}this.rendererService.needHighLightRender.emit(true);}event.stopPropagation();event.preventDefault();this.currentCount = 0;this.origin = null;this.firstPoint = null;this.pointList = [];this.measureItems = [];this.measureItemsManagerService.removeItem(this.measureItemsManagerService.getCurrentMeasureItem());}};(window as any).addEventListener('keydown', this.keydownEvent, false);this.needHighLightSubscription = this.rendererService.needHighLightRender.subscribe(() => {if (this.sprite) {let scale = this.sprite.position.distanceTo(this.viewerService.activeCamera.position); // virtual_d;this.sprite.scale.set(scale / 80, scale / 80, scale / 80);}});let spriteMap = new THREE.TextureLoader().load(BimConfig.FILE_PATH_PROD + '/assets/bim/measure/cross1.png');let spriteMaterial = new THREE.SpriteMaterial({map: spriteMap,color: 0xffffff});this.sprite = new THREE.Sprite(spriteMaterial);this.sprite.visible = false;this.viewerService.highlightScene.add(this.sprite);let MAX_POINTS = 4;// geometrylet line_geometry = new THREE.BufferGeometry();let line_material = new THREE.LineBasicMaterial({color: 0x0000ff});line_material.depthTest = false;// attributeslet positions = new Float32Array(MAX_POINTS * 3); // 3 vertices per pointline_geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));// draw rangelet drawCount = 4; // draw the first 2 points, onlyline_geometry.setDrawRange(0, drawCount);this.line_measure = new THREE.Line(line_geometry, line_material);this.line_measure.frustumCulled = false;// this.viewerService.highlightScene.add(this.line_measure);this.onmousemoveSubscription = this.pickerService.onmousemoveemitter.subscribe((event: MouseEvent) => {try {let point = this.raycasterService.getMousePosition(event);this.raycasterService.getFace3Intersects(point).then((intersection: THREE.Intersection) => {if (intersection.face) { this.currentPoint = this.snapperService.testVertices(intersection);this.updateCross();this.rendererService.needHighLightRender.emit(true);let isShow = this.isInPlane(this.currentPoint);if (isShow) {            if (this.currentCount > 0) {           this.measureItemsManagerService.getCurrentMeasureItem().highlightLine.setSecondVertice(this.currentPoint);}if(this.currentCount>3){}}}// console.log(intersections[0].face);});} catch (e) {//console.log(e);}});this.mousewheelSubscription = this.pickerService.mousewheelemitter.subscribe((event) => {try {let point = this.raycasterService.getMousePosition(event);this.raycasterService.getFace3Intersects(point).then((intersection: THREE.Intersection) => {if (intersection.face) {this.currentPoint = intersection.point;this.updateCross();this.rendererService.needHighLightRender.emit(true);} else {// console.log(intersection);}});} catch (e) {console.log(e);}});this.onmousedownSubscription = this.pickerService.onmousedownemitter.subscribe(() => {let date = new Date();this.downTime = date.getTime();});this.onmouseupSubscription = this.pickerService.onmouseupemitter.subscribe((event: MouseEvent) => {let date = new Date();this.upTime = date.getTime();let deltaTime = this.upTime - this.downTime;if (deltaTime < 250) {let isShow = this.isInPlane(this.currentPoint);if (event.button === 0 && isShow) {if (this.measureItemsManagerService.getCurrentMeasureItem()) {this.measureItemsManagerService.hideCurrentMeasureItemAuxiliary();this.measureItemsManagerService.deactiveCurrentMeasureLine();}if (this.origin === null) {this.origin = this.currentPoint.clone();}this.firstPoint = this.currentPoint.clone();this.currentCount++;this.pointList.push(this.currentPoint.clone());let highlightLine = new HighLightLine(this.measureItemsManagerService.getHighlightMaterial());let highlightLabel = new HighLightLabel();highlightLabel.setPosition(this.firstPoint);// if (this.measureItemsManagerService.getCurrentMeasureItem()) {//   this.measureItemsManagerService.getCurrentMeasureItem().active = false;// }//let measureItem2 = this.measureItemsManagerService.createMeasureItem();let measureItem = this.measureItemsManagerService.createMeasureItem();measureItem.setHighlightLine(highlightLine);if (this.currentCount === 3) {this.getPlane();measureItem.setHighlightLabel(highlightLabel);this.createDashedLine();this.viewerService.highlightScene.add(this.dashedLine);}measureItem.active = true;this.viewerService.highlightScene.add(measureItem.highlightLine.getHighlightLine());this.measureItems.push(measureItem);if (this.currentCount >= 3) { this.measureItemsManagerService.getCurrentMeasureItem().highlightLine.setFirstVertice(this.firstPoint);this.measureItemsManagerService.getCurrentMeasureItem().highlightLine.setSecondVertice(this.origin);           //this.measureItemsManagerService.getCurrentMeasureItem().setHighlightLabel(highlightLabel);this.measureItems[2].highlightLabel.setPosition(this.geometricCenter());this.measureItems[2].highlightLabel.setLabelText((this.areaPolygon()).toFixed(1) + '㎡');this.measureItems[2].highlightLabel.updatePosition(this.viewerService.activeCamera);} else {measureItem.highlightLine.setFirstVertice(this.firstPoint);}if(this.currentCount>3){this.viewerService.highlightScene.remove(this.dashedLine);this.createDashedLine();this.viewerService.highlightScene.add(this.dashedLine);}this.rendererService.needHighLightRender.emit(true);}}});}//面的方程private getPlane() {this.aParameter = ((this.pointList[1].y - this.pointList[0].y) * (this.pointList[2].z - this.pointList[0].z) -(this.pointList[1].z - this.pointList[0].z) * (this.pointList[2].y - this.pointList[0].y));this.bParameter = ((this.pointList[1].z - this.pointList[0].z) * (this.pointList[2].x - this.pointList[0].x) -(this.pointList[1].x - this.pointList[0].x) * (this.pointList[2].z - this.pointList[0].z));this.cParameter = ((this.pointList[1].x - this.pointList[0].x) * (this.pointList[2].y - this.pointList[0].y) -(this.pointList[1].y - this.pointList[0].y) * (this.pointList[2].x - this.pointList[0].x));this.dParameter = (0 - (this.aParameter * this.pointList[0].x + this.bParameter * this.pointList[0].y + this.cParameter * this.pointList[0].z));}//判断是否在面内private isInPlane(point: THREE.Vector3): boolean {if (this.currentCount < 3) {return true;} else {let a = this.aParameter * point.x + this.bParameter * point.y + this.cParameter * point.z + this.dParameter;if (a == 0) {return true;} else {return false;}}}//计算任意多边形面积private areaPolygon(): number {let area = 0;for (let i = 0, j = 1, k = 2; k < this.currentCount; j++, k++) {let a = this.pointList[i].distanceTo(this.pointList[j]);let b = this.pointList[j].distanceTo(this.pointList[k]);let c = this.pointList[k].distanceTo(this.pointList[i]);let p = (a + b + c) / 2;area += Math.sqrt(p * (p - a) * (p - b) * (p - b));}return area;}//计算三维任意多边形面积(慎用)private area3DPolygon(): number {let area = 0;let an, ax, ay, az; //法向及其坐标的abs值let coord;let tempa, tempb, tempc;let normal = new Vector3(this.aParameter, this.bParameter, this.cParameter);let unitNormal = new Vector3(normal.x / Math.sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z),normal.y / Math.sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z),normal.z / Math.sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z)); //面的单位法向量if (this.currentCount > 3) {//为投影选择要忽略的最大abs坐标ax = (unitNormal.x > 0 ? unitNormal.x : -unitNormal.x);ay = (unitNormal.y > 0 ? unitNormal.y : -unitNormal.y);az = (unitNormal.z > 0 ? unitNormal.z : -unitNormal.z);coord = 3;if (ax > ay) {if (ax > az)coord = 1;} else if (ay > az) {coord = 2;}//计算二维投影面积for (tempa = 1, tempb = 2, tempc = 0; tempb < this.currentCount; tempa++, tempb++, tempc++) {switch (coord) {case 1:area += (this.pointList[tempa].y * (this.pointList[tempb].z - this.pointList[tempc].z));continue;case 2:area += (this.pointList[tempa].x * (this.pointList[tempb].z - this.pointList[tempc].z));continue;case 3:area += (this.pointList[tempa].x * (this.pointList[tempb].y - this.pointList[tempc].y));continue;}}// console.log(area);an = Math.sqrt(ax * ax + ay * ay + az * az);switch (coord) {case 1:area *= (an / (2 * ax));break;case 2:area *= (an / (2 * ay));break;case 3:area *= (an / (2 * az));}return area;} else {let slideA = this.pointList[0].distanceTo(this.pointList[1]);let slideB = this.pointList[1].distanceTo(this.pointList[2]);let slideC = this.pointList[2].distanceTo(this.pointList[0]);let p = (slideA + slideB + slideC) / 2;area = (p * (p - slideA) * (p - slideB) * (p - slideC));return area;}}//几何体中心点private geometricCenter(): THREE.Vector3 {let x = 0,y = 0,z = 0;for (let i = 0; i < this.pointList.length; i++) {x += this.pointList[i].x;y += this.pointList[i].y;z += this.pointList[i].z;}x = x / this.currentCount;y = y / this.currentCount;z = z / this.currentCount;return new Vector3(x, y, z);}//绘制虚线private createDashedLine(){let geometry = new THREE.Geometry();geometry.vertices.push(this.origin, this.firstPoint);let material = new THREE.LineDashedMaterial({color:0xff0000,dashSize:3, gapSize:0.5, linewidth:3, scale: 3 });this.dashedLine = new THREE.LineSegments(geometry,material);(this.dashedLine as any).computeLineDistances();
}private updateCross() {if (!this.sprite.visible) {this.sprite.visible = true;}this.sprite.position.copy(this.currentPoint);this.sprite.updateMatrixWorld(true);}public clear(){this.currentCount = 0;this.origin = null;this.firstPoint = null;this.pointList = [];this.measureItems = [];this.viewerService.highlightScene.remove(this.dashedLine);}
}

threejs+angular 实现面积测量相关推荐

  1. 使用ThreeJs搭建BIM模型浏览器 QModel的诞生记

    QModel模型浏览器展示页面:QModel链接入口,支持Revit文件上传,在Web端浏览,并集成与模型相关的业务. 首先关于笔者的介绍: 2010年机缘巧合进行建筑信息化行业,当时主要还是在做工程 ...

  2. Angular No name was provided for external module 'XXX' in output.globals 错误

    Angular 7 开发自定义库时,引用ngZorroAntd,build过程中出现 No name was provided for external module 'ng-zorro-antd' ...

  3. angular.isUndefined()

    <!DOCTYPE html> <html><head><meta charset="UTF-8"><title>ang ...

  4. 五:Angular 数据绑定 (Data Binding)

    通常来说,数据绑定要么是从页面流向组件中的数据,要么是从组件中的数据流向页面.下面我们来介绍在Angular 2中数据绑定的几种不同方式.  1. 使用{{}}将组件中的数据显示在html页面上  实 ...

  5. angular初步认识一

    最近比较流行MVC前端框架开发,最近研究了一个框架AngularJS框架 不说那么多,先上例子,我是个代码控 <!DOCTYPE html> <html lang="en& ...

  6. 【讲人话】Angular如何通过@ViewChildren获取实时渲染的动态DOM节点元素(@ViewChild只能获取静态的固定DOM节点)

    故事背景:有一天,强哥整了个动态渲染的列表代码如下 app.component.html <div><button (click)="add()">添加一行 ...

  7. Angular的ChangeDetectorRef.detectChanges()实现angularJS的$apply()方法,强制刷新数据渲染

    在Javascript代码里,都是按照一定顺序来执行的,当轮到一个代码片段执行的时候,浏览器就只会去执行当前的片段,不会做任何其他的事情.所以有时候一些做得不是很好的网页,当点击了某个东西之后会卡住, ...

  8. Angular多个页面引入同一个组件报错The Component ‘MyComponentComponent‘ is declared by more than one NgModule怎么办?

    有一天,我写了一个自信满满的自定义组件myComponent,在多个页面import使用了,结果控制台给我来这个 我特么裤子都脱了,你给我来这个提示是几个意思 仔细一看 The Component ' ...

  9. 【硬核解说】一口气讲明白Angular的5种路由守卫RouteGuard是嘛玩意儿

    Angular的常用路由守卫有5种,按照执行顺序: ① CanLoad:进入到当前路由的时候触发(若用户没有权限访问,相应的模块并不会被加载.这里是指对应组件的代码). ↓ ② CanAcitivat ...

最新文章

  1. 慕课python第四周测试卷_中国大学慕课用Python玩转数据期末考试查题公众号答案...
  2. 使用tag标签是SEO优化的重要性是什么?
  3. 网络部署加实验步骤( 续)
  4. 首个面向手绘草图的深度自监督表示学习
  5. ajax 获取openid,纯前端获取当前用户的openid(微信小程序)
  6. brew安装mysql 卸载_Mac卸载mysql并安装mysql升级到8.0.13版本
  7. android 中 FLAG_SHOW_WHEN_LOCKED 的用法及解释
  8. 刀塔传奇公会管理系统 ------ Python界面访问Pgsql
  9. python实践winrm,实现远程连接Windows服务器,并执行指定命令
  10. 爱普生R230打印机两个红灯交替闪怎么回事?
  11. ace unlck工具下载_iPhoneX如何解锁ID激活锁
  12. 毕业论文 一级标题段前段后问题
  13. 阿里职级体系与薪酬全曝光,P10以上都是世界大牛!
  14. 掌握这个小技巧,让你的 C++ 编译速度提升 50 倍!
  15. matlab均衡的算法有哪些,从Matlab到Python的算法均衡
  16. 【案例】如何让阀门制造提高排产效率?APS系统帮你实现
  17. IDEA配置tomcat静态资源目录
  18. HADOOP SPILL FAILED原因
  19. 【157.1】golang+beego零基础入门实践教程it营大地
  20. 基于java网上蛋糕销售系统的设计与实现

热门文章

  1. 入门JAVA第十六天 数据库
  2. Leetcode刷题笔记12:【20】有效的括号【155】最小栈【255】用队列实现栈(STL:stackC++ 单引号和双引号)
  3. Electron 实现百度快搜
  4. Linux DMA Engine framework(2)_功能介绍及解接口分析
  5. 笔记本cpu温度多少正常?温度过高的4个常见原因
  6. 江苏五大姓氏世家,第一王,第二张,看看你是不是来自名门望族
  7. cai鸡——处女作博客“横空出世”
  8. Excel PivotTable 使用心得手顺分享(三)
  9. 720°VR全景网站制作
  10. 【LOJ】#3090. 「BJOI2019」勘破神机