I'm playing a game and I want to draw a line on it. I can't use Graphics.DrawLine because it takes the window/screen coordinates, not the game coordinates.
I want to draw a line from game's pos A to games's pos B. If I put those coordinates in DrawLine, it will take X's and Y's coordinates of the window/screen, not from the game.
In the image below as a example, I want to draw the blue line, but using DrawLine it will draw the grey line.
I want to draw even if the points were not visible in the screen, as I showed in this example. If I move the screen through the game's scenario, the line keeps static since the coordinates of the points A and B keep the same.
Is there a way to do it?
The point here is to convert the world coordinates into screen coordinates. For example, suppose I want to draw a line from point x = 100, y = 600 to point x = 700, y 600 in the world map and the left of my screen is at x = 300 and it's bottom is at y = 300 in world coordinates, then the drawing should start from x = -200, y = 300 to x = 400, y = 300 in screen coordinates which would finish the drawed line at the center of the screen, assuming it's resolution is 800x600.
Since the screen moves in relation to the world scenario, the code for the world to screen method could be:
static int[] WorldToScreen(int worldX, int worldY, int worldX2, int worldY2, int screenLeft, int screenBottom)
{
int screenX = worldX - screenLeft;
int screenY = worldY - screenBottom;
int screenX2 = worldX2 - screenLeft;
int screenY2 = worldY2 - screenBottom;
return new int[] { screenX, screenY, screenX2, screenY2 };
}
Now we just take these converted coordinates and draw on the screen using GDI, DirectX or whatever you want.
PS: Screen (or camera) coordinates are usually related to the center of the screen. I used the edges here just for simplification.
I have a BitMap image where the image contains a black circle. I have found all the pixels from the image that are black which represent the circle and have saved the points into a List.
Where I am lost is finding the center of the circle from coordinates saved in the list. I am thinking that I need to find the diameter of the circle somehow but how do I loop though the pixels to do that to determine that?
One naive approach could be to find the bounding box for the circle.
Seeing as you already have all of the points in a list you can find the top, bottom, left and right.
Assuming the (0,0) is the top left of the co-ordinate system:
The top is the point with min Y.
The bottom is the point with max Y.
The left is the point with min X.
The right is the point with max X.
The center of the bounding box is the center of the circle.
Similarly the width/height of the bounding box is its diameter.
Edit: an alternative solution
Find the mean of all the points in the circle.
This will then give you the center of the circle.
var aggregate = points.Aggregate((point, result) => new Point{ X = point.X + result.X, Y = point.Y + result.Y });
var center = new Point { X = aggregate.X / points.Count, Y = aggregate.Y / points.Count };
This may be a more optimal solution because it could be done while you are scanning the image for the black pixels. Rather than finding the black pixels and then using LINQ.
Circle is a relative term when it comes to images, that's to say, that the shape you are referring to is shown in pixels and may only be representative of a circle.
However to get the midpoint all you need to do is get the extents.
Assuming you have a List<Point>
var left = list.Min(x => x.X);
var right = list.Max(x => x.X);
var top= list.Min(x => x.Y);
var bottom = list.Max(x => x.Y);
Point mid = new Point();
mid.X = left + (right-left) / 2; //calculate mid point x
mid.Y = top + (bottom-top) / 2; //calculate mid point y
Note : Totally untested
Got a quick question about an assignment in c#, wpf. The task is to read in an XML file, containing one description for a root panel, and from then on it follows a fixed pattern where each panel has a number of child panels, each of which can have a number of child patterns. Pretty obvious. I can read it in just fine, and traversing the model is no problem.
The thing is: I have to print these panels on a wpf canvas. The relationship between parent and child panels is the following:
the root panel has X Y coordinates to determine its starting point. Other panels do not
each panel, including the root, has a width and height (not necessarily the same)
each panel (except the root panel) has a property 'attachedToSide' which has a value from 0 to 3. The value signifies the side of the parent the child should be placed against.
When printing a panel against a parent panel, we should always put the '0' side of the panel against the parents' side.
So to demonstrate: look at the draft below. The root panel is printed. The root has 4 children. Taking the panel to the right of the root. That panel would have a property attachedToSide='1' to signify it should be stuck against the 1-side of its parent panel. Now since the rule is that the 0 side is the one that should stick to the parent, we have to flip it 90°.
And the story goes on like that.
Now, printing itself is no problem. Where I'm kinda struggling is calculating the actual positions of each square. The first children of the parent are easy, but from then on, I have to do some calculations to position them correctly, based on the previous panel, and I don't want to take the route of nested if-statements. There probably exists a really simple algorithm to fix this, but since I'm not home in that field, I'm struggling a bit. Can anyone give me a nudge in the right direction?
Detail: doing it all purely mvvm too (just for the heck of it), so 0 code in the code-behind. The shapes are an itemcollection with a custom itemspaneltemplate and itemtemplate, I'm doing the rotation by binding the rotation angle to a property in my model.
user3386109's answer shoved me in the right direction, but I got some extra info about the problem that helped me solve this. Take a look at this example:
The parent is printed with the 0-side down (this is standard). It has 3 children: right, top, left. Now, the parent is the only panel for which I receive an X, Y coordinate. That (X,Y) is the center of the 0-side. Additionally I get the width and height. For all children onward, I then get the width, height, and the side of the parent it is on. Since a child should always be connected to its parent with its own 0-side, I can calculate the childs bottom side very easily using the mod-wrapping formule user3386109 already showed:
bottom side child = (bottom side parent + 6 - parents attachment side) % 4
That's the easiest part. Now, one complication is that each child can be wider or less wide than the parent, higher or less high than the parent. That could complicate matters in terms of calculating the top left (X,Y) point from where we need to draw. One thing I always know however, is that the center point of the parent side to which the child is attached, should be the same point as the child side center that is touching that parent (see the red lines on the picture, that'll tell you what I mean).
Now I used the following approach: I decided to calculate coordinates for the top left point, assuming I could draw the child "upright", so with the bottom being the 0-side. Then, I would just rotate along that point.
Using an example:
Notice the parent panel in black. I know from the XML that I need to attach the child panel on side 1 of the parent. Therefor, I calculate the center point of the parents 1 side from its own 0-side center. I know that will be the center of the childs 0-side, since that is where I need to attach them together. I then calculate the childs top left (X,Y) coordinate, which is simple. After that, I can just rotate the child along its center 0-side point. Then we get the following result, where parent and child are connected in the center, and the child is rotated the right way as well.
In short, it's always the same approach:
take the center of the 0-side of a parent (which we will store in each panel object)
relative to that point, calculate where the 0-side center of the child will be
if we have that point, calculate the childs top left point, so we know from where to draw
rotate the child along its 0-side center point (we know the rotation degrees from the side that is at the bottom)
Done. One extra complication was that each child received a certain "offset" value. In short, this is a positive or negative value indicating to push the child to a certain direction (still attached to the parent). This problem is easily solved with just adjusting the right coordinate.
Now, to calculate all the points, it's obvious that it all depends from parent rotation, own rotation and so on. When inspecting the variations, I came to the conclusion that a lot of formulas looked suspiciously similar. The total explanation would require a lot of typing, and frankly I can't be bothered. However: here is the code that creates a child rectangle based on a given parent rectangle, child width, height which side of the parent it should be on, and the offset.
private static Rectangle CreateRectangle(string name, float width, float height, int sideOfParent, float offset, Rectangle parent)
{
Rectangle rect = new Rectangle() { Name = name, Width = width, Height = height, Offset = offset };
// Calculate which side should be at the bottom, depending on the bottom side of the parent,
// and which side of the parent the new rectangle should be attached to
rect.BottomSide = (parent.BottomSide + 6 - sideOfParent) % 4;
// Calculate the bottom mid point of the rectangle
// If | bottom side parent - bottom side child | = 2, just take over the mid bottom point of the parent
if (Math.Abs(parent.BottomSide - rect.BottomSide) == 2) { rect.MidBottom = parent.MidBottom; }
else
{
// Alternative cases
// Formulas for both bottom side parent = 0 or 2 are very similar per bottom side child variation (only plus/minus changes for Y formulas)
// Formulas for both bottom side parent = 1 or 3 are vary similar per bottom side child variation (only plus/minus changes for X formulas)
// Therefor, we create a "mutator" 1 / -1 if needed, to multiply one part of the formula with, so that we either add or subtract
Point parPoint = parent.MidBottom;
if (parent.BottomSide % 2 == 0)
{
// Parent has 0 or 2 at the bottom
int mutator = (parent.BottomSide == 0) ? 1 : -1;
switch (rect.BottomSide % 2 == 0)
{
case true: rect.MidBottom = new Point(parPoint.X, parPoint.Y - (mutator * parent.Height)); break;
case false:
if (rect.BottomSide == 1) rect.MidBottom = new Point(parPoint.X + (parent.Width / 2), parPoint.Y - (mutator * (parent.Height / 2)));
else rect.MidBottom = new Point(parPoint.X - (parent.Width / 2), parPoint.Y - (mutator * (parent.Height / 2)));
break;
}
}
else
{
// Parent has 1 or 3 at the bottom
int mutator = (parent.BottomSide == 1) ? 1 : -1;
switch (rect.BottomSide % 2 == 1)
{
case true: rect.MidBottom = new Point(parPoint.X + (mutator * parent.Height), parPoint.Y); break;
case false:
if (rect.BottomSide == 0) rect.MidBottom = new Point(parPoint.X + (mutator * (parent.Height / 2)), parPoint.Y - (parent.Width / 2));
else rect.MidBottom = new Point(parPoint.X + (mutator * (parent.Height / 2)), parPoint.Y + (parent.Width / 2));
break;
}
}
}
return rect;
}
An example of a real life result of all that:
As I already mentioned, the actual drawing just happens by putting an ItemCollection on a standard grid, binding to the collection of rectangles and setting an appropriate ItemsPanel and ItemTemplate, standard WPF there.
The model for each panel consists of
X,Y coordinates
W,H dimensions
R rotation value (one of four choices)
C a list of up to four children
A attached to side
The rotation value can be encoded as: an angle in degrees, an angle in radians, or just a number between 0 and 3. I would choose the 0 to 3 encoding, where the number represents the side at the bottom. So the root panel has a rotation value of 0.
You are given a complete set of parameters (ignoring A) for the root panel. For all the other panels, you have parameters W,H,C,A but you're missing X,Y,R. So your task is to compute X,Y,R for each panel to complete the model.
Computing the rotation value for the child
Consider the following cases which show the four possible children for each orientation of the parent:
The sequences below the drawings are the child R values, ordered by the child's A value. For example, if the parentR is 0, and the childA is 0, the childR is 2. If parentR is 0 and childA is 1, childR is 1, etc.
First thing to note is that the first number in each sequence is the number at the top of the parent. Second thing to note is that the numbers decrease by 1 (as the childA increases), wrapping to 3 after 0.
So if you take the parent's R value, add 6, and subtract the child's A value, and then apply modulo 4, you get the child's rotation value:
childR = (parentR + 6 - childA) % 4;
Computing the Y value for the child
Note that the location of the child depends primarily on the child's rotation value. If the childR is 0, the child is above the parent. If childR is 1, the child is to the right, etc.
So if the childR is odd, the child has the same Y value as the parent. If the childR is 0, then the childY is the parentY adjusted by the child's height. When the childR is 2, then the childY is the parentY adjusted by either the parent width (parentR odd), or the parent's height (parentR even).
Which results in an if-else chain that looks like this:
if ( childR % 2 ) // odd values, child left or right
childY = parentY
else if ( childR == 0 ) // child above
childY = parentY - childH
else if ( parentR % 2 ) // odd values, adjust by parent width
childY = parentY + parentW
else // even values, adjust by parent height
childY = parentY + parentH
(I'm assuming here that the X,Y coordinate represents the location of the upper-left corner of the panel, and positive Y is down.)
The X calculations are similar to the Y calculations.
So you start at the root, compute X,Y,R for the children of the root, and recursively compute those parameters for each child's children.
That completes your model. Displaying the panels on the view is easy enough, since you have X,Y,W,H,R for each panel.
You could go with a recursive function that print all the children of a panel and you pass said panel as parameters so you have an easy access to the position, the transform etc... Something in the lines of :
public void PrintSelfAndChildren(Panel parent)
{
ApplyTransform();
PrintPanel();
foreach(var child in parent.children)
{
PrintSelfAndChildren(child);
}
}
I may be asking the wrong question, but what I need is to add a "Guide Line" to my windows form chart. In other words I have a chart with a simple data series and I need to draw a line on the y axis at the passing score, or 80%. I don't want to add a second series as the first series has an undetermined number of data points. Is there a simple way to simply draw one line on the y axis?
The dashed line below is what I am shooting for(it does not need the arrows).
100|
|
90|
| o
80|<----------------------->
|
70| o o
|
60| o
| o o
50|o o
|_________________________
1 2 3 4 5 6 7 8 9
Apologies for repeating Don Kirkby's answer, but I don't have the rep to add a comment yet.
Using HorizontalLineAnnotation you can set the ClipToChartArea which will limit the extent of the line to within the chart, to solve the problem you mentioned.
ChartArea area = ...;
var line = new HorizontalLineAnnotation();
line.IsInfinitive = true; // make the line infinite
line.ClipToChartArea = area.Name;
line.LineDashStyle = ChartDashStyle.Dash;
Assuming your y-axis holds values on a scale of 0..1 then you can attach the line to the Y-Axis using line.AxisY = area.AxisY, which results in its position being interpreted as an axis value, then set line.Y = 0.8; to attach at the 80% position.
You can add a StripLine.
Use StripWidth property to set line position:
var series = chart1.Series[0]; //series object
var chartArea = chart1.ChartAreas[series.ChartArea];
chartArea.AxisY.StripLines.Add(new StripLine
{
BorderDashStyle = ChartDashStyle.Dash,
BorderColor = Color.DarkBlue,
StripWidth = 80//Here is your y value
});
UPDATE: Previous version of this answer used Interval instead of StripWidth. As #dthor correctly pointed out in the comments setting the Interval will draw a repeated strip line. In the example above, Interval is set to 0 by default.
I've never used charts, but HorizontalLineAnnotation sounds promising.
You can add dots to the frame with a loop that has for example 900 loop for 1 to 9 values. For each loop the compiler will calculate the value and left a dot for that perimeter and another for the next one so it will looks like a line of a equation :)
Assuming I have a form and paint an oval on it. I then want to take a control (such as a picturebox) and (while keeping the top left corner of the control exactly on the line) I want to move the control pixel by pixel following the drawn oval.
Basically I want to calculate the Top/Left point for each position/pixel in my oval. I know its a basic formula but cant for the life of me remember what its called or how its accomplished.
Anyone care to help?
double step=1.0; // how fast do you want it to move
double halfWidth=100.0; // width of the ellipse divided by 2
double halfHeight=50.0; // height of the ellipse divided by 2
for (double angle=0; angle<360; angle+=step)
{
int x=(int)halfWidth * Math.Cos(angle/180*Math.PI);
int y=(int)halfHeight * Math.Sin(angle/180*Math.PI);
pictureBox.TopLeft=new Point(x,y);
}
EDIT:
Now, if you are about to ask why isn't it moving if you write it like that - you'll have to add message loop processing to it, in form of:
Application.DoEvents();
which you will place inside the loop.
Ellipse canonical form:
x-x^2/a^2 + y^2/b^2 = 1
where a = Xradius and b = Yradius. So, for example, if you want the top-left point of a rectangle on the bottom side of an ellipse:
y = Sqrt((1-x^2/a^2)*b^2)
upd: to move an ellipse to specified point XC,YC, replace each x with (x-XC) and (y-YC). so if you're (in C#) drawing an ellipse in a rectangle, so XC = rect.X + a YC = rect.Y + b and the final equation is y = Sqrt((1 - Pow(x - rect.X - rect.Width / 2, 2) * Pow(rect.Height / 2, 2)) + rect.Y + rect.Height / 2... seems to be correct)