Trouble multiplying a double with an int - c#

So basically i'm making healthbars for my enemies in my C# XNA game. I am getting the percentage of health left so e.g. 80/100 = 0.8 = 80% so my game knows how big to draw the green portion of the health bar. So if there is 80% hp it will draw the healthbar to 80%. The problem I am having is as soon as the health goes down from 100, the percentage automatically gets dropped to 0.0/0% and nothing is drawn. Here is my code:
//calculate the width of the green portion of healbar
//zDay.Game1.hp_top.Width = Original size of image (35px)
float size = (hp/max_hp) * zDay.Game1.hp_top.Width;
//draw percentage of health
sb.DrawString(zDay.Game1.CourierNew, (hp/max_hp).ToString(), new Vector2(50, 80), Color.Black);
//draw size of green portion
sb.DrawString(zDay.Game1.CourierNew, (size).ToString(), new Vector2(50, 50), Color.Black);
//draw green portion of health bar
Rectangle bg = new Rectangle(x - 20, y - 30, (int)size, zDay.Game1.hp_top.Height);
sb.Draw(zDay.Game1.hp_top, bg, Color.White);
Any ideas as to why this is happening?

I would guess that the issue isn't with multiplication, but division; the statement:
(hp/max_hp)
is most likely being evaluated to zero (since you're diving by int's (I assume) and the result will be between 0 and 1, or in Integer terms, 0).
Try using
((float)hp/(float)max_hp)
Instead.

Related

How can you find the radius from a 2D canvas width in Unity?

I have an object that spins perfectly around a circle non-dynamically with the following code.
Radius = 4.7f;
angle += .25f;
float x = Radius * Mathf.Cos(angle);
float y = Radius * Mathf.Sin(angle);
float z = 0;
ball.transform.position = new Vector3(x,y,z);
I wish to get a radius of the circle dynamically and have the circle as a UI image inside of a canvas. The width of the UI Image is 125 and can be seen by the blue arrows. The logic I though that would work would be to have radius = width/2.
125/2=62.5, which is not close 4.7f. What else am I not taking into account? Scaling or pixel image sizing?
Any help is appreciated!
First, you need to account for the scale of the image (as #derHugo suggested in comment)
But you also need to account for the fact that your canvas will also apply a scale on the units, depending on how you set it.
A good solution is to use RectTransform.GetWorldCorners. This will allow you to know the world coordinate of your circle without having to do the scale computation yourself.
Vector3[] v = new Vector3[4];
rt.GetWorldCorners(v);
var radius = v[0].x - v[2].x; // Not sure about the order of the corners, the indices might be wrong
Another comment: your code is framerate dependent: you add 0.25 degrees every frame. Meaning that the FPS will impact the speed of the rotation (with 60 fps, you will rotate 0.25 * 60 = 15 degrees per seconds. But with 30 FPS you will rotate 0.25 * 30 = 7.5 degrees per seconds)
To be framerate independent, you can do a simple computation using Time.deltaTime (this is the time the last frame took to completely render)
Instead of angle += .25f;
Do that:
var anglePerSeconds = 15; // Choose the angular speed you want
angle += anglePerSeconds * Time.deltaTime;

How does one set an image as or along a chart axis?

I am trying to use a colored spectrum strip as an axis for a chart. The idea is to match the color on the image with its associated wavelength along the x-axis at the bottom. The strip needs to change in size to match changes of the chart area and expand and contract sections to match scroll-zooming in the chart area.
I have tried using image annotations but as the chart area changes, the annotation dimensions remain fixed. Also, the scroll zooming that focuses in on mouse position obviously has no effect on the annotation.
The approach that came closest was using the image as a background for the chart area. This automatically scaled the image as the chart area changed but scroll-zooming has no effect on the background image. Also, it would be ideal to have the background clear so as to avoid obscuring data plot points. I can edit the image to have a large transparent section and only a colored strip at the bottom but even then, that strip could obscure lower intensity data points.
Spectrum as annotation and background:
Annotation not scaling, background scales well:
Both annotation and background not scaling with zooming:
This is a nice idea.
The simplest way is to draw the image in a Paint event of the Chart, maybe PrePaint.
Let's go to work.. We will use the DrawImage overload that allows us zooming as well as cropping. For this we need two rectangles.
The first challenge is to always get the correct target rectangle.
For this we need to convert the InnerPlotPosition from relative positions to absolute pixels.
These two functions will help:
RectangleF ChartAreaClientRectangle(Chart chart, ChartArea CA)
{
RectangleF CAR = CA.Position.ToRectangleF();
float pw = chart.ClientSize.Width / 100f;
float ph = chart.ClientSize.Height / 100f;
return new RectangleF(pw * CAR.X, ph * CAR.Y, pw * CAR.Width, ph * CAR.Height);
}
RectangleF InnerPlotPositionClientRectangle(Chart chart, ChartArea CA)
{
RectangleF IPP = CA.InnerPlotPosition.ToRectangleF();
RectangleF CArp = ChartAreaClientRectangle(chart, CA);
float pw = CArp.Width / 100f;
float ph = CArp.Height / 100f;
return new RectangleF(CArp.X + pw * IPP.X, CArp.Y + ph * IPP.Y,
pw * IPP.Width, ph * IPP.Height);
}
With these numbers setting the destination rectangle is as simple as:
Rectangle tgtR = Rectangle.Round(new RectangleF(ipr.Left, ipr.Bottom - 15, ipr.Width, 15));
You can chose a height as you like..
The next challenge is the source rectangle.
Without zooming it would simply be:
Rectangle srcR = new Rectangle( 0, 0, bmp.Width, bmp.Height);
But for zooming and panning we need to scale it; for this we can use the x-axis and the ScaleView's Minimum and Maximum values.
We calculate factors for the first and last spot on the axis:
double f1 = ax.ScaleView.ViewMinimum / (ax.Maximum - ax.Minimum);
double f2 = ax.ScaleView.ViewMaximum / (ax.Maximum - ax.Minimum);
now we get the source rectangle maybe like this:
int x = (int)(bmp.Width * f1);
int xx = (int)(bmp.Width * f2);
Rectangle srcR = new Rectangle( x, 0, xx - x, bmp.Height);
Let's put it together:
private void chart_PrePaint(object sender, ChartPaintEventArgs e)
{
// a few short names
Graphics g = e.ChartGraphics.Graphics;
ChartArea ca = chart.ChartAreas[0];
Axis ax = ca.AxisX;
// pixels of plot area
RectangleF ipr = InnerPlotPositionClientRectangle(chart, ca);
// scaled first and last position
double f1 = ax.ScaleView.ViewMinimum / (ax.Maximum - ax.Minimum);
double f2 = ax.ScaleView.ViewMaximum / (ax.Maximum - ax.Minimum);
// actual drawing with the zooming overload
using (Bitmap bmp = (Bitmap)Bitmap.FromFile(imagePath))
{
int x = (int)(bmp.Width * f1);
int xx = (int)(bmp.Width * f2);
Rectangle srcR = new Rectangle( x, 0, xx - x, bmp.Height);
Rectangle tgtR = Rectangle.Round(
new RectangleF(ipr.Left , ipr.Bottom - 15, ipr.Width, 15));
g.DrawImage(bmp, tgtR, srcR, GraphicsUnit.Pixel);
}
}
A few notes:
Of course I would recomend to use an Image resource instead of always loading from disk!
The Drawing will always overlay the data points and also the grids. You can either..
choose a different minimum to make room
make the image smaller
move it below the x-axis labels
make the image semi-transparent
make the x-axis so fat that it can hold the image strip : ax.LineWidth = 10
For the latter solution you would want to offset the y-position depending on the zoom state. Quick and dirty: int yoff = (ax.ScaleView.IsZoomed ? 12 : 5);. To avoid black stripes also make the axis Transparent or chart.BackColor..
Update:
You can also revert to using a StripLine. It can scale its BackgroundImage and you would have to create a suitable image whenever changing the scaleview, i.e. when zooming or panning. For this much of the above code would be used to create the new images. See this post for examples of adding and replacing varying NamedImage to a Chart! (The relevant portion is close to the end about the marker images!)
In fact I found that way to be the best solution and have added a second answer.
Alternative and recommended solution:
I dabbled with the last option I mentioned in my other answer and found it to be rather nice; it is similarily extensive, so I decided to post a second answer.
The idea is to use a StripLine with just the right BackgroundImage.
The advantage is that is will display nicely under all chart elements and never draw over the axis, grid, datapoints or conflict with the zoom tools.
Since the StripLine must be updated repeatedly I put it in a function:
Here is the function; it makes use of the same two helper functions to calculate pixel positions as the other answer does..:
void updateStripLine(Chart chart, ChartArea ca, string name)
{
// find our stripline; one could pass in a class level variable as well
StripLine sl = ca.AxisY.StripLines.Cast<StripLine>()
.Where(s => s.Tag.ToString() == name).FirstOrDefault();
if (sl != null) // either clean-up the resources..
{
var oldni = chart.Images.FindByName(name);
if (oldni != null)
{
oldni.Image.Dispose();
chart.Images.Remove(oldni);
oldni.Dispose();
}
}
else // or, create the line
{
sl = new StripLine();
sl.Tag = name;
ca.AxisY.StripLines.Add(sl);
}
ca.RecalculateAxesScale();
RectangleF ipr = InnerPlotPositionClientRectangle(chart, ca);
Axis ax = ca.AxisX;
Axis ay = ca.AxisY;
double f1 = ax.ScaleView.ViewMinimum / (ax.Maximum - ax.Minimum);
double f2 = ax.ScaleView.ViewMaximum / (ax.Maximum - ax.Minimum);
Bitmap b0 = (Bitmap)chart.Images["spectrum"].Image;
int x = (int)(b0.Width * f1);
int xx = (int)(b0.Width * f2);
Rectangle srcR = new Rectangle( x, 0, xx - x, b0.Height);
Rectangle tgtR = Rectangle.Round(new RectangleF(0,0, ipr.Width , 10));
// create bitmap and namedImage:
Bitmap bmp = new Bitmap( tgtR.Width, tgtR.Height);
using (Graphics g = Graphics.FromImage(bmp))
{ g.DrawImage(b0, tgtR, srcR, GraphicsUnit.Pixel); }
NamedImage ni = new NamedImage(name, bmp);
chart.Images.Add(ni);
sl.BackImageWrapMode = ChartImageWrapMode.Scaled;
sl.StripWidth = ay.PixelPositionToValue(0) - ay.PixelPositionToValue(12);
sl.Interval = 100; // make large enough to avoid another sLine showing up
sl.IntervalOffset = 0;
sl.BackImage = name;
}
Much of the comments and links apply, especially wrt to the NamedImage we use for the StripLine.
A few more notes:
I use one of the (four) axis conversion functions, PixelPositionToValue to calculate a pixel height of 12px; the StripLine takes values, so I use two pixel values to get the right difference value.
To identify the StripLine I use the Tag property. Of course the Name property would be much more natural, but it is read-only. No idea why?!
The function is called from the AxisViewChanged, the Resize event and also the the PrePaint event; this makes sure it will always be called when needed. To avoid invalid calls from the PrePaint there I do it like this: if (ay.StripLines.Count == 0) updateStripLine(chart, ca, "sl"); Of course you should adapt if you use other StripLines on this axis..
The code makes use of the same image as before; but I have put it into a first NamedImage called spectrum. This would be an option in the 1st answer as well.
NamedImage spectrum = new NamedImage("spectrum", Bitmap.FromFile(imagePath);
chart.Images.Add(spectrum);
It also makes sure to dispose of the old images properly, I hope..

How to correct change position of a Object in Unity?

I currently have a code to generate a 2D array into GameObjects, my currently properties are:
Width: 1920
Height: 1080
Camera Size: 540
2D Array size: 30x16
Each block represents 64x64 pixels
My problem is that I position my blocks from -960 to 960 (1920 width) and -540 to 540. But when I do that, the block that was supposed to be at the first position gets wrongly put on:
So from the vision of the camera, probably most people would figure out that I have to put half of the block width added to its x position, and half of blocks height decreased from its y position, but when I try that, this occour:
Code:
float height = Camera.main.orthographicSize *2f;
float width = height / (float)Screen.height * (float)Screen.width;
Destroy(map);
map = new GameObject();
print("W: "+width+". H: "+height);
lastH=height;
lastW=width;
for (int i=0; i<30; i++) {
for (int j=0; j<16; j++) {
if(mapa[i,j]>0){
GameObject aux = objetos[mapa[i,j]-1];
float wX=aux.transform.localScale.x,hY=aux.transform.localScale.y;
print ("wX: "+wX+", hY: "+hY);
float unidadeW = 2*(float)lastW/(float)(20*wX);
float unidadeH = 2*(float)lastH/(float)(20*hY);
//aux.transform.localScale = new Vector3(unidadeW,unidadeH,0);
GameObject t = (GameObject)Instantiate(aux, new Vector2(j*wX*0.64f-width/2,-i*hY*0.64f+height/2),Quaternion.identity);
t.transform.parent = map.transform;
}
}
}
OBS: There are several stuff that I don't use anymore because i'm changing the code, so don't mid it.
And I ask, WHYYYY?
The correct way to make a distance between two units is their diagonal, see the image below:
Doesn't matter which is your orientation point, if its top left or center, the distance between the blocks will be √2*side, so the correct distance between mine was around 22, and I can find it by doing this equation:
sqrt(2)/2*32=22.62 (the correct distance between two tiles of 64x64), instead of using 64+ at the last x and 64+ at the last y the right way to do is by adding 22.62 at both multiplications.

Resize drawn rectangle to fit original image

I'm developing an application to manipulate images scanned on a wide-image scanner. These images are shown as a ImageBrush on a Canvas.
On this Canvas they can a make Rectangle with the mouse, to define an area to be cropped.
My problem here is to resize the Rectangle according to the original image size, so that it crops the exact area on the original image.
I've tried many things so far and it's just sqeezing my brain, to figure out the right solution.
I know that I need to get the percent that the original image is bigger than the image shown on the canvas.
The dimentions of the original image are:
h: 5606
w: 7677
And when I show the image, they are:
h: 1058,04
w: 1910
Which gives these numbers:
float percentWidth = ((originalWidth - resizedWidth) / originalWidth) * 100;
float percentHeight = ((originalHeight - resizedHeight) / originalHeight) * 100;
percentWidth = 75,12049
percentHeight = 81,12665
From here I can't figure how to resize the Rectangle correctly, to fit the original image.
My last approach was this:
int newRectWidth = (int)((originalWidth * percentWidth) / 100);
int newRectHeight = (int)((originalHeight * percentHeight) / 100);
int newRectX = (int)(rectX + ((rectX * percentWidth) / 100));
int newRectY = (int)(rectY + ((rectY * percentHeight) / 100));
Hopefully someone can lead me in the right direction, because i'm off track here and I can't see what i'm missing.
Solution
private System.Drawing.Rectangle FitRectangleToOriginal(
float resizedWidth,
float resizedHeight,
float originalWidth,
float originalHeight,
float rectWidth,
float rectHeight,
double rectX,
double rectY)
{
// Calculate the ratio between original and resized image
float ratioWidth = originalWidth / resizedWidth;
float ratioHeight = originalHeight / resizedHeight;
// create a new rectagle, by resizing the old values
// by the ratio calculated above
int newRectWidth = (int)(rectWidth * ratioWidth);
int newRectHeight = (int)(rectHeight * ratioHeight);
int newRectX = (int)(rectX * ratioWidth);
int newRectY = (int)(rectY * ratioHeight);
return new System.Drawing.Rectangle(newRectX, newRectY, newRectWidth, newRectHeight);
}
I think the only reliable option is to let your users zoom in to the image (100% or higher zoom level) and make a selection on part of the image. This way they can make an exact pixel-based selection. (Assuming that the purpose of your selection rectangle is to select part of an image.)
Your problem now is that you're using floating-point calculations because of the 75% zoom level and rounding errors will make your selection rectangles inaccurate. No matter what you do, when you try to make a selection on a shrinked image, you're not selecting exact pixels - you're selecting parts of pixels as you resize your rectangle. Since a partial pixel cannot be selected, the selection edges will be rounded up or down so you either select one pixel too many or one pixel too few in a given direction.
Another issue that I just noticed is that you distort your image - horizontally it's 75% zoom, vertically it's 81%. This makes it even harder for users because the image will be smoothed differently in the two directions. Horizontally 4 original pixels will be interpolated on 3 output pixels; vertically 5 original pixels will be interpolated on 4 output pixels.
You are actually doing a form of projection. Don't use percentages, just use the ratio between 5606 and 1058,4 = ~5.30. When the user drags the rectangle, reproject it which is selectedWidth * 5606/1058.4.

Drawing an I-Beam based on two points

I have two Point structures and I need to draw an I-Beam based on those points, where each point represents the cross-section on either side of the I-Beam. The width of the end caps should be fixed and arbitrary.
Basically I need to draw three lines. First I'll DrawLine(Point1, Point2), then I need the math to figure out how to draw the next two lines on perpendicular angles so that they are centered on Point1 and Point2.
The image below shows what I need to draw based on the center line. However, this line can be at any angle. The Point1 and Point2 that connect the line can be anywhere in a 2D space.
You can try playing around with LineCaps:
protected void DrawIBeam(Graphics g, Point fromPoint, Point toPoint)
{
using (GraphicsPath hPath = new GraphicsPath())
{
hPath.AddLine(new Point(-5, 0), new Point(5, 0));
CustomLineCap myCap = new CustomLineCap(null, hPath);
myCap.SetStrokeCaps(LineCap.Round, LineCap.Round);
using (Pen myPen = new Pen(Color.Black, 2))
{
myPen.CustomStartCap = myCap;
myPen.CustomEndCap = myCap;
g.DrawLine(myPen, fromPoint, toPoint);
}
}
}
and call it:
DrawIBeam(e.Graphics, new Point(10, 10), new Point(60, 60));
From CustomLineCap Class
Assuming a width that's half the width of the I part of the I beam, first you find the slope of the first line you drew.
Next, you take the negative inverse of the slope, and draw a line from Point1 of length width in both directions. That's why width is half of the width you want to draw.
Finally you draw a line from Point 2 of length width in both directions.
Here's the mathematical formula for drawing a perpendicular line.

Categories