feat: 点云数据修改

main
JINGYJ 2 months ago
parent ed49032208
commit 40f28bf4e3

@ -34,274 +34,430 @@ const props = withDefaults(defineProps<Props>(), {
const emit = defineEmits<Emits>();
//
let scene;
let renderer;
//
const pcdContainer = ref(null);
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 progress = ref(0);
//
const handleClose = () => {
if (pcdContainer.value) {
// DOM
while (pcdContainer.value.firstChild) {
pcdContainer.value.removeChild(pcdContainer.value.firstChild);
}
//
if (scene) {
scene.traverse((object) => {
if (object instanceof THREE.Mesh) {
object.geometry.dispose();
if (object.material instanceof THREE.Material) {
object.material.dispose();
} else {
object.material.forEach((material) => material.dispose());
}
}
});
}
//
if (controls) {
controls.dispose();
}
//
// window.removeEventListener('resize', onWindowResize);
//
scene = null;
camera = null;
renderer = null;
controls = null;
pcdLoaded.value = false;
}
emit("update:value", false);
const errorMessage = ref("");
const topView = ref(true); // 使
// ID
let animationId = null;
//
let initialCameraPosition = null;
let initialCameraRotation = null;
let initialControlsTarget = null;
let initialCameraZoom = 1;
let pointCloudCenter = new THREE.Vector3(); //
let pointCloudSize = new THREE.Vector3(); //
let normalViewPosition = null; //
let normalViewRotation = null; //
//
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);
}
};
const show = computed({
get() {
return props.value;
//
const loadPointCloud = (url) => {
//
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) {
emit("update:value", val);
(xhr) => {
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
if (!props.info.point_cloud_url) {
return;
}
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 loader = new PCDLoader();
// true
loading.value = true;
loader.load(
props.info.point_cloud_url,
function (pointCloud) {
// false
loading.value = false;
//
pointCloud.position.set(0, 100, 0);
//
const geometry = pointCloud.geometry;
//
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 {
//
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);
}
}
//
const processPointCloud = (parsedPointCloud) => {
//
pointCloud.value = markRaw(parsedPointCloud);
//
const box = new THREE.Box3().setFromObject(pointCloud.value);
box.getCenter(pointCloudCenter);
box.getSize(pointCloudSize);
const maxDim = Math.max(pointCloudSize.x, pointCloudSize.y, pointCloudSize.z);
// 使
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;
//
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;
};
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 {
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) {
camera.position.multiplyScalar(0.9);
camera.updateProjectionMatrix();
if (controls) {
controls.update();
}
//
const initScene = () => {
//
initialCameraPosition = null;
initialCameraRotation = null;
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) {
camera.position.multiplyScalar(1.1);
camera.updateProjectionMatrix();
if (controls) {
controls.update();
}
//
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 returnToCenter = () => {
if (initialCameraPosition && initialCameraRotation && initialControlsTarget && camera && controls) {
//
controls.reset();
//
camera.position.copy(initialCameraPosition);
//
camera.rotation.copy(initialCameraRotation);
//
camera.zoom = initialCameraZoom;
//
for (let i = 0; i < 3; i++) {
camera.updateProjectionMatrix();
if (controls) {
controls.update();
}
//
const handleClose = () => {
//
resetViewBeforeClose();
//
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 {
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);
controls.position0.copy(initialCameraPosition);
controls.zoom0 = initialCameraZoom;
//
const zoomIn = () => {
if (camera.value) {
camera.value.position.multiplyScalar(0.9); //
camera.value.updateProjectionMatrix();
controls.value.update();
}
};
//
setTimeout(() => {
if (controls) {
controls.update();
}
}, 100);
} else {
console.error("Initial state is not defined");
}
const zoomOut = () => {
if (camera.value) {
camera.value.position.multiplyScalar(1.1); //
camera.value.updateProjectionMatrix();
controls.value.update();
}
};
//
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>
<style lang="scss">
/* 样式保持不变 */
.pointModal-wrap.el-dialog {
box-sizing: border-box;
width: 1600px;
@ -311,10 +467,12 @@ const returnToCenter = () => {
background-position: center;
background-repeat: no-repeat;
background-color: #000;
.pcd-container {
position: relative;
width: 1560px;
height: 760px;
.button-first,
.button-second,
.button-thrid {
@ -322,35 +480,30 @@ const returnToCenter = () => {
width: 24px;
height: 24px;
cursor: pointer;
background-image: url("@/assets/common/amplify_btn.png");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
bottom: 112px;
right: 32px;
/* 添加过渡效果 */
transition: all 0.3s ease;
}
.button-first {
background-image: url("@/assets/common/amplify_btn.png");
}
.button-second {
bottom: 72px;
right: 32px;
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 {
bottom: 33px;
right: 32px;
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 {
position: absolute;
bottom: 20px;
@ -362,6 +515,7 @@ const returnToCenter = () => {
border-radius: 10px;
overflow: hidden;
}
.progress {
height: 100%;
background-color: #007bff;

Loading…
Cancel
Save