Fastest algorithm to edge detect between two simple rectangles. - c#

Given two simple, rectangles:
class Rectangle
{
int x;
int y;
int width;
int height;
}
Rectangle a;
Rectangle b;
and the following enumeration:
[Flags]
enum Edges
{
None,
Top,
Bottom,
Left,
Right,
Inside,
}
What is the quickest way to detect the edges on rectangle a which are collided with by rectangle b?
Edges e = EdgeDetect(a, b);

public Edges DetectEdge(Rect A, Rect B) {
rectC = rectA.Intersect(rectB);
if(rectC.IsEmpty) return Edges.None;
Edge edge = Edges.Inside;
if(rectA.X+rectA.Width == rectB.X || rectA.X == rectB.X){
edge = Edges.Left;
}
if(rectA.Y+rectA.Height == rectB.Y || rectA.Y == rectB.Y){
edge = edge | Edges.Top;
}
if(rectA.X == rectB.X + rectB.Width
|| rectA.X + rectA.Width == rectB.X + rectB.Width){
edge = edge | Edges.Right;
}
if(rectA.Y == rectB.Y + rectB.Heigth
|| rectA.Y + rectA.Height == rectB.Y + rectB.Height){
edge = edge | Edges.Bottom;
}
return edge;
}

First of all, you have to defines explicitly values of your enum in order to have flags working correctly. In you case Left == Top + Bottom + None. Here is a possible declaration :
[Flags]
public enum Edges
{
None = 0,
Top = 1,
Bottom = 2,
Left = 4,
Right = 8,
Identical = Top + Bottom + Left + Right,
Inside = 16,
Covers = 32
}
Next, a possible implementation of edge collision detection. Note that I use the builtin System.Drawing.Rectangle instead of rewriting the class. The immediate advantage is the availability of the Intersect method. :
public static Edges DetectEdgesCollision(Rectangle a, Rectangle b)
{
var result = Edges.None;
if (a == b) return Edges.Identical;
b.Intersect(a);
if (b.IsEmpty) return Edges.None;
if (a == b) return Edges.Covers;
if (a.Top == b.Top && (a.Right >= b.Right && a.Left<=b.Left ))
result |= Edges.Top;
if (a.Bottom == b.Bottom && (a.Right >= b.Right && a.Left<=b.Left ))
result |= Edges.Bottom;
if (a.Left == b.Left && (a.Bottom >= b.Bottom && a.Top <= b.Top))
result |= Edges.Left;
if (a.Right == b.Right && (a.Bottom >= b.Bottom && a.Top <= b.Top))
result |= Edges.Right;
return result == Edges.None ? Edges.Inside : result;
}
Here is a set of tests that validates this implementation :
[TestMethod]
public void RectDoesNotIntersect()
{
var a = new Rectangle(0, 0, 10, 10);
var b = new Rectangle(20, 20, 10, 10);
var result = Program.DetectEdgesCollision(a, b);
Assert.AreEqual<Edges>(Edges.None, result);
}
[TestMethod]
public void RectAreNested()
{
var a = new Rectangle(0, 0, 30,30);
var b = new Rectangle(10, 10, 10, 10);
var result = Program.DetectEdgesCollision(a, b);
Assert.AreEqual<Edges>(Edges.Inside, result);
}
[TestMethod]
public void RectCollidesOnTopAndLeft()
{
var a = new Rectangle(10, 10, 10, 10);
var b = new Rectangle(0, 0, 10, 10);
var result = Program.DetectEdgesCollision(a, b);
Assert.AreEqual<Edges>(Edges.Left | Edges.Top, result);
}
[TestMethod]
public void RectCollidesOnBottom()
{
var a = new Rectangle(0, 0, 20, 20);
var b = new Rectangle(10, 10, 5, 50);
var result = Program.DetectEdgesCollision(a, b);
Assert.AreEqual<Edges>(Edges.Bottom , result);
}
[TestMethod]
public void RectAreIdenticals()
{
var a = new Rectangle(10, 10, 10, 10);
var b = new Rectangle(10, 10, 10, 10);
var result = Program.DetectEdgesCollision(a, b);
Assert.AreEqual<Edges>(Edges.Identical, result);
}
[TestMethod]
public void RectBCoversA()
{
var a = new Rectangle(10, 10, 10, 10);
var b = new Rectangle(0, 0, 30, 30);
var result = Program.DetectEdgesCollision(a, b);
Assert.AreEqual<Edges>(Edges.Covers, result);
}

Related

Custom Calendar through Graphics & Paint Event

This is a little project for myself, just learning graphics for WinForms and so on.
The goal is to program my own Calendar control which will meet some very specific requirements.
Here's how I handle the Paint Event (very hacky atm):
private void XCalendar_Paint(object sender, PaintEventArgs e)
{
// Pen
var pen_border = new Pen(BorderColorGrid);
var pen_row_border = new Pen(BorderColorGridRow);
var pen_column_border = new Pen(BorderColorGridColumn);
//var grid_pen = new Pen(Color.Black);
var bez_pen = new Pen(Color.FromArgb(192, 241, 231));
var description_row_height = 30;
var description_column_width = 65;
// Calculate Full
int full_width = this.Width - 1 - description_column_width;
int full_height = this.Height - 1 - description_row_height;
if (this.Users.Count == 0)
{
var s = "Keine Benutzer gefunden.";
var f = new Font(new FontFamily("Trebuchet MS"), 12f);
var s_size = e.Graphics.MeasureString(s, f);
var p = new Point((int)(full_width / 2) - (int)(s_size.Width / 2) + 30, 30);
e.Graphics.DrawString(s, f, new SolidBrush(Color.FromArgb(180, 180, 180)), p);
return;
}
// Calculate Rows
int row_count = 0;
switch (_calendarView)
{
case CalendarViews.Day:
row_count = 1;
break;
case CalendarViews.Week:
row_count = 7;
break;
case CalendarViews.Month:
row_count = DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month);
break;
}
int row_height = full_height / row_count;
// Calculate Columns
int col_count = Users.Count;
int col_width = full_width / col_count;
// Draw Description Column BackColor
var desc_col_rect = new Rectangle(new Point(1, description_row_height + 1), new Size(description_column_width - 1, full_height - 1));
e.Graphics.FillRectangle(new SolidBrush(bez_pen.Color), desc_col_rect);
// Draw Border
var rect = new Rectangle(new Point(0, 0), new Size(this.Width - 1, this.Height - 1));
e.Graphics.DrawRectangle(pen_border, rect);
var desc_font = new Font(new FontFamily("Trebuchet MS"), 9f);
var desc_brush = new SolidBrush(Color.Black);
// Draw Columns
var col_x = description_column_width;
for (int x = 0; x < col_count; x++)
{
var rect_col = new Rectangle(new Point(col_x, 1), new Size(col_width, description_row_height));
var user = Users[x];
var short_name = user.Shortname;
var desc_size = e.Graphics.MeasureString(short_name, desc_font);
e.Graphics.FillRectangle(new SolidBrush(user.Color), rect_col);
e.Graphics.DrawLine(pen_column_border, new Point(col_x, 0), new Point(col_x, full_height + description_row_height));
e.Graphics.DrawString(short_name, desc_font, desc_brush, new Point(col_x + (int)(col_width / 2 - desc_size.Width / 2), (int)(description_row_height / 2 - desc_size.Height / 2)));
col_x += col_width;
}
// Draw Rows
var row_y = description_row_height;
DateTime dt = this.StartDateTime == null ? DateTime.Now : StartDateTime;
switch (this._calendarView)
{
case CalendarViews.Week:
dt = DateTimeExtensions.StartOfWeek(dt, DayOfWeek.Monday);
break;
case CalendarViews.Month:
dt = new DateTime(dt.Year, dt.Month, 1);
break;
}
for (int x = 0; x < row_count; x++)
{
var rect_row = new Rectangle(new Point(1, row_y), new Size(description_column_width - 1, row_height));
var s = new DateTimeFormatInfo().GetShortestDayName(dt.DayOfWeek) + ", " + dt.ToString("dd.MM");
var cur_row_fill_color = Color.Transparent;
if (dt.Date == DateTime.Now.Date)
{
cur_row_fill_color = this.FillColorCurrentDateRow;
}
else if (dt.DayOfWeek == DayOfWeek.Saturday || dt.DayOfWeek == DayOfWeek.Sunday)
{
cur_row_fill_color = this.FillColorWeekendDays;
}
var s_size = e.Graphics.MeasureString(s, desc_font);
e.Graphics.FillRectangle(new SolidBrush(cur_row_fill_color), rect_row);
e.Graphics.DrawLine(pen_row_border, new Point(0, row_y), new Point(full_width + description_column_width, row_y));
e.Graphics.DrawString(s, desc_font, desc_brush, new Point((int)(description_column_width / 2 - s_size.Width / 2), row_y + 3));
dt = dt.AddDays(1);
row_y += row_height;
}
}
Which works absolutely fine (design-wise):
The calendar will handle multiple users:
Now of course, this is just a simple grid and some rectangles. Unfortunately I cannot use any event with rectangles since it is solely a painted component.
I was hoping to get some input here on how I could handle clickable calendar items (like appointments). Putting Panels or UserControls everywhere sounds pretty damn inefficient to me...? Any input will be apreciated!

How to segmentation object c#

I used this code for segmentation, I'm trying to detect pixels one by one because my object is a binary, not a grayscale. when i run the program, it draws 2 object. The first object is successfully drawn (object still has a black color and a red rectangle), but the second object fails get drawn. Screenshot is here. Please help me, why does this happen?
#region Edge Detection
private void btnSegmentasi_Click(object sender, EventArgs e)
{
Segments = new List<ImageSegment>();
Bitmap bmp = (Bitmap)pb2.Image;
imageArea = new Rectangle(0, 0, pb2.Image.Width - 1, pb2.Image.Height - 1);
for (int y = 0; y < pb2.Image.Height; y++)
{
for (int x = 0; x < pb2.Image.Width; x++)
{
bool skip = false;
foreach (ImageSegment segment in Segments)
{
if (pointIsInRect(x, y, segment.Rect))
{
skip = true;
break;
}
}
if (skip) continue;
Color warna = bmp.GetPixel(x, y);
if (warna.G == 0)
startEdgeDetection(x, y, ref bmp);
}
}
DGVProses.DataSource = Segments;
if (Segments.Count > 0)
{
Graphics g = pb2.CreateGraphics();
Rectangle[] rects = (from theSegment in Segments select theSegment.Rect).ToArray();
g.DrawRectangles(new Pen(Brushes.Red), rects);
g.Dispose();
}
}
private void startEdgeDetection(int x, int y, ref Bitmap bmp)
{
Point startPoint = new Point(x, y);
Point currPoint = new Point(x, y);
int sudut = 180;
int xMin = x, yMin = y, xMax = x, yMax = y;
do
{
sudut -= 45;
Point offset = angleToPoint(ref sudut);
Point trialPoint = new Point(currPoint.X + offset.X, currPoint.Y + offset.Y);
if (!pointIsInRect(trialPoint.X, trialPoint.Y, imageArea))
continue;
Color theColor = bmp.GetPixel(trialPoint.X, trialPoint.Y);
if (theColor.G == 0)
{
currPoint = trialPoint;
sudut -= 180;
if (currPoint.X > xMax)
xMax = currPoint.X;
else if (currPoint.X < xMin)
xMin = currPoint.X;
if (currPoint.Y > yMax)
yMax = currPoint.Y;
else if (currPoint.Y < yMin)
yMin = currPoint.Y;
if (sudut < 0)
sudut += 360;
if (currPoint == startPoint && sudut == 180)
break;
}
}
while (!(currPoint == startPoint && sudut == 180));
Rectangle r = new Rectangle(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1);
Bitmap newImage = new Bitmap(r.Width + 2, r.Height + 2);
using (Graphics g = Graphics.FromImage(newImage))
{
g.FillRectangle(Brushes.White, 0, 0, newImage.Width, newImage.Height);
g.DrawImage(bmp, new Rectangle(1, 1, r.Width, r.Height), r, GraphicsUnit.Pixel);
g.Dispose();
}
Segments.Add(new ImageSegment(r, newImage));
}
private Point angleToPoint(ref int sudut)
{
if (sudut < 0)
sudut += 360;
switch (sudut)
{
case 135: return new Point(-1, -1);
case 90: return new Point(0, -1);
case 45: return new Point(1, -1);
case 0: return new Point(1, 0);
case 315: return new Point(1, 1);
case 270: return new Point(0, 1);
case 225: return new Point(-1, 1);
default: return new Point(-1, 0);
}
}
private bool pointIsInRect(int x, int y, Rectangle rect)
{
if (x < rect.X)
return false;
if (x > rect.X + rect.Width)
return false;
if (x < rect.Y)
return false;
if (x > rect.Y + rect.Height)
return false;
return true;
}
#endregion
Okay, I think I've now got a clue of how your algorithm is supposed to work. I'd guess you are running around in circles within the object. I do not really know why it does not happen for the first object, but this is another story.
When you enter startEdgeDetection you start at some point, check if it's black, move by an angle and repeat the whole procedure. You stop when the current point reaches the starting point. The crux is, that this algorithm does not guarantee to walk the whole object, but may just do the following (I do not know it is exactly like this, but pretty much):
OOOOOO
O####O
O####O
OOOOOO
OOOOOO
O*###O
O####O
OOOOOO
OOOOOO
O**##O
O####O
OOOOOO
OOOOOO
O**##O
O#*##O
OOOOOO
OOOOOO
O**##O
O**##O
OOOOOO
O = pixels filled with white
# = pixels filled with black
* = pixels you stepped through
You've reached your starting point again and the algorithm stops, but the bounding box does not contain the whole object, but just a part. If all of your objects bounding boxes have either a width or a height of 1 you fill up your whole object with bounding boxes, hence it appears red.
You'll have to fix the startEdgeDetection to avoid the described case and make sure that you really detect the edge.
I made up a simple class that finds the bounding box of an object. It should be easy to apply it to your problem.
public class BoundingBoxCalculator
{
Bitmap bitmapToCalculateBoundingBoxFor;
Point startingPoint;
Point[] neighborOffsets =
{new Point(-1,-1),
new Point(0,-1),
new Point(1,-1),
new Point(-1, 0),
new Point(1, 0),
new Point(-1,1),
new Point(0,1),
new Point(1,1)};
public BoundingBoxCalculator(Bitmap bitmapContainingObject, Point borderPoint)
{
this.bitmapToCalculateBoundingBoxFor = bitmapContainingObject;
this.startingPoint = borderPoint;
}
public Rectangle CalculateBoundingBox()
{
List<Point> edgePoints = CalculateEdge();
int minX = edgePoints.Min(p => p.X);
int maxX = edgePoints.Max(p => p.X);
int minY = edgePoints.Min(p => p.Y);
int maxY = edgePoints.Max(p => p.Y);
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
}
List<Point> CalculateEdge()
{
List<Point> edgePoints = new List<Point>();
Point currentPoint = startingPoint;
do
{
IEnumerable<Point> neighboringEdgePoints = GetNeighboringEdgePoints(currentPoint);
IEnumerable<Point> neighboringEdgePointsNotVisited = from p in neighboringEdgePoints where !edgePoints.Contains(p) select p;
edgePoints.Add(currentPoint);
if(neighboringEdgePointsNotVisited.Count() == 0
&& neighboringEdgePoints.Contains(startingPoint))
{
currentPoint = startingPoint;
}
else if(neighboringEdgePointsNotVisited.Count() == 1)
{
Point nextPoint = neighboringEdgePointsNotVisited.First();
currentPoint = nextPoint;
}
else if(neighboringEdgePointsNotVisited.Count() > 1)
{
Point nextPoint = GetPointWithMinDistance(currentPoint, neighboringEdgePointsNotVisited);
currentPoint = nextPoint;
}
else
{
throw new Exception();
}
} while(currentPoint != startingPoint);
return edgePoints;
}
Point GetPointWithMinDistance(Point origin, IEnumerable<Point> pointsToTest)
{
double minDistance = double.MaxValue;
Point pointWithMinDistance = new Point(0,0);
foreach(Point pointToTest in pointsToTest)
{
double currentDistance = GetPointsDistance(origin, pointToTest);
if(currentDistance < minDistance)
{
minDistance = currentDistance;
pointWithMinDistance = pointToTest;
}
}
return pointWithMinDistance;
}
double GetPointsDistance(Point p1, Point p2)
{
return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
}
IEnumerable<Point> GetNeighboringEdgePoints(Point currentPoint)
{
IEnumerable<Point> neighboringPoints = GetNeighboringPoints(currentPoint);
List<Point> neighboringEdgePoints = new List<Point>();
foreach(Point pointToTest in neighboringPoints)
{
if(GetNeighboringPoints(pointToTest).Count() < 8)
{
neighboringEdgePoints.Add(pointToTest);
}
}
return neighboringEdgePoints;
}
IEnumerable<Point> GetNeighboringPoints(Point currentPoint)
{
List<Point> neighbors = new List<Point>();
for(int offsetsCount = 0; offsetsCount < neighborOffsets.Length; offsetsCount++)
{
Point currentPointWithOffset = AddPointOffset(currentPoint, neighborOffsets[offsetsCount]);
if(IsInImage(currentPointWithOffset) &&
IsInObject(currentPointWithOffset))
{
neighbors.Add(currentPointWithOffset);
}
}
return neighbors;
}
bool IsInImage(Point pointToTest)
{
return pointToTest.X >= 0
&& pointToTest.X < bitmapToCalculateBoundingBoxFor.Width
&& pointToTest.Y >= 0
&& pointToTest.Y < bitmapToCalculateBoundingBoxFor.Height;
}
bool IsInObject(Point pointToTest)
{
Color colorInPointPosition = bitmapToCalculateBoundingBoxFor.GetPixel(pointToTest.X, pointToTest.Y);
//assume object is color is not white
return colorInPointPosition.R != 255
|| colorInPointPosition.G != 255
|| colorInPointPosition.B != 255;
}
Point AddPointOffset(Point point, Point offset)
{
return new Point(point.X + offset.X, point.Y + offset.Y);
}
}
Find an example at:
https://dotnetfiddle.net/49bnTV
I just tested it with a rectangle, but I guess it should work with any shape. Just give it a try.

Comparing two sets of data (x,y)

I have stored numerical data in lists with its coordinates (xValues, yValues), and if I want to compare (add, subtract, divide...) that set of data to another I have to be aware of that I can't compare if the xValues don't match (because there is nothing to compare with). So I need to interpolate linearly between the "missing" xValues, that actually exist in the other set and generate new points. Please check this picture:
The cyan squares on the red line represent the stored points (xValues2), and (generally) they won't match the other's set xValues (xValues1). The two squares on the green line are examples of the desired generated points. With them I can work with this two graphs without problem.
For linear interpolation It's pretty straightforward: If I have two points (x0,y0) and (x1,y1) and I want to add a new point between them given a "x2":
y2=y0+(x2-x0)*(y1-y0)/(x1-x0)
To make this work I think I have to implement something like this:
Create new lists (xValuesNew, yValuesNew).
Make a union between xValues1 and xValues2 (xValuesNew).
Check what are the differences between the original xValues1 and the xValuesNew.
For each new value found generate the "y" using the formula written above.
Put that 4 steps in a method and use it again but now with the set2.
I've been on this all day, trying to find an easy solution, maybe using Linq or lambda expressions but I'm not used to work with them and my lack of knowledge on that topics ishuge. Note that this operation will be made pretty often so I have to make it not too heavy. I've thought that it will be a good idea to generate a new list instead inserting points in the middle of the original for that reason.
Please if someone could guide me a little bit or tell me if there is a math library actually doing this would be great. Thank you.
EDIT: Sorry if I haven't explained me properly.
Here I have an example (done in Excel):
Note that I can't directly add together Series1 and Series2 (+) or any other operation because the X spacing in them is different. So what I want is to generate a new point in the Series1 when is needed.
For that I would like to simple use a linear interpolation. Say that I have P1(0,40) and P2(0,60) in series1, but in series2 I have a point (1,10). I need to generate a point P3 between P1 and P2 with (1,50) coordinates.
I was trying to do this with SkipWhile and comparing the next X value of both series, if XValue of series1 is lower, then add that XValue and corresponding YValue in the newSeries. Else use the XValue2 for generating an Y and add it to the newSeries. Here is one of my attempts (doesn't work):
List<double> series1X = new List<double> { 0, 2, 4, 6, 8 };
List<double> series1Y = new List<double> { 120, 100, 110, 105, 70 };
List<double> series2X = new List<double> { 0, 1, 7, 8,9 };
List<double> newSeries1X = new List<double>();
List<double> newSeries1Y = new List<double>();
double lastX1 = series1X[series1X.Count()-1];
int i = 0;
while (next1X <= lastX1)
{
next2X = series2X.SkipWhile(p => p <= next1X).First();
Console.WriteLine(next2X.ToString());
if (next1X <= next2X)
{
newSeries1X.Add(series1X[i]);
newSeries1Y.Add(series1Y[i]);
}
if (next2X < next1X)
{
while (next2X < next1X)
{
newSeries1X.Add(next2X);
newY = series1Y[i] + (next2X - series1X[i]) * (series1Y[i + 1] - series1Y[i]) / (series1X[i + 1] - series1X[i]);
newSeries1Y.Add(newY);
next2X = series2X.SkipWhile(p => p <= next2X).First();
}
}
next1X = series1X.SkipWhile(p => p <= next2X).First();
Console.WriteLine(next1X.ToString());
i++;
}
It would be AWESOME to do this with your Zip method. But I have no idea how to write that condition in the predicate.
First off, I'd probably use an appropriate 'point' class that contains both the x and y coordinates instead of two separate lists for each coordinate. Then you can use the Zip method to quickly iterate through them:
IEnumerable<PointF> points0 = ...
IEnumerable<PointF> points0 = ...
float x2 = ...
IEnumerable<PointF> newPoints = point0.Zip(points1,
(p0, p1) => new PointF(p0.X, p0.Y + (x2-p0.X) * (p1.Y-p0.Y) / (p1.X-p0.X)));
This makes it easy to calculate a new set of points from your input data. If you just care about a single y-value, you can still do this with your current data, it will just look weird:
IEnumerable<double> y2values =
xValues1.Zip(yValues1, (x, y) => new { x, y }).Zip(
xValues2.Zip(yValues2, (x, y) => new { x, y }),
(p0, p1) => p0.y + (x2-p0.x) * (p1.y-p0.y) / (p1.x-p0.x));
I appologize if in the process of coding this answer I somehow mangled your math.
Update
Now that I have a better grasp on what you're trying to do, I don't think any Linq method will work out quite right. Here what I've come up with using indexes:
List<double> series1X = new List<double> { 0, 2, 4, 6, 8 };
List<double> series1Y = new List<double> { 120, 100, 110, 105, 70 };
List<double> series2X = new List<double> { 0, 1, 7, 8, 9 };
// in the worst case there are n + m new points
List<double> newSeries1X = new List<double>(series1X.Count + series2X.Count);
List<double> newSeries1Y = new List<double>(series1X.Count + series2X.Count);
int i = 0, j = 0;
for ( ; i < series1X.Count && j < series2X.Count; )
{
if (series1X[i] <= series2X[j])
{
newSeries1X.Add(series1X[i]);
newSeries1Y.Add(series1Y[i]);
if (series1X[i] == series2X[j])
{
j++;
}
i++;
}
else
{
int k = (i == 0) ? i : i - 1;
// interpolate
double y0 = series1Y[k];
double y1 = series1Y[k + 1];
double x0 = series1X[k];
double x1 = series1X[k + 1];
double y = y0 + (y1 - y0) * (series2X[j] - x0) / (x1 - x0);
newSeries1X.Add(series2X[j]);
newSeries1Y.Add(y);
j++;
}
}
for ( ; i < series1X.Count; i++)
{
newSeries1X.Add(series1X[i]);
newSeries1Y.Add(series1Y[i]);
}
for ( ; j < series2X.Count; j++)
{
// interpolate
double y0 = series1Y[i - 2];
double y1 = series1Y[i - 1];
double x0 = series1X[i - 2];
double x1 = series1X[i - 1];
double y = y0 + (y1 - y0) * (series2X[j] - x0) / (x1 - x0);
newSeries1X.Add(series2X[j]);
newSeries1Y.Add(y);
}
Output is
newSeries1X = { 0, 1, 2, 4, 6, 7, 8, 0 }
newSeries1Y = { 120, 110, 100, 110, 105, 87.5, 70, 52.5 }
This solution handles cases where the first series2X[0] < series1X[0] and when series2X[n] > series1X[m] by linearly 'projecting' the data outward from the first / last pair of points.
Here's another solution using enumerators (mostly), but it's not nearly as elegant as I'd hoped it would be. It could probably be improved a bit:
bool hasS1 = true, hasS2 = true, preinterp = true;
double x0 = 0, y0 = 0, x1 = 0, y1 = 0, x = 0, y = 0;
using(var s1xEnumerator = series1X.GetEnumerator())
using(var s1yEnumerator = series1Y.GetEnumerator())
using(var s2xEnumerator = series2X.GetEnumerator())
{
hasS1 = s1xEnumerator.MoveNext();
hasS2 = s2xEnumerator.MoveNext();
s1yEnumerator.MoveNext();
while(hasS1 && hasS2)
{
x1 = s1xEnumerator.Current;
y1 = s1yEnumerator.Current;
x = s2xEnumerator.Current;
if (x1 <= x)
{
newSeries1X.Add(x1);
newSeries1Y.Add(y1);
hasS1 = s1xEnumerator.MoveNext();
s1yEnumerator.MoveNext();
preinterp = false;
if (hasS1)
{
x0 = x1;
y0 = y1;
}
if (x1 == x)
{
hasS2 = s2xEnumerator.MoveNext();
}
}
else
{
// we have to look ahead to get the next interval to interpolate before x0
if (preinterp)
{
x0 = x1;
y0 = y1;
x1 = series1X[1]; // can't peek with enumerator
y1 = series1Y[1];
preinterp = false;
}
y = y0 + (y1 - y0) * (x - x0) / (x1 - x0);
newSeries1X.Add(x);
newSeries1Y.Add(y);
hasS2 = s2xEnumerator.MoveNext();
}
}
while(hasS1)
{
newSeries1X.Add(s1xEnumerator.Current);
newSeries1Y.Add(s1yEnumerator.Current);
hasS1 = s1xEnumerator.MoveNext();
s1yEnumerator.MoveNext();
}
while(hasS2)
{
x = s2xEnumerator.Current;
y = y0 + (y1 - y0) * (x - x0) / (x1 - x0);
newSeries1X.Add(x);
newSeries1Y.Add(y);
hasS2 = s2xEnumerator.MoveNext();
}
}
For working with two series with different spacing I first need to generate points in the first set, then in the second (with the same method) and finally sum point to point.
Here is the code for that method:
using OxyPlot.Series;
using OxyPlot;
namespace Algorithm1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<DataPoint> S1 = new List<DataPoint> ();
List<DataPoint> S2 = new List<DataPoint>();
List<DataPoint> NS1 = new List<DataPoint>();
S1.Add(new DataPoint(4, 10));
S1.Add(new DataPoint(6, 20));
S1.Add(new DataPoint(8, 15));
S1.Add(new DataPoint(9, 70));
S1.Add(new DataPoint(10, 5));
S2.Add(new DataPoint(1, 0));
S2.Add(new DataPoint(2, 0));
S2.Add(new DataPoint(3, 0));
S2.Add(new DataPoint(6, 0));
S2.Add(new DataPoint(7, 0));
S2.Add(new DataPoint(8.1, 0));
S2.Add(new DataPoint(8.2, 0));
S2.Add(new DataPoint(8.3, 0));
S2.Add(new DataPoint(8.4, 0));
S2.Add(new DataPoint(9, 0));
S2.Add(new DataPoint(9.75, 0));
S2.Add(new DataPoint(11, 0));
S2.Add(new DataPoint(12, 0));
S2.Add(new DataPoint(16, 0));
NS1 = GetMiddlePoints(S1, S2);
foreach (DataPoint pointin NS1)
{
MessageBox.Show( point.X.ToString()+" : "+ point.Y.ToString());
}
}
#region GetMiddlePoints
private List<DataPoint> GetMiddlePoints(List<DataPoint> S1, List<DataPoint> S2)
{
List<DataPoint> NS1 = new List<DataPoint>();
int i = 0;
int j = S2.TakeWhile(p => p.X < S1[0].X).Count();
int PointsInS1 = S1.Count;
int PointsInS2 = S2.Count;
DataPoint newPoint = new DataPoint();
while (i < PointsInS1 )
{
if (j < PointsInS2 )
{
if (S1[i].X < S2[j].X)
{
newPoint = S1[i];
NS1.Add(newPoint );
i++;
}
else if (S1[i].X == S2[j].X)
{
newPoint = S1[i];
NS1.Add(newPoint );
i++;
j++;
}
else if (S1[i].X > S2[j].X)
{
newPoint .X = S2[j].X;
newPoint .Y = S1[i-1].Y + (S2[j].X - S1[i-1].X) * (S1[i].Y - S1[i-1].Y) / (S1[i].X - S1[i-1].X);
NS1.Add(newPoint );
j++;
}
}
if (j == PointsInS2 )
{
newPoint = S1[i];
NS1.Add(newPoint );
i++;
}
}
return NS1;
}
#endregion
}
}

Trouble detecting the number of blocks drawn on the board *XNA*

I have this code that draws the boxes on a Qbert board, how would i figure out how to detect what color blocks are stepped on?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace QBert
{
public class Map
{
public int[,] board;
public Color[] blockColors = new Color[] { Color.Blue, Color.Green }; //this makes it so it takes one time to step to get to green
Texture2D block;
public Map(Texture2D block) //draws the map of the blocks
{
this.block = block;
board = new int[8, 7]
{
{ 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 0, 0 },
{ 0, 1, 1, 1, 1, 0, 0 },
{ 0, 1, 1, 1, 1, 1, 0 },
{ 1, 1, 1, 1, 1, 1, 0 },
{ 1, 1, 1, 1, 1, 1, 1 },
{ -1, -1, -1, -1, -1, -1, -1 },
}
;
}
public Vector2 GetSquareCoords(int x, int y) //cordinates of the block
{
int ofs = block.Width / 2;
ofs *= y % 2;
return new Vector2(x * block.Width + ofs, y * 96); // 96
}
public Vector2 GetSquareCenter(int x, int y) //method for to jump on the middle of a block
{
Vector2 coords = GetSquareCoords(x, y);
return new Vector2(coords.X + block.Width / 2, coords.Y + 32); //32
}
public Vector2 GetNextSquare(bool down, bool left, Vector2 position) //this is how you jump to a next square
{
// If on even row, right is directly below and left is below and to the left
// If on odd row, left is directly below and right is below and to the right
int next_x = 0, next_y = 0;
int x = (int)position.X;
int y = (int)position.Y;
if (down)
{
next_y = y + 1;
if (left)
{
next_x = x - 1; // -1
}
else
{
next_x = x;
}
}
else
{
next_y = y - 1;
}
if (y % 2 == 0)
{
if (left)
next_x = x - 1;
else
next_x = x;
}
else
{
if (left)
next_x = x;
else
next_x = x + 1; //+1
}
if (next_x < 0)
{
next_x += 1;
}
if (next_x > 6)
{
next_x -= 1;
}
if (next_y < 0)
{
next_y += 1;
}
if (next_y > 7)
{
next_y -= 1;
}
if (board[next_y, next_x] == 0)
{
return new Vector2(x, y);
}
else
{
return new Vector2(next_x, next_y);
}
}
public void Draw(SpriteBatch spriteBatch) //draws the blocks and colors of the block
{
int drawXOffset = 30;
int drawYOffset = 60;
for (int x = 0; x < 7; x++)
for (int y = 0; y < 7; y++)
{
Vector2 coord = GetSquareCoords(x, y);
if (board[y, x] > 0)
spriteBatch.Draw(block, new Rectangle(drawXOffset + (int)coord.X, drawYOffset + (int)coord.Y, block.Width, block.Height), blockColors[board[y, x] - 1]);
}
}
}
}
I am trying to have the code detect the number of blocks drawn so that I know when they are all a certain color.
I need to make it a certain color of a block to end the game.
Right now, i have it starting out as a Blue Block Color then changing to a Green Block, how would i make it detect that if all the green blocks are stepped on that the game ends?
Somewhere in your Update method, you will want something like this:
bool finished = true;
for (int x = 0; x < 7; x++)
{
for (int y = 0; y < 7; y++)
{
if (board != 0 && board != 2) // 2 is green
{
finished = true;
break;
}
}
if (finished)
break;
}
if (finished)
{
// Move to next level
}
I think somthing like this is what you want
public Vector2 GetNextSquare(bool down, bool left, Vector2 position)
{
int x = (int)position.X;
int y = (int)position.Y;
//...other code
if (blockColors[board[next_y, next_x]] == Color.Green)
{
//End
}
else if (board[next_y, next_x] == 0)
{
return new Vector2(x, y);
}
else
{
return new Vector2(next_x, next_y);
}
}
Usually you have some sort of data representing your game field and rendering code simply renders visual representation of the field. Your game code only works with internal field representation (i.e. in your case set of cubes objects with "Color" property).
You definitely can check color on the screen, but it will require significantly more effort.
#Jaview "what do you mean, can you show me an example how i can check?"
Here is an example how to get a pixel in XNA:
ResolveTexture2D backBufferData;
backBufferData = new ResolveTexture2D(
graphics.GraphicsDevice,
graphics.GraphicsDevice.PresentationParameters.BackBufferWidth,
graphics.GraphicsDevice.PresentationParameters.BackBufferHeight,
1,
graphics.GraphicsDevice.PresentationParameters.BackBufferFormat
);
Rectangle sourceRectangle = new Rectangle(Mouse.GetState().X, Mouse.GetState().Y, 1, 1);
Color[] retrievedColor = new Color[1];
graphics.GraphicsDevice.ResolveBackBuffer(backBufferData);
backBufferData.GetData<Color>(
0,
sourceRectangle,
retrievedColor,
0,
1);
selectedColor = retrievedColor[0];

Are different color fills of curve items possible in ZedGraph?

I have a single curve with circle points. Is it possible to set CurveItemPoints to a different fill color? I would set significantly points to different colors.
Short answer: no.
To do similar stuff I created additional SymbolObj class and added objects of that class to the pane to mark some significant points.
Here's some code for that:
internal class SymbolObj : ZedGraph.GraphObj
{
private ZedGraph.Symbol symbol;
public SymbolObj(ZedGraph.SymbolType type, Color color, PointF position, float size)
{
this.symbol = new ZedGraph.Symbol(type, color);
this.symbol.Size = size;
if((type== SymbolType.Plus || type == SymbolType.Star || type== SymbolType.HDash || type == SymbolType.XCross || type == SymbolType.VDash) && size >= 4)
this.symbol.Border.Width = 3f;
this.symbol.Fill.IsVisible = true;
this.symbol.Fill.Color = color;
this.Location.X = position.X;
this.Location.Y = position.Y;
}
public SymbolObj(SymbolObj rhs)
: base(rhs)
{
this.symbol = new Symbol(rhs.symbol);
}
public override void Draw(Graphics g, ZedGraph.PaneBase pane, float scaleFactor)
{
if (((GraphPane)pane).XAxis.Type == AxisType.Text)
{
if (Location.X > 0)
{
var xx = new double[(int)Location.X];
var yy = new double[(int)Location.X];
for (int i = 0; i < Location.X; i++)
{
xx[i] = i;
yy[i] = double.NegativeInfinity;
}
yy[yy.Count() - 1] = Location.Y;
LineItem line = new LineItem("Symbol", xx, yy, symbol.Fill.Color, SymbolType.None);
symbol.Draw(g, (GraphPane)pane, line, scaleFactor, false);
}
}
else
{
LineItem line = new LineItem("Symbol", new double[] { Location.X }, new double[] { Location.Y }, symbol.Fill.Color, SymbolType.None);
symbol.Draw(g, (GraphPane)pane, line, scaleFactor, false);
}
}
public override void GetCoords(ZedGraph.PaneBase pane, Graphics g, float scaleFactor, out string shape, out string coords)
{
shape = "point";
coords = this.Location.X.ToString() + ", " + this.Location.Y.ToString();
}
}

Categories

Resources