首页 > 其他分享 >在线CAD如何配合three.js绘制带线宽的线段

在线CAD如何配合three.js绘制带线宽的线段

时间:2023-07-19 15:11:43浏览次数:43  
标签:线宽 return material three js points obj

前言

1.在线CAD的产品经常会被集成到很多用户的网页系统内,前端开发人员只要会Java Script,就可以对在线CAD进行集成和二次开发,今天这篇文章我们讲一下梦想CAD控件云图(H5方式)如何配合three.js绘制带线宽的线段。

2.在这之前,如果还没有安装梦想CAD控件的朋友,可以查看快速入门,链接如下:http://help.mxdraw.com/?pid=32

函数配置

首先mxdraw的图形是有线宽属性的,但是在连续线段可能会存在一些问题,或者你希望用three.js来实现一些自定义的图形,那么我们就可以使用mxdraw提供的MxDbEntity来实现这样一个带线宽的线段,我们先把最基本需要重写的函数写出来:

 

import { McGiWorldDraw, MxDbEntity } from "mxdraw"

class MxDbLine extends MxDbEntity {

    getTypeName(): string {

        return "MxDbLine"

    }

    worldDraw(pWorldDraw: McGiWorldDraw): void {

 

    }

    getGripPoints(): THREE.Vector3[] {

        return []

    }

    moveGripPointsAt(index: number, offset: THREE.Vector3): boolean {

       return true

    }

    dwgIn(obj: any): boolean {

       this.onDwgIn(obj)

       return true

    }

    dwgOut(obj: any): object {

        this.onDwgOut(obj)

        return obj

    }

}

 

 

 

定义线段数据

现在我们就有了一个MxDbLine类,用来表示它是一条线段,但是它没有任何与线段有关的数据,我们要先定义一些线段数据,代码如下:

class MxDbLine extends MxDbEntity {

    // ...

    points: THREE.Vector3[] = []

    dwgIn(obj: any): boolean {

       this.onDwgIn(obj)

       this.dwgInHelp(obj, ["points"])

       return true

    }

    dwgOut(obj: any): object {

        this.onDwgOut(obj)

        this.dwgOutHelp(obj, ["points"])

        return obj

    }

}

 

现在我们有了points数据了 这些点可以构成一段段的线段,但是它现在还不能在画布中渲染,这时还需要用three.js来实现一个带线宽的线段结合体,这个如何实现呢?

首先,可以在three.js示例中找到Line2 这样相关的类,它就可以实现带线宽的线段,我们先安装:

npm i [email protected]

现在只需要Line2、LineGeometry、LineMaterial这三个类,你可以不用安装[email protected]的依赖,只需要找到它对应示例的文件,引入到项目中就可以了,以下是具体的实现代码:

import { Line2 } from 'three/examples/jsm/lines/Line2'

import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry'

import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial'

export class MxDbLine extends MxDbEntity {

      // ...

     // 临时存放的material

     material: LineMaterial

     worldDraw(pWorldDraw: McGiWorldDraw): void {

        const material = new LineMaterial()

        this.material = material

        const geometry = new LineGeometry()

        // 得到mxdraw中使用的three.js

        const THREE = MxFun.getMxFunTHREE()

        // 设置颜色

        material.color = new THREE.Color(this.color)

 

        // line2必须设置的分辨率

        const canvas = MxFun.getCurrentDraw().getCanvas()

        material.resolution.set( canvas.width, canvas.height)

        // 设置透明 因为这样才能覆盖底层的cad图纸

        material.transparent = true

 

        // 设置点向量位置,line2需要这样设置

        const positions: number[] = []

        for(let i = 0; i < this.points.length; i++) {

            positions.push(this.points[i].x, this.points[i].y, (this.points[i] as any)?.z || 0)

        }

        geometry.setPositions(positions)

 

        const line2 = new Line2(geometry, material)

        // 最后把这个three.js生成的线段绘制出来

        pWorldDraw.drawEntity(line2)

    }

}

 

宽度的配置

现在我们基本上用three.js示例中提供的Line2类绘制线段,MxDbLine也可以完整的显示一条线段了,但是它还没有宽度。

在MxDbEntity中提供了dLinewidth属性用于表示线宽,用lineWidthByPixels属性表示线宽是否始终跟随屏幕宽度,也就是画布缩放,线的宽度始终不变,当lineWidthByPixels为false时就是另一种three.js中的坐标系宽度,这种宽度是固定的了,不会随着画布缩放而变化。

要实现这两种宽度还需要了解到MxDbEntity重写方法onViewChange 当画布缩放时onViewChange 就会执行,还需要了解的是要将当前的屏幕坐标长度转成three.js坐标系长度,在mxdraw中提供了MxFun.screenCoordLong2World来转换。

默认dLinewidth都是跟随屏幕的宽度,我们需要先记录当前绘制这条线段时,1屏幕像素转成three.js坐标系长度的值是多少,然后后面需要根据lineWidthByPixels属性判断是用跟随屏幕像素的宽度还是three.js坐标系一样的固定宽度。

如果lineWidthByPixels = false 那么我们就可以通过当时绘制时记录的three.js坐标系长度的值去比上现在这个时候的1屏幕像素下的three.js坐标系长度,这样就得到了一个线宽比,用线宽比去乘以目前设置的dLinewidth宽度,就算实现需要的宽度了。

如果lineWidthByPixels = true 就不用这么麻烦了,dLinewidth就算我们需要的宽度,具体的代码如下:

export class MxDbLine extends MxDbEntity {

    // 记录1屏幕像素下three.js的长度 比例

     _lineWidthRatio: number

    //  更新实际的线宽

    updateLineWidth() {

        if(!this._lineWidthRatio) {

            this._lineWidthRatio = MxFun.screenCoordLong2World(1)

        }

        this.material.linewidth =  this.lineWidthByPixels ? this.dLineWidth :  this.dLineWidth *  this._lineWidthRatio / MxFun.screenCoordLong2World(1)

    }

     worldDraw(pWorldDraw: McGiWorldDraw): void {

        // ...

        this.updateLineWidth()

        // ...

     }

    //  在画布视图缩放变化时立即触发更新重写渲染

     onViewChange() {

        // 只有当时以three.js长度为单位的时候才这样立即更新去调整宽度

        if(!this.lineWidthByPixels) {

            this.setNeedUpdateDisplay()

            MxFun.updateDisplay()

        }

        return true

    }

    // 顺便我们把夹点操作移动给写上,这样就可以移动每一个向量点来来改变线段了

    getGripPoints(): THREE.Vector3[] {

        return this.points

    }

    moveGripPointsAt(index: number, offset: THREE.Vector3): boolean {

        this.points[index] = this.points[index].clone().add(offset)

       return true

    }

    //  _lineWidthRatio属性必须一直存在 这样线宽才算正确的

    dwgIn(obj: any): boolean {

       this.onDwgIn(obj)

       this.dwgInHelp(obj, ["points", "_lineWidthRatio"])

       return true

    }

    dwgOut(obj: any): object {

        this.onDwgOut(obj)

        this.dwgOutHelp(obj, ["points", "_lineWidthRatio"])

        return obj

    }

}

 

最后就是使用我们写好的MxDbLine类,代码如下:

import { MxFun, MrxDbgUiPrPoint } from "mxdraw"

const drawLine = ()=> {

    const line = new MxDbLine()

    line.dLineWidth = 10

    const getPoint = new MrxDbgUiPrPoint()

    // 这是绘制过程中设置的动态绘制函数

    getPoint.setUserDraw((currentPoint, pWorldDraw)=> {

        if(line.points.length === 0) return

        if(line.points.length >= 2) {

            pWorldDraw.drawCustomEntity(line)

        }

        pWorldDraw.drawLine(currentPoint, line.points[line.points.length - 1])

    })

    getPoint.goWhile(()=> {

        // 鼠标左键点击

        line.points.push(getPoint.value())

    }, ()=> {

        // 鼠标右键结束绘制

        MxFun.getCurrentDraw().addMxEntity(line)

    })

}

 

绘制带宽度的线段过程,如下图:

 

 

这时绘制完成后的效果:

 

 

把line.lineWidthByPixels 设置成false 当缩放画布时,线段就不会始终是屏幕宽度了,而是当时绘制时的three.js实际宽度。

带宽度的线段当画布缩放时宽度不随屏幕一起变大,如下图:

 

 

下面给MxDbLine的完整代码:

import { McGiWorldDraw, McGiWorldDrawType, MxDbEntity, MxFun } from "mxdraw"

import { Line2 } from 'three/examples/jsm/lines/Line2'

import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry'

import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial'

export class MxDbLine extends MxDbEntity {

    points: THREE.Vector3[] = []

    getTypeName(): string {

        return "MxDbLine"

    }

    material: LineMaterial

    _lineWidthRatio: number

    worldDraw(pWorldDraw: McGiWorldDraw): void {

        const material = new LineMaterial()

        this.material = material

        const geometry = new LineGeometry()

        // 得到mxdraw中使用的three.js

        const THREE = MxFun.getMxFunTHREE()

        // 设置颜色

        material.color = new THREE.Color(this.color)

        // line2必须设置的分辨率

        const canvas = MxFun.getCurrentDraw().getCanvas()

        material.resolution.set( canvas.width, canvas.height)

        // 设置透明 因为这样才能覆盖底层的cad图纸

        material.transparent = true

        // 更新线宽

        this.updateLineWidth()

        // 设置点向量位置,line2需要这样设置

        const positions: number[] = []

        for(let i = 0; i < this.points.length; i++) {

            positions.push(this.points[i].x, this.points[i].y, (this.points[i] as any)?.z || 0)

        }

        geometry.setPositions(positions)

       const line2 = new Line2(geometry, material)

        pWorldDraw.drawEntity(line2)

    }

    updateLineWidth() {

        if(!this._lineWidthRatio) {

            this._lineWidthRatio = MxFun.screenCoordLong2World(1)

        }

        this.material.linewidth =  this.lineWidthByPixels ? this.dLineWidth :  this.dLineWidth *  this._lineWidthRatio / MxFun.screenCoordLong2World(1)

    }

    onViewChange() {

        this.setNeedUpdateDisplay()

        MxFun.updateDisplay()

     

        return true

    }

    getGripPoints(): THREE.Vector3[] {

        return this.points

    }

    moveGripPointsAt(index: number, offset: THREE.Vector3): boolean {

        this.points[index] = this.points[index].clone().add(offset)

       return true

    }

    dwgIn(obj: any): boolean {

       this.onDwgIn(obj)

       this.dwgInHelp(obj, ["points", "_lineWidthRatio"])

       return true

    }

    dwgOut(obj: any): object {

        this.onDwgOut(obj)

        this.dwgOutHelp(obj, ["points", "_lineWidthRatio"])

        return obj

    }

}

 

Demo源码链接:

https://github.com/mxcad/mxdraw-article/tree/master/mxdraw%E5%A6%82%E4%BD%95%E9%85%8D%E5%90%88three.js%E5%AE%9E%E7%8E%B0%E5%B8%A6%E7%BA%BF%E5%AE%BD%E7%9A%84%E7%BA%BF%E6%AE%B5/demo

以上,在线CAD如何配合three.js绘制带线宽的线段功能就完成了,有不清楚的请移步梦想CAD控件官网。

 

标签:线宽,return,material,three,js,points,obj
From: https://www.cnblogs.com/yzy0224/p/17565671.html

相关文章

  • 关于用JS判断PC客户端是否安装某个软件的方法
    开发过程中,我们经常会碰到这样的需求:在web网页上,检测电脑上是否安装了某个软件。由于浏览器的安全机制,js没有办法通过浏览器去访问注册表信息,除非用浏览器扩展。 如果想在web网页中判断PC客户端是否安装了某个软件,可以试用标准的浏览器特性:检查浏览器是否支持自定义协议处理程......
  • IC卡读卡器web插件中使用js异步await/async调用接口
    js中使用异步await/async方式,对于程序的结构和逻辑都有非常大的好处,对于异步await/async有如下描述:async表示这是一个async函数,await只能用在async函数里面,不能单独使用.async返回的是一个Promise对象,await就是等待这个promise的返回结果后,再继续执行.await等待的是一个Pro......
  • android 加载assets 本地json
    Android加载Assets本地JSON的实现作为一名经验丰富的开发者,我很荣幸能够教会你如何在Android应用中加载本地JSON文件。下面我将逐步介绍整个实现过程,并提供相应的代码示例和注释。实现步骤首先,我们来看一下加载Assets本地JSON的实现步骤:步骤说明1获取AssetsManager对......
  • android 遍历json串
    Android遍历JSON串的流程在Android开发中,我们经常会使用JSON(JavaScriptObjectNotation)作为数据交换的格式。遍历JSON串是一项基本的开发技能,它可以帮助我们在应用程序中获取和使用JSON数据。下面是遍历JSON串的基本流程:步骤描述步骤一创建JSON对象或解析JSON字符串......
  • JS 嵌套循环
    for(vari=0;i<5;i++){while(i%3==0){i++;console.log(i);break;}console.log(i);}for(vari=0;i<5;i++){while(i||i++){console.log(i);continue;console.l......
  • JSONP方式解决跨域
     <script>//封装一个jsonp函数functionjsonp({url,params,callback}){returnnewPromise((resolve,reject)=>{//定义回调函数window[callback]=function(data){resolve(data)}......
  • Nest.js框架下,nest g app auth 命令报错!
    首先报错信息是:Failedtoexecutecommand:node@nestjs/schematics:sub-app--name=auth--no-dry-run--no-skip-import--language="ts"--source-root="apps/reservations/src"--spec--no-flat--spec-file-suffix="spec",大概的意思就是无法执行这条命令。......
  • Uncaught Error: Unsupported GeoJSON type: undefined
     渲染一个json数据(含坐标,应该就是Geojson类型)出现该错误。 AI:这表示在您的JavaScript代码中,您正在处理的GeoJSON数据存在问题。错误明确指出GeoJSON类型为undefined,这意味着您试图访问或处理的GeoJSON数据没有有效或被识别的类型  ---------------------shp文件构成--......
  • JS标识符
    什么是标识符? 变量名函数名属性名都称为标识符. 定义标识符规范如下1)标识符只能由字母数字下划线$组成.2)标识符不能以数字开头,例如:1name.3)标识符不能实JS中的关键字或保留字,例如:forifwhile等.4)标识符一般采用驼峰命名法定义,第一个单词首字母为小......
  • Go语言读取网上Json格式的天气预报数据例子
    天气预报接口使用的是:http://www.weather.com.cn/data/sk/101010100.html这里的Json数据如下:{"weatherinfo":{"city":"北京","cityid":"101010100","temp":"1......