Web端顯示CAD圖紙的應用場景很廣泛,單純的瀏覽DWG逐漸滿足不了用戶的實際需求,瀏覽的同時再加上簡單的繪制和批注更符合大家的應用場景,接下來我們講一下如何利用Mxdraw庫實現AutoCAD中的畫圓命令。
首先我們知道DWG圖紙要在網頁上顯示需要安裝轉換程序,在測試開始之前,我們要熟悉轉換方法和原理,請查看快速入門(https://help.mxdraw.com/?pid=32)中的《如何在自己系統中瀏覽dwg文件》章節,如下圖所示:
如果還有不清楚的,可以查看:(https://help.mxdraw.com/?pid=107)中《mxdraw前端庫預覽圖紙》章節,如下圖:
關于[MxDbCircleShape]
mxdraw庫是一款用于繪制CAD圖形的JAVAScript庫,它提供了一系列的圖形形狀類,可以實現類似于Autocad的繪圖功能,在此之前先看一下圓弧形狀類的描述:[MxDbCircleShape圓(弧)形狀類]https://mxcadx.gitee.io/mxdraw_api_docs/classes/MxDbCircleShape.html。
基于這個形狀類, 我們可以實現類似autocad繪制圓的功能,首先我們先通過繼承類的方式,為圓提供可以改變這個圓的夾點,代碼如下:
import { MxDbCircleShape } from "mxdraw";
class MxDbCircle extends MxDbCircleShape {
/**是否閉合到中心位置 */
isClosedToCenter = false
/**
* 返回自定義對象的夾點.
* @param
* @returns Array<THREE.Vector3>
*/
getGripPoints() {
const { x, y, z } = this.center;
// 計算圓的上下左右夾點
let upPoint = new THREE.Vector3(x, y + this.radius, z),
downPoint = new THREE.Vector3(x, y - this.radius, z),
leftPoint = new THREE.Vector3(x - this.radius, y, z),
rightPoint = new THREE.Vector3(x + this.radius, y, z);
return [this.center, upPoint, downPoint, leftPoint, rightPoint];
}
/**
* 移動自定義對象的夾點.
* @param
* @returns boolean
*/
moveGripPointsAt(index: number, offset: THREE.Vector3) {
const [center, upPoint, downPoint, leftPoint, rightPoint] =
this.getGripPoints();
// 改變上下左右的夾點則改變radius半徑
if (index === 0) this.center = center.add(offset);
if (index === 1) this.radius = upPoint.add(offset).distanceTo(this.center);
if (index === 2)
this.radius = downPoint.add(offset).distanceTo(this.center);
if (index === 3)
this.radius = leftPoint.add(offset).distanceTo(this.center);
if (index === 4)
this.radius = rightPoint.add(offset).distanceTo(this.center);
return true;
}
}
屬性列表如下:
需要注意的是MxDbCircleShape繼承自[MxDbEllipseShape](https://mxcadx.gitee.io/mxdraw_api_docs/classes/MxDbEllipseShape.html),因此MxDbCircleShape也擁有MxDbEllipseShape的所有屬性。我們只需要知道圓心和半徑就可與直接繪制一個圓了,在Autocad中繪制圓的方式有很多種, 下面介紹兩種方式來繪制圓:
方法一:兩點繪制圓
參考代碼如下:
import { MrxDbgUiPrPoint, MxFun, MxDbCircleShape, McEdGetPointWorldDrawObject, } from "mxdraw";
const drawCircleAtTwoPoints = async () => {
const getPoint = new MrxDbgUiPrPoint();
const circle = new MxDbCircle();
// 直接確定圓心
circle.center = awAIt getPoint.go()
getPoint.setUserDraw(
(
currentPoint: THREE.Vector3,
pWorldDraw: McEdGetPointWorldDrawObject
)=> {
// 根據圓心和圓弧上任意一點確定半徑
circle.radius = circle.center.distanceTo(currentPoint)
pWorldDraw.drawCustomEntity(circle);
// 再繪制一根圓弧和圓心的連接線表示現在正在確定半徑
pWorldDraw.drawLine(circle.center, currentPoint);
}
);
// 確定最后繪制的圓的半徑
circle.radius = circle.center.distanceTo(await getPoint.go())
MxFun.getCurrentDraw().addMxEntity(circle);
}
drawCircleAtTwoPoints()
方法二:三點繪制圓
通過三元一次方程組求解圓心的坐標的具體步驟如下:
1)假設圓心的坐標為(cx, cy, cz),將三個點的坐標代入圓的一般方程,得到三個方程:
a1 * cx + b1 * cy + c1 * cz + d1 = 0
a2 * cx + b2 * cy + c2 * cz + d2 = 0
a3 * cx + b3 * cy + c3 * cz + d3 = 0
2)將三個方程進行整理,得到以下形式的方程:
(a1 * b2 * c3 - a1 * b3 * c2 - a2 * b1 * c3 + a2 * b3 * c1 + a3 * b1 * c2 - a3 * b2 * c1) * cx +
(b1 * c2 * d3 - b1 * c3 * d2 - b2 * c1 * d3 + b2 * c3 * d1 + b3 * c1 * d2 - b3 * c2 * d1) * cy +
(a1 * b2 * d3 - a1 * b3 * d2 - a2 * b1 * d3 + a2 * b3 * d1 + a3 * b1 * d2 - a3 * b2 * d1) * cz +
(a1 * b2 * c3 - a1 * b3 * c2 - a2 * b1 * c3 + a2 * b3 * c1 + a3 * b1 * c2 - a3 * b2 * c1) = 0
3)根據方程的系數,將cx、cy和cz的系數分別除以(a1 * b2 * c3 - a1 * b3 * c2 - a2 * b1 * c3 + a2 * b3 * c1 + a3 * b1 * c2 - a3 * b2 * c1),得到cx、cy和cz的值。將得到的cx、cy和cz的值作為圓心的坐標,返回一個新的THREE.Vector3對象。
4)這樣就可以通過三元一次方程組的求解方法,求得三個點確定的圓心的坐標,代碼如下:
export const threePointsToDeterm.NETheCenterOfTheCircle = (
points: [THREE.Vector3, THREE.Vector3, THREE.Vector3]
) => {
const [point1, point2, point3] = points;
const { x: x1, y: y1, z: z1 } = point1;
const { x: x2, y: y2, z: z2 } = point2;
const { x: x3, y: y3, z: z3 } = point3;
const a1 = y1 * z2 - y2 * z1 - y1 * z3 + y3 * z1 + y2 * z3 - y3 * z2,
b1 = -(x1 * z2 - x2 * z1 - x1 * z3 + x3 * z1 + x2 * z3 - x3 * z2),
c1 = x1 * y2 - x2 * y1 - x1 * y3 + x3 * y1 + x2 * y3 - x3 * y2,
d1 = -(
x1 * y2 * z3 -
x1 * y3 * z2 -
x2 * y1 * z3 +
x2 * y3 * z1 +
x3 * y1 * z2 -
x3 * y2 * z1
),
a2 = 2 * (x2 - x1),
b2 = 2 * (y2 - y1),
c2 = 2 * (z2 - z1),
d2 = x1 * x1 + y1 * y1 + z1 * z1 - x2 * x2 - y2 * y2 - z2 * z2,
a3 = 2 * (x3 - x1),
b3 = 2 * (y3 - y1),
c3 = 2 * (z3 - z1),
d3 = x1 * x1 + y1 * y1 + z1 * z1 - x3 * x3 - y3 * y3 - z3 * z3,
// 計算圓心的坐標
cx =
-(
b1 * c2 * d3 -
b1 * c3 * d2 -
b2 * c1 * d3 +
b2 * c3 * d1 +
b3 * c1 * d2 -
b3 * c2 * d1
) /
(a1 * b2 * c3 -
a1 * b3 * c2 -
a2 * b1 * c3 +
a2 * b3 * c1 +
a3 * b1 * c2 -
a3 * b2 * c1),
cy =
(a1 * c2 * d3 -
a1 * c3 * d2 -
a2 * c1 * d3 +
a2 * c3 * d1 +
a3 * c1 * d2 -
a3 * c2 * d1) /
(a1 * b2 * c3 -
a1 * b3 * c2 -
a2 * b1 * c3 +
a2 * b3 * c1 +
a3 * b1 * c2 -
a3 * b2 * c1),
cz =
-(
a1 * b2 * d3 -
a1 * b3 * d2 -
a2 * b1 * d3 +
a2 * b3 * d1 +
a3 * b1 * d2 -
a3 * b2 * d1
) /
(a1 * b2 * c3 -
a1 * b3 * c2 -
a2 * b1 * c3 +
a2 * b3 * c1 +
a3 * b1 * c2 -
a3 * b2 * c1);
return new THREE.Vector3(cx, cy, cz);
};
5)已經知道通過三個圓上的點計算出圓心的算法,那么我們就可以通過三個點繪制一個圓,代碼如下:
import { MrxDbgUiPrPoint, MxFun, McEdGetPointWorldDrawObject, } from "mxdraw"
const drawCircleAtThreePoints = async () => {
const getPoint = new MrxDbgUiPrPoint();
const circle = new MxDbCircle();
let points = [] as unknown as [THREE.Vector3, THREE.Vector3, THREE.Vector3]
points.push(await getPoint.go())
getPoint.setUserDraw((currentPoint, pWorldDraw) => {
pWorldDraw.drawLine(points[0], currentPoint)
})
points.push(await getPoint.go())
getPoint.setUserDraw(
(
currentPoint: THREE.Vector3,
pWorldDraw: McEdGetPointWorldDrawObject
)=> {
circle.center = threePointsToDetermineTheCenterOfTheCircle([points[0], points[1], currentPoint])
circle.radius = circle.center.distanceTo(currentPoint)
pWorldDraw.drawCustomEntity(circle);
}
);
points.push(await getPoint.go())
circle.center = threePointsToDetermineTheCenterOfTheCircle(points);
circle.radius = circle.center.distanceTo(points[0]);
MxFun.getCurrentDraw().addMxEntity(circle);
}
drawCircleAtThreePoints()
效果圖如下:
Demo源碼:
https://gitee.com/mxcadx/mxdraw-article/tree/master/mxdraw%E5%BA%93%E5%AE%9E%E7%8E%B0autocad%E4%B8%AD%E7%9A%84%E5%9C%86