From cc6189e162c620c82faa4a6881d0b35b114ca726 Mon Sep 17 00:00:00 2001 From: brunoherbelin Date: Tue, 18 Nov 2025 17:13:27 +0100 Subject: [PATCH] New MousePointer Brownian --- rsc/images/icons.dds | Bin 1638528 -> 1638528 bytes src/MousePointer.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++ src/MousePointer.h | 18 ++++++++++++++++- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/rsc/images/icons.dds b/rsc/images/icons.dds index 02e16135e4827d775e02e84a08c3424fe911b5d2..532257197379779de107952409ea863e85a92e27 100644 GIT binary patch delta 4663 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

ii4HnAzl zkFSvSs^pf?>R@lHzQ@{-xhoPH}Qy zr9(Y@G?D&U*3;^|alE;s5TFYG0uOTNiE$*Y8O|swbWb&M3zYZFu}+Vl5aZj0)n%t#tw+6 z|6`>5E0yC4*wlZSprk<~_<$YcCKqy}f^r{P6XJ^ee&L6F~6|2VQdM}|ybslQ{IA)T1^P4x}gxJ0)(8>!8h!3(MD-~buG z$DVwwChZiVS3u8qmhon({deg~PUx&zVVY;r$%lxnQeJ%nXa4haY5e=%2U9ue=nEve z7iB9oad*;fs`*E}P-;wX<dHRJf^e z(*rH3 zC=AmPIz5cXpCNO^yV9=vCOQPtA%vEnO=2;dV0Df^oH{|L;HyZ*vAxkL^6F`pDGo(+ z+pALbBTzEQB0!=gE#rwPQ0qn-QI-8whpQA#zIJSd1-~DK$Dj*6$ zy0#)Ge5Xdx=TrPbSPc8>*6Jw^Tb7>>TWns6m1gx^;JrB%t3%FB25(R%n-my>_tbi0 zls8cM>R5d&7&DqEj<@E1_xhE)J=j?svqu5Ewe?jS>t>hBpI^fE> z*2WK4)UcB}=4h>ND;DL0i!2z-uk<;>N=B|x71J7d8#9Ol4X#Yqn@c9G4!Byz{Wv<6 zz@jG-^MrKHFiyc_os*nvT+Ptpq1d+Huwz!kvTp~G*#q!uL&C)gMXhjy6;*p6;|CCBNtm#~}8XIE_ouc3!IbQSbCJ}Yl7p*Akj zvL|Ppi|b)EtOVwa#Z}^45s0wxb6Xsqhc`Lw)cAsryrzaiFJ@zCO91D(caA;Oy0Ziu zyW!u-AzW+pc?%<679EOupHw$gA<^;0Wk>&G;J-FKOL5%F#loMzwt=x>2&bKWV3+Gn zs^D0d@7nUTqlB>eDPEvV>GkZa_pp4LRbCc&tZQXG)wh+D+c$11jZhpk7>wsU@3P{P z4-Tlnd``%3UalX;x$c8~U?+y4IZjNGr;%@h=Dig(ZwOfxa8qqr`*1Pup;-y|4D=rRy#*2>Fga;e%FybKkNR<; zN;0hD9Kn}X`&eoz>Q^`#_pNbIdCcT99z7OjxeYppzwLPkD|`#VKvBptY#Jg2i`ksK|c*c9E58y90cNyDSR2pvP{}HrR|fcTW$S-JUb1 zf7R&hEhd}~r-vJ6rqA?qma6R`FAnF6!{qAK;|hn?e`)`cj`u?t?uCbL2BT8n4B^!y z&6je-_y9Zr=Rgh%5{FgJ{jU2K7+KB~yZ3O#Y7aXPOV`odI z3!EDDT2?f8wb84pHZgrQZ__9_JKKf0P6rSaLIU+t5L7msDo01Lh1ZK(U(Gs-b%Ii9 zv$$AMDApN^IY+IZ$~A)3p#mG*Mx)*83Fs_lDlY*)E^5A;$l`U(Vi$PvCE+MtB(l=z zP>E|;Xhb3&gTuR`h00gvIPBhh-w}fGPD3PYFcoXIyhU+q#A9?gHf^Wa9kbg6Yso%} z>nrUh&_pKBoed$cn$zf5`MA#`>a38@YONzsQ>Qcp%g6*|o6~nXMP;B%q_{u~h4aJJ zELJOa2AzQ_7u)uqbh^#8R*9WUZP?#}$(#E>Qz|E7F(X%uTcHk~f~Tla#R{X?u&A8H zqR;CH{if+u1CbNfJYK(R-ihfLFMAZe{`#!fXvryg#5f-qlHEJe+8~Eq6qSm%&}I09YkzjQ(3^Ym1uY%tjA-u-;}25Icz-=1OBl z-D*$T`&!2X#_T(_)L~i~AT274zB#90h?Eav>nCau4Yc*O; zRw~!~H$Gx9@kWA)!FqL1y~SS4Q>=ldPzowJsj>K~IS$ z^MttSufga4E4__!J#@~dr z_TjWH##_=l$zn8$A1tc(l4mY5E1^y2X4}_IK1<2danOkOkjj-BJRZL&uEGB!r(&^l zD%NPH;&>20ASEw4!TkEExc1?xIH;eB)jN{-^f0b7_=%~_sZ^03gf_n~aCRYNR88Qd zLw?L+QZ8d@@L*Q$KYnIG|0(=_O?UE%Mp8&h<^fJAo!=w`zF+. **/ +#include #include // for diskRand #include "imgui.h" @@ -37,6 +38,7 @@ std::vector< std::tuple > Pointer::Modes = { { ICON_POINTER_LINEAR, "Line", "Speed" }, { ICON_POINTER_SPRING, "Spring", "Mass" }, { ICON_POINTER_WIGGLY, "Wiggly", "Radius" }, + { ICON_POINTER_BROWNIAN, "Brownian", "Radius" }, { ICON_POINTER_METRONOME, "Metronome", "Jump" } }; @@ -138,6 +140,49 @@ void PointerWiggly::draw() ImGui::GetBackgroundDrawList()->AddCircle(IMVEC_IO(current_), max * 0.5f, color, 0, 2.f + 4.f * strength_); } +void PointerBrownian::update(const glm::vec2 &pos, float) +{ + current_ = pos; + radius_ = (POINTER_WIGGLY_MAX_RADIUS - POINTER_WIGGLY_MIN_RADIUS) * strength_; + radius_ += POINTER_WIGGLY_MIN_RADIUS; + + // Brownian motion: add small random displacement in 2D + // Generate random step using gaussian distribution for each axis + glm::vec2 random_step = glm::gaussRand(glm::vec2(0.0f), glm::vec2(1.f) ); + + // Scale by radius and apply damping to keep motion bounded + float factor = 0.3f; + if (TabletInput::instance().hasPressure() && TabletInput::instance().isPressed()) { + factor *= TabletInput::instance().getPressure(); + } + float damping = 0.92f; + brownian_offset_ = brownian_offset_ * damping + random_step * radius_ * factor; + + // Clamp offset to stay within maximum radius + float offset_length = glm::length(brownian_offset_); + if (offset_length > radius_) { + brownian_offset_ = brownian_offset_ * (radius_ / offset_length); + } + + glm::vec2 p = pos + brownian_offset_; + + // smooth a little and apply + const float emaexp = 2.0 / float( POINTER_WIGGLY_SMOOTHING + 1); + target_ = emaexp * p + (1.f - emaexp) * target_; +} + +void PointerBrownian::draw() +{ + const ImU32 color = ImGui::GetColorU32(ImGuiCol_HeaderActive); + ImGui::GetBackgroundDrawList()->AddLine(IMVEC_IO(current_), IMVEC_IO(target_), color, 5.f); + + const float max = POINTER_WIGGLY_MIN_RADIUS + (POINTER_WIGGLY_MAX_RADIUS - POINTER_WIGGLY_MIN_RADIUS) * strength_; + if (TabletInput::instance().hasPressure() && TabletInput::instance().isPressed()) + ImGui::GetBackgroundDrawList()->AddCircle(IMVEC_IO(current_), radius_ * 0.8f, color, 0); + ImGui::GetBackgroundDrawList()->AddCircle(IMVEC_IO(current_), max * 0.8f, color, 0, 2.f + 4.f * strength_); +} + + #define POINTER_METRONOME_RADIUS 36.f void PointerMetronome::initiate(const glm::vec2 &pos) @@ -256,6 +301,7 @@ MousePointer::MousePointer() : mode_(Pointer::POINTER_DEFAULT) pointer_[Pointer::POINTER_LINEAR] = new PointerLinear; pointer_[Pointer::POINTER_SPRING] = new PointerSpring; pointer_[Pointer::POINTER_WIGGLY] = new PointerWiggly; + pointer_[Pointer::POINTER_BROWNIAN] = new PointerBrownian; pointer_[Pointer::POINTER_METRONOME] = new PointerMetronome; } @@ -266,5 +312,6 @@ MousePointer::~MousePointer() delete pointer_[Pointer::POINTER_LINEAR]; delete pointer_[Pointer::POINTER_SPRING]; delete pointer_[Pointer::POINTER_WIGGLY]; + delete pointer_[Pointer::POINTER_BROWNIAN]; delete pointer_[Pointer::POINTER_METRONOME]; } diff --git a/src/MousePointer.h b/src/MousePointer.h index 73fc9d1..e2d524d 100644 --- a/src/MousePointer.h +++ b/src/MousePointer.h @@ -12,6 +12,7 @@ #define ICON_POINTER_LINEAR 14, 9 #define ICON_POINTER_GRID 15, 9 #define ICON_POINTER_WIGGLY 16, 9 +#define ICON_POINTER_BROWNIAN 11, 9 #define ICON_POINTER_METRONOME 6, 13 /// @@ -33,6 +34,7 @@ public: POINTER_LINEAR, POINTER_SPRING, POINTER_WIGGLY, + POINTER_BROWNIAN, POINTER_METRONOME, POINTER_INVALID } Mode; @@ -104,7 +106,21 @@ class PointerWiggly : public Pointer { float radius_; public: - PointerWiggly() {} + PointerWiggly() : radius_(0.0f) {} + void update(const glm::vec2 &pos, float) override; + void draw() override; +}; + +/// +/// \brief The PointerBrownian moves with a Brownian movement +/// Strength modulates the radius of the movement +/// +class PointerBrownian : public Pointer +{ + float radius_; + glm::vec2 brownian_offset_; +public: + PointerBrownian() : brownian_offset_(0.0f, 0.0f) {} void update(const glm::vec2 &pos, float) override; void draw() override; };