Rust-builds-TIN-triangulation-with-kiss3D

rust实现TIN三角网

头像
逆旅 2022-08-21T00:21:41

1.TIN三角网简介

三角网是由一系列连续三角形构成的网状的平面控制图形,是三角测量中布设连续三角形的两种主要扩展形式,同时向各方向扩展而构成网状,优点为点位分布均匀、各点之间互相牵制、图形强度较高,缺点是扩展较缓慢。

三角网是实现地形三维可视化,数字地面模型(Digital Terrain Model,简称 DTM)是一种很有效的途径。DTM 主要是由栅格和不规则三角网(Triangulated Irregular Network,简称 TIN )两种数据格式来表示,相比于栅格 TIN 具有许多优点,几乎能适用于任何复杂的地形,所以 TIN 是 DTM 常采用的一种格式。

2.实现逻辑

st=>start: 生成随机数据 op1=>operation: 遍历-三点确定圆,内部不含第四个点 op2=>operation: 每一次遍历三个点集合,选出最小角最大的三角形 op3=>operation: 过滤掉重复点的集合 e=>end: 绘制三角形 st->op1->op2->op3->e

3.具体代码

cargo.toml

[dependencies]
nalgebra = "0.31.0"
kiss3d = "0.31.0"
rand="*"
libm="*"

point.rs

坐标点相关操作

extern crate rand;
use libm::acos;
use rand::Rng;
use std::f32::consts::PI;

#[derive(Clone, Copy, Debug)]
pub struct Point {
    pub x: f64,
    pub y: f64,
}

#[derive(Clone, Copy, Debug)]
pub struct Circle {
    pub x: f64,
    pub y: f64,
    pub r: f64,
}

#[derive(Clone, Copy, Debug)]
pub struct CirclePoint {
    pub point1: usize,
    pub point2: usize,
    pub point3: usize,
}

#[derive(Clone, Debug)]
pub struct RenderPoint {
    pub render: Vec<Point>,
}

//判断三个点是否在一条直线上
pub fn is_line(point1: Point, point2: Point, point3: Point) -> bool {
    let a = (point1.x - point2.x) / (point1.y - point2.y);
    let b = (point1.x - point3.x) / (point1.y - point3.y);
    if a == b {
        true
    } else {
        false
    }
}

//三点确定一个圆
pub fn round(point1: Point, point2: Point, point3: Point) -> Circle {
    let a = point1.x - point2.x;
    let b = point1.y - point2.y;
    let c = point1.x - point3.x;
    let d = point1.y - point3.y;
    let e = ((point1.x.powi(2) - point2.x.powi(2)) - (point2.y.powi(2) - point1.y.powi(2))) / 2.0;
    let f = ((point1.x.powi(2) - point3.x.powi(2)) - (point3.y.powi(2) - point1.y.powi(2))) / 2.0;
    let x = -(d * e - b * f) / (b * c - a * d);
    let y = -(a * f - c * e) / (b * c - a * d);
    let r = ((x - point1.x).powi(2) + (y - point1.y).powi(2)).sqrt();
    Circle { x, y, r }
}

pub fn rand_data() -> f64 {
    let mut rng = rand::thread_rng();

    let n1 = rng.gen::<f64>();
    n1 * 600.0 - 300.0
}

pub fn rand_point() -> Vec<Point> {
    //生成随机数据
    let mut data = Vec::new();
    for _i in 0..10 {
        let x = rand_data();
        let y = rand_data();
        data.push(Point { x, y })
    }
    data
}

pub fn min_angle(point_a: Point, point_b: Point, point_c: Point) -> f64 {
    //返回三角形最小的角
    let ab = ((point_a.x - point_b.x).powi(2) + (point_a.y - point_b.y).powi(2)).sqrt();
    let ac = ((point_a.x - point_c.x).powi(2) + (point_a.y - point_c.y).powi(2)).sqrt();
    let cb = ((point_c.x - point_b.x).powi(2) + (point_c.y - point_b.y).powi(2)).sqrt();
    let cos_a = (ab * ab + ac * ac - cb * cb) / (2.0 * ab * ac);
    let cos_b = (ab * ab + cb * cb - ac * ac) / (2.0 * ab * cb);
    let cos_c = (cb * cb + ac * ac - ab * ab) / (2.0 * cb * ac);
    let angle_a = acos(cos_a);
    let angle_b = acos(cos_b);
    let angle_c = acos(cos_c);
    let mut min = angle_a;
    if min > angle_b {
        min = angle_b;
    };
    if min > angle_c {
        min = angle_c;
    };
    min / (PI as f64) * 180.0
}
pub fn get_max_index(data: &Vec<f64>) -> usize {
    let mut max = data[0];
    let mut index = 0;
    for i in 0..data.len() {
        if max < data[i] {
            max = data[i];
            index = i;
        };
    }
    index
}

pub fn remove_duplicate(data: Vec<CirclePoint>) -> Vec<CirclePoint> {
    //除去重复元素
    let mut new_data = Vec::new();
    let mut data_cp = Vec::new();
    for k in 0..data.len() {
        let mut tem = vec![data[k].point1, data[k].point2, data[k].point3];
        tem.sort();
        data_cp.push(CirclePoint {
            point1: tem[0],
            point2: tem[1],
            point3: tem[2],
        });
    }
    let num = data.len();
    new_data.push(data_cp[0]);
    'one: for i in 0..num {
        let mut flag = false;
        for j in 0..new_data.len() {
            if (data_cp[i].point1 == new_data[j].point1)
                && (data_cp[i].point2 == new_data[j].point2)
                && (data_cp[i].point3 == new_data[j].point3)
            {
                flag = false;
                continue 'one;
            } else {
                flag = true;
            };
        }
        if flag {
            new_data.push(data_cp[i]);
        }
    }
    new_data
}

pub fn tin_data(point_data: Vec<Point>) -> Vec<RenderPoint> {
    //处理数据,返回最终结果
    let data_cp = point_data.clone();
    let data_num = data_cp.len();
    let mut render_data = Vec::new();
    for i in 0..data_num {
        for j in 0..data_num {
            if i == j {
                continue;
            } else {
                let mut angle = Vec::new();
                let mut circle_points = Vec::new();
                'three: for k in 0..data_num {
                    if k == j || k == i {
                        continue;
                    } else {
                        let a = data_cp[i];
                        let b = data_cp[j];
                        let c = data_cp[k];
                        let circle = round(a, b, c);
                        let mut flag: bool = false;
                        'four: for n in 0..data_num {
                            if n == j || n == i || n == k {
                                continue 'four;
                            } else {
                                let x = data_cp[n].x;
                                let y = data_cp[n].y;
                                let r = ((x - circle.x).powi(2) + (y - circle.y).powi(2)).sqrt();
                                if r < circle.r {
                                    flag = false;
                                    continue 'three;
                                } else {
                                    flag = true;
                                };
                            };
                        }
                        if flag {
                            circle_points.push(CirclePoint {
                                point1: i,
                                point2: j,
                                point3: k,
                            });
                            angle.push(min_angle(data_cp[i], data_cp[j], data_cp[k]));
                        };
                    };
                    let index = get_max_index(&angle);
                    render_data.push(circle_points[index])
                }
            }
        }
    }
    let tem = remove_duplicate(render_data);
    let mut render_point = Vec::new();
    for i in tem {
        let mut render = Vec::new();
        render.push(point_data[i.point1]);
        render.push(data_cp[i.point2]);
        render.push(data_cp[i.point3]);
        render_point.push(RenderPoint { render });
    }
    render_point
}

draw.rs

绘制三角形

use crate::point::RenderPoint;
extern crate kiss3d;
extern crate nalgebra as na;
use kiss3d::light::Light;
use kiss3d::nalgebra::{Point2, Point3};
use kiss3d::window::Window;

pub fn line_2_d(render_point: Vec<RenderPoint>) {
    let mut window = Window::new("TIN 三角图");
    window.set_light(Light::StickToCamera);
    while window.render() {
        for i in 0..render_point.len() {
            let a = render_point[i].render[0];
            let b = render_point[i].render[1];
            let c = render_point[i].render[2];
            let point1 = Point2::new(a.x as f32, a.y as f32);
            let point2 = Point2::new(b.x as f32, b.y as f32);
            let point3 = Point2::new(c.x as f32, c.y as f32);

            window.draw_planar_line(&point1, &point2, &Point3::new(1.0, 0.0, 0.0));
            window.draw_planar_line(&point2, &point3, &Point3::new(0.0, 1.0, 0.0));
            window.draw_planar_line(&point3, &point1, &Point3::new(0.0, 0.0, 1.0));
        }
    }
}

main.rs

主程序

mod point;
mod draw;
use point::{rand_point, tin_data};
use draw::line_2_d;


fn main() {
    let data = rand_point();
    let data = tin_data(data);
    line_2_d(data);
}

4.成果

point.rs 中的 rand_point()调整生成点的数量

30个点生成TIN三角网

最后修改: 2022-08-21T00:21:41

版权声明:署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)

comment 评论

验证图片
评论
仿 Valine