feat: 点云数据修改

main
JINGYJ 2 months ago
parent ed49032208
commit 40f28bf4e3

@ -34,274 +34,430 @@ 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();
if (controls) { renderer.value.setSize(container.clientWidth, container.clientHeight);
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 = "";
loading.value = true;
//
if (pointCloud.value && scene.value) {
scene.value.remove(pointCloud.value);
pointCloud.value.geometry.dispose();
pointCloud.value.material.dispose();
pointCloud.value = null;
}
const loader = new PCDLoader();
loader.load(
url,
(parsedPointCloud) => {
console.log("PCD解析成功点数:", parsedPointCloud.geometry.attributes.position.count);
processPointCloud(parsedPointCloud);
}, },
set(val: boolean) { (xhr) => {
emit("update:value", val); const percent = Math.round((xhr.loaded / xhr.total) * 100);
console.log(`加载进度: ${percent}%`);
},
(error) => {
console.error("点云加载失败:", error);
errorMessage.value = "点云文件加载失败";
loading.value = false;
} }
}); );
const pcdContainer = ref(null); };
let initialCameraPosition;
let initialCameraRotation;
let initialControlsTarget;
let initialCameraZoom;
let controls;
let camera;
const pcdLoaded = ref(false);
const onDialogOpened = () => { //
// URL const processPointCloud = (parsedPointCloud) => {
if (!props.info.point_cloud_url) { //
return; pointCloud.value = markRaw(parsedPointCloud);
}
if (pcdContainer.value) { //
scene = new THREE.Scene(); const box = new THREE.Box3().setFromObject(pointCloud.value);
camera = new THREE.PerspectiveCamera( box.getCenter(pointCloudCenter);
75, box.getSize(pointCloudSize);
pcdContainer.value.clientWidth / pcdContainer.value.clientHeight,
0.1, const maxDim = Math.max(pointCloudSize.x, pointCloudSize.y, pointCloudSize.z);
1000
); // 使
renderer = new THREE.WebGLRenderer(); pointCloud.value.position.sub(pointCloudCenter);
renderer.setSize(pcdContainer.value.clientWidth, pcdContainer.value.clientHeight);
pcdContainer.value.appendChild(renderer.domElement); //
applyColorGradient(pointCloud.value, maxDim);
// OrbitControls
controls = new OrbitControls(camera, renderer.domElement); //
scene.value.add(pointCloud.value);
const loader = new PCDLoader();
// true //
loading.value = true; const fov = camera.value.fov * (Math.PI / 180);
loader.load( const distance = maxDim / 2 / Math.tan(fov / 2);
props.info.point_cloud_url,
function (pointCloud) { //
// false normalViewPosition = new THREE.Vector3(0, distance, 0);
loading.value = false; normalViewRotation = new THREE.Euler(0, 0, 0);
//
pointCloud.position.set(0, 100, 0); //
setTopView(distance);
//
const geometry = pointCloud.geometry; //
initialCameraPosition = camera.value.position.clone();
// initialCameraRotation = camera.value.rotation.clone();
const colors = []; initialCameraZoom = camera.value.zoom;
const positions = geometry.attributes.position.array; initialControlsTarget = controls.value.target.clone();
const numPoints = positions.length / 3;
//
// pcdLoaded.value = true;
const startColor = new THREE.Color(0x0000ff); // loading.value = false;
const midColor1 = new THREE.Color(0x00ff00); // 绿
const midColor2 = new THREE.Color(0xffff00); // //
const endColor = new THREE.Color(0xff0000); // controls.value.update();
};
// z
let minZ = Infinity; //
let maxZ = -Infinity; const setTopView = (distance) => {
for (let i = 0; i < numPoints; i++) { if (!camera.value || !controls.value) return;
const z = positions[i * 3 + 2];
if (z < minZ) minZ = z; //
if (z > maxZ) maxZ = z; camera.value.position.set(0, 0, distance);
} // (沿Z)
camera.value.lookAt(0, 0, 0);
for (let i = 0; i < numPoints; i++) {
const z = positions[i * 3 + 2]; // Z ()
// controls.value.minPolarAngle = Math.PI / 2;
const factor = (z - minZ) / (maxZ - minZ); controls.value.maxPolarAngle = Math.PI / 2;
let color; topView.value = true;
if (factor < 0.33) { };
// 绿
const subFactor = factor / 0.33;
color = startColor.clone().lerp(midColor1, subFactor);
} else if (factor < 0.66) {
// 绿
const subFactor = (factor - 0.33) / 0.33;
color = midColor1.clone().lerp(midColor2, subFactor);
} else {
//
const subFactor = (factor - 0.66) / 0.34;
color = midColor2.clone().lerp(endColor, subFactor);
}
//
colors.push(color.r, color.g, color.b);
}
//
const colorAttribute = new THREE.Float32BufferAttribute(colors, 3);
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 = () => {
// camera.aspect = pcdContainer.value.clientWidth / pcdContainer.value.clientHeight;
// camera.updateProjectionMatrix();
// renderer.setSize(pcdContainer.value.clientWidth, pcdContainer.value.clientHeight);
// };
// window.addEventListener('resize', onWindowResize);
},
function (xhr) {
//
progress.value = (xhr.loaded / xhr.total) * 100;
},
function (error) {
// false
loading.value = false;
console.error('点云文件加载失败:', error);
}
);
function animate() {
requestAnimationFrame(animate);
//
if (controls) {
controls.update();
}
if (renderer && scene && camera) {
renderer.render(scene, camera);
}
}
animate(); //
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;
//
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 positions = geometry.attributes.position.array;
const numPoints = positions.length / 3;
//
const startColor = new THREE.Color(0x0000ff); //
const midColor1 = new THREE.Color(0x00ff00); // 绿
const midColor2 = new THREE.Color(0xffff00); //
const endColor = new THREE.Color(0xff0000); //
// z
let minZ = Infinity;
let maxZ = -Infinity;
for (let i = 0; i < numPoints; i++) {
const z = positions[i * 3 + 2];
if (z < minZ) minZ = z;
if (z > maxZ) maxZ = z;
}
//
for (let i = 0; i < numPoints; i++) {
const z = positions[i * 3 + 2];
//
const factor = (z - minZ) / (maxZ - minZ);
let color;
if (factor < 0.33) {
// 绿
const subFactor = factor / 0.33;
color = startColor.clone().lerp(midColor1, subFactor);
} else if (factor < 0.66) {
// 绿
const subFactor = (factor - 0.33) / 0.33;
color = midColor1.clone().lerp(midColor2, subFactor);
} else { } else {
console.error('pcdContainer is null'); //
const subFactor = (factor - 0.66) / 0.34;
color = midColor2.clone().lerp(endColor, subFactor);
} }
//
colors.push(color.r, color.g, color.b);
}
//
const colorAttribute = new THREE.Float32BufferAttribute(colors, 3);
geometry.setAttribute("color", colorAttribute);
//
pointCloud.material = new THREE.PointsMaterial({
vertexColors: true,
size: maxDim * 0.005,
sizeAttenuation: true
});
}; };
const zoomIn = () => { //
if (camera) { const initScene = () => {
camera.position.multiplyScalar(0.9); //
camera.updateProjectionMatrix(); initialCameraPosition = null;
if (controls) { initialCameraRotation = null;
controls.update(); initialControlsTarget = null;
} initialCameraZoom = 1;
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
)
);
//
renderer.value = markRaw(new THREE.WebGLRenderer({ antialias: true }));
renderer.value.setSize(container.clientWidth, container.clientHeight);
container.appendChild(renderer.value.domElement);
//
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);
} }
animationId = requestAnimationFrame(animate);
};
animationId = requestAnimationFrame(animate);
//
window.addEventListener("resize", handleResize);
}; };
const zoomOut = () => { //
if (camera) { const resetViewBeforeClose = () => {
camera.position.multiplyScalar(1.1); if (controls.value && camera.value && initialCameraPosition && pcdLoaded.value) {
camera.updateProjectionMatrix(); //
if (controls) { camera.value.position.copy(initialCameraPosition);
controls.update(); 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 returnToCenter = () => { //
if (initialCameraPosition && initialCameraRotation && initialControlsTarget && camera && controls) { const handleClose = () => {
// //
controls.reset(); resetViewBeforeClose();
// //
camera.position.copy(initialCameraPosition); if (animationId) {
// cancelAnimationFrame(animationId);
camera.rotation.copy(initialCameraRotation); animationId = null;
// }
camera.zoom = initialCameraZoom;
//
// if (pcdContainer.value && renderer.value) {
for (let i = 0; i < 3; i++) { pcdContainer.value.removeChild(renderer.value.domElement);
camera.updateProjectionMatrix(); }
if (controls) {
controls.update(); //
} 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 {
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);
};
// //
controls.target.copy(initialControlsTarget); const onDialogOpened = () => {
if (!props.info.point_cloud_url) {
errorMessage.value = "没有点云数据URL";
return;
}
//
initScene();
//
loadPointCloud(props.info.point_cloud_url);
};
// //
controls.target0.copy(initialControlsTarget); const zoomIn = () => {
controls.position0.copy(initialCameraPosition); if (camera.value) {
controls.zoom0 = initialCameraZoom; camera.value.position.multiplyScalar(0.9); //
camera.value.updateProjectionMatrix();
controls.value.update();
}
};
// const zoomOut = () => {
setTimeout(() => { if (camera.value) {
if (controls) { camera.value.position.multiplyScalar(1.1); //
controls.update(); camera.value.updateProjectionMatrix();
} controls.value.update();
}, 100); }
} else {
console.error("Initial state is not defined");
}
}; };
//
const returnToCenter = () => {
if (initialCameraPosition && initialCameraRotation && initialControlsTarget) {
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();
}
};
//
onUnmounted(() => {
//
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
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