画立方体软件开发笔记 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.mzph.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 这个叫做神经元 比如对于第一层的神经元 这里说的很…