当前位置: 首页 > news >正文

画立方体软件开发笔记 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

急急国王

相关文章:

  • 常见音频主控芯片以及相关厂家总结
  • win10-启动django项目时报错
  • Go语言——goflow工作流使用
  • MySQL 中 count(*)、count(1) 和 count(字段名) 有什么区别?
  • 访问者模式(Visitor Pattern)详解
  • excel大表导入数据库
  • RAG 2.0 深入解读
  • OSPF不规则区域划分
  • 从代码学习深度学习 - 语义分割和数据集 PyTorch版
  • 部署RocketMQ
  • 垃圾对象回收
  • 2025年5月15日前 免费考试了! Oracle AI 矢量搜索专业​​认证
  • 青藏高原东北部祁连山地区250m分辨率多年冻土空间分带指数图(2023)
  • [虚幻官方教程学习笔记]深入理解实时渲染(An In-Depth Look at Real-Time Rendering)
  • LeetCode热题100--240.搜索二维矩阵--中等
  • kotlin flow防抖
  • 聊一聊接口测试时如何处理接口或版本变更
  • 基于STM32的甲醛检测
  • Win10无法上网:Windows 无法访问指定设备、路径或文件。你可能没有适当的权限访问该项目找不到域 TEST 的域控制器DNS 解析存在问题
  • Git简介和发展
  • 国家主席习近平会见斯洛伐克总理菲佐
  • 中非民间对话在赞比亚举行
  • 虚假认定实质性重组、高估不良债权价值,原中国华融资产重庆分公司被罚180万元
  • 新华每日电讯:给“男性妇科病论文”开一剂复方药
  • 人民日报评“组团退演出服”:市场经济诚信原则需全社会维护
  • 南通市委常委、市委秘书长童剑跨市调任常州市委常委、组织部部长