前言
本文使用mxcad3d在網頁中創建一個簡單的三維窗戶模型,mxcad3d提供了豐富的三維建模功能和便捷的API,使得創建各種三維模型變得簡單方便,最終效果如下圖:

環境搭建和入門
首先學習mxcad的基本使用方法,可通過官方的入門教程來搭建一個最基本的項目模板,依次查看教程:安裝`Node.js`以及`VS Code`開發工具、創建mxcad開發項目、API文檔接口使用說明。
壓縮包下載解壓後需要在項目目錄下打開`cmd命令行`,然後在命令行中執行`npm install`來安裝依賴,然後再按照本教程中的方式來運行項目查看效果。
編寫創建窗戶模型的代碼
1. 根據官方快速入門教程來創建一個名為`Test3dWindow`的項目,如下圖:

2. 編寫繪製窗戶模型的代碼
在index.html中插入一個按鈕"繪製窗戶模型", 的完整代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vite use mxcad</title>
</head>
<body>
<div > <canvas id="myCanvas"></canvas></div>
<button>繪製窗戶模型</button>
<script type="module" src="./src/index.ts"></script>
</body>
</html>
在src/index.ts中編寫繪製窗戶模型的函數,src/index.ts的完整代碼如下:
import { MdGe, Mx3dGeAxis, Mx3dGeColor, Mx3dGeDir, Mx3dGeMaterial, Mx3dGeomPlane, Mx3dGePoint, Mx3dGeVec, Mx3dMkBox, Mx3dMkFace, Mx3dMkPolygon, Mx3dMkPrism, MxCAD3DObject } from "mxcad"
// 創建mxcad3d對象
const mxcad3d = new MxCAD3DObject
// 初始化mxcad3d對象
mxcad3d.create({
// canvas元素的css選擇器字符串(示例中是id選擇器),或canvas元素對象
canvas: "#myCanvas",
// 獲取加載wasm相關文件(wasm/js/worker.js)路徑位置
locateFile: (fileName)=> new URL(`/node_modules/mxcad/dist/wasm/3d/${fileName}`, import.meta.url).href,
})
// 初始化完成
mxcad3d.on("init", =>{
console.log("初始化完成");
// 修改背景顏色
const color1 = new Mx3dGeColor(61/255, 139/255, 221/255, MdGe.MxTypeOfColor.Color_TOC_sRGB);
const color2 = new Mx3dGeColor(203/255, 223/255, 247/255, MdGe.MxTypeOfColor.Color_TOC_sRGB);
mxcad3d.setGradientBgColor(color1, color2, MdGe.MxGradientFillMethod.GFM_VER);
// 設置透視投影
mxcad3d.setProjectionType(MdGe.MxCameraProjection.CProjection_Perspective);
// 打開光照陰影
mxcad3d.enableDirLightSrc(true);
});
function drawWindow{
// 窗戶邊框橫截面輪廓點
const pts: Mx3dGePoint = ;
pts.push(new Mx3dGePoint(0, 0, 0));
pts.push(new Mx3dGePoint(1, 0, 0));
pts.push(new Mx3dGePoint(1, 0, 2));
pts.push(new Mx3dGePoint(4, 0, 2));
pts.push(new Mx3dGePoint(4, 0, 0));
pts.push(new Mx3dGePoint(5, 0, 0));
pts.push(new Mx3dGePoint(5, 0, 10));
pts.push(new Mx3dGePoint(3, 0, 10));
pts.push(new Mx3dGePoint(3, 0, 8));
pts.push(new Mx3dGePoint(2, 0, 8));
pts.push(new Mx3dGePoint(2, 0, 10));
pts.push(new Mx3dGePoint(0, 0, 10));
// 窗戶邊框橫截面輪廓多段線
const polygon = new Mx3dMkPolygon;
pts.forEach((pt) => polygon.Add(pt));
polygon.Close;
// 窗戶邊框橫截面輪廓線
const wire = polygon.Wire;
// 窗戶邊框橫截面輪廓線生成窗框橫截面
const makeface = new Mx3dMkFace(wire);
const face = makeface.Face;
const vec = new Mx3dGeVec(0, 100, 0);
// 橫截面拉伸出窗框體形狀
const frame = new Mx3dMkPrism(face, vec);
let frameShape = frame.Shape;
// 構造兩個平面用於分割窗戶邊框(邊框兩端的斜45度角)
const pt = new Mx3dGePoint(0, 0, 0);
const dir = new Mx3dGeDir(0, -1, 1);
const plane = new Mx3dGeomPlane(pt, dir);
// 平面1
const planeFace = plane.Face(1e-5);
pt.setXYZ(0, 50, 0);
dir.SetXYZ(0, 0, 1);
const axis = new Mx3dGeAxis(pt, dir);;
// 平面2
const planeFace2 = planeFace.MirroredByAxis(axis);
// 分割窗戶邊框(分割成了兩個斜45度的小三角形部分和中間的一個梯形部分)
const parts = frameShape.spliter([planeFace, planeFace2]);
// 篩選出中間那個梯形的部分(這裡是通過質心的位置來判斷的)
parts.forEach((shape)=>{
// 這裡Centroid的參數添填的MdGe.MxQuantAspect.Quant_Volume這個枚舉,是因為shape是實體,它的質心是體質心,所以這裡填MdGe.MxQuantAspect.Quant_Volume
// 如果shape是面,它的質心是面質心,所以要填MdGe.MxQuantAspect.Quant_Area
// 如果shape是線,它的質心是線質心,所以要填MdGe.MxQuantAspect.Quant_Length
const centroid = shape.Centroid(MdGe.MxQuantAspect.Quant_Volume);
if (centroid.Y > 45 && centroid.Y < 55) {
frameShape = shape;
}
});
// 通過旋轉得到另外三個邊的邊框
const frameShape2 = frameShape.Rotated(new Mx3dGeAxis([0, 50, 50], [1, 0, 0]), Math.PI / 2);
const frameShape3 = frameShape.Rotated(new Mx3dGeAxis([0, 50, 50], [1, 0, 0]), Math.PI);
const frameShape4 = frameShape2.Rotated(new Mx3dGeAxis([0, 50, 50], [1, 0, 0]), Math.PI);
// 合併四個邊框,獲得邊框整體形狀
frameShape = frameShape.fuse(frameShape2).fuse(frameShape3).fuse(frameShape4);
// 窗框顏色
const frameColor = new Mx3dGeColor(0.5, 0.5, 0.5, MdGe.MxTypeOfColor.Color_TOC_RGB);
// 窗框材質
const frameMaterial = new Mx3dGeMaterial(MdGe.MxNameOfMaterial.Material_NOM_ShinyPlastified);
// 玻璃
const glass = new Mx3dMkBox([2, 8, 8], [3, 92, 92]);
// 玻璃形狀
const glassShape = glass.Shape;
// 玻璃顏色
const glassColor = new Mx3dGeColor(0, 0.9, 0.549, MdGe.MxTypeOfColor.Color_TOC_RGB);
// 玻璃材質,看起來是透明的
const glassMaterial = new Mx3dGeMaterial(MdGe.MxNameOfMaterial.Material_NOM_Glass);
// 獲取模型文檔
const doc = mxcad3d.getDocument;
// 新增一個形狀標籤用於保存邊框形狀
const frameLabel = doc.addShapeLabel;
frameLabel.setShape(frameShape);
frameLabel.setColor(frameColor)
frameLabel.setMaterial(frameMaterial);
// 新增一個形狀標籤用於保存玻璃形狀
const glassLabel = doc.addShapeLabel;
glassLabel.setShape(glassShape);
glassLabel.setColor(glassColor)
glassLabel.setMaterial(glassMaterial);
// 更新視圖顯示
mxcad3d.update;
}
// 給button添加點擊事件,點擊後調用drawWindow函數,進行窗戶模型的繪製
// 立即執行函數
(function addEventToButton{
const btn = document.querySelector("button");
if (btn) {
btn.addEventListener("click", => {
drawWindow;
});
}
})
3. 新建終端,運行`npx vite`命令來運行項目,效果如下圖:
