How to smoothly rotate knob with mouse ?

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

Hello!

I'm thinking to use this type technique for to rotate a knob with mouse :

Lets say, there's image list for 100 knob positions (0-99) covering rotation "range" -140.0 to 140.0 degrees. It's for to adjust the gain in "range" -12.0 to 12.0 dB. Linear scale so image 49 = 0dB.

1. when focus is in knob image; calculate the current "base" direction (image center - knob's position mark) (t0 = atan2(markY - centerY, markX - centerX) * 360 / 2pi)
2. when left mouse is pressed down and ready for dragging; set the "working" direction t1 = atan2(mouseY - centerY, mouseX - centerX) * 360 / 2pi
3. when mouse is moved (dragged); calculate the "current" direction t2 = atan2(currentMouseY - centerY, currentMouseX - centerX) * 360 / 2pi
4. calculate the "angle" between the "current" and "working" direction (alfa = t2 - t1)
5. sum the "angle" with "base" direction (check that "ranges" are not past) (t = t0 + alfa)
6. calculate the new image index using certain formula
- loop 3-6 as long as mouse is been dragged

Is this too complicated method? Any bottlenecks? Any suggestions for better techniques?

Post

Simplier technique, where just the direction (angle) between mouseXY and centerXY is used works otherwise well (knob marker follows the mouse cursor)

Code: Select all

private: System::Void HandleKnobMovement(PictureBox^ picBox, NumericUpDown^ lbl, MouseEventArgs^ e, int type, int band){
		//global_sw->WriteLine("HandleKnobMovement()");
		// type = hz, bwq, gain
		bool valid = true;

		if (picBox->Enabled){ lbl->Focus(); } // numericupdown control allows use of mousewheel --> rotate the knob through this control

		if (GetKeyState(VK_LBUTTON) < 0){ // left button must be down
			
			int max = 99; // bw
			int idx = 0;
	
			switch (type){
			case 0: // hz
				max = 199;
				break;
			case 1: // bw
				break;
			case 2: // gain
				max = 179;
				break;
			case 3: // masterGain
				max = 119;
			}
                        // new coordinate system
			double dx = (e->X - (picBox->Width / 2));
			double dy = ((picBox->Height / 2) - e->Y);
			double value = 0.0; // value for numericupdown control
			double expVal = 0.0;
			
			double alfa = atan2(dx, dy) * 360.0 / (2 * 3.14159265); // range -180.0, 180.0
			if (alfa < -140.0){ alfa = -140.0; }
			if (alfa > 140.0){ alfa = 140.0; }

			switch (type){
			case 0: // Hz (logarithmic knob)
				idx = 0.7107142857 * alfa + 99.5000001;
				if (idx < 0){ idx = 0; }
				if (idx > max){ idx = max; }
				picBox->Image = HzKnob->Images[idx]; // set new image
				expVal = exp(0.0271460802 * alfa);
				value = 447.2135955 * expVal;
				if (value < 10){ value = 10; }
				if (value > 20000){ value = 20000; }
				break;
			case 1: // BWoct/Q (logaritmic knob)
				// 1 = null
				// 2 = bw
				// 3 = Q
				idx = 0.3535714286 * alfa + 49.5;
				if (idx < 0){ idx = 0; }
				if (idx > max){ idx = max; }
				if (bandData[currentSpeakerMode][currentChannel][band].BwQNull == 2){ // bw/oct
					picBox->Image = BWQKnob->Images[idx];
					expVal = exp(0.0221950289 * alfa);
					value = 0.4472135955 * expVal;
					if (value < 0.02){ value = 0.02; }
					if (value > 10.0){ value = 10.0; }
				}
				else if (bandData[currentSpeakerMode][currentChannel][band].BwQNull == 3){ // Q
					picBox->Image = BWQKnob->Images[idx];
					expVal = exp(-0.0277972286 * alfa);
					value = 1.4696938457 * expVal;
					if (value < 0.03){ value = 0.03; }
					if (value > 72.0){ value = 72.0; }
				}
				else{ // -
					value = 0.0;
				}
				break;
			case 2: // Gain (linear)
				idx = 0.639285714 * alfa + 89.5000001;
				if (idx < 0){ idx = 0; }
				if (idx > max){ idx = max; }
				picBox->Image = GainKnob->Images[idx];
				value = 0.1285714286 * alfa; // 0.2011173184 * idx - 18;
				if (value < -18.0){ value = -18.0; } 
				if (value > 18.0){ value = 18.0; }
				break;
			case 3: //Master Gain (linear)
				idx = 0.428571426 * alfa + 60.000001; //
				if (idx > max){ idx = max; }
				if (idx < 0){ idx = 0; }
				picBox->Image = masterGainKnob->Images[idx];
				value = 0.0857142857 * alfa;
				if (value > 12.0){ value = 12.0; } 
				if (value < -12.0){ value = -12.0; }
				break;
			}
			lbl->Value = System::Convert::ToDecimal(value); // update numericupdown control (triggers some other calls)
			updateBandChanges(band, false); //
		}
	}
but one issue ... when mouse is down and moved a bit, knob rotates to point the current mouseXY direction (using the technique in 1st post should fix this).

Post

what you're describing is circular response, i hate it
but you could set up a minimum distance from the center where you can ignore the mouse and not change the knob position
distance formula is: sqrt(dx*dx+dy*dy) where dx=centerX-mouseX; dy=centerY-mouseY;

and i would advise you to have an odd number of frames for your knob, so that there is one frame exactly in the middle
It doesn't matter how it sounds..
..as long as it has BASS and it's LOUD!

irc.libera.chat >>> #kvr

Post

Thanks for the reply and advising (I almost started to think this is one of those hush hush subjects ... :))

I quess the other way is to use the length of mouse movement in calculation of rotation angle (frame index) and equivalent value ... or are there other techniques ...?

Selection of the "circular response" method was based on thought that it would be easier to
- "fine tune" the knob already with mouse because of finer tuning capability is there already without extra coding needed (drag mouse further from the coordinate center = smaller angle division)
- calculate the image list index and value directly from angle (I'm using simple trendline formulas got from OOCalc graph)

Post Reply

Return to “DSP and Plugin Development”