feat: 点云数据修改

main
JINGYJ 2 months ago
parent ed49032208
commit 40f28bf4e3

@ -34,106 +34,191 @@ const props = withDefaults(defineProps<Props>(), {
const emit = defineEmits<Emits>(); const emit = defineEmits<Emits>();
// //
let scene; const pcdContainer = ref(null);
let renderer; const show = computed({
get() { return props.value; },
set(val) { emit("update:value", val); }
});
// // Three.js
const scene = ref(null);
const camera = ref(null);
const renderer = ref(null);
const controls = ref(null);
const pointCloud = ref(null);
//
const pcdLoaded = ref(false);
const loading = ref(false); const loading = ref(false);
const progress = ref(0); const errorMessage = ref("");
// const topView = ref(true); // 使
const handleClose = () => {
if (pcdContainer.value) { // ID
// DOM let animationId = null;
while (pcdContainer.value.firstChild) {
pcdContainer.value.removeChild(pcdContainer.value.firstChild); //
} let initialCameraPosition = null;
// let initialCameraRotation = null;
if (scene) { let initialControlsTarget = null;
scene.traverse((object) => { let initialCameraZoom = 1;
if (object instanceof THREE.Mesh) { let pointCloudCenter = new THREE.Vector3(); //
object.geometry.dispose(); let pointCloudSize = new THREE.Vector3(); //
if (object.material instanceof THREE.Material) { let normalViewPosition = null; //
object.material.dispose(); let normalViewRotation = null; //
} else {
object.material.forEach((material) => material.dispose()); //
} const handleResize = () => {
if (camera.value && renderer.value && pcdContainer.value) {
const container = pcdContainer.value;
camera.value.aspect = container.clientWidth / container.clientHeight;
camera.value.updateProjectionMatrix();
renderer.value.setSize(container.clientWidth, container.clientHeight);
} }
});
}
//
if (controls) {
controls.dispose();
}
//
// window.removeEventListener('resize', onWindowResize);
//
scene = null;
camera = null;
renderer = null;
controls = null;
pcdLoaded.value = false;
}
emit("update:value", false);
}; };
const show = computed({ //
get() { const loadPointCloud = (url) => {
return props.value; //
}, errorMessage.value = "";
set(val: boolean) { loading.value = true;
emit("update:value", val);
//
if (pointCloud.value && scene.value) {
scene.value.remove(pointCloud.value);
pointCloud.value.geometry.dispose();
pointCloud.value.material.dispose();
pointCloud.value = null;
} }
});
const pcdContainer = ref(null);
let initialCameraPosition;
let initialCameraRotation;
let initialControlsTarget;
let initialCameraZoom;
let controls;
let camera;
const pcdLoaded = ref(false);
const onDialogOpened = () => { const loader = new PCDLoader();
// URL loader.load(
if (!props.info.point_cloud_url) { url,
return; (parsedPointCloud) => {
console.log("PCD解析成功点数:", parsedPointCloud.geometry.attributes.position.count);
processPointCloud(parsedPointCloud);
},
(xhr) => {
const percent = Math.round((xhr.loaded / xhr.total) * 100);
console.log(`加载进度: ${percent}%`);
},
(error) => {
console.error("点云加载失败:", error);
errorMessage.value = "点云文件加载失败";
loading.value = false;
} }
if (pcdContainer.value) {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(
75,
pcdContainer.value.clientWidth / pcdContainer.value.clientHeight,
0.1,
1000
); );
renderer = new THREE.WebGLRenderer(); };
renderer.setSize(pcdContainer.value.clientWidth, pcdContainer.value.clientHeight);
pcdContainer.value.appendChild(renderer.domElement);
// OrbitControls //
controls = new OrbitControls(camera, renderer.domElement); const processPointCloud = (parsedPointCloud) => {
//
pointCloud.value = markRaw(parsedPointCloud);
const loader = new PCDLoader(); //
// true const box = new THREE.Box3().setFromObject(pointCloud.value);
loading.value = true; box.getCenter(pointCloudCenter);
loader.load( box.getSize(pointCloudSize);
props.info.point_cloud_url,
function (pointCloud) { const maxDim = Math.max(pointCloudSize.x, pointCloudSize.y, pointCloudSize.z);
// false
// 使
pointCloud.value.position.sub(pointCloudCenter);
//
applyColorGradient(pointCloud.value, maxDim);
//
scene.value.add(pointCloud.value);
//
const fov = camera.value.fov * (Math.PI / 180);
const distance = maxDim / 2 / Math.tan(fov / 2);
//
normalViewPosition = new THREE.Vector3(0, distance, 0);
normalViewRotation = new THREE.Euler(0, 0, 0);
//
setTopView(distance);
//
initialCameraPosition = camera.value.position.clone();
initialCameraRotation = camera.value.rotation.clone();
initialCameraZoom = camera.value.zoom;
initialControlsTarget = controls.value.target.clone();
//
pcdLoaded.value = true;
loading.value = false; loading.value = false;
//
pointCloud.position.set(0, 100, 0);
// //
controls.value.update();
};
//
const setTopView = (distance) => {
if (!camera.value || !controls.value) return;
//
camera.value.position.set(0, 0, distance);
// (沿Z)
camera.value.lookAt(0, 0, 0);
// Z ()
controls.value.minPolarAngle = Math.PI / 2;
controls.value.maxPolarAngle = Math.PI / 2;
topView.value = true;
};
//
const toggleTopView = () => {
if (!pcdLoaded.value) return;
if (topView.value) {
//
camera.value.position.copy(normalViewPosition);
camera.value.rotation.copy(normalViewRotation);
//
controls.value.minPolarAngle = 0;
controls.value.maxPolarAngle = Math.PI;
topView.value = false;
} else {
//
const fov = camera.value.fov * (Math.PI / 180);
const distance = Math.max(pointCloudSize.x, pointCloudSize.y, pointCloudSize.z) / 2 / Math.tan(fov / 2);
setTopView(distance);
}
controls.value.update();
};
//
const applyColorGradient = (pointCloud, maxDim) => {
const geometry = pointCloud.geometry; const geometry = pointCloud.geometry;
//
if (geometry.attributes.color) {
console.log("点云已有颜色信息,使用顶点颜色");
pointCloud.material = new THREE.PointsMaterial({
vertexColors: true,
size: maxDim * 0.0001, // 使
sizeAttenuation: true
});
return;
}
console.log("点云没有颜色信息,应用颜色渐变");
// //
const colors = []; const colors = [];
const positions = geometry.attributes.position.array; const positions = geometry.attributes.position.array;
const numPoints = positions.length / 3; const numPoints = positions.length / 3;
// //
const startColor = new THREE.Color(0x0000ff); // const startColor = new THREE.Color(0x0000ff); //
const midColor1 = new THREE.Color(0x00ff00); // 绿 const midColor1 = new THREE.Color(0x00ff00); // 绿
const midColor2 = new THREE.Color(0xffff00); // const midColor2 = new THREE.Color(0xffff00); //
@ -148,6 +233,7 @@ const onDialogOpened = () => {
if (z > maxZ) maxZ = z; if (z > maxZ) maxZ = z;
} }
//
for (let i = 0; i < numPoints; i++) { for (let i = 0; i < numPoints; i++) {
const z = positions[i * 3 + 2]; const z = positions[i * 3 + 2];
// //
@ -171,137 +257,207 @@ const onDialogOpened = () => {
// //
colors.push(color.r, color.g, color.b); colors.push(color.r, color.g, color.b);
} }
// //
const colorAttribute = new THREE.Float32BufferAttribute(colors, 3); const colorAttribute = new THREE.Float32BufferAttribute(colors, 3);
geometry.setAttribute('color', colorAttribute); geometry.setAttribute("color", colorAttribute);
//
const material = new THREE.PointsMaterial({ vertexColors: true });
pointCloud.material = material;
//
pointCloud.scale.set(1, 1, 1);
//
const box = new THREE.Box3().setFromObject(pointCloud);
const size = box.getSize(new THREE.Vector3());
const maxSize = Math.max(size.x, size.y, size.z);
const fov = camera.fov * (Math.PI / -180);
const distance = maxSize / 2 / Math.tan(fov / 3);
//
camera.position.set(0, distance, 0);
camera.rotation.set(0, 0, 0);
camera.lookAt(0, 0, 0);
// y 45 Math.PI / 4
pointCloud.rotation.set(0, 0, 0);
//
initialCameraPosition = camera.position.clone();
initialCameraRotation = camera.rotation.clone();
initialCameraZoom = camera.zoom;
initialControlsTarget = controls.target.clone();
scene.add(pointCloud);
pcdLoaded.value = true;
// // //
// const onWindowResize = () => { pointCloud.material = new THREE.PointsMaterial({
// camera.aspect = pcdContainer.value.clientWidth / pcdContainer.value.clientHeight; vertexColors: true,
// camera.updateProjectionMatrix(); size: maxDim * 0.005,
// renderer.setSize(pcdContainer.value.clientWidth, pcdContainer.value.clientHeight); sizeAttenuation: true
// }; });
// window.addEventListener('resize', onWindowResize); };
},
function (xhr) { //
// const initScene = () => {
progress.value = (xhr.loaded / xhr.total) * 100; //
}, initialCameraPosition = null;
function (error) { initialCameraRotation = null;
// false initialControlsTarget = null;
loading.value = false; initialCameraZoom = 1;
console.error('点云文件加载失败:', error); pointCloudCenter = new THREE.Vector3();
} pointCloudSize = new THREE.Vector3();
//
scene.value = markRaw(new THREE.Scene());
scene.value.background = new THREE.Color(0x000000);
//
const container = pcdContainer.value;
camera.value = markRaw(
new THREE.PerspectiveCamera(
75,
container.clientWidth / container.clientHeight,
0.1,
1000
)
); );
function animate() { //
requestAnimationFrame(animate); renderer.value = markRaw(new THREE.WebGLRenderer({ antialias: true }));
// renderer.value.setSize(container.clientWidth, container.clientHeight);
if (controls) { container.appendChild(renderer.value.domElement);
controls.update();
//
controls.value = markRaw(
new OrbitControls(camera.value, renderer.value.domElement)
);
controls.value.enableDamping = true;
controls.value.dampingFactor = 0.05;
//
const ambientLight = markRaw(new THREE.AmbientLight(0xffffff, 0.5));
scene.value.add(ambientLight);
//
const directionalLight = markRaw(new THREE.DirectionalLight(0xffffff, 0.8));
directionalLight.position.set(0, 0, 1); //
scene.value.add(directionalLight);
//
const animate = () => {
if (scene.value && camera.value && renderer.value && controls.value) {
controls.value.update();
renderer.value.render(scene.value, camera.value);
} }
if (renderer && scene && camera) { animationId = requestAnimationFrame(animate);
renderer.render(scene, camera); };
animationId = requestAnimationFrame(animate);
//
window.addEventListener("resize", handleResize);
};
//
const resetViewBeforeClose = () => {
if (controls.value && camera.value && initialCameraPosition && pcdLoaded.value) {
//
camera.value.position.copy(initialCameraPosition);
camera.value.rotation.copy(initialCameraRotation);
camera.value.zoom = initialCameraZoom;
camera.value.updateProjectionMatrix();
controls.value.target.copy(initialControlsTarget);
controls.value.update();
//
if (scene.value && renderer.value) {
renderer.value.render(scene.value, camera.value);
} }
} }
};
//
const handleClose = () => {
//
resetViewBeforeClose();
animate(); //
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
//
if (pcdContainer.value && renderer.value) {
pcdContainer.value.removeChild(renderer.value.domElement);
}
//
if (scene.value) {
scene.value.traverse((obj) => {
if (obj instanceof THREE.Mesh || obj instanceof THREE.Points) {
obj.geometry.dispose();
if (obj.material instanceof THREE.Material) {
obj.material.dispose();
} else { } else {
console.error('pcdContainer is null'); obj.material.forEach(m => m.dispose());
}
}
});
}
//
if (controls.value) {
controls.value.dispose();
controls.value = null;
} }
//
scene.value = null;
camera.value = null;
renderer.value = null;
pointCloud.value = null;
pcdLoaded.value = false;
loading.value = false;
//
window.removeEventListener("resize", handleResize);
//
emit("update:value", false);
}; };
const zoomIn = () => { //
if (camera) { const onDialogOpened = () => {
camera.position.multiplyScalar(0.9); if (!props.info.point_cloud_url) {
camera.updateProjectionMatrix(); errorMessage.value = "没有点云数据URL";
if (controls) { return;
controls.update();
} }
//
initScene();
//
loadPointCloud(props.info.point_cloud_url);
};
//
const zoomIn = () => {
if (camera.value) {
camera.value.position.multiplyScalar(0.9); //
camera.value.updateProjectionMatrix();
controls.value.update();
} }
}; };
const zoomOut = () => { const zoomOut = () => {
if (camera) { if (camera.value) {
camera.position.multiplyScalar(1.1); camera.value.position.multiplyScalar(1.1); //
camera.updateProjectionMatrix(); camera.value.updateProjectionMatrix();
if (controls) { controls.value.update();
controls.update();
}
} }
}; };
//
const returnToCenter = () => { const returnToCenter = () => {
if (initialCameraPosition && initialCameraRotation && initialControlsTarget && camera && controls) { if (initialCameraPosition && initialCameraRotation && initialControlsTarget) {
// camera.value.position.copy(initialCameraPosition);
controls.reset(); camera.value.rotation.copy(initialCameraRotation);
camera.value.zoom = initialCameraZoom;
// camera.value.updateProjectionMatrix();
camera.position.copy(initialCameraPosition);
// controls.value.target.copy(initialControlsTarget);
camera.rotation.copy(initialCameraRotation); controls.value.update();
//
camera.zoom = initialCameraZoom;
//
for (let i = 0; i < 3; i++) {
camera.updateProjectionMatrix();
if (controls) {
controls.update();
}
} }
};
// //
controls.target.copy(initialControlsTarget); onUnmounted(() => {
//
// if (animationId) {
controls.target0.copy(initialControlsTarget); cancelAnimationFrame(animationId);
controls.position0.copy(initialCameraPosition); animationId = null;
controls.zoom0 = initialCameraZoom;
//
setTimeout(() => {
if (controls) {
controls.update();
}
}, 100);
} else {
console.error("Initial state is not defined");
} }
};
window.removeEventListener("resize", handleResize);
});
</script> </script>
<style lang="scss"> <style lang="scss">
/* 样式保持不变 */
.pointModal-wrap.el-dialog { .pointModal-wrap.el-dialog {
box-sizing: border-box; box-sizing: border-box;
width: 1600px; width: 1600px;
@ -311,10 +467,12 @@ const returnToCenter = () => {
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-color: #000; background-color: #000;
.pcd-container { .pcd-container {
position: relative; position: relative;
width: 1560px; width: 1560px;
height: 760px; height: 760px;
.button-first, .button-first,
.button-second, .button-second,
.button-thrid { .button-thrid {
@ -322,35 +480,30 @@ const returnToCenter = () => {
width: 24px; width: 24px;
height: 24px; height: 24px;
cursor: pointer; cursor: pointer;
background-image: url("@/assets/common/amplify_btn.png");
background-size: 100% 100%; background-size: 100% 100%;
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
bottom: 112px; bottom: 112px;
right: 32px; right: 32px;
/* 添加过渡效果 */
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.button-first {
background-image: url("@/assets/common/amplify_btn.png");
}
.button-second { .button-second {
bottom: 72px; bottom: 72px;
right: 32px; right: 32px;
background-image: url("@/assets/common/reduce_btn.png"); background-image: url("@/assets/common/reduce_btn.png");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
/* 添加过渡效果 */
transition: all 0.3s ease;
} }
.button-thrid { .button-thrid {
bottom: 33px; bottom: 33px;
right: 32px; right: 32px;
background-image: url("@/assets/common/reset_btn.png"); background-image: url("@/assets/common/reset_btn.png");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
/* 添加过渡效果 */
transition: all 0.3s ease;
} }
.progress-bar { .progress-bar {
position: absolute; position: absolute;
bottom: 20px; bottom: 20px;
@ -362,6 +515,7 @@ const returnToCenter = () => {
border-radius: 10px; border-radius: 10px;
overflow: hidden; overflow: hidden;
} }
.progress { .progress {
height: 100%; height: 100%;
background-color: #007bff; background-color: #007bff;

Loading…
Cancel
Save