vue3 3d饼图

完整3D饼图项目下载 https://download.csdn.net/download/weixin_54645059/91716476 只有一个vue文件 直接下滑到完整代码就阔以

本文介绍了如何使用ECharts和ECharts-GL插件实现3D饼图效果,并提出了数值显示未解决的问题。主要包含以下内容:

安装所需插件:yarn add echartsyarn add echarts-gl
提供了完整的Vue3组件代码,包括3D饼图的配置和渲染逻辑
核心功能:
1. 支持自定义饼图高度和空心比例
2. 实现3D饼图的参数曲面方程计算
3. 包含鼠标点击和滑动特效
4. 图例的选中状态后 显示完整的饼图( selectedMode: true )
作者尚未研究明白如何在每个模块直接显示数值,并希望有解决方案的人能给予反馈。该实现通过计算数据百分比和构建3D曲面参数方程来渲染饼图,但标签显示功能目前被注释掉,需要进一步优化。

效果图

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/1b0314b006dd407fbb8b23f98ddd8bff.png

始终保持圆 isCircle.value = false

在这里插入图片描述

isCircle.value = true

在这里插入图片描述

安装插件

yarn add echarts
yarn add echarts-gl

直接上完整代码

<template><div class="container"><div class="chartsGl" id="charts"></div></div>
</template><script setup>
import * as echarts from "echarts";
import "echarts-gl";
import { nextTick, onMounted, ref } from "vue";
const pieHeight = ref(20); // 饼图高度 0 自适应高度  其他值固定高度
const hollow = ref(0); // 空心的大小  0 实心  大于0 小于1 (空心的大小根据数据的大小来决定)onMounted(() => {init();
});
const optionData = ref([{ id: 0, name: "文案1", num: 50, itemStyle: { color: "#2196f3" } },{ id: 1, name: "文案2", num: 10, itemStyle: { color: "#0ce4d1" } },{ id: 2, name: "文案3", num: 20, itemStyle: { color: "#fbc02d" } },{ id: 3, name: "文案4", num: 30, itemStyle: { color: "#ff5252" } },{ id: 4, name: "文案5", num: 40, itemStyle: { color: "#ff9800" } },{ id: 5, name: "文案6", num: 100, itemStyle: { color: "#333" } },
]);const option = ref(null);
const init = () => {const total = optionData.value.reduce((sum, item) => sum + item.num, 0);// 计算每个项的百分比并添加到对象中optionData.value.forEach((item) => ((item.value = item.num), (item.bfb = ((item.value / total) * 100).toFixed(2) + "%")));//构建3d饼状图let myChart = echarts.init(document.getElementById("charts"));// 传入数据生成 option ; getPie3D(数据,透明的空心占比(调节中间空心范围的0就是普通饼1就很镂空))option.value = getPie3D(optionData.value, hollow.value);//将配置项设置进去myChart.setOption(option.value);// 是否需要label指引线,如果要就添加一个透明的2d饼状图并调整角度使得labelLine和3d的饼状图对齐,并再次 setOption// option.value.series.push({//   name: "pie2d",//   type: "pie",//   // color: ["#fbc02d", "#2196f3", "#0ce4d1"],//   labelLine: {//     // length: 15, // 延长线第一段的长度//     // length2: 0, //延长线长度//     // maxSurfaceAngle: 80,//   },//   startAngle: -53, //起始角度,支持范围[0, 360]。//   clockwise: false, //饼图的扇区是否是顺时针排布。上述这两项配置主要是为了对齐3d的样式//   radius: ["20%", "20%"],//   center: ["35%", "44%"],//   data: optionData.value,//   label: {//     alignTo: "edge",//     formatter: "{name|{b}}}",//     size: 30,//     // minMargin: 5,//     // edgeDistance: 10,//     // lineHeight: 15,//     rich: {//       time: {//         // fontSize: 10,//         color: "#999",//       },//     },//   },//   labelLine: {//     // show: true,//   },// });// myChart.setOption(option.value);//鼠标移动上去特效效果bindListen(myChart);
};
const getPie3D = (pieData, internalDiameterRatio) => {let series = [];let sumValue = 0;let startValue = 0;let endValue = 0;let legendData = [];let legendBfb = [];let k = 1 - internalDiameterRatio;// 为每一个饼图数据,生成一个 series-surface(参数曲面) 配置for (let i = 0; i < pieData.length; i++) {sumValue += pieData[i].value;let seriesItem = {//系统名称name: typeof pieData[i].name === "undefined" ? `series${i}` : pieData[i].name,type: "surface",//是否为参数曲面(是)parametric: true,//曲面图网格线(否)上面一根一根的wireframe: {show: false,},pieData: pieData[i],pieStatus: {selected: false,hovered: false,k: k,},//设置饼图在容器中的位置(目前没发现啥用)// center: ["50%", "100%"],};//曲面的颜色、不透明度等样式。if (typeof pieData[i].itemStyle != "undefined") {let itemStyle = {};typeof pieData[i].itemStyle.color != "undefined" ? (itemStyle.color = pieData[i].itemStyle.color) : null;typeof pieData[i].itemStyle.opacity != "undefined" ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null;seriesItem.itemStyle = itemStyle;}series.push(seriesItem);}// 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,// 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。legendData = [];legendBfb = [];for (let i = 0; i < series.length; i++) {endValue = startValue + series[i].pieData.value;series[i].pieData.startRatio = startValue / sumValue;series[i].pieData.endRatio = endValue / sumValue;series[i].pieData.value = pieHeight.value > 0 ? pieHeight.value : series[i].pieData.value;series[i].parametricEquation = getParametricEquation(series[i].pieData.startRatio, series[i].pieData.endRatio, false, false, k, series[i].pieData.value);startValue = endValue;let bfb = fomatFloat(series[i].pieData.value / sumValue, 4);legendData.push({name: series[i].name,value: bfb,});legendBfb.push({name: series[i].name,value: bfb,});}series = series.sort((a, b) => {return a.pieData.id - b.pieData.id;});//(第二个参数可以设置你这个环形的高低程度)let boxHeight = getHeight3D(series, 16); //通过传参设定3d饼/环的高度// 准备待返回的配置项,把准备好的 legendData、series 传入。let option = {color: ["#1C9FFD", "#0FF0FF", "#EBC542"],//图例组件 //右侧展示的文案legend: {selectedMode: true,data: legendData,//图例列表的布局朝向。orient: "vertical",// right: 10,// top: 140,top: "middle",right: "5%",//图例文字每项之间的间隔itemGap: 15,textStyle: {color: "#333",},show: true,icon: "rect",// 这个可以显示百分比那种(可以根据你想要的来配置)formatter: function (param) {let item = optionData.value.filter((item) => item.name == param)[0];return `${item.name}: ${item.bfb}`;},},//移动上去提示的文本内容(我没来得及改 你们可以根据需求改)tooltip: {formatter: (params) => {if (params.seriesName !== "mouseoutSeries" && params.seriesName !== "pie2d") {return (`名称: ${params.seriesName}<br/>` + `值: ${optionData.value[params.seriesIndex].value}<br/>` + `百分比: ${optionData.value[params.seriesIndex].bfb}`);}},},itemStyle: {borderRadius: 5,},//这个可以变形xAxis3D: {min: -1,max: 1,},yAxis3D: {min: -1,max: 1,},zAxis3D: {min: -1,max: 1,},//此处是修改样式的重点grid3D: {show: false,boxHeight: boxHeight, //圆环的高度//这是饼图的位置top: "middle",left: "-15%",viewControl: {//3d效果可以放大、旋转等,请自己去查看官方配置alpha: 34, //角度(这个很重要 调节角度的)distance: 300, //调整视角到主体的距离,类似调整zoom(这是整体大小)rotateSensitivity: true, //设置为0无法旋转zoomSensitivity: 0, //设置为0无法缩放panSensitivity: 0, //设置为0无法平移autoRotate: false, //自动旋转},},series: series,};return option;
};//获取3d丙图的最高扇区的高度
const getHeight3D = (series, height) => {if (pieHeight.value > 0) return pieHeight.value;series.sort((a, b) => {return b.pieData.value - a.pieData.value;});return (height * 35) / series[0].pieData.value;
};// 生成扇形的曲面参数方程,用于 series-surface.parametricEquation
const getParametricEquation = (startRatio, endRatio, isSelected, isHovered, k, h) => {// 计算let midRatio = (startRatio + endRatio) / 2;let startRadian = startRatio * Math.PI * 2;let endRadian = endRatio * Math.PI * 2;let midRadian = midRatio * Math.PI * 2;// 如果只有一个扇形,则不实现选中效果。if (startRatio === 0 && endRatio === 1) {isSelected = false;}// 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)k = typeof k !== "undefined" ? k : 1 / 3;// 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)let offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0;let offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0;// 计算高亮效果的放大比例(未高亮,则比例为 1)let hoverRate = isHovered ? 1.05 : 1;// 返回曲面参数方程return {u: {min: -Math.PI,max: Math.PI * 3,step: Math.PI / 32,},v: {min: 0,max: Math.PI * 2,step: Math.PI / 20,},x: function (u, v) {if (u < startRadian) {return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate;}if (u > endRadian) {return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate;}return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;},y: function (u, v) {if (u < startRadian) {return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate;}if (u > endRadian) {return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate;}return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;},z: function (u, v) {if (u < -Math.PI * 0.5) {return Math.sin(u);}if (u > Math.PI * 2.5) {return Math.sin(u) * h * 0.1;}return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;},};
};//这是一个自定义计算的方法
const fomatFloat = (num, n) => {var f = parseFloat(num);if (isNaN(f)) {return false;}f = Math.round(num * Math.pow(10, n)) / Math.pow(10, n); // n 幂var s = f.toString();var rs = s.indexOf(".");//判定如果是整数,增加小数点再补0if (rs < 0) {rs = s.length;s += ".";}while (s.length <= rs + n) {s += "0";}return s;
};
const bindListen = (myChart) => {let selectedIndex = "";let hoveredIndex = "";// 监听点击事件,实现选中效果(单选)myChart.on("click", function (params) {// 从 option.series 中读取重新渲染扇形所需的参数,将是否选中取反。let isSelected = !option.value.series[params.seriesIndex].pieStatus.selected;let isHovered = option.value.series[params.seriesIndex].pieStatus.hovered;let k = option.value.series[params.seriesIndex].pieStatus.k;let startRatio = option.value.series[params.seriesIndex].pieData.startRatio;let endRatio = option.value.series[params.seriesIndex].pieData.endRatio;// 如果之前选中过其他扇形,将其取消选中(对 option 更新)if (selectedIndex !== "" && selectedIndex !== params.seriesIndex) {option.value.series[selectedIndex].parametricEquation = getParametricEquation(option.value.series[selectedIndex].pieData.startRatio,option.value.series[selectedIndex].pieData.endRatio,false,false,k,option.value.series[selectedIndex].pieData.value);option.value.series[selectedIndex].pieStatus.selected = false;}// 对当前点击的扇形,执行选中/取消选中操作(对 option 更新)option.value.series[params.seriesIndex].parametricEquation = getParametricEquation(startRatio,endRatio,isSelected,isHovered,k,option.value.series[params.seriesIndex].pieData.value);option.value.series[params.seriesIndex].pieStatus.selected = isSelected;// 如果本次是选中操作,记录上次选中的扇形对应的系列号 seriesIndexisSelected ? (selectedIndex = params.seriesIndex) : null;// 使用更新后的 option,渲染图表myChart.setOption(option.value);});// 监听 mouseover,近似实现高亮(放大)效果myChart.on("mouseover", function (params) {// 准备重新渲染扇形所需的参数let isSelected;let isHovered;let startRatio;let endRatio;let k;// 如果触发 mouseover 的扇形当前已高亮,则不做操作if (hoveredIndex === params.seriesIndex) {return;// 否则进行高亮及必要的取消高亮操作} else {// 如果当前有高亮的扇形,取消其高亮状态(对 option 更新)if (hoveredIndex !== "") {// 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 false。isSelected = option.value.series[hoveredIndex].pieStatus.selected;isHovered = false;startRatio = option.value.series[hoveredIndex].pieData.startRatio;endRatio = option.value.series[hoveredIndex].pieData.endRatio;k = option.value.series[hoveredIndex].pieStatus.k;// 对当前点击的扇形,执行取消高亮操作(对 option 更新)option.value.series[hoveredIndex].parametricEquation = getParametricEquation(startRatio,endRatio,isSelected,isHovered,k,option.value.series[hoveredIndex].pieData.value);option.value.series[hoveredIndex].pieStatus.hovered = isHovered;// 将此前记录的上次选中的扇形对应的系列号 seriesIndex 清空hoveredIndex = "";}// 如果触发 mouseover 的扇形不是透明圆环,将其高亮(对 option 更新)if (params.seriesName !== "mouseoutSeries" && params.seriesName !== "pie2d") {// 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 true。isSelected = option.value.series[params.seriesIndex].pieStatus.selected;isHovered = true;startRatio = option.value.series[params.seriesIndex].pieData.startRatio;endRatio = option.value.series[params.seriesIndex].pieData.endRatio;k = option.value.series[params.seriesIndex].pieStatus.k;// 对当前点击的扇形,执行高亮操作(对 option 更新)option.value.series[params.seriesIndex].parametricEquation = getParametricEquation(startRatio,endRatio,isSelected,isHovered,k,option.value.series[params.seriesIndex].pieData.value + 5);option.value.series[params.seriesIndex].pieStatus.hovered = isHovered;// 记录上次高亮的扇形对应的系列号 seriesIndexhoveredIndex = params.seriesIndex;}// 使用更新后的 option,渲染图表myChart.setOption(option.value);}});// 修正取消高亮失败的 bugmyChart.on("globalout", function () {// 准备重新渲染扇形所需的参数let isSelected;let isHovered;let startRatio;let endRatio;let k;if (hoveredIndex !== "") {// 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 true。isSelected = option.value.series[hoveredIndex].pieStatus.selected;isHovered = false;k = option.value.series[hoveredIndex].pieStatus.k;startRatio = option.value.series[hoveredIndex].pieData.startRatio;endRatio = option.value.series[hoveredIndex].pieData.endRatio;// 对当前点击的扇形,执行取消高亮操作(对 option 更新)option.value.series[hoveredIndex].parametricEquation = getParametricEquation(startRatio,endRatio,isSelected,isHovered,k,option.value.series[hoveredIndex].pieData.value);option.value.series[hoveredIndex].pieStatus.hovered = isHovered;// 将此前记录的上次选中的扇形对应的系列号 seriesIndex 清空hoveredIndex = "";}// 使用更新后的 option,渲染图表myChart.setOption(option.value);});myChart.on("legendselectchanged", function (event) {const { selected } = event; // 获取当前所有图例的选中状态(key:图例name,value:是否选中)const k = 1 - hollow.value; // 复用空心比例配置optionData.value.forEach((item) => (item.value = item.num));// 1. 筛选选中的原始数据(未选中则排除,选中为空时默认显示全部)const selectedData = Object.keys(selected).length ? optionData.value.filter((item) => selected[item.name]) : [...optionData.value]; // 无选中时显示全部// 2. 重新计算筛选后数据的总数值(用于计算扇形比例)const newSumValue = selectedData.reduce((sum, item) => sum + item.value, 0);if (newSumValue === 0) return; // 避免无数据时出错// 3. 重置起始比例,遍历选中数据计算新的startRatio/endRatiolet startValue = 0;const dataRatioMap = new Map(); // 存储{图例name: {startRatio, endRatio}}selectedData.forEach((item) => {const startRatio = startValue / newSumValue;const endRatio = (startValue + item.value) / newSumValue;dataRatioMap.set(item.name, { startRatio, endRatio });startValue += item.value;});// 4. 遍历所有3D扇形系列,更新配置(显示/隐藏、比例、3D形态)option.value.series.forEach((seriesItem) => {const seriesName = seriesItem.name;const isShow = selected[seriesName] ?? true; // 未选中项默认隐藏const pieData = optionData.value.find((item) => item.name === seriesName); // 关联原始数据const ratioInfo = dataRatioMap.get(seriesName) || { startRatio: 0, endRatio: 0 }; // 未选中项比例置0// 4.1 更新扇形基础信息(比例、高度)seriesItem.pieData = {...pieData,startRatio: ratioInfo.startRatio,endRatio: ratioInfo.endRatio,value: pieHeight.value > 0 ? pieHeight.value : pieData.value, // 复用高度配置};// 4.2 更新3D参数方程(核心:控制扇形的3D形态和位置)seriesItem.parametricEquation = getParametricEquation(ratioInfo.startRatio, // 新起始比例ratioInfo.endRatio, // 新结束比例seriesItem.pieStatus.selected, // 保留原点击选中状态seriesItem.pieStatus.hovered, // 保留原hover状态k, // 空心比例seriesItem.pieData.value // 扇形高度);// 4.3 隐藏未选中的扇形(通过设置z轴范围使其不可见)if (!isShow) {seriesItem.parametricEquation.z = () => -2; // 低于可视范围(原配置z轴max=1)}});option.value.series[1].value = 100;// 5. 重渲染图表,显示完整3D饼图myChart.setOption(option.value);});
};
</script>
<style scoped lang="scss">
//饼图(外面的容器)
.container {width: 500px;height: 400px;background: #fff;
}
//饼图的大小
.chartsGl {width: 500px;height: 400px;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/94177.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/94177.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全球电商业财一体化趋势加速,巨益科技助力品牌出海精细化运营

行业背景&#xff1a;跨境电商进入品牌化发展新阶段随着国内电商市场竞争日趋激烈&#xff0c;跨境电商已成为中国品牌寻求增长突破的重要赛道&#xff0c;在TikTok、Temu等平台出海浪潮推动下&#xff0c;越来越多的中国品牌开始布局全球市场。然而&#xff0c;从单一市场的铺…

【序列晋升】13 Spring Cloud Bus微服务架构中的消息总线

Spring Cloud Bus作为微服务架构中的关键组件&#xff0c;通过消息代理实现分布式系统中各节点的事件广播与状态同步&#xff0c;解决了传统微服务架构中配置刷新效率低下、系统级事件传播复杂等问题。它本质上是一个轻量级的事件总线&#xff0c;将Spring Boot Actuator的端点…

[激光原理与应用-314]:光学设计 - 光学系统设计与电子电路设计的相似或相同点

光学系统设计与电子电路设计虽分属不同工程领域&#xff0c;但在设计理念、方法论和工程实践中存在诸多相似或相同点。这些共性源于两者均需解决复杂系统的优化问题&#xff0c;并遵循工程设计的通用规律。以下是具体分析&#xff1a;一、设计流程的相似性需求分析与规格定义光…

Linux学习:信号的保存

目录1. 进程的异常终止与core dump标志位1.1 进程终止的方式1.2 core方案的作用与使用方式2. 信号的保存2.1 信号的阻塞2.2 操作系统中的sigset_t信号集类型2.3 进程PCB中修改block表的系统调用接口2.4 信号阻塞的相关问题验证1. 进程的异常终止与core dump标志位 1.1 进程终止…

数据分析编程第二步: 最简单的数据分析尝试

2.1 数据介绍有某公司的销售数据表 sales.csv 如下:第一行是标题&#xff0c;解释每一列存了什么东西。第二行开始每一行是一条数据&#xff0c;对应一个订单。这种数据有个专业的术语&#xff0c;叫结构化数据。这是现代数据处理中最常见的数据类型。整个表格的数据统称为一个…

UDP报文的数据结构

主要内容参照https://doc.embedfire.com/net/lwip/zh/latest/doc/chapter14/chapter14.html#id6&#xff0c;整理出来自用。 1. UDP 报文首部结构体&#xff08;udp_hdr&#xff09; 为清晰定义 UDP 报文首部的各个字段&#xff0c;LwIP 设计了udp_hdr结构体&#xff0c;其包含…

图论与最短路学习笔记

图论与最短路在数学建模中的应用 一、图论模型图 G(V,E)G(V,E)G(V,E) VVV&#xff1a;顶点集合EEE&#xff1a;边集合每条边 (u,v)(u,v)(u,v) 赋予权值 w(u,v)w(u,v)w(u,v)&#xff0c;可用 邻接矩阵 或 邻接表 表示。二、最短路问题的数学形式 目标&#xff1a;寻找从源点 sss…

第九节 Spring 基于构造函数的依赖注入

当容器调用带有一组参数的类构造函数时&#xff0c;基于构造函数的 DI 就完成了&#xff0c;其中每个参数代表一个对其他类的依赖。接下来&#xff0c;我们将通过示例来理解 Spring 基于构造函数的依赖注入。示例&#xff1a;下面的例子显示了一个类 TextEditor&#xff0c;只能…

【数据库】PostgreSQL详解:企业级关系型数据库

文章目录什么是PostgreSQL&#xff1f;核心特性1. 标准兼容性2. 扩展性3. 高级功能4. 可靠性数据类型1. 基本数据类型2. 高级数据类型基本操作1. 数据库操作2. 表操作3. 数据操作高级查询1. 连接查询2. 子查询3. 窗口函数JSON操作1. JSON数据类型2. JSON查询3. JSON索引全文搜索…

FFMPEG相关解密,打水印,合并,推流,

1&#xff1a;ffmepg进行打水印解密 前提ffmepg安装利用静态版就可以这个什么都有&#xff0c;不用再配置其他信息&#xff1a;&#xff08;这个利用ffmpeg终端命令是没问题的&#xff0c;但是如果要是再C中调用ffmpeg库那么还需要从新编译安装下&#xff09; 各个版本 Inde…

MySql知识梳理之DML语句

注意: 插入数据时&#xff0c;指定的字段顺序需要与值的顺序是一一对应的。 字符串和日期型数据应该包含在引号中。 插入的数据大小&#xff0c;应该在字段的规定范围内注意:修改语句的条件可以有&#xff0c;也可以没有&#xff0c;如果没有条件&#xff0c;则会修改整张表的所…

GaussDB GaussDB 数据库架构师修炼(十八)SQL引擎-SQL执行流程

1 SQL执行流程查询解析&#xff1a;词法分析、语法分析、 语义分析 查询重写&#xff1a;视图和规则展开、基于规则的查询优化 计划生成&#xff1a;路径搜索和枚举、选出最优执行计划 查询执行&#xff1a;基于优化器生成的物理执行计划对数据进行获取和计算2 解析器和优化器S…

grpc 1.45.2 在ubuntu中的编译

要在 Ubuntu 上编译 gRPC 1.45.2&#xff0c;需要按照以下步骤操作。以下指南基于 gRPC 官方文档和相关资源&#xff0c;确保环境配置正确并成功编译。请确保你有管理员权限&#xff08;sudo&#xff09;以安装依赖项和执行相关命令。 1. 准备环境 确保你的 Ubuntu 系统已安装…

lesson45:Linux基础入门指南:从内核到实践操作全解析

目录 一、Linux简介与核心概念 1.1 Linux的起源与发展 1.2 内核与发行版的关系 二、Linux内核版本解析 2.1 内核版本命名规则 2.2 2025年主流内核版本 三、主流Linux发行版对比 3.1 桌面用户首选 Ubuntu 24.04 LTS Linux Mint 22 3.2 技术爱好者之选 Fedora 41 Ar…

PCL点云库入门(第24讲)——PCL库点云特征之NARF特征描述 Normal Aligned Radial Feature(NARF)

一、算法原理 1、NARF 特征概述 NARF(Normal Aligned Radial Feature)是 2011 年由 Bastian Steder 等人在论文 《Point Feature Extraction on 3D Range Scans Taking into Account Object Boundaries》中提出的一种 稀疏局部 3D 特征描述子。 核心目标是提取具有“边界意…

使用 eventpp 构建跨 RT-Thread 与 ARM-Linux 的轻量级 Active Object(AO)事件驱动框架

0. 引言 本文展示一个实践路径&#xff1a;以轻量级 C 事件库 eventpp 为核心&#xff0c;设计并实现一个面向嵌入式的、可移植的 Active Object&#xff08;AO&#xff09;事件驱动架构。该架构满足以下目标&#xff1a; 跨平台兼容&#xff1a;单套代码在 RT-Thread&#xff…

【python实用小脚本-193】Python全能PDF小助手:剪切/合并/旋转/加密一条龙——再也不用开会员

Python全能PDF小助手&#xff1a;剪切/合并/旋转/加密一条龙——再也不用开会员 PDF编辑, 本地处理, 零会员费, 多功能脚本, 瑞士军刀 故事开场&#xff1a;一把瑞士军刀救了周五下班的你 周五 17:55&#xff0c;老板甩来一堆 PDF&#xff1a; “把第 3、7 页删掉”“再和合同合…

Ubuntu根分区扩容

目录 1.先查看/dev/sda 整块磁盘设备的分区占用情况&#xff1a; 2.在VMware中编辑虚拟机&#xff1a; 3.进入虚拟机&#xff0c;进入disk应用程序&#xff1a; 4.扩容文件系统 5.最后通过df-h lsblk或通过可视化GParted进行验证。 1.先查看/dev/sda 整块磁盘设备的分区占…

智慧城市SaaS平台/市政设施运行监测系统之空气质量监测系统、VOC气体监测系统、污水水质监测系统及环卫车辆定位调度系统架构内容

1. 空气质量监测系统1) 监测点管理 a) 监测点基本信息 支持记录空气质量监测点的名称、位置、类型、设备配置等信息。 b) 监测点分布地图 支持通过GIS地图展示监测点的分布情况&#xff0c;支持地图查询和导航。 2) 空气质量监测 a) 实时数据采集 支持实时采集空气质量数据&…

PiscCode迅速集成YOLO-Pose 实现姿态关键点轨迹跟踪应用

在计算机视觉领域&#xff0c;人体姿态检测与轨迹跟踪是很多应用场景的核心技术&#xff0c;例如运动分析、行为识别、智能监控等。本文将介绍如何在 PiscCode 平台上&#xff0c;利用 YOLO-Pose 模型进行姿态估计&#xff0c;并实现多人关键点轨迹跟踪。 一、什么是 PiscCode …