JS 离散点生成等高线图的几种方式

JS 离散点生成等高线图的几种方式


最近公司有需求,需要根据一组离散点来生成等高线图。我上网搜索了一些资料,然后自己也试了一下。我总结了三种实现方式:

  1. turf + d3

  2. turf + vjmap + d3

  3. observable Plot (推荐)

接下来我详细介绍一下这三种不同的实现方式。

首先给大家看下我的数据结构:

XYZ
4319056.86
5142355.06
5817155.84
5847874.29
635056.56

turf + d3

基本思路:
① 将数据处理成地理格式

{
    "type": "Feature",
    "properties": {
        "x": 565,
        "y": 293,
        "value": 96.9
    },
    "geometry": {
        "type": "Point",
        "coordinates": [
            200.88888888888889,
            104.17777777777778
        ]
    }
}

② 通过 turf.interpolate (基于 IDW(反距离权重))进行数据插值
③ 通过 turf.isobands,将插值数据计算出等边线,数据格式:

{
    "type": "Feature",
    "properties": {
        "fill": "rgb(7, 117, 243)",
        "value": "0-25"
    },
    "geometry": {
        "type": "MultiPolygon",
        "coordinates": [
            [
                [
                    [
                        257.14193519824784,
                        255.1806484413806
                    ],
                    [
                        255.62813160239872,
                        255.8773511961486
                    ],
                    [
                        257.14193519824784,
                        256.5056402838994
                    ],
                    [
                        259.39336680305524,
                        255.8773511961486
                    ],
                    [
                        257.14193519824784,
                        255.1806484413806
                    ]
                ]
            ],
            [
                [
                    [
                        139.29531358573226,
                        219.80851022549035
                    ],
                    [
                        138.98122511088943,
                        220.06321454991826
                    ],
                    [
                        139.29531358573226,
                        220.31375106192775
                    ],
                    [
                        139.70261352090898,
                        220.06321454991826
                    ],
                    [
                        139.29531358573226,
                        219.80851022549035
                    ]
                ]
            ],
            [
                [
                    [
                        205.77289500817696,
                        116.74637282143532
                    ],
                    [
                        205.63455078271272,
                        116.8342324519602
                    ],
                    [
                        205.77289500817696,
                        116.92095334929672
                    ],
                    [
                        205.99880882366955,
                        116.8342324519602
                    ],
                    [
                        205.77289500817696,
                        116.74637282143532
                    ]
                ]
            ]
        ]
    }
}

④ 通过 d3 生成svg图:
在这里插入图片描述
代码如下(Vue3):

<template>
    <div id="hello" style="height: 500px;width: 700px;">
        <button class="but" @click="setPoint">显示统计点</button>
    </div>
</template>

<script>
import * as turf from "@turf/turf"
import * as d3 from "d3"
import $ from "jquery"

export default {
    name: 'turfContours',
    data() {
        return {
            h: 440,
            w: 400,
            margin: 40,
            hasPoint: true,
            data: []
        }
    },
    mounted() {
    	new Array(100).fill(0).forEach(e => {
    		let X=Math.floor(Math.random()*1000);
    		let Y=Math.floor(Math.random()*1000);
    		let Z=(Math.random()*100).toFixed(2);
    		this.data.push({
               x: X,
               y: Y,
               z: Z
           })
    	});
    	this.draw();   		
    },
    methods: {
        draw() {
            // 坐标系长高
            let axX = this.w - this.margin * 2;
            let axY = this.h - this.margin * 3;

            const svg = d3.select('#hello')
                .append("svg")
                .attr("viewBox", [0, 0, this.w, this.h]);

            // 图表
            let g = svg.append('g')
                .attr("transform", 'translate(' + this.margin + ',' + this.margin + ')');

            // 值域区间
            let val = d3.extent(this.data, d => d.z)
            let valS = d3.nice(val[0], val[1], 10)
            let breaks = d3.range(5).map(i => {
                return d3.quantile(valS, i * 0.25)
            })

            // 添加颜色图例
            let color = this.colorLine(svg, breaks);

            // 坐标轴
            let x = d3.scaleLinear()
                .domain(d3.extent(this.data, (d) => d.x))
                .nice()    // 对齐
                .rangeRound([0, axX])  // 起止位置
            let xAxis = d3.axisTop(x)

            let y = d3.scaleLinear()
                .domain(d3.extent(this.data, (d) => d.y))
                .nice()
                .rangeRound([0, axY])
            let yAxis = d3.axisLeft(y)

            g.append("g")
                .attr("transform", "translate(0, 0)")
                .attr("class", "axisX")
                .call(xAxis)
                .selectAll("text")
                .style("font-size", "4px")

            g.append("g").attr("transform", "translate(0,0)")
                .attr("class", "axisY")
                .call(yAxis)
                .selectAll("text")
                .style("font-size", "4px")

            // 根据坐标系,添加两端点
            // 目的:端点没有值,插值的时候可能会在端点处显示有问题
            let xS = d3.selectAll(".axisX text")._groups[0]
            let yS = d3.selectAll(".axisY text")._groups[0]

            let maxXs = d3.max(xS, d => Number(d.innerHTML))
            let minXs = d3.min(xS, d => Number(d.innerHTML))
            let maxYs = d3.max(yS, d => Number(d.innerHTML))
            let minYs = d3.min(yS, d => Number(d.innerHTML))

            let mPoint = []     // 对角顶点
            let minDis = -1;
            let maxDis = -1;

            this.data.forEach(i => {
                let distanceMin = turf.distance(turf.point([minXs, minYs]), turf.point([i.x, i.y]), {units: 'degrees'});
                if (minDis === -1 || distanceMin < minDis) {
                    minDis = distanceMin;
                    mPoint[0] = {x: minXs, y: minYs, z: i.z}
                }
                let distanceMax = turf.distance(turf.point([i.x, i.y]), turf.point([maxXs, maxYs]), {units: 'degrees'});
                if (maxDis === -1 || distanceMax < maxDis) {
                    maxDis = distanceMax;
                    mPoint[1] = {x: maxXs, y: maxYs, z: i.z}
                }
            })

            // 原有点阵
            // coordinates 通过比例计算出点对应在坐标系的位置
            let featuresPoint = this.data.map(i => {
                    return {
                        type: "Feature",
                        properties: {
                            x: i.x,
                            y: i.y,
                            value: i.z
                        },
                        geometry: {
                            type: "Point",
                            coordinates: [(i.x - minXs) / (maxXs - minXs) * axX, (i.y - minYs) / (maxYs - minYs) * axY]
                        }
                    }
                }
            )

            this.data = this.data.concat(mPoint)

            // 计算点坐标
            let features = this.data.map(i => {
                    return {
                        type: "Feature",
                        properties: {
                            x: i.x,
                            y: i.y,
                            value: i.z
                        },
                        geometry: {
                            type: "Point",
                            coordinates: [(i.x - minXs) / (maxXs - minXs) * axX, (i.y - minYs) / (maxYs - minYs) * axY]
                        }
                    }
                }
            )
            
            // 离散点生成等值线图
            let isobands = this.grid(features, color, breaks)

            // 路径
            g.append("g")
                .selectAll("path")
                .data(isobands.features)
                .enter()
                .append("path")
                .attr("stroke", "white")
                .attr("stroke-width", 0.5)
                .attr("fill", d => d.properties.fill)
                .attr("d", d3.geoPath());

            this.showPoint(g, featuresPoint)
        },
        grid(features, color, breaks) {
            let points = turf.featureCollection(features);

            // 基于 IDW(反距离权重)算法的将数据插值为格点
            let grid = turf.interpolate(points, 0.05, {
                gridType: "points",
                property: "value",
                units: "degrees",
                weight: 1
            });

            let isobands_options = {
                zProperty: "value",
                breaksProperties: []
            };

            breaks.map((v, i) => {
                isobands_options.breaksProperties.push({'fill': color[i]});
                return v
            })

            let isobands = turf.isobands(grid, breaks, isobands_options);

            return isobands
        },
        colorLine(svg, breaks) {
            // 显示渐变矩形条

            let colorB = '#f50b36'
            let colorA = '#0775f3'
            let compute = d3.interpolate(colorA, colorB) // 返回一个函数

            let color = [];

            svg.selectAll("rect")
                .data(d3.range(breaks.length - 1))
                .enter()
                .append("rect")
                .attr("x", (d, i) => {
                    return this.w - (this.margin + 16 * (breaks.length - i));
                })
                .attr("y", 5)
                .attr("width", 16)
                .attr("height", 15)
                .style("fill", (d, i) => {
                    let c = compute(i / (breaks.length - 1));
                    color.push(c)
                    return c;
                });

            // 数据初值
            svg.selectAll("text")
                .data(breaks)
                .enter()
                .append("text")
                .attr("x", (d, i) => {
                    return this.w - (this.margin + 16 * (breaks.length - i) + 3);
                })
                .attr("y", 25)
                .attr("font-size", 5)
                .text(d => d)

            return color;
        },
        showPoint(g, featuresPoint) {
            // 展示点
            g.append("g")
                .selectAll("circle")
                .data(featuresPoint)
                .enter()
                .append("circle")
                .attr("cx", d => d.geometry.coordinates[0])
                .attr("cy", d => d.geometry.coordinates[1])
                .attr("r", 3)
                .attr("class", 'point')
                .attr("stroke", 'black')
                .attr("stroke-width", 0.3)
                .style("fill", "rgb(242,253,2)");

            // 展示text
            g.selectAll(".point")
                .on("mouseover", function (d, i) {
                    g.append("text")
                        .attr("x", i.geometry.coordinates[0] + 5)
                        .attr("y", i.geometry.coordinates[1] + 2)
                        .attr("class", "tpo")
                        .text('(' + i.properties.x + ',' + i.properties.y + ',' + i.properties.value + ')')
                        .attr("font-size", 6)
                    d3.select(this)
                        .style("fill", "rgb(228,2,253)");
                })
                .on("mouseout", function (d, i) {
                    g.selectAll(".tpo")
                        .remove()
                    d3.select(this)
                        .style("fill", "rgb(242,253,2)");
                })

        },
        setPoint() {
            if (!this.hasPoint) {
                this.hasPoint = true;
                $('.but').text('隐藏统计点');
                d3.select('#hello').selectAll(".point").attr("display", true);
            } else {
                this.hasPoint = false;
                $('.but').text('显示统计点');
                d3.select('#hello').selectAll(".point").attr("display", "none");
            }
        }
    }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.but {
    position: absolute;
    top: 20px;
    left: 70px;
}
</style>

注:
这种方式的优缺点 如下:
优点:自主可控,可以对svg图进行操作。
缺点:算法效果不理想,没有将值很好的区分;计算量大了之后速度会很慢。

turf + vjmap + d3

基本思路:
这个方案大体跟上一个方案类似,主要是针对上一种方案的缺点进行了优化。通过Vjmap的等高线图生成算法进行插值计算,生成等边线。

① 将数据处理成地理格式

{
    "type": "Feature",
    "properties": {
        "x": 565,
        "y": 293,
        "value": 96.9
    },
    "geometry": {
        "type": "Point",
        "coordinates": [
            200.88888888888889,
            104.17777777777778
        ]
    }
}

② 通过 vjmap.WorkerProxy(vjmap.vectorContour) (Vjmap等高线图)进行数据插值,得出等边线。

③ 通过 d3 生成svg图:
在这里插入图片描述
代码如下(Vue2):

<template>
    <div id="hello" style="height: 500px;width: 700px;">
        <button class="but" @click="setPoint">显示统计点</button>
    </div>
</template>

<script>
import * as d3 from "d3"
import $ from "jquery"
import vjmap from "vjmap";

export default {
    name: 'turfContours',
    data() {
        return {
            h: 440,
            w: 400,
            margin: 40,
            hasPoint: true,
            data: []
        }
    },
    mounted() {
		new Array(100).fill(0).forEach(e => {
    		let X=Math.floor(Math.random()*1000);
    		let Y=Math.floor(Math.random()*1000);
    		let Z=(Math.random()*100).toFixed(2);
    		this.data.push({
               x: X,
               y: Y,
               z: Z
           })
    	});
    	this.draw();         
    },
    methods: {
        async draw() {
            // 坐标系长高
            let axX = this.w - this.margin * 2;
            let axY = this.h - this.margin * 3;

            const svg = d3.select('#hello')
                .append("svg")
                .attr("viewBox", [0, 0, this.w, this.h]);

            // 图表
            let g = svg.append('g')
                .attr("transform", 'translate(' + this.margin + ',' + this.margin + ')');

            // 统计区间
            let val = d3.extent(this.data, d => d.z)
            let valS = d3.nice(val[0], val[1], 10)
            let breaks = d3.range(5).map(i => {
                return d3.quantile(valS, i * 0.25)
            })

            // 渐变颜色
            let color = this.colorLine(svg, breaks);

            // 坐标轴
            let x = d3.scaleLinear()
                .domain(d3.extent(this.data, (d) => d.x))
                .nice()    // 对齐
                .rangeRound([0, axX])  // 起止位置
            let xAxis = d3.axisTop(x)

            let y = d3.scaleLinear()
                .domain(d3.extent(this.data, (d) => d.y))
                .nice()
                .rangeRound([0, axY])
            let yAxis = d3.axisLeft(y)

            g.append("g")
                .attr("transform", "translate(0, 0)")
                .attr("class", "axisX")
                .call(xAxis)
                .selectAll("text")
                .style("font-size", "4px")

            g.append("g").attr("transform", "translate(0,0)")
                .attr("class", "axisY")
                .call(yAxis)
                .selectAll("text")
                .style("font-size", "4px")

            // 根据坐标系,添加两端点
            let xS = d3.selectAll(".axisX text")._groups[0]
            let yS = d3.selectAll(".axisY text")._groups[0]

            let maxXs = d3.max(xS, d => Number(d.innerHTML))
            let minXs = d3.min(xS, d => Number(d.innerHTML))
            let maxYs = d3.max(yS, d => Number(d.innerHTML))
            let minYs = d3.min(yS, d => Number(d.innerHTML))

            let mPoint = []     // 对角顶点
            let minDis = -1;
            let maxDis = -1;

            this.data.forEach(i => {
                let distanceMin = turf.distance(turf.point([minXs, minYs]), turf.point([i.x, i.y]), {units: 'degrees'});
                if (minDis === -1 || distanceMin < minDis) {
                    minDis = distanceMin;
                    mPoint[0] = {x: minXs, y: minYs, z: i.z}
                }
                let distanceMax = turf.distance(turf.point([i.x, i.y]), turf.point([maxXs, maxYs]), {units: 'degrees'});
                if (maxDis === -1 || distanceMax < maxDis) {
                    maxDis = distanceMax;
                    mPoint[1] = {x: maxXs, y: maxYs, z: i.z}
                }
            })

            // 原有点阵
            let featuresPoint = this.data.map(i => {
                    return {
                        type: "Feature",
                        properties: {
                            x: i.x,
                            y: i.y,
                            value: i.z
                        },
                        geometry: {
                            type: "Point",
                            coordinates: [(i.x - minXs) / (maxXs - minXs) * axX, (i.y - minYs) / (maxYs - minYs) * axY]
                        }
                    }
                }
            )

            this.data = this.data.concat(mPoint)

            // 计算点坐标
            let features = this.data.map(i => {
                    return {
                        type: "Feature",
                        properties: {
                            x: i.x,
                            y: i.y,
                            value: i.z
                        },
                        geometry: {
                            type: "Point",
                            coordinates: [(i.x - minXs) / (maxXs - minXs) * axX, (i.y - minYs) / (maxYs - minYs) * axY]
                        }
                    }
                }
            )

            // 离散点生成等值线图
            let isobands = await this.grid2(features, "value", breaks, color)

            // 路径
            g.append("g")
                .selectAll("path")
                .data(isobands.features)
                .enter()
                .append("path")
                .attr("stroke", "white")
                .attr("stroke-width", 0.5)
                .attr("fill", d => d.properties.fill)
                .attr("d", d3.geoPath());

            this.showPoint(g, featuresPoint)
        },
        async grid2(dataset, propField, contours, color) {

            // 唯杰地图
            let createContourWorker = vjmap.WorkerProxy(vjmap.vectorContour);

            let points = turf.featureCollection(dataset);
            let {grid, contour, variogram} = await createContourWorker(points, propField, contours, {
                model: 'exponential', // 'exponential','gaussian','spherical',三选一,默认exponential
                sigma2: 0, // sigma2是σ²,对应高斯过程的方差参数,也就是这组数据z的距离,方差参数σ²的似然性反映了高斯过程中的误差,并应手动设置。一般设置为 0 ,其他数值设了可能会出空白图
                alpha: 50, // [如果绘制不出来,修改此值,可以把此值改小] Alpha α对应方差函数的先验值,此参数可能控制钻孔扩散范围,越小范围越大,少量点效果明显,但点多了且分布均匀以后改变该数字即基本无效果了,默认设置为100
                // extent: extent // 如果要根据数据范围自动生成此范围,则无需传此参数
            }, []);

            contour.features.forEach(a => {
                let val = a.properties.value
                let ind = color.length - 1
                contours.forEach((k, i) => {
                    if (k <= val) {
                        ind = i
                    }
                })
                a.properties.fill = color[ind]
            })
            return contour;
        },
        colorLine(svg, breaks) {
            // 显示渐变矩形条

            let colorB = '#f50b36'
            let colorA = '#0775f3'
            let compute = d3.interpolate(colorA, colorB) // 返回一个函数

            let color = [];

            svg.selectAll("rect")
                .data(d3.range(breaks.length - 1))
                .enter()
                .append("rect")
                .attr("x", (d, i) => {
                    return this.w - (this.margin + 16 * (breaks.length - i));
                })
                .attr("y", 5)
                .attr("width", 16)
                .attr("height", 15)
                .style("fill", (d, i) => {
                    let c = compute(i / (breaks.length - 1));
                    color.push(c)
                    return c;
                });

            // 数据初值
            svg.selectAll("text")
                .data(breaks)
                .enter()
                .append("text")
                .attr("x", (d, i) => {
                    return this.w - (this.margin + 16 * (breaks.length - i) + 3);
                })
                .attr("y", 25)
                .attr("font-size", 5)
                .text(d => d)

            return color;
        },
        showPoint(g, featuresPoint) {
            // 展示点
            g.append("g")
                .selectAll("circle")
                .data(featuresPoint)
                .enter()
                .append("circle")
                .attr("cx", d => d.geometry.coordinates[0])
                .attr("cy", d => d.geometry.coordinates[1])
                .attr("r", 3)
                .attr("class", 'point')
                .attr("stroke", 'black')
                .attr("stroke-width", 0.3)
                .style("fill", "rgb(242,253,2)");

            // 展示text
            g.selectAll(".point")
                .on("mouseover", function (d, i) {
                    g.append("text")
                        .attr("x", i.geometry.coordinates[0] + 5)
                        .attr("y", i.geometry.coordinates[1] + 2)
                        .attr("class", "tpo")
                        .text('(' + i.properties.x + ',' + i.properties.y + ',' + i.properties.value + ')')
                        .attr("font-size", 6)
                    d3.select(this)
                        .style("fill", "rgb(228,2,253)");
                })
                .on("mouseout", function (d, i) {
                    g.selectAll(".tpo")
                        .remove()
                    d3.select(this)
                        .style("fill", "rgb(242,253,2)");
                })

        },
        setPoint() {
            if (!this.hasPoint) {
                this.hasPoint = true;
                $('.but').text('隐藏统计点');
                d3.select('#hello').selectAll(".point").attr("display", true);
            } else {
                this.hasPoint = false;
                $('.but').text('显示统计点');
                d3.select('#hello').selectAll(".point").attr("display", "none");
            }
        }
    }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.but {
    position: absolute;
    top: 20px;
    left: 70px;
}
</style>

注:
这种方式的优缺点 如下:
优点:自主可控,可以对svg图进行操作;相对于第一种方式,等高线的计算更加准确;
缺点:计算量大了之后速度会很慢(2000个点需要计算30~40s)

observable Plot (推荐)

这个方案是最近才有的,因为官方给的案例5月份才有。之前 d3的等高线demo一直不是我想要的处理方式,直到最近看到的这个神器 observable Plot,发现简直是鬼斧神工。

基本思路:
①直接照抄 observable Plot 的API吧 (Plot)

效果展示:
在这里插入图片描述
代码如下(Vue3):

<template>
    <div id="hello" style="height: 500px;width: 700px;">
    </div>
</template>

<script>
import * as d3 from "d3"
import * as Plot from "@observablehq/plot";
import $ from "jquery"

export default {
    name: "observable",
    data() {
        return {
            data: []
        }
    },
    mounted() {
        new Array(100).fill(0).forEach(e => {
    		let X=Math.floor(Math.random()*1000);
    		let Y=Math.floor(Math.random()*1000);
    		let Z=Number((Math.random()*100).toFixed(2));
    		this.data.push({
               x: X,
               y: Y,
               z: Z
           })
    	});
    	this.draw();    
    },
    methods: {
        draw() {
            let ps = Plot.plot({
                color: {legend: true,scheme: "sinebow"},
                marks: [
                    Plot.contour(this.data, {x: "x", y: "y", fill: "z", blur: 5}),
                    Plot.dot(this.data, {x: "x", y: "y", channels: {z: "z"}, tip: true}),
                    Plot.frame(),
                    Plot.text(["图1"], {frameAnchor: "top-right",dy: -15,fontSize:15,fontWeight:900}),
                ]
            })

            $("#hello").append(ps);
        },
    }
}
</script>

<style scoped>

</style>

注:
这种方式的优缺点 如下:
缺点:需要按照封装好的API进行绘制,有的时候达不到想要的效果(eg:颜色图例,svg图自定义)
优点:算法一流,等高线的计算更加准确,快速(真NB,5K个点1s出结果);

支持原创!!
欢迎加入讨论!!!

  • 16
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 离散生成STL网格是指将离散云数据转化为STL格式的三维网格模型。实现这个过程可以采用以下步骤: 首先,读取离散数据,这些的坐标信息通常保存在一个文件中,包括的X、Y、Z坐标。 其次,创建一个数据结构来存储云数据。可以使用数组、矩阵等数据结构来保存每个的坐标。 然后,根据云数据创建三角形网格。这可以通过使用三角形剖分算法来实现,常用的算法包括Delaunay三角剖分、最小生成树等。 接下来,对于每个生成的三角形,计算法线向量。法线向量的计算可以根据三角形的顶坐标计算得到,一般使用叉积计算。利用计算得到的法线向量可以更好地显示模型表面的光照效果。 最后,将生成的三角形网格保存为STL格式的文件。STL格式是一种常用的三维模型文件格式,它用于描述三维物体的表面几何信息。STL文件包含了一系列的三角面片,每个面片由三个顶和一个法线向量构成。 通过上述步骤,我们可以将离散云数据转化为STL格式的三维网格模型。这种网格模型可以在各种三维建模软件中进行进一步的处理和分析,比如进行网格优化、拓扑修改等操作。同时,也方便在三维打印中使用,进一步应用于实际的工程或艺术设计中。 ### 回答2: 离散生成STL网格是一种将离散云数据转化为三维图形的方法。STL(Standard Triangle Language)是一种用于描述三角形面片的文件格式。 生成STL网格的过程可以分为以下几个步骤: 1. 收集离散数据:首先需要收集一组离散的三维坐标数据。这些可以是通过三维扫描仪、CAD软件或其他方式获取的。 2. 三角化处理:离散数据通常是不规则的,需要将其转化为等边三角形网格。常用的方法有Delaunay三角剖分算法或法向量配准法。这些算法可以自动将离散连接成三角形,并且保证三角形之间尽可能接近均匀。 3. 生成三角面片:对于生成的三角形网格,需要计算每个三角形面片的法向量。法向量的方向表示了表面的朝向。可以通过计算三个顶的向量叉积得到每个面片的法向量。 4. 输出为STL文件:最后,将生成的三角面片和法向量信息输出为STL文件。STL文件由顶坐标和法向量构成。可以使用C/C++编程语言的库函数进行文件的读写操作。 总的来说,离散生成STL网格是一种将离散云数据转化为三维图形的过程,使得这些能够在计算机中被渲染和显示出来。该过程需要经历数据收集、三角化处理、生成三角面片以及输出为STL文件等步骤。 ### 回答3: 离散生成STL网格是一种将离散云数据转换为三角面片的过程。在生成STL网格时,我们需要按照以下步骤进行操作: 1. 获取离散云数据。这些数据可以是通过扫描仪、摄像机或其他方式获得的离散。 2. 对云数据进行预处理。这包括对噪声、离群和重复进行过滤和去除,以保证最后生成的网格质量较高。 3. 根据云数据,构建三角面片。一般来说,我们可以使用Delaunay三角化算法来生成三角网格。该算法会根据云的分布特性,在空间上连结多边形生成三角形,并确保所有的三角形都满足Delaunay条件。 4. 对生成的三角网格进行表面拟合。这一步旨在根据云数据的分布特征,进一步改进和优化生成的三角网格。常用的方法包括插值方法和曲面拟合方法。 5. 对网格进行平滑处理和细化。这可以通过不同的算法和工具实现,如Laplacian平滑、边缘凸包、Triangulated Surface Visualization (TSV)等。 6. 导出STL文件。将生成的网格导出为标准的STL文件格式,以便在不同的软件和设备间进行使用和交流。 总的来说,离散生成STL网格是一个将离散云数据转化为三角网格的过程。通过预处理、三角化、表面拟合、平滑处理和导出,我们能够得到一个高质量的STL网格文件。这对于许多工程和科学应用来说是十分重要和有用的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值