畫立方體軟件開發筆記 js three 投影 參數建模 旋轉相機 @tarikjabiri/dxf導出dxf

gitee:?

?njsgcs/njsgcs_3d

mainwindow.js:4 Uncaught SyntaxError: The requested module '/3dviewport.js' does not provide an export named 'default'一定要default嗎

2025-05-10 14-27-58 專門寫了個代碼畫立方體

import{ scene,camera,renderer}  from './3dviewport';
import {update2DViewport }from './2dviewport';
const sidebar3d = document.createElement('div');
sidebar3d.id = 'sidebar3d';
// 固定側邊欄寬度sidebar3d.style.padding = '10px';
sidebar3d.style.backgroundColor = '#f0f0f0';import * as THREE from 'three';function init_sidebar3d() {const ox=document.createElement('input');ox.type='text';ox.placeholder = '左下角坐標x'; // 改為placeholder提示,避免覆蓋用戶輸入const oy=document.createElement('input');oy.type='text';oy.placeholder = '左下角坐標y';const oz=document.createElement('input');oz.type='text';oz.placeholder = '左下角坐標z';const sx=document.createElement('input');sx.type='text';sx.placeholder = '尺寸x';const sy=document.createElement('input');sy.type='text';sy.placeholder = '尺寸y';const sz=document.createElement('input');sz.type='text';sz.placeholder = '尺寸z';const createBtn = document.createElement('button');createBtn.textContent = '確定';createBtn.style.width = '100%';createBtn.style.margin = '5px 0';createBtn.addEventListener('click', () => {// 獲取輸入值并轉換為數值const oxVal = parseFloat(ox.value);const oyVal = parseFloat(oy.value);const ozVal = parseFloat(oz.value);const sxVal = parseFloat(sx.value);const syVal = parseFloat(sy.value);const szVal = parseFloat(sz.value);addcube_button(oxVal, oyVal, ozVal, sxVal, syVal, szVal); // 傳遞參數});ox.style.display="none"oy.style.display="none"oz.style.display="none"sx.style.display="none"sy.style.display="none"sz.style.display="none"createBtn.style.display="none"const addCubeBtn = document.createElement('button');addCubeBtn.textContent = '添加立方體';addCubeBtn.style.width = '100%';addCubeBtn.style.margin = '5px 0';addCubeBtn.addEventListener('click', () => {if (ox.style.display=="none"){ox.style.display="block";oy.style.display="block";oz.style.display="block";sx.style.display="block";sy.style.display="block";sz.style.display="block";createBtn.style.display="block";}else{ox.style.display="none";oy.style.display="none";oz.style.display="none";sx.style.display="none";sy.style.display="none";sz.style.display="none";createBtn.style.display="none";}// 點擊后更新按鈕列表});sidebar3d.appendChild(addCubeBtn);sidebar3d.appendChild(ox);sidebar3d.appendChild(oy);sidebar3d.appendChild(oz);sidebar3d.appendChild(sx);sidebar3d.appendChild(sy);sidebar3d.appendChild(sz);sidebar3d.appendChild(createBtn);addcube_button(0,0,0,100,100,100);addcube_button(0,100,0,100,100,100);addcube_button(100,0,0,100,100,100);addcube_button(-100,0,0,100,100,100);// 首次生成按鈕}
// 創建更新側邊欄按鈕的函數function addcube_button(ox,oy,oz,sx,sy,sz){// 使用輸入的尺寸創建幾何體(默認值防止未輸入時出錯)const geometry = new THREE.BoxGeometry(sx || 100, sy || 100, sz || 100);const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });const cube = new THREE.Mesh(geometry, material);// 設置位置(默認原點防止未輸入時出錯)cube.position.set(ox || 0, oy || 0, oz || 0);cube.name = 'CustomCube'; scene.add(cube);renderer.render(scene, camera);const btn = document.createElement('button');btn.textContent = `立方體`; // 按鈕文本btn.style.width = '100%'; // 按鈕寬度btn.style.margin = '5px 0'; // 按鈕間距btn.addEventListener('click', () => {if (deleteBtn.style.display=="none"){deleteBtn.style.display = 'block';}else{deleteBtn.style.display = 'none';}});const deleteBtn = document.createElement('button');deleteBtn.textContent = '刪除';deleteBtn.style.width = '100%';deleteBtn.style.margin = '5px 0';deleteBtn.addEventListener('click', () => {scene.remove(cube); // 刪除對象renderer.render(scene, camera);sidebar3d.removeChild(deleteBtn); // 移除按鈕sidebar3d.removeChild(btn); // 移除對應的按鈕});deleteBtn.style.display="none"sidebar3d.appendChild(btn);sidebar3d.appendChild(deleteBtn);update2DViewport();}
// 創建幾何體和網格init_sidebar3d();// 不再直接添加到body,改為導出供mainwindow管理
export default sidebar3d;

旋轉功能

2025-05-10 14-41-38 旋轉視圖

ai寫的我連看都沒看?

import * as THREE from 'three';// 創建 canvas 并導出
const viewportCanvas3d = document.createElement('canvas');
viewportCanvas3d.id = 'scene';
viewportCanvas3d.style.flex = '1';
viewportCanvas3d.style.height = '100vh';// 創建場景和相機
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000
);// 創建渲染器并綁定 canvas
const renderer = new THREE.WebGLRenderer({ canvas: viewportCanvas3d });
renderer.setSize(window.innerWidth, window.innerHeight);// 設置相機位置
camera.position.z = 500;// ---------- 新增鼠標中鍵旋轉邏輯 ----------
let isRotating = false;
let startX = 0;
let startY = 0;
const target = new THREE.Vector3(0, 0, 0); // 假設零件中心在場景原點// 鼠標按下事件(中鍵觸發旋轉)
function onMouseDown(event) {if (event.button === 1) { // 鼠標中鍵(button=1)isRotating = true;startX = event.clientX;startY = event.clientY;event.preventDefault(); // 阻止默認滾動行為}
}// 鼠標移動事件(旋轉相機)
function onMouseMove(event) {if (!isRotating) return;const deltaX = event.clientX - startX;const deltaY = event.clientY - startY;startX = event.clientX;startY = event.clientY;// 計算相機繞目標點的旋轉(水平/垂直移動)const cameraPos = camera.position.clone().sub(target); // 轉換為相對目標點的坐標// 水平移動繞Y軸旋轉const rotateY = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), deltaX * 0.005);cameraPos.applyQuaternion(rotateY);// 垂直移動繞X軸旋轉(負號調整方向)const rotateX = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), -deltaY * 0.005);cameraPos.applyQuaternion(rotateX);camera.position.copy(cameraPos.add(target)); // 轉換回世界坐標camera.lookAt(target); // 始終看向目標點renderer.render(scene, camera); // 更新渲染
}// 鼠標釋放事件(結束旋轉)
function onMouseUp(event) {if (event.button === 1) isRotating = false;
}// 綁定事件監聽
viewportCanvas3d.addEventListener('mousedown', onMouseDown);
viewportCanvas3d.addEventListener('mousemove', onMouseMove);
viewportCanvas3d.addEventListener('mouseup', onMouseUp);
// ---------- 新增邏輯結束 ----------export  { viewportCanvas3d as default , scene , camera, renderer };

太復雜了讓ai減少方法數量

就差億點了

沒毛病

連出來是右視圖?

            const edges = [[0, 1], [1, 3], [3, 2], [2, 0],[4, 5], [5, 7], [7, 6], [6, 4],[1, 4], [0, 5], [2, 7], [3, 6]];

?

import * as THREE from 'three';
import { scene } from './3dviewport.js';
import { DxfWriter, point3d } from '@tarikjabiri/dxf';
// 創建 Canvas 元素
const viewportCanvas2d = document.createElement('canvas');
viewportCanvas2d.width = 900;
viewportCanvas2d.height = 600;
viewportCanvas2d.style.border = '1px solid #000';const ctx = viewportCanvas2d.getContext('2d');// 預定義相機集合
const cameras = [{camera: new THREE.OrthographicCamera(-450, 450, 300, -300, -1000, 1000),position: [0, 0, 500],label: "Front View",offset: { x: 0, y: 0 },scale: 0.5,},{// 原參數:-300, 300, 300, -300(水平/垂直范圍 600/600)// 調整后:根據畫布寬高比 3:2,將垂直范圍調整為 400(600/3*2=400)camera: new THREE.OrthographicCamera(-450, 450, 300, -300, -1000, 1000),position: [500, 0, 0],label: "Right View",offset: { x: 450, y: 0 },scale: 0.5,},{camera: new THREE.OrthographicCamera(-450, 450, 300, -300, -1000, 1000),position: [0, 500, 0],label: "Top View",offset: { x: 0, y: 300 },scale: 0.5,}
];// 初始化相機參數
cameras.forEach(({ camera, position }) => {camera.position.set(...position);camera.lookAt(0, 0, 0);
});// 臨時變量const tempV1 = new THREE.Vector3();
const tempV2 = new THREE.Vector3();
const linelist=[];function update2DViewport() {ctx.clearRect(0, 0, viewportCanvas2d.width, viewportCanvas2d.height);for (const config of cameras) {const { camera, offset, label, scale } = config;// 獲取相機位置const cameraPos = new THREE.Vector3();camera.getWorldPosition(cameraPos);const Edges = [];// 遍歷場景中的立方體scene.traverse(obj => {if (!(obj instanceof THREE.Mesh && obj.name === 'CustomCube')) return;const geometry = obj.geometry;if (!(geometry instanceof THREE.BoxGeometry)) return;const vertices = geometry.attributes.position.array;const edges = [[0, 1], [1, 3], [3, 2], [2, 0],[4, 5], [5, 7], [7, 6], [6, 4],[1, 4], [0, 5], [2, 7], [3, 6]];edges.forEach(([v1Idx, v2Idx]) => {tempV1.fromBufferAttribute(geometry.attributes.position, v1Idx).applyMatrix4(obj.matrixWorld);tempV2.fromBufferAttribute(geometry.attributes.position, v2Idx).applyMatrix4(obj.matrixWorld);Edges.push([tempV1.clone(), tempV2.clone()]);});});// 繪制標簽ctx.fillStyle = "#000";ctx.font = `${12 * scale}px sans-serif`;ctx.fillText(label, offset.x + 10 * scale, offset.y + 20 * scale);ctx.strokeStyle = '#000';ctx.lineWidth = 1;for (const [p1, p2] of Edges) {const sp1 = projectPoint(p1, camera, scale);const sp2 = projectPoint(p2, camera, scale);ctx.beginPath();ctx.moveTo(sp1.x + offset.x, sp1.y + offset.y);ctx.lineTo(sp2.x + offset.x, sp2.y + offset.y);ctx.stroke();linelist.push([sp1.x + offset.x, sp1.y + offset.y,sp2.x + offset.x, sp2.y + offset.y])}}}
function export_dxf() {// 如果是瀏覽器環境const dxf = new DxfWriter();// 添加一個圖層(可選)dxf.addLayer('Lines', 7); // 顏色索引 7 是黑色// 輔助函數:保留兩位小數function toFixed(num) {return parseFloat(parseFloat(num).toFixed(2));}// 遍歷 linelist 添加線段for (const [p1x, p1y,p2x,p2y] of linelist) {const start = point3d(toFixed(p1x), toFixed(p1y));const end = point3d(toFixed(p2x), toFixed(p2y));dxf.addLine(start, end, 'Lines'); // 'Lines' 圖層名}// 生成 DXF 字符串內容const dxfString = dxf.stringify();// 創建 Blob 并觸發下載const blob = new Blob([dxfString], { type: "application/dxf" });const url = URL.createObjectURL(blob);const now = new Date().toISOString().replace(/[:.]/g, '') // 移除冒號和點.replace('T', '_');const a = document.createElement("a");a.href = url;a.download = `${now} output.dxf`;a.click();// 釋放資源URL.revokeObjectURL(url);
}
// 投影函數
function projectPoint(point, camera, scale) {const ndc = point.clone().project(camera);const x = (ndc.x + 1) * viewportCanvas2d.width / 2 * scale;const y = (1 - ndc.y) * viewportCanvas2d.height / 2 * scale;return { x, y };
}export {viewportCanvas2d as default,update2DViewport,export_dxf} ;

import {export_dxf} from './2dviewport';
const sidebar2d = document.createElement('div')
sidebar2d.id = 'sidebar2d'// 固定側邊欄寬度const export_dxfBtn = document.createElement('button');export_dxfBtn.textContent = '導出dxf';export_dxfBtn.style.width = '100%';export_dxfBtn.style.margin = '5px 0';export_dxfBtn.addEventListener('click', () => {export_dxf() // 傳遞參數});sidebar2d.appendChild(export_dxfBtn);
// 不再直接添加到body,改為導出供mainwindow管理
export default sidebar2d

急急國王

2025-05-10 22-09-20

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/80642.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/80642.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/80642.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【工具】HandBrake使用指南:功能詳解與視頻轉碼

HandBrake使用指南:功能詳解與視頻轉碼 一、前言 高清視頻在當下日益普及,從影視制作到個人拍攝,從社交媒體發布到遠程教育,如何高效地壓縮、轉換和管理視頻文件的體積與清晰度,成為內容創作者與技術開發者的核心任務…

Docker容器網絡架構深度解析與技術實踐指南——基于Linux內核特性的企業級容器網絡實現

第1章 容器網絡基礎架構 1 Linux網絡命名空間實現原理 1.1內核級隔離機制深度解析 1.1.1進程隔離的底層實現 通過clone()系統調用創建新進程時,設置CLONE_NEWNET標志位將觸發內核執行以下操作: 內核源碼示例(linux-6.8.0/kernel/fork.c&a…

SAP 交貨單行項目含稅金額計算報cx_sy_zerodivide處理

業務背景:SAP交貨單只有數量,沒有金額,所以開發報表從訂單的價格按數量計算交貨單的金額。 用戶反饋近期報表出現異常: ****2012/12/12 清風雅雨 規格變更 Chg 修改開始 ** 修改原因:由于余數為0時,可能會報錯溢出。…

【高數上冊筆記01】:從集合映射到區間函數

【參考資料】 同濟大學《高等數學》教材樊順厚老師B站《高等數學精講》系列課程 (注:本筆記為個人數學復習資料,旨在通過系統化整理替代厚重教材,便于隨時查閱與鞏固知識要點) 僅用于個人數學復習,因為課…

每日算法刷題 Day3 5.11:leetcode數組2道題,用時1h(有點慢)

5.LC 零矩陣(中等) 面試題 01.08. 零矩陣 - 力扣(LeetCode) 思想: 法一: 利用兩個集合分別儲存要清0的行和列索引 另外兩種原地優化空間的做法暫時不是目前刷題目標,故不考慮 代碼 c: class Solution { public:void setZeroes(vector&l…

【小記】excel vlookup一對多匹配

一個學生報四門課,輸出每個學生課程 應用概述操作預處理數據計數指令 COUNTIFS進行一對多匹配 vlookup 應用概述 應用場景:學生報名考試,需要整理成指定格式,發給考試院。 一個學生最多報考四門 格式實例:準考證號 …

《從零構建大模型》PDF下載(中文版、英文版)

內容簡介 本書是關于如何從零開始構建大模型的指南,由暢銷書作家塞巴斯蒂安? 拉施卡撰寫,通過清晰的文字、圖表和實例,逐步指導讀者創建自己的大模型。在本書中,讀者將學習如何規劃和編寫大模型的各個組成部分、為大模型訓練準備…

基于 Ubuntu 24.04 部署 WebDAV

1. 簡介 WebDAV(Web Distributed Authoring and Versioning)是一種基于 HTTP 的協議, 允許用戶通過網絡直接編輯和管理服務器上的文件。 本教程介紹如何在 Ubuntu 24.04 上使用 Apache2 搭建 WebDAV 服務,無需域名,…

node.js 實戰——在express 中將input file 美化,并完成裁剪、上傳進度條

美化上傳按鈕 在ejs 頁面 <!DOCTYPE html> <html> <head><meta charset"utf-8"></meta><title><% title %></title><link relstylesheet href/stylesheets/form.css/><!-- 本地 Bootstrap 引入方式 -->…

MySQL為什么選擇B+樹

1.hash表&#xff1a;不支持范圍查詢 2.跳表&#xff1a;索引層增加太快&#xff0c;IO成本增加太快 3.二叉樹、AVL樹、紅黑樹&#xff1a;樹高度增加太快&#xff0c;IO成本增加太快 4.B樹&#xff1a;樹高增加太快&#xff1b;范圍查詢只能走中序遍歷&#xff0c;IO成本很…

go程序編譯成動態庫,使用c進行調用

以下是使用 Go 語言打包成 .so 庫并使用 C 語言調用的完整步驟&#xff1a; 1. Go 語言打包成 .so 庫 &#xff08;1&#xff09;編寫 Go 代碼 創建一個 Go 文件&#xff08;如 calculator.go&#xff09;&#xff0c;并定義需要導出的函數。導出的函數名必須以大寫字母開頭…

YOLO-World:基于YOLOv8的開放詞匯目標檢測

文章目錄 前言1、出發點2、方法2.1.TextEncoder2.2.ReparmVLPAN2.3.輸出頭 3、實驗3.1.數據集3.2.LVIS測試集 總結 前言 本文介紹一篇來自騰訊的開放詞匯檢測工作&#xff0c;發表自CVPR2024&#xff0c;論文鏈接&#xff0c;開源地址。 1、出發點 GroundingDINO在開放詞匯檢測…

華為網路設備學習-21 IGP路由專題-路由過濾(filter-policy)

一、路由過濾&#xff08;filter-policy&#xff09; 1、用于控制路由更新、接收的一個工具 2、只能過濾路由信息&#xff0c;無法過濾LSA 二、路由過濾&#xff08;filter-policy&#xff09;與動態路由協議 1、距離矢量路由協議 RIP動態路由協議 交換的是路由表&#xff0…

美化IDEA注釋:Idea 中快捷鍵 Ctrl + / 自動注釋的縮進(避免添加注釋自動到行首)以及 Ctrl + Alt + l 全局格式化代碼的注釋縮進

打開 Settings 界面&#xff0c;依次選擇 Editor -> Code Style -> Java&#xff0c;選擇 Code Generation&#xff0c; 取消 Line comment at first column 和 Block comment at first column 的勾選即可&#xff0c; 1、Line comment at first column (行注釋在第一列…

服務器數據恢復—硬盤壞道導致EqualLogic存儲不可用的數據恢復

服務器存儲數據恢復環境&故障&#xff1a; 一臺EqualLogic某型號存儲中有一組由16塊SAS硬盤組建的RAID5陣列。上層采用VMFS文件系統&#xff0c;存放虛擬機文件&#xff0c;上層一共分了4個卷。 磁盤故障導致存儲不可用&#xff0c;且設備已經過保。 服務器存儲數據恢復過程…

openharmony系統移植之gpu mesa3d適配

openharmony系統移植之gpu mesa3d適配 文章目錄 openharmony系統移植之gpu mesa3d適配1. 環境說明2. gpu內核panfrost驅動2.1 使能panfrost驅動2.2 panfrost dts配置 3. buildroot下測試gpu驅動3.1 buildroot配置編譯 4. ohos下mesa3d適配4.1 ohos下mesa3d編譯調試4.1.2 編譯4.…

Kafka生產者send方法詳解

Kafka生產者send方法詳解 1. send方法的工作原理 1.1 基本流程 #mermaid-svg-EXvKiyf8oSlenrxK {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-EXvKiyf8oSlenrxK .error-icon{fill:#552222;}#mermaid-svg-EXvKiyf…

【sdkman】sdk命令使用簡介

SDKMAN! 使用指南 SDKMAN! 是一個用于管理多個軟件開發工具包版本的命令行工具。 基本命令 安裝 SDK # 安裝最新穩定版 sdk install java# 安裝特定版本 sdk install scala 3.4.2# 安裝本地版本 sdk install groovy 3.0.0-SNAPSHOT /path/to/groovy-3.0.0-SNAPSHOT sdk ins…

開源字體設計工具字玩 FontPlayer

開源字體設計工具字玩 FontPlayer 內測版 v0.2.0 于 2025 年 5 月 9 日發布 基礎功能&#xff1a;用戶可以使用該工具繪制字體并導出 otf 字體文件&#xff0c;設計屬于自己的字庫。腳本功能&#xff1a;提供了腳本功能&#xff0c;用戶可以用程序的方式繪制字形組件&#xff0…

快速入門深度學習系列(3)----神經網絡

本文只針對圖進行解釋重要內容 這就是入門所需要掌握的大部分內容 對于不懂的名詞或概念 你可以及時去查 對于層數 標在上面 對于該層的第幾個元素 標在下面 輸入層算作第0層 對于第一層的w b 參數 維度如下w:4*3 b:4*1 這個叫做神經元 比如對于第一層的神經元 這里說的很…