最小包围盒辅助计算工具,是用来计算三维模型最小包裹体尺寸的一套SDK。区别于传统基于X、Y、Z轴计算的包裹体,本工具基于PCA主成分分析法,通过计算几何体顶点的相关性,来计算三维模型的做小包围尺寸(一般情况下改尺寸要小于或等于上述传统计算结果),更符合业务需求,能够为基于三维模型尺寸计算提供更加精确的判断依据。您可以通过它来评估模型的最小外接矩形体积,辅助下料、装运等各类场景的衍生应用。
esm方式
import { ObbHelper } from './obbHelper.js';
umd方式
<script type="module" src="/obb.js"></script>
cjs方式
const { ObbHelper } = require('./obbHelper.js')
获取节点列表,包含节点ID、名称以及尺寸信息(size为传统尺寸;obb为最小包围尺寸)
按照最小包裹体尺寸对元素进行分组后获得到分组列表
获取选中的节点列表
注册选中事件的监听函数
高亮对象 其中uuid取值来自于innerList数据集中id属性
选中分组
取消选中分组 unique 来自groupList数据集中unique属性(unique 按照最小包裹体尺寸标识分组x-y-z)
选中指定元素(mesh)
取消选中指定元素(mesh)
显示/隐藏所有标注 (如果没有主动调用addLabel添加标签,会自动按照分组进行第一次添加标签,并显示)
export declare class ObbHelper {
scene: THREE.Scene;
container: any;
camera: any;
labels: any[];
innerList: NodeItem[];
// 按照最小包裹体尺寸标识分组x-y-z 合并
groupList: GroupItem[];
selected: any[];
mapColor: {
[key: string]: any;
};
mode: 'single' | 'multi';
constructor(scene: THREE.Scene, domId: string, camera: THREE.Camera);
// 选中分组
selectGroup(unique: any): void;
// 取消选中分组 unique 来自groupList数据集中unique属性(unique 按照最小包裹体尺寸标识分组x-y-z)
unselectedGroup(unique: any): void;
// 选中指定元素(mesh)
selectNode(id: any): void;
// 取消选中指定元素(mesh)
unselectedNode(id: any): void;
// 显示/隐藏所有标注 (如果没有主动调用addLabel添加标签,会自动按照分组进行第一次添加标签,并显示)
toggleAnnotation(): void;
// 注册选中事件的监听函数
addOnChangeLisener(handler: (selectedKeys: string[], cancelKeys: string[]) => void): void;
// 高亮对象 其中uuid取值来自于innerList数据集中id属性
highlightObj(uuid: string): void;
// 添加一个标注
addLabel(mesh: THREE.Mesh, name?: string): any;
// 注册点击事件监听器
addEvenetListener(handler: (mesh: THREE.Mesh) => void): void;
}
interface NodeItem {
id: string,
name: string,
obb: number[], // obb最小包裹体尺寸
size: number[], // 传统包围盒尺寸
}
interface GroupItem {
unique: string,
name: string,
obb: number[], // obb最小包裹体尺寸
children: NodeItem[]
}
下面以在Vue中使用为例(该SDK不局限于Vue,任何浏览器或node端均可使用)
import * as THREE from 'three';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer();
const container = document.body // 您可以自己设定宿主DOM,此处以body作为宿主dom
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
const gltfLoader = new THREE.GLTFLoader();
gltfLoader.load('/test.glb', async (e) => {
scene.add(e.scene);
});
// 您需要将场景、宿主dom以及场景的相机作为参数传递给obb helper( 当然,这是可选的,如果您需要用到他的选中、标绘功能,那必须传递这三个参数 )
obbHelper = new ObbHelper(scene, document.body, camera)
// 功能1 获取当前场景中元素列表(并携带最小尺寸信息)
obbHelper.addOnChangeLisener((selectedKeys) => {
data.selected = selectedKeys
})
<script setup>
import { onMounted, reactive } from 'vue'
import { ObbHelper } from './obb'
import * as THREE from 'three';
import { GLTFLoader } THREE from './GLTFLoader.js';
const data = reactive({
groups: [],
selected: [],
showAnnotation: false
})
let obbHelper;
onMounted(() => {
// 1.创建场景
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer();
const container = document.getElementById('mine-viewer') // 您可以自己设定宿主DOM,此处以body作为宿主dom
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
// 2. 加载模型
const gltfLoader = new THREE.GLTFLoader();
gltfLoader.load('/test.glb', async (e) => {
scene.add(e.scene);
// 初始化 obbHelper
obbHelper = new ObbHelper(scene, 'mine-viewer', camera)
// 注册选中监听函数
obbHelper.addOnChangeLisener((selectedKeys) => {
data.selected = selectedKeys
})
// 获取分组
data.groups = obbHelper.groupList
// 打开标签
toggleAnnotation()
});
})
// 显示分组
function showGroup(unique){
if (obbHelper) {
obbHelper.selectGroup(unique)
}
}
// 隐藏分组
function hiddenGroup(unique){
if (obbHelper) {
obbHelper.unselectedGroup(unique)
}
}
// 显示节点
function showNode(id){
if (obbHelper) {
obbHelper.selectNode(id)
}
}
// 隐藏节点
function hiddenNode(id){
if (obbHelper) {
obbHelper.unselectedNode(id)
}
}
// 显示/隐藏节点
function toggleNode(id) {
if (obbHelper) {
obbHelper.highlightObj(id)
}
}
// 显示/隐藏标签
function toggleAnnotation() {
if (obbHelper) {
obbHelper.toggleAnnotation()
data.showAnnotation = !data.showAnnotation
}
}
</script>
<template>
<div class="viewer-wrapper">
<div id="mine-viewer" class="viewer"></div>
</div>
<div class="data">
<div >
<label>是否显示标签:</label>
<span @click="toggleAnnotation()" style="padding-left: 0.5rem">
{{ data.showAnnotation ? '隐藏' : '显示' }}
</span>
</div>
<ul >
<li v-for="item in data.groups">
<h2>
<span >{{ `${item.name}(${item.children.length})` }}</span>
<span class="right">
<span @click="showGroup(item.unique)">全部选中</span>
<span @click="hiddenGroup(item.unique)">全部取消选中</span>
</span>
</h2>
<div :class="data.selected.find((k) => item.children.find(c => c.id === k) != undefined) ? 'actived': 'default'">
<p >
{{ `x: ${item.obb[0]}, y: ${item.obb[1]}, z: ${item.obb[2]}, ` }}
</p>
<ul >
<li v-for="child in item.children" :class="data.selected.find((k) => child.id === k) ? 'actived': 'default'" >
<span @click="toggleNode(child.id)">{{ child.name }}</span>
<span class="right">
<span @click="showNode(child.id)">选中</span>
<span @click="hiddenNode(child.id)">取消选中</span>
</span>
</li>
</ul>
</div>
</li>
</ul>
</div>
</template>
<style scoped>
.viewer-wrapper {
width: 100%;
height: 100%;
position: relative;
}
.viewer {
width: 100%;
height: 100%;
}
.data {
width: 24rem;
text-align: left;
max-height: calc(100% - 2rem);
overflow-y: auto;
position: absolute;
right: 12px;
top: 12px;
background-color: white;
}
.data li {
user-select: none;
cursor: pointer;
}
.data .default {
background-color: #ffffff;
}
.data .actived {
background-color: #1890ff44;
}
.right {
clear: both;
float: right;
}
.right span{
color: #1890ff;
margin-right: 0.5rem;
font-size: 1rem;
}
</style>
This software is provided by 中交简石数字科技(苏州)有限公司 (hereinafter referred to as "Licensor") under the following terms and conditions:
Grant of License: Licensor hereby grants to you a non-exclusive, non-transferable license to use this software on one computer for your own personal or internal business purposes.
Use Restrictions: You may not copy, modify, reverse engineer, decompile, disassemble, or otherwise attempt to discover the source code of this software. You may not sublicense, assign, rent, loan, sell, or transfer any rights in or to this software except with the express written consent of Licensor.
No Export: You may not export this software to any country or region without the prior written permission of Licensor.
Term and Termination: This license will remain in effect until terminated. You may terminate it at any time by destroying all copies of this software. It will also automatically terminate if you fail to comply with any term or condition of this agreement. Upon termination, you must destroy all copies of this software.
Limited Warranty: Licensor warrants that the software will perform in substantial accordance with its accompanying documentation for a period of ninety (90) days from the date of delivery. If the software does not perform substantially in accordance with such documentation, Licensor will, at its option, either replace the software or refund the license fee paid by you.
[TOC]