摘要
使用rust绘制界面;界面包含一个三维坐标轴,使用鼠标旋转坐标轴,左侧显示对应的旋转向量,四元数等信息.
Use Rust to draw the interface; the interface contains a three-dimensional coordinate axis, which can be rotated using the mouse, and the corresponding rotation vector, quaternion and other information are displayed on the left side.
关键词
rust;nalgebra;egui;three-d;
关键信息
[package]
name = "exp65-rust-ziglang-slambook2"
version = "0.1.0"
edition = "2021"[dependencies]
nalgebra = { version = "0.33.2",features = ["rand"]}
rand = "0.8.5"
wgpu = "23.0.1"
winit = "0.30.8"eframe = "0.30.0"
egui = { version = "0.30.0", features = ["default"
]}
egui_extras = {version = "0.30.0",features = ["default", "image"]}three-d = {path = "./static/three-d" , features=["egui-gui"] }
three-d-asset = {version = "0.9",features = ["hdr", "http"] }env_logger = { version = "0.11.6", default-features = false, features = ["auto-color","humantime",
] }
原理简介
rust打印变量的类型
在Rust中,类型推断是一个强大的特性,但在某些情况下,编译器可能无法推断出具体的类型。你提供的两个代码片段就展示了这种情况。
第一个代码片段:
(|view_matrix| {let type_name = std::any::type_name::<_>(&view_matrix);println!("typeof view_matrix is {:?}", type_name);
})(&view_matrix);
在这个片段中,编译器无法推断出view_matrix
的类型,因为它被用作一个闭包的参数。在这个上下文中,view_matrix
的类型是未知的,因此编译器无法为std::any::type_name::<_>(&view_matrix)
推断出具体的类型参数。
第二个代码片段:
fn type_name_of<T>(_: &T) -> &'static str {std::any::type_name::<T>()
}
(|view_matrix| println!("typeof view_matrix is {:?}", type_name_of(view_matrix)))(&view_matrix);
在这个片段中,type_name_of
函数是一个泛型函数,它接受一个引用作为参数,并返回一个静态字符串。当调用type_name_of(view_matrix)
时,编译器可以推断出T
的类型,因为view_matrix
被传递给了这个函数。这样,std::any::type_name::<T>()
就可以正确地推断出T
的类型。
总结来说,第一个代码片段中,编译器无法推断出view_matrix
的类型,因为它被用作一个闭包的参数,且没有足够的信息来确定其类型。而在第二个代码片段中,通过使用泛型函数type_name_of
,编译器可以推断出view_matrix
的类型,因为函数的泛型参数T
提供了所需的信息。
rust的交互界面GUI库:egui和three_d简介
[https://github.com/asny/three-d]
[https://www.egui.rs/#demo]
[https://github.com/emilk/egui]
egui(发音为“e-gooey”)是一个简单、快速且高度可移植的Rust即时模式GUI库。egui可以在网页、本地以及您喜欢的游戏引擎中运行。
egui旨在成为最容易使用的Rust GUI库,也是用Rust制作网页应用的最简单方式。
只要您能绘制纹理三角形,就可以使用egui,这意味着您可以轻松将其集成到您选择的游戏引擎中。
eframe是官方的egui框架,支持为Web、Linux、Mac、Windows和Android编写应用。
一个OpenGL/WebGL/OpenGL ES渲染器,旨在使图形简单化,同时仍具有高效绘制您想要的内容的能力。
three-d
适用于那些只想绘制东西的人和那些想避免繁琐设置但仍希望获得低级访问权限的人。
使得结合高级功能与自定义低级实现成为可能,例如自定义着色器。
尝试用几行简单的代码完成工作。
力求尽可能明确,以便对您来说没有意外——没有隐藏的魔法。
适用于桌面、网页和移动设备。
three-d可以用于例如:
数据可视化
图像处理
UI渲染
工具(2D或3D)
游戏(2D或3D)
egui (pronounced "e-gooey") is a simple, fast, and highly portable immediate mode GUI library for Rust. egui runs on the web, natively, and in your favorite game engine.
egui aims to be the easiest-to-use Rust GUI library, and the simplest way to make a web app in Rust.
egui can be used anywhere you can draw textured triangles, which means you can easily integrate it into your game engine of choice.
eframe is the official egui framework, which supports writing apps for Web, Linux, Mac, Windows, and Android.
A OpenGL/WebGL/OpenGL ES renderer which seeks to make graphics simple but still have the power to efficiently draw exactly what you want.
three-d
targets those who just want to draw something and those who want to avoid the tedious setup but still wants low-level access.
makes it possible to combine high-level features with custom low-level implementations for example custom shaders.
tries to do stuff in a few simple lines of code.
aims to be as explicit as possible so there is no surprises for you - no hidden magic.
targets desktop, web and mobile.
three-d can for example be used for
data visualization
image processing
UI rendering
tools (2D or 3D)
games (2D or 3D)
实现
原始代码:[https://github.com/gaoxiang12/slambook2/blob/master/ch3/visualizeGeometry/visualizeGeometry.cpp]
#include <iostream>
#include <iomanip>using namespace std;#include <Eigen/Core>
#include <Eigen/Geometry>using namespace Eigen;#include <pangolin/pangolin.h>struct RotationMatrix {Matrix3d matrix = Matrix3d::Identity();
};ostream &operator<<(ostream &out, const RotationMatrix &r) {out.setf(ios::fixed);Matrix3d matrix = r.matrix;out << '=';out << "[" << setprecision(2) << matrix(0, 0) << "," << matrix(0, 1) << "," << matrix(0, 2) << "],"<< "[" << matrix(1, 0) << "," << matrix(1, 1) << "," << matrix(1, 2) << "],"<< "[" << matrix(2, 0) << "," << matrix(2, 1) << "," << matrix(2, 2) << "]";return out;
}istream &operator>>(istream &in, RotationMatrix &r) {return in;
}struct TranslationVector {Vector3d trans = Vector3d(0, 0, 0);
};ostream &operator<<(ostream &out, const TranslationVector &t) {out << "=[" << t.trans(0) << ',' << t.trans(1) << ',' << t.trans(2) << "]";return out;
}istream &operator>>(istream &in, TranslationVector &t) {return in;
}struct QuaternionDraw {Quaterniond q;
};ostream &operator<<(ostream &out, const QuaternionDraw quat) {auto c = quat.q.coeffs();out << "=[" << c[0] << "," << c[1] << "," << c[2] << "," << c[3] << "]";return out;
}istream &operator>>(istream &in, const QuaternionDraw quat) {return in;
}int main(int argc, char **argv) {pangolin::CreateWindowAndBind("visualize geometry", 1000, 600);glEnable(GL_DEPTH_TEST);pangolin::OpenGlRenderState s_cam(pangolin::ProjectionMatrix(1000, 600, 420, 420, 500, 300, 0.1, 1000),pangolin::ModelViewLookAt(3, 3, 3, 0, 0, 0, pangolin::AxisY));const int UI_WIDTH = 500;pangolin::View &d_cam = pangolin::CreateDisplay().SetBounds(0.0, 1.0, pangolin::Attach::Pix(UI_WIDTH), 1.0, -1000.0f / 600.0f).SetHandler(new pangolin::Handler3D(s_cam));// uipangolin::Var<RotationMatrix> rotation_matrix("ui.R", RotationMatrix());pangolin::Var<TranslationVector> translation_vector("ui.t", TranslationVector());pangolin::Var<TranslationVector> euler_angles("ui.rpy", TranslationVector());pangolin::Var<QuaternionDraw> quaternion("ui.q", QuaternionDraw());pangolin::CreatePanel("ui").SetBounds(0.0, 1.0, 0.0, pangolin::Attach::Pix(UI_WIDTH));while (!pangolin::ShouldQuit()) {glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);d_cam.Activate(s_cam);pangolin::OpenGlMatrix matrix = s_cam.GetModelViewMatrix();Matrix<double, 4, 4> m = matrix;RotationMatrix R;for (int i = 0; i < 3; i++)for (int j = 0; j < 3; j++)R.matrix(i, j) = m(j, i);rotation_matrix = R;TranslationVector t;t.trans = Vector3d(m(0, 3), m(1, 3), m(2, 3));t.trans = -R.matrix * t.trans;translation_vector = t;TranslationVector euler;euler.trans = R.matrix.eulerAngles(2, 1, 0);euler_angles = euler;QuaternionDraw quat;quat.q = Quaterniond(R.matrix);quaternion = quat;glColor3f(1.0, 1.0, 1.0);pangolin::glDrawColouredCube();// draw the original axisglLineWidth(3);glColor3f(0.8f, 0.f, 0.f);glBegin(GL_LINES);glVertex3f(0, 0, 0);glVertex3f(10, 0, 0);glColor3f(0.f, 0.8f, 0.f);glVertex3f(0, 0, 0);glVertex3f(0, 10, 0);glColor3f(0.2f, 0.2f, 1.f);glVertex3f(0, 0, 0);glVertex3f(0, 0, 10);glEnd();pangolin::FinishFrame();}
}
注意:three-d
库需要使用[https://github.com/asny/three-d]的最新版本,参考toml方式引用本地库文件.
#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_imports)]
#![allow(unused_mut)]
#![allow(unused_assignments)]
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#![allow(rustdoc::missing_crate_level_docs)]
#![allow(unsafe_code)]
#![allow(clippy::undocumented_unsafe_blocks)]use nalgebra::{Matrix3, Vector3, UnitQuaternion, Quaternion, Isometry3, Rotation3, UnitComplex, Rotation2, Unit,Translation3, Perspective3, Orthographic3, Vector4, Point3, Const,ArrayStorage, Matrix4, ViewStorage
};// 绘图和界面库
use three_d::*;
use three_d::egui::*;use env_logger::init;use std::fmt;
use std::sync::Arc;
use std::any::type_name;// 1. 定义一个结构体,用于表示旋转矩阵
#[derive(Debug)]
#[derive(PartialEq)]
#[derive(Default)]
struct RotationMatrix {matrix: Matrix3<f64>,
}// 实现Display trait,以便输出旋转矩阵
impl fmt::Display for RotationMatrix {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {write!(f, "{:?}", self.matrix)}
}// 2. 定义一个结构体,用于表示平移向量
#[derive(Debug)]
#[derive(PartialEq)]
#[derive(Default)]
struct TranslationVector {trans: Vector3<f64>,
}
// 实现Display trait,以便输出平移向量
impl fmt::Display for TranslationVector {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {write!(f, "=[{}, {}, {}]", self.trans.x, self.trans.y, self.trans.z)}
}
// 3. 定义一个结构体,用于表示四元数
#[derive(Debug)]
#[derive(PartialEq)]
#[derive(Default)]
struct QuaternionDraw {q: UnitQuaternion<f64>,
}
// 实现Display trait,以便输出四元数
impl fmt::Display for QuaternionDraw {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {let i = self.q.quaternion().i;let j = self.q.quaternion().j;let k = self.q.quaternion().k;let w = self.q.quaternion().w;write!(f, "=[{}, {}, {}, {}]", i, j, k, w)}
}/* start 测试 */
fn test() {// 创建一个单位旋转矩阵let rotation_matrix = RotationMatrix {matrix: Matrix3::identity(),};// 测试矩阵是否为单位矩阵assert_eq!(rotation_matrix.matrix, Matrix3::identity(), "The matrix should be the identity matrix.");// 测试矩阵的行列式是否为1assert_eq!(rotation_matrix.matrix.determinant(), 1.0, "The determinant of the identity matrix should be 1.");// 打印旋转矩阵println!("{}", rotation_matrix);// 创建一个平移向量let translation_vector = TranslationVector {trans: Vector3::new(1.0, 2.0, 3.0),};// 定义一个预期的平移向量let expected_translation_vector = TranslationVector {trans: Vector3::new(1.0, 2.0, 3.0),};// 测试平移向量是否相等assert_eq!(translation_vector, expected_translation_vector, "The translation vectors should be equal.");// 打印平移向量println!("{}", translation_vector);// 创建一个单位四元数let quaternion_draw = QuaternionDraw {q: UnitQuaternion::identity(),};// 定义一个预期的单位四元数let expected_quaternion_draw = QuaternionDraw {q: UnitQuaternion::identity(),};// 测试四元数是否相等assert_eq!(quaternion_draw, expected_quaternion_draw, "The quaternions should be equal.");// 打印四元数println!("{}", quaternion_draw);
}
/* end 测试 *//* start 绘制界面和绘图 */
pub fn main() {let window = three_d::Window::new(WindowSettings {title: "Shapes!".to_string(),max_size: Some((1280, 720)),..Default::default()}).unwrap();let context = window.gl();let mut camera = three_d::Camera::new_perspective(window.viewport(),vec3(5.0, 2.0, 2.5),vec3(0.0, 0.0, -0.5),vec3(0.0, 1.0, 0.0),degrees(45.0),0.1,1000.0,);let mut control = three_d::OrbitControl::new(camera.target(), 1.0, 100.0);/* start 绘制立体图形 */let mut sphere = three_d::Gm::new(three_d::Mesh::new(&context, &CpuMesh::sphere(16)),three_d::PhysicalMaterial::new_transparent(&context,&CpuMaterial {albedo: Srgba {r: 255,g: 0,b: 0,a: 200,},..Default::default()},),);sphere.set_transformation(Mat4::from_translation(vec3(0.0, 1.3, 0.0)) * Mat4::from_scale(0.2));let mut cylinder = three_d::Gm::new(three_d::Mesh::new(&context, &CpuMesh::cylinder(16)),three_d::PhysicalMaterial::new_transparent(&context,&CpuMaterial {albedo: Srgba {r: 0,g: 255,b: 0,a: 200,},..Default::default()},),);cylinder.set_transformation(Mat4::from_translation(vec3(1.3, 0.0, 0.0)) * Mat4::from_scale(0.2));let mut cube = three_d::Gm::new(three_d::Mesh::new(&context, &CpuMesh::cube()),three_d::PhysicalMaterial::new_transparent(&context,&CpuMaterial {albedo: Srgba {r: 0,g: 0,b: 255,a: 100,},..Default::default()},),);cube.set_transformation(Mat4::from_translation(vec3(0.0, 0.0, 1.3)) * Mat4::from_scale(0.2));let axes = three_d::Axes::new(&context, 0.1, 2.0);let bounding_box_sphere = Gm::new(BoundingBox::new(&context, sphere.aabb()),ColorMaterial {color: Srgba::BLACK,..Default::default()},);let bounding_box_cube = three_d::Gm::new(three_d::BoundingBox::new(&context, cube.aabb()),ColorMaterial {color: Srgba::BLACK,..Default::default()},);let bounding_box_cylinder = three_d::Gm::new(three_d::BoundingBox::new(&context, cylinder.aabb()),ColorMaterial {color: Srgba::BLACK,..Default::default()},);let light0 = DirectionalLight::new(&context, 1.0, Srgba::WHITE, vec3(0.0, -0.5, -0.5));let light1 = DirectionalLight::new(&context, 1.0, Srgba::WHITE, vec3(0.0, 0.5, 0.5));/* end 绘制立体图形 */let mut gui = three_d::GUI::new(&context);// main loopwindow.render_loop(move |mut frame_input| {let mut panel_width = 0.0;gui.update(&mut frame_input.events,frame_input.accumulated_time,frame_input.viewport,frame_input.device_pixel_ratio,|gui_context| {SidePanel::left("side_panel").show(gui_context, |ui| {ui.heading("Camera Pose");// 显示相机的平移向量let translation_vector = camera.position();ui.label(format!("Translation Vector: {:?}", translation_vector));// 获取相机的视图矩阵let view_matrix = camera.view();/* start 打印camera.view()返回值的数据类型 */// fn type_name_of<T>(_: &T) -> &'static str {// std::any::type_name::<T>()// }// (|view_matrix| println!("typeof view_matrix is {:?}", type_name_of(view_matrix)))(&view_matrix);/* end 打印camera.view()返回值的数据类型 */// 从视图矩阵中提取旋转矩阵// view_matrix:cgmath::Matrix4<f32>转为nal_view_matrix:nalgebra::Matrix4<f32>数据let nal_view_matrix = nalgebra::Matrix4::new(view_matrix.x.x, view_matrix.x.y, view_matrix.x.z, view_matrix.x.w,view_matrix.y.x, view_matrix.y.y, view_matrix.y.z, view_matrix.y.w,view_matrix.z.x, view_matrix.z.y, view_matrix.z.z, view_matrix.z.w,view_matrix.w.x, view_matrix.w.y, view_matrix.w.z, view_matrix.w.w,);let rotation_matrix = nal_view_matrix.try_inverse().unwrap_or_else(|| Matrix4::identity());// let rotation_matrix = nalgebra::Matrix4::<f64>::identity();// 从 rotation_matrix 中提取 3x3 的视图let rotation_matrix_view = rotation_matrix.fixed_view::<3, 3>(0, 0);// 创建一个新的 nalgebra::Matrix3<f32> 实例let rotation_matrix3 = nalgebra::Matrix3::from(rotation_matrix_view);ui.label(format!("Rotation Matrix: {:?}", rotation_matrix3));// 从旋转矩阵中提取四元数// let quaternion = Quaternion::new(1.0, 0.0, 0.0, 0.0); // 提供了合适的数值// 注意:这里假设 rotation_matrix3 是一个纯旋转矩阵,没有平移部分let unit_quaternion = UnitQuaternion::from_matrix(&rotation_matrix3);ui.label(format!("Quaternion: {:?}", unit_quaternion));// 从四元数中提取欧拉角let euler_angles = unit_quaternion.euler_angles();ui.label(format!("Euler Angles: {:?}", euler_angles));}); // end SidePanel showpanel_width = gui_context.used_rect().width();}, // end gui_context); // end gui.update// 更新相机视图camera.set_viewport(Viewport {x: (panel_width * frame_input.device_pixel_ratio) as i32,y: 0,width: frame_input.viewport.width- (panel_width * frame_input.device_pixel_ratio) as u32,height: frame_input.viewport.height,});control.handle_events(&mut camera, &mut frame_input.events);// 绘制场景frame_input.screen().clear(ClearState::color_and_depth(0.8, 0.8, 0.8, 1.0, 1.0)).render(&camera,sphere.into_iter().chain(&cylinder).chain(&cube).chain(&axes).chain(&bounding_box_sphere).chain(&bounding_box_cube).chain(&bounding_box_cylinder),&[&light0, &light1],).write(|| gui.render()).unwrap();FrameOutput::default()});
}
/* end 绘制界面和绘图 */
效果
界面显示四元数 |
---|
检查结果合理性
**总结性解决方案**
1. **翻译向量检查**- **数值**:`[4.5996685, 3.6870298, 1.3024255]`- **分析**:向量值在三维空间中大小适中,无极端值。- **结论**:翻译向量数值正确。
2. **旋转矩阵检查**- **矩阵**:\[\begin{bmatrix}0.3648478 & -0.5568863 & 0.74616593 \\0.0 & 0.8014093 & 0.59811616 \\-0.9310671 & -0.21822138 & 0.29239246 \\\end{bmatrix}\]- **正交性检查**:- 每行和每列的模长均接近1。- 不同行向量和列向量的点积接近0,满足正交条件。- **行列式检查**:- 计算得行列式约为1,符合旋转矩阵要求。- **结论**:旋转矩阵数值正确。
3. **四元数检查**- **数值**:`[0.26031038, -0.5348292, -0.17757763, 0.78400415]`- **模长检查**:- 模长计算为1,为单位四元数。- **与旋转矩阵对应性检查**:- 将四元数转换为旋转矩阵,发现与给定旋转矩阵不一致,特别是第一行第二列的元素不符。- **结论**:四元数可能有误。
4. **欧拉角检查**- **数值**:`(1.1161038, -0.8422845, -0.99080443)`(单位:弧度)- **范围分析**:- 滚转约 \(64^\circ\),俯仰约 \(-48^\circ\),偏航约 \(-56.8^\circ\),均在合理范围内。- **结论**:欧拉角数值合理。
**最终结论**
\[
\boxed{\text{翻译向量和旋转矩阵数值正确,四元数可能有误。}}
\]