Make an Image Scale with Mouse Scroll Wheel XNA - c#

I have an image that I want to increase the size or decrease the size if the mouse wheel is scrolled up or down accordingly. If the image reaches a set max size it will not get ant larger and vice versa if making the image smaller. The problem I am having is once you reach the maximum size of the image and continue scrolling up then go to scroll down the image will not get smaller right away until you scrolled down the same number of times you scrolled up while having the image at the max size and again reverse for making the image smaller. ScrollWheelValue is an read only property so it cannot be reset. I was trying to add some logic where if the wheel is scrolled up and the image is max size subtract 120 because 120 is what the mouse increases per scroll. Can anyone help me with this issue? Thanks very much
Original Code:
float scale = ms.ScrollWheelValue / 120;
scaleFactor = scale * scaleChange;
if (scaleFactor > MAX_SCALE)
{
scaleFactor = MAX_SCALE;
}
else if (scaleFactor < MIN_SCALE)
{
scaleFactor = MIN_SCALE;
}
New Code:
if (scaleFactor > MAX_SCALE)
{
scaleFactor = MAX_SCALE;
float newScale = ms.ScrollWheelValue / 120;
if (newScale > scale)
{
scaleCount = scaleCount - 120;
}
if (newScale < scale)
{
scaleCount = scaleCount + 120;
}
}
else if (scaleFactor < MIN_SCALE)
{
scaleFactor = MIN_SCALE;
float newScale = ms.ScrollWheelValue / 120;
if (newScale > scale)
{
scaleCount = scaleCount - 120;
}
if (newScale < scale)
{
scaleCount = scaleCount + 120;
}
}
else
{
scale = ms.ScrollWheelValue / 120 + scaleCount;
scaleFactor = scale * scaleChange;
}

If you read:
MSDN MouseState Scroll Wheel Value
You'll see that it keeps a running value from the beginning of the game. So what you want to do is check it for a change vs. the previous value and do something accordingly.
How you have it set up it seems you don't care about the actual value, just the difference since the last time they scrolled the wheel.
declare these outside of your update loop:
float prevWheelValue;
float currWheelValue;
Then in your update:
prevWheelValue = currWheelValue;
currWheelValue = ms.ScrollWheelValue;
now your checks can simply be if prevWheelValue > < or == to currWheelValue and clamp the value to the boundaries that you want.
Mathhelper.Clamp

Related

c# Chart y-axis scaling uniformly

I'm using a device that receives a pressure reading in a 4-20MA, converts it to a voltage and scales that within a range and then charts the data in a line graph, live. I am scaling the y-axis and have buttons in place to adjust the y-axis scale during runtime depending on how big the range is needed. So in otherwords, if the pressure starts at 0 psi, then jumps all the way to 20,000, I want the chart to show -100 (minimum - only so that if PSI is at 0, its visible) to 0, 100, 200, ..., 19800, 19900, 20000. But instead if pressure is at say 19743, the scale is "-43, 43, 143, ..., 19843, 19943" something like that. Is there a way to change this so it only counts by "whole hundreds" or something?
My y-axis interval is jumping by 50's so the axis can go 0, 50, 100,... or 0, 100, 200, etc. and so on (Able to jump 500 psi at a time if desired). But I just want to y-axis numbers to show whole hundreds, if that makes sense.
My maximum y-value is the max incoming value (I'm comparing 3 input values) and adding 1,000 to that. So if the current max pressure is 19,000 psi, the chart should show -100 to 20,0000.
My comparisons of incoming values - The names are "RawIn#" but these are already scaled. Bad variable naming, I know.
if (rawIn0 > rawIn1)
{
maxY = Convert.ToInt32(rawIn0);
}
else if (rawIn1 > rawIn0)
{
maxY = Convert.ToInt32(rawIn1);
}
if (maxY < rawIn2)
{
maxY = Convert.ToInt32(rawIn2);
}
if (maxY > absoluteMaxY)
{
absoluteMaxY = maxY;
}
if (rawIn0 > rawIn1)
{
minY = Convert.ToInt32(rawIn1);
}
else if (rawIn1 > rawIn0)
{
minY = Convert.ToInt32(rawIn0);
}
if (minY > rawIn2)
{
minY = Convert.ToInt32(rawIn2);
}
The current way I'm scaling the chart. MinY is always 0. So minimum = -100.
chart1.ChartAreas[0].AxisY.Maximum = absoluteMaxY + 1000;
chart1.ChartAreas[0].AxisY.Minimum = minY - 100;
My buttons on runtime to increase and decrease the interval. The higher the // interval, the easier it is to see the entire chart with less y-axis lines.
private void yValueSizeInc_Click(object sender, EventArgs e)
{
if (chart1.ChartAreas[0].AxisY.Interval < 50)
{
chart1.ChartAreas[0].AxisY.Interval = 50;
}
else
{
chart1.ChartAreas[0].AxisY.Interval += 50;
}
Properties.Settings.Default.pressureInterval =
(int)chart1.ChartAreas[0].AxisY.Interval;
yValueSize.Text = chart1.ChartAreas[0].AxisY.Interval.ToString();
}
private void yValueSizeDec_Click(object sender, EventArgs e)
{
if (chart1.ChartAreas[0].AxisY.Interval <= 50)
{
chart1.ChartAreas[0].AxisY.Interval = 50;
}
else
{
chart1.ChartAreas[0].AxisY.Interval -= 50;
}
Properties.Settings.Default.pressureInterval = (int)
chart1.ChartAreas[0].AxisY.Interval;
yValueSize.Text = chart1.ChartAreas[0].AxisY.Interval.ToString();
}
Pressure count can be seen not uniform

C# zoom image without zooming the picturebox

I would like to zoom image with mouse in the picturebox. The code below zoom the image but the picture box zoom itself. I want to zoom the image without zooming the picturebox. What can i edit the code below? Thanks
protected override void OnMouseWheel(MouseEventArgs ea)
{
Image img = Image.FromFile("C:/Users/User/Desktop/11.png");
// flag = 1;
// Override OnMouseWheel event, for zooming in/out with the scroll wheel
if (pictureBox1.Image != null)
{
// If the mouse wheel is moved forward (Zoom in)
if (ea.Delta > 0)
{
// Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
if ((pictureBox1.Width < (15 * this.Width)) && (pictureBox1.Height < (15 * this.Height)))
{
// Change the size of the picturebox, multiply it by the ZOOMFACTOR
pictureBox1.Width = (int)(pictureBox1.Width * 1.25);
pictureBox1.Height = (int)(pictureBox1.Height * 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
pictureBox1.Top = (int)(ea.Y - 1.25 * (ea.Y - pictureBox1.Top));
pictureBox1.Left = (int)(ea.X - 1.25 * (ea.X - pictureBox1.Left));
}
}
else
{
// Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
if ((pictureBox1.Width > (img.Width)) && (pictureBox1.Height > (img.Height)))
{// Change the size of the picturebox, divide it by the ZOOMFACTOR
pictureBox1.Width = (int)(pictureBox1.Width / 1.25);
pictureBox1.Height = (int)(pictureBox1.Height / 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
pictureBox1.Top = (int)(ea.Y - 0.80 * (ea.Y - pictureBox1.Top));
pictureBox1.Left = (int)(ea.X - 0.80 * (ea.X - pictureBox1.Left));
}
}
}

Scaling Zoom factors

I am using the scroll wheel to zoom in and out on an object using the following method:
void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
lastMousePositionOnTarget = Mouse.GetPosition(grid);
double max = 255;
double min = .005;
var deltaScale = Math.Max(Math.Log(scaleTransform.ScaleX), double.Epsilon);
var delta = e.Delta > 0 ? Math.Max(e.Delta * deltaScale, .5) : Math.Min(e.Delta * deltaScale, -.5);
double newScale = Math.Max(Math.Min((delta / 250d) + scaleTransform.ScaleX, max), min);
scaleTransform.ScaleX = newScale;
scaleTransform.ScaleY = newScale;
System.Diagnostics.Debug.WriteLine(newScale);
e.Handled = true;
}
I want to have it zoom more slowly when it is zoomed in closer to the max, and faster when the number is closer to the minimum. What I currently have sort of works but not very well. How can I fix this to do what I want?
Assuming I understand this right, you want your deltaScale to be large when zoomed in, but small when zoomed out. Thereby giving you fast zooming when close and slow zooming when far.
If scaletransform.ScaleX gets smaller as you're zoomed in, try something like this:
var deltaScale = Math.Max(K * 1/(scaleTransform.ScaleX), double.Epsilon);
where K is just some const you mess around with until it feels right.
If scaletransform.ScaleX gets larger as you're zoomed in, then try something like like your log, with a larger coefficient than 1:
var deltaScale = Math.Max(5*Math.Log(scaleTransform.ScaleX), double.Epsilon);
This is my final answer, I wouldn't have been able to come up with it if not for GEEF & Pawel so a big thanks to them for giving it their best shot.
void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
double max = 255;
double min = .005;
double scaler = 4;
var deltaScale = Math.Min(Math.Max(scaler * scaleTransform.ScaleX / max, min),1) * Math.Sign(e.Delta);
double newScale = Math.Max(Math.Min(deltaScale + scaleTransform.ScaleX, max), min);
scaleTransform.ScaleX = newScale;
scaleTransform.ScaleY = newScale;
e.Handled = true;
}
I use a ratio of the current scale to the max to get the slope -- and don't allow it to go to fast (max 1) or too slow, min == the min scale. Works OK.
First of all I would not use value e.Delta for calculations (sign is of course needed). This value is not very reliable - in my experience it was close to random noise. Instead use constant value or some counter like how many times in a row zoom in was invoked and how many times in a row zoom out was invoked.
So my first try (with constant value) would look somehow like this.
void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
lastMousePositionOnTarget = Mouse.GetPosition(grid);
double max = 255;
double min = .005;
var deltaScale = Math.Max(Math.Log(scaleTransform.ScaleX), double.Epsilon);
var delta = e.Delta > 0 ? Math.Max(1 * deltaScale, .5) : Math.Min(1 * deltaScale, -.5);
double newScale = Math.Max(Math.Min((delta / 250d) + scaleTransform.ScaleX, max), min);
scaleTransform.ScaleX = newScale;
scaleTransform.ScaleY = newScale;
System.Diagnostics.Debug.WriteLine(newScale);
e.Handled = true;
}
Version with counters would look something like this.
private double counter=0;
private bool isZoomIn=true;
void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
lastMousePositionOnTarget = Mouse.GetPosition(grid);
if( (e.Delta > 0 && isZoomIn) || (e.Delta < 0 && !isZoomIn))
IncreaseCounter(counter);//is another one in a row
else
counter=1;
isZoomIn = e.Delta > 0;//we set this flag for next time
double max = 255;
double min = .005;
var deltaScale = Math.Max(Math.Log(scaleTransform.ScaleX), double.Epsilon);
var delta = e.Delta > 0 ? Math.Max(counter * deltaScale, .5) : Math.Min(counter * deltaScale, -.5);
double newScale = Math.Max(Math.Min((delta / 250d) + scaleTransform.ScaleX, max), min);
scaleTransform.ScaleX = newScale;
scaleTransform.ScaleY = newScale;
System.Diagnostics.Debug.WriteLine(newScale);
e.Handled = true;
}
Note that I used pseudofunction IncreaceCounter instead of just incrementaion because you may want something else than just linear increase.

Resize a box with the mouse while maintaining the aspect ratio?

I'm trying to create a resizable image overlay (for cropping purposes). It seems pretty easy to resize the overlay if I ignore the aspect ratio, but I can't figure out how to perform a constrained resize that respects the AR. I figure that I obviously can't obey the overlay's "grip" positions (or even borders) unless I force the mouse to follow it, but that seems unnatural, so I'll just have to rely on the mouse gesture (which I don't mind doing).
I can also easily resize the overlay and then force it into the proper dimensions afterwards (like every other question about this topic on this site is about), but it's not very intuitive when using a mouse.
This is sort of what I'm going for:
http://deepliquid.com/projects/Jcrop/demos.php?demo=live_crop
I've written an application like this before but it was browser-based so I used a javascript library. This is a desktop application and I haven't found a suitable library for this.
I've left a lot of details out of this code snippet and simplified some conditions with booleans.
private void pbImage_Paint(object sender, PaintEventArgs e)
{
//Overlay
e.Graphics.FillRectangle(brushRect, overlayRect);
// Grips
e.Graphics.FillRectangle(gripRect, leftTopGrip);
e.Graphics.FillRectangle(gripRect, rightTopGrip);
e.Graphics.FillRectangle(gripRect, leftBottomGrip);
e.Graphics.FillRectangle(gripRect, rightBottomGrip);
AdjustGrips();
base.OnPaint(e);
}
public void AdjustGrips()
{
// The next section only causes the grips to partly obey
// the AR - the rest of the overlay ignores it
if (overlayRect.Height * arWidth <= overlayRect.Width)
overlayRect.Width = overlayRect.Height * arWidth;
else if (overlayRect.Width * arHeight <= overlayRect.Height)
overlayRect.Height = overlayRect.Width * arHeight;
leftTopGrip.X = overlayRect.Left;
leftTopGrip.Y = overlayRect.Top;
rightTopGrip.X = overlayRect.Right - rightTopGrip.Width;
rightTopGrip.Y = overlayRect.Top;
leftBottomGrip.Y = overlayRect.Bottom - leftBottomGrip.Height;
leftBottomGrip.X = overlayRect.Left;
rightBottomGrip.X = overlayRect.Right - rightBottomGrip.Width;
rightBottomGrip.Y = overlayRect.Bottom - rightBottomGrip.Height;
}
private void pbImage_MouseMove(object sender, MouseEventArgs e)
{
Point pt = new Point(e.X, e.Y);
// Details elided
if (e.Button == MouseButtons.Left && mouseinGrip)
{
if (bottomRightIsGripped)
{
newOverlayRect.X = overlayRect.X;
newOverlayRect.Y = overlayRect.Y;
newOverlayRect.Width = pt.X - newOverlayRect.Left;
newOverlayRect.Height = pt.Y - newOverlayRect.Top;
if (newOverlayRect.X > newOverlayRect.Right)
{
newOverlayRect.Offset(-width, 0);
if (newOverlayRect.X < 0)
newOverlayRect.X = 0;
}
if (newOverlayRect.Y > newOverlayRect.Bottom)
{
newOverlayRect.Offset(0, -height);
if (newOverlayRect.Y < 0)
newOverlayRect.Y = 0;
}
pbImage.Invalidate();
oldOverlayRect = overlayRect = newOverlayRect;
Cursor = Cursors.SizeNWSE;
}
// Code for other grips elided
}
AdjustGrips();
pbImage.Update();
base.OnMouseMove(e);
}
// Mouse up and down elided
You have complete control over the new size for the overlay as it drags.
The example link that you've given, is simply selecting a starting point based on the click down, then selecting Max(Abs(pt.x - start.x), Abs(pt.y - start.y)), and basing the crop square off of that.
To use a non square ratio, normalize the distances first.
// given known data
//
// Point start;
// The starting location of the mouse down for the drag,
// or the top left / bottom right of the crop based on if the mouse is
// left/above the starting point
//
// Size ratio;
// The ratio of the result crop
//
// pt = (20)x(-20)
// start = (0),(0)
// ratio = (1)x(2)
var dist = new Point(pt.X - start.X, pt.Y - start.Y);
// "normalize" the vector from the ratio
// normalized vector is the distances with respect to the ratio
// ratio is (1)x(2). A (20)x(-20) is normalized as (20),(-10)
var normalized = new Point(dist.X / ratio.Width, dist.Y / ratio.Height);
// In our (20),(-10) example, we choose the ratio's height 20 as the larger normal.
// we will base our new size on the height
var largestNormal = (Math.Abs(normalized.X) > Math.Abs(normalized.Y)
? Math.Abs(normalized.X) : Math.Abs(normalized.Y);
// The calcedX will be 20, calcedY will be 40
var calcedOffset = (largestNormal * ratio.Width, largestNormal * ratio.Height);
// reflect the calculation back to the correct quarter
// final size is (20)x(-40)
if (distX < 0) calcedOffset.X *= -1;
if (distY < 0) calcedOffset.Y *= -1;
var newPt = new Point(start.X + calcedOffset.X, start.Y + calcedOffset.Y);
Notice that one of the lengths can grow greater than the mouse location, but it will never be less. This will have the effect of the mouse traveling along the edge of the new crop box, and the box maintaining ratio.
I've figured out what was causing the original problems in my code. Unlike a static image resize, the aspect ratio code depends on which grip you're "holding", so putting it in a common location for all cases (eg. when the grip positions are set) will not work. You can easily calculate the size of the what the rect should be on the next update, but the position should be set depending on which grip is being held.
If, for example, you're resizing by holding the top left grip, then the bottom and right sides of the cropping rectangle should remain stationary. If you leave the code the same, then the rectangle resizes correctly, but it moves around the canvas and/or the grips go out of sync with the corners of the rect. There is probably a better way to do this but here's some crude code that works. I've only included code for the bottom right and top left grips to illustrate the differences. Extraneous things like setting the mouse pointer and error checking omitted.
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
Point mousePosition = new Point(e.X, e.Y);
if (e.Button == MouseButtons.Left)
{
// This resizeMode, moveMode and other booleans
// are set in the MouseUp event
if (resizeBottomLeft)
{
// Top and Right should remain static!
newCropRect.X = mousePosition.X;
newCropRect.Y = currentCropRect.Y;
newCropRect.Width = currentCropRect.Right - mousePosition.X;
newCropRect.Height = mousePosition.Y - newCropRect.Top;
if (newCropRect.X > newCropRect.Right)
{
newCropRect.Offset(cropBoxWidth, 0);
if (newCropRect.Right > ClientRectangle.Width)
newCropRect.Width = ClientRectangle.Width - newCropRect.X;
}
if (newCropRect.Y > newCropRect.Bottom)
{
newCropRect.Offset(0, -cropBoxHeight);
if (newCropRect.Y < 0)
newCropRect.Y = 0;
}
// Aspect Ratio + Positioning
if (newCropRect.Width > newCropRect.Height)
{
newCropRect.Height = (int)(newCropRect.Width / ASPECT_RATIO);
}
else
{
int newWidth = (int)(newCropRect.Height * ASPECT_RATIO);
newCropRect.X = newCropRect.Right - newWidth;
newCropRect.Width = newWidth;
}
}
else if (resizeTopRight)
{
// Bottom and Left should remain static!
newCropRect.X = oldCropRect.X;
newCropRect.Y = mousePosition.Y;
newCropRect.Width = mousePosition.X - newCropRect.Left;
newCropRect.Height = oldCropRect.Bottom - mousePosition.Y;
if (newCropRect.X > newCropRect.Right)
{
newCropRect.Offset(-cropBoxWidth, 0);
if (newCropRect.X < 0)
newCropRect.X = 0;
}
if (newCropRect.Y > newCropRect.Bottom)
{
newCropRect.Offset(0, cropBoxHeight);
if (newCropRect.Bottom > ClientRectangle.Height)
newCropRect.Y = ClientRectangle.Height - newCropRect.Height;
}
// Aspect Ratio + Positioning
if (newCropRect.Width > newCropRect.Height)
{
int newHeight = (int)(newCropRect.Width / ASPECT_RATIO);
newCropRect.Y = newCropRect.Bottom - newHeight;
newCropRect.Height = newHeight;
}
else
{
int newWidth = (int)(newCropRect.Height * ASPECT_RATIO);
newCropRect.Width = newWidth;
}
}
else if (moveMode) //Moving the rectangle
{
newMousePosition = mousePosition;
int dx = newMousePosition.X - oldMousePosition.X;
int dy = newMousePosition.Y - oldMousePosition.Y;
currentCropRect.Offset(dx, dy);
newCropRect = currentCropRect;
oldMousePosition = newMousePosition;
}
if (resizeMode || moveMode)
{
oldCropRect = currentCropRect = newCropRect;
// Set the new position of the grips
AdjustGrips();
pictureBox1.Invalidate();
pictureBox1.Update();
}
}
}

How to zoom in using Mouse Position on that image

I have a userControl library, which consists of the main Panel and a PictureBox, I want to make a zoomable PictureBox tool, I zoom in and out using mouseWheel event of the main Panel, the problem that I can't figure out how do I zoom in by the mouse position on the image, so whenever I zoom in, the zoom goes the Top-Left corner of the panel, so how do I fix that?
private double ZOOMFACTOR = 1.15; // = 15% smaller or larger
private int MINMAX = 5;
void picPanel_MouseWheel(object sender, MouseEventArgs e)
{
if (e.Delta > 0)
{
ZoomIn();
}
else
{
ZoomOut();
}
}
private void ZoomIn()
{
if ((picBox.Width < (MINMAX * this.Width)) &&
(picBox.Height < (MINMAX * this.Height)))
{
picBox.Width = Convert.ToInt32(picBox.Width * ZOOMFACTOR);
picBox.Height = Convert.ToInt32(picBox.Height * ZOOMFACTOR);
}
}
private void picBox_MouseEnter(object sender, EventArgs e)
{
if (picBox.Focused) return;
picBox.Focus();
}
Update :
I have tried this, it looks like working, but not exactly as it should be!! Any ideas?
private void ZoomIn()
{
if ((picBox.Width < (MINMAX * this.Width)) &&
(picBox.Height < (MINMAX * this.Height)))
{
picBox.Width = Convert.ToInt32(picBox.Width * ZOOMFACTOR);
picBox.Height = Convert.ToInt32(picBox.Height * ZOOMFACTOR);
Point p = this.AutoScrollPosition;
int deltaX = e.X - p.X;
int deltaY = e.Y - p.Y;
this.AutoScrollPosition = new Point(deltaX, deltaY);
}
}
This is the example of Zoom image on mouse position....
tested verified.
protected override void OnMouseWheel(MouseEventArgs ea)
{
// flag = 1;
// Override OnMouseWheel event, for zooming in/out with the scroll wheel
if (picmap1.Image != null)
{
// If the mouse wheel is moved forward (Zoom in)
if (ea.Delta > 0)
{
// Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
if ((picmap1.Width < (15 * this.Width)) && (picmap1.Height < (15 * this.Height)))
{
// Change the size of the picturebox, multiply it by the ZOOMFACTOR
picmap1.Width = (int)(picmap1.Width * 1.25);
picmap1.Height = (int)(picmap1.Height * 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
picmap1.Top = (int)(ea.Y - 1.25 * (ea.Y - picmap1.Top));
picmap1.Left = (int)(ea.X - 1.25 * (ea.X - picmap1.Left));
}
}
else
{
// Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
if ((picmap1.Width > (imagemappan.Width)) && (picmap1.Height > (imagemappan.Height)))
{
// Change the size of the picturebox, divide it by the ZOOMFACTOR
picmap1.Width = (int)(picmap1.Width / 1.25);
picmap1.Height = (int)(picmap1.Height / 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
picmap1.Top = (int)(ea.Y - 0.80 * (ea.Y - picmap1.Top));
picmap1.Left = (int)(ea.X - 0.80 * (ea.X - picmap1.Left));
}
}
}
}
The problem is that your control is acting like a viewport - the origin is top left, so every time you stretch the image you're doing it from that corner - the upshot is you wind up zooming into the top left corner, you need to offset the stretched image and centre the point the user zoomed in on.
image size: 200,200
user clicks 100,50 and zooms in x2
stretch the image
image size 400,400, and the place the user clicked is now effectively at 200,100
you need to slide the image 100 px left and 50 px up to correct for re-sizing the image
You'll need to override the paint event handler to draw the image offset:
RectangleF BmpRect = new RectangleF((float)(Offset.X), (float)(Offset.Y), (float)(ZoomedWidth), (float)(ZoomedHeight));
e.Graphics.DrawImage(Bmp, ViewPort , BmpRect, GraphicsUnit.Pixel);
Bmp is your image; ViewPort is a Rectangle defined by your pictureBox control
Here is a thread that might help.

Categories