From f5d578ccf3e7d3361962c04c433cb8ac8fd5f609 Mon Sep 17 00:00:00 2001 From: numzero Date: Sun, 18 Oct 2026 16:48:33 +0300 Subject: [PATCH] control gear params from the UI --- src/bevel_gear.rs | 4 +- src/lib.rs | 26 ++++- src/main.rs | 4 +- ui/src/api.cxx | 4 +- ui/src/api.hxx | 3 +- ui/src/lib.rs | 8 +- ui/src/main_window.cxx | 32 +++++-- ui/src/main_window.hxx | 2 +- ui/src/main_window.ui | 209 ++++++++++++++++++++++++++++++++++++++++- ui/src/viewport.cxx | 8 +- ui/src/viewport.hxx | 2 + 11 files changed, 275 insertions(+), 27 deletions(-) diff --git a/src/bevel_gear.rs b/src/bevel_gear.rs index 3ba5ecd..e203372 100644 --- a/src/bevel_gear.rs +++ b/src/bevel_gear.rs @@ -23,11 +23,11 @@ impl BevelGearDesc { } } - fn pitch_radius(&self) -> f32 { + pub fn pitch_radius(&self) -> f32 { 0.5 * self.teeth as f32 } - fn pitch_distance(&self) -> f32 { + pub fn pitch_distance(&self) -> f32 { self.pitch_radius() / self.cone_halfangle.tan() } } diff --git a/src/lib.rs b/src/lib.rs index 56aba16..db011d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,22 @@ mod render; const BACKGROUND: Vec4 = vec4(0.00, 0.39, 0.52, 1.00); +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct ResetArgs { + pub tooth: u32, + pub cone_halfangle: f32, +} + +impl Default for ResetArgs { + fn default() -> Self { + Self { + tooth: 24, + cone_halfangle: std::f32::consts::FRAC_PI_4, + } + } +} + #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct RedrawArgs { @@ -72,7 +88,7 @@ fn color_to_wgpu(color: Vec4) -> wgpu::Color { } impl Core { - pub fn new(gpu: Gpu, pixel_size: UVec2) -> Self { + pub fn new(gpu: Gpu, pixel_size: UVec2, args: &ResetArgs) -> Self { let Gpu { device, queue, @@ -100,14 +116,14 @@ impl Core { let bevel_gear_desc = bevel_gear::BevelGearDesc { profile: Default::default(), - teeth: 24, - cone_halfangle: 0.5, + teeth: args.tooth, + cone_halfangle: args.cone_halfangle, }; let bevel_gear_mesh = bevel_gear::make_mesh( &bevel_gear_desc, &bevel_gear::BevelGearVisual { - inner_distance: 8.0, - outer_distance: 12.0, + inner_distance: bevel_gear_desc.pitch_distance() - 4.0, + outer_distance: bevel_gear_desc.pitch_distance(), }, &bevel_gear::BevelGearShape::new_blank(&bevel_gear_desc, 60), ) diff --git a/src/main.rs b/src/main.rs index 7559120..2f1f50d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use gear_cutter::{Core, RedrawArgs, init_gpu_inner}; +use gear_cutter::{Core, RedrawArgs, ResetArgs, init_gpu_inner}; use glam::uvec2; use winit::{ application::ApplicationHandler, @@ -26,7 +26,7 @@ impl MainWindow { instance.create_surface(Arc::clone(&window)) })) .unwrap(); - let core = Core::new(gpu, uvec2(1, 1)); + let core = Core::new(gpu, uvec2(1, 1), &ResetArgs::default()); Self { window, core } } diff --git a/ui/src/api.cxx b/ui/src/api.cxx index 5b8e689..9d23eb0 100644 --- a/ui/src/api.cxx +++ b/ui/src/api.cxx @@ -19,13 +19,13 @@ BoxCore& BoxCore::operator=(BoxCore&& b) { return *this; } -BoxCore BoxCore::from_xcb(xcb_connection_t* connection, std::uint32_t window, std::uint32_t width, std::uint32_t height) { +BoxCore BoxCore::from_xcb(xcb_connection_t* connection, std::uint32_t window, std::uint32_t width, std::uint32_t height, const ResetArgs& args) { if (!connection) throw std::logic_error("attempt to use a null connection"); if (!window) throw std::logic_error("attempt to use a null window"); BoxCore out; - out.ptr.ptr = ffi::rt4_viewport_create(connection, window, width, height); + out.ptr.ptr = ffi::rt4_viewport_create(connection, window, width, height, &args); return out; } diff --git a/ui/src/api.hxx b/ui/src/api.hxx index 4847123..a2dcf67 100644 --- a/ui/src/api.hxx +++ b/ui/src/api.hxx @@ -6,6 +6,7 @@ struct xcb_connection_t; +using ffi::ResetArgs; using ffi::RedrawArgs; class MutCore { @@ -36,7 +37,7 @@ public: void reset(); - static BoxCore from_xcb(xcb_connection_t* connection, std::uint32_t window, std::uint32_t width, std::uint32_t height); + static BoxCore from_xcb(xcb_connection_t* connection, std::uint32_t window, std::uint32_t width, std::uint32_t height, const ResetArgs& args); private: MutCore ptr; diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 6092656..7477574 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -1,6 +1,6 @@ use std::{ffi::c_void, num::NonZero, ptr::NonNull}; -use gear_cutter::{Core, RedrawArgs, init_gpu_inner}; +use gear_cutter::{Core, RedrawArgs, ResetArgs, init_gpu_inner}; use glam::{UVec2, uvec2}; use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle}; @@ -8,6 +8,7 @@ unsafe fn create_viewport( display: impl Into, window: impl Into, size: UVec2, + args: &ResetArgs, ) -> Box { let target = wgpu::SurfaceTargetUnsafe::RawHandle { raw_display_handle: display.into(), @@ -17,7 +18,7 @@ unsafe fn create_viewport( instance.create_surface_unsafe(target) })) .unwrap(); - Box::new(Core::new(gpu, size)) + Box::new(Core::new(gpu, size, args)) } #[unsafe(no_mangle)] @@ -26,10 +27,11 @@ unsafe extern "C" fn rt4_viewport_create( window: NonZero, width: u32, height: u32, + args: &ResetArgs, ) -> Box { let display = XcbDisplayHandle::new(Some(connection), 0); let window = XcbWindowHandle::new(window); - unsafe { create_viewport(display, window, uvec2(width, height)) } + unsafe { create_viewport(display, window, uvec2(width, height), args) } } #[unsafe(no_mangle)] diff --git a/ui/src/main_window.cxx b/ui/src/main_window.cxx index afd61d4..0e65394 100644 --- a/ui/src/main_window.cxx +++ b/ui/src/main_window.cxx @@ -6,13 +6,38 @@ GearCutter::GearCutter(QWidget* parent) : QMainWindow(parent), m_ui(new Ui::MainWindow) { m_ui->setupUi(this); + updateParams(); updateView(); } GearCutter::~GearCutter() = default; +void GearCutter::updateParams() { + const std::uint32_t tooth = m_ui->bevTooth->value(); + const std::uint32_t mate_tooth = m_ui->bevMateTooth->value(); + const float shaft_angle = qDegreesToRadians((float)m_ui->bevShaftAngle->value()); + + const float r_gear = 0.5f * tooth; + const float r_mate = 0.5f * mate_tooth; + const float s = std::sin(shaft_angle); + const float c = std::cos(shaft_angle); + const float x_mate = s * r_mate; + const float y_mate = r_gear + c * r_mate; + const float h_gear = x_mate + y_mate * c / s; + const float cone_halfangle = std::atan2(r_gear, h_gear); + + m_ui->bevAngle->setValue(qRadiansToDegrees(cone_halfangle)); + m_ui->bevMateAngle->setValue(qRadiansToDegrees(shaft_angle - cone_halfangle)); + + const ResetArgs args{ + .tooth = tooth, + .cone_halfangle = cone_halfangle, + }; + m_ui->viewport->setParams(args); +} + void GearCutter::updateView() { - RedrawArgs args{ + const RedrawArgs args{ .cam_yaw = qDegreesToRadians((float)m_ui->camYaw->value()), .cam_pitch = qDegreesToRadians((float)m_ui->camPitch->value()), .cam_view_y = (float)m_ui->camFoVLin->value(), @@ -21,9 +46,4 @@ void GearCutter::updateView() { m_ui->viewport->setView(args); } -void GearCutter::updateViewIf(bool update) { - if (update) - updateView(); -} - #include "moc_main_window.cpp" diff --git a/ui/src/main_window.hxx b/ui/src/main_window.hxx index 09c810f..1c64949 100644 --- a/ui/src/main_window.hxx +++ b/ui/src/main_window.hxx @@ -15,8 +15,8 @@ public: ~GearCutter() override; public slots: + void updateParams(); void updateView(); - void updateViewIf(bool update); // for radio buttons private: const std::unique_ptr m_ui; diff --git a/ui/src/main_window.ui b/ui/src/main_window.ui index 748c5e2..e06a6f4 100644 --- a/ui/src/main_window.ui +++ b/ui/src/main_window.ui @@ -26,14 +26,14 @@ 0 0 1600 - 34 + 33 - QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable + QDockWidget::DockWidgetFeature::DockWidgetFloatable|QDockWidget::DockWidgetFeature::DockWidgetMovable 2 @@ -159,10 +159,152 @@ + + + + Bevel gear + + + + + + Tooth + + + + + + + 6 + + + 500 + + + 24 + + + + + + + Cone half-angle + + + + + + + false + + + true + + + ° + + + 3 + + + 15.000000000000000 + + + 90.000000000000000 + + + 5.000000000000000 + + + 90.000000000000000 + + + + + + + Mate tooth + + + + + + + 6 + + + 500 + + + 32 + + + + + + + Mate half-angle + + + + + + + false + + + true + + + ° + + + 3 + + + 90.000000000000000 + + + 5.000000000000000 + + + + + + + Shaft angle + + + + + + + ° + + + 1 + + + 15.000000000000000 + + + 150.000000000000000 + + + 5.000000000000000 + + + 90.000000000000000 + + + + + + - Qt::Vertical + Qt::Orientation::Vertical @@ -184,6 +326,17 @@ 1 + + camYaw + camPitch + camFoVLin + camFoVAng + bevTooth + bevAngle + bevMateTooth + bevMateAngle + bevShaftAngle + @@ -250,9 +403,57 @@ + + bevTooth + valueChanged(int) + MainWindow + updateParams() + + + 1557 + 431 + + + 1464 + 488 + + + + + bevMateTooth + valueChanged(int) + MainWindow + updateParams() + + + 1509 + 556 + + + 1462 + 561 + + + + + bevShaftAngle + valueChanged(double) + MainWindow + updateParams() + + + 1489 + 681 + + + 1461 + 689 + + + updateView() - updateViewIf(bool) + updateParams() diff --git a/ui/src/viewport.cxx b/ui/src/viewport.cxx index e681bd9..833cd83 100644 --- a/ui/src/viewport.cxx +++ b/ui/src/viewport.cxx @@ -14,6 +14,12 @@ Viewport::Viewport(QWidget* parent, Qt::WindowFlags f) Viewport::~Viewport() = default; +void Viewport::setParams(ResetArgs new_args) { + setup = new_args; + recreate(); + update(); +} + void Viewport::setView(RedrawArgs new_args) { args = new_args; update(); @@ -63,7 +69,7 @@ void Viewport::recreate() try { const QSize device_size = size() * devicePixelRatio(); core.reset(); - core = BoxCore::from_xcb(xcb_connection, x11_window, device_size.width(), device_size.height()); + core = BoxCore::from_xcb(xcb_connection, x11_window, device_size.width(), device_size.height(), setup); } catch (const std::exception& e) { fprintf(stderr, "failed to recreate the viewport: %s", e.what()); } diff --git a/ui/src/viewport.hxx b/ui/src/viewport.hxx index 95839b9..cba4363 100644 --- a/ui/src/viewport.hxx +++ b/ui/src/viewport.hxx @@ -12,6 +12,7 @@ public: ~Viewport() override; QPaintEngine* paintEngine() const override; + void setParams(ResetArgs new_args); void setView(RedrawArgs new_args); protected: @@ -21,6 +22,7 @@ protected: private: BoxCore core; + ResetArgs setup = {}; RedrawArgs args = {}; void recreate();