在Autodesk Viewer SDK中加载本地PDF后,如何获取选中元素的原始坐标/向量数据并程序化绘制Polyline
在Autodesk Viewer SDK中加载本地PDF后,如何获取选中元素的原始坐标/向量数据并程序化绘制Polyline
我完全理解你遇到的问题——PDF导入Viewer之后,并不会像传统BIM模型那样生成完整的属性数据库(Property Database)和实例树(Instance Tree)结构,所有图形元素都会被拆解成几何片段(Fragments),所以用getPropertyDb()的方式肯定拿不到数据。咱们换个思路,直接从Fragment List里提取原始几何坐标,再用Edit2D Extension绘制Polyline就行。
一、先搞懂PDF在Viewer中的存储逻辑
当你加载PDF页面到Viewer时,Viewer会把PDF的每个图形元素(比如线条、多边形)转换成THREE.js的Mesh,然后打包成Fragment。每个Fragment对应一个或多个dbId,但这些dbId只是片段的关联标识,没有BIM模型那样的属性数据。所以我们需要直接从Fragment中提取顶点数据。
二、具体实现步骤
1. 监听选择事件,定位选中元素的关联Fragment
在你的ApsViewerManager的事件绑定逻辑里,添加选择事件监听,通过InstanceTree和FragmentList找到选中dbId对应的Fragment:
class ApsViewerManager { // ... 其他已有代码 ... attachViewerEvents() { const viewer = this.viewer; const self = this; viewer.addEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT, async (event) => { if (event.selections.length === 0) return; // 无选中元素则跳过 const selectedDbId = event.selections[0].dbIdArray[0]; // 取第一个选中元素 const model = viewer.model; const instanceTree = model.getInstanceTree(); const fragmentList = model.getFragmentList(); // 遍历当前dbId关联的所有Fragment instanceTree.enumNodeFragments(selectedDbId, (fragId) => { self.extractGeometryFromFragment(fragmentList, fragId); }, false); }); } }
2. 从Fragment中提取原始顶点坐标
添加一个方法,直接从Fragment中提取THREE.js的几何体顶点,并转换成Viewer世界坐标(适配Edit2D的绘制空间):
class ApsViewerManager { // ... 其他已有代码 ... extractGeometryFromFragment(fragmentList, fragId) { const geometry = fragmentList.getGeometry(fragId); if (!geometry) { console.warn("无法获取当前Fragment的几何数据"); return; } // 获取Fragment的世界矩阵,用于将局部顶点转换为世界坐标 const worldMatrix = new THREE.Matrix4(); fragmentList.getWorldMatrix(fragId, worldMatrix); const positions = geometry.attributes.position.array; const worldCoords = []; // 遍历所有顶点,转换为世界坐标(PDF是2D,可忽略Z轴) for (let i = 0; i < positions.length; i += 3) { const localPoint = new THREE.Vector3(positions[i], positions[i+1], positions[i+2]); const worldPoint = localPoint.applyMatrix4(worldMatrix); // 若发现Y轴方向反转,可改为 y: -worldPoint.y worldCoords.push({ x: worldPoint.x, y: worldPoint.y }); } // 至少2个点才能绘制Polyline if (worldCoords.length >= 2) { this.drawPolylineWithEdit2D(worldCoords); } } }
3. 用Edit2D Extension程序化绘制Polyline
最后,用你已经加载的Edit2D实例,调用官方API绘制Polyline:
class ApsViewerManager { // ... 其他已有代码 ... async drawPolylineWithEdit2D(coords) { if (!this.edit2d) { console.error("Edit2D Extension未加载"); return; } // 创建Polyline路径 const polylinePath = new Autodesk.Edit2D.PolylinePath(); coords.forEach(coord => { polylinePath.addPoint(new Autodesk.Edit2D.Point(coord.x, coord.y)); }); // 设置线条样式(可根据需求自定义) const style = new Autodesk.Edit2D.Style(); style.setStrokeColor(new THREE.Color(0xff0000)); // 红色 style.setStrokeWidth(3); // 线宽3px // 添加到Viewer中 const drawnShape = await this.edit2d.addShape(polylinePath, style); console.log("程序化绘制的Polyline已添加:", drawnShape); } }
三、适配你的PDF加载逻辑
把上述代码整合到你的ApsViewerManager后,加载PDF的逻辑可以保持不变,只需要确保在初始化Viewer后调用attachViewerEvents绑定事件即可:
// 你的PDF加载逻辑(已适配) async loadLocalPdf(url, page = 1) { const options = { accessToken: "", useCredentials: false, env: 'Local' }; await Autodesk.Viewing.Initializer(options, async () => { const htmlDiv = document.querySelector(this.containerSelector); this.viewer = new Autodesk.Viewing.GuiViewer3D(htmlDiv); this.attachViewerEvents(); // 绑定选择事件 const startedCode = this.viewer.start(null, options); if (startedCode > 0) { this.report_error('Failed to create Viewer', {startCode: startedCode}); return; } // 加载所需扩展 this.edit2d = await this.viewer.loadExtension('Autodesk.Edit2D', {enableArcs: true}); await this.viewer.loadExtension('Autodesk.PDF'); // 加载PDF页面 await this.viewer.loadModel(url, {page: page}); }); }
四、可能遇到的小问题及解决
- Polyline位置偏移:如果发现绘制的线条和选中的线条位置上下颠倒,把提取坐标的Y轴取反即可:
worldCoords.push({ x: worldPoint.x, y: -worldPoint.y }); - 多个Fragment的情况:复杂PDF图形可能对应多个Fragment,你可以选择合并所有Fragment的顶点,或者只处理第一个Fragment(根据业务需求调整)。
- Edit2D权限问题:确保Edit2D Extension已正确加载,并且Viewer处于可编辑状态(默认加载后是允许添加形状的)。
这样就能实现你要的功能:选中PDF里的线条,提取原始坐标,再程序化绘制一条新的Polyline。如果还有细节问题,随时调整样式或坐标转换逻辑就行~




