I have a problem: I need an algorithm for my tile engine.
I have an 2d-array which stores my un-walkable tiles.
Now I want to implement a light engine, but this engine need shadow-hulls.
So I need an algorithm which will create these shadow-hulls.
I need a set of rectangles that bound the un-walkable portions of the array (the cells that have 1s)
For example:
The black tile are 1s; I need to find the set of red rectangles that fully enclose them.
After further thought, I've come up with a faster solution:
Create an immutable Range struct with StartX, StartY, and EndY properties.
Maintain a sparse array of current Ranges, with length equal to the height of the image, and a single nullable currentRange variable. This array will contain the range, if any, that starts at each Y coordinate
For each column,
Clear currentRange
For each cell in the column:
If there is no currentRange, or if there is but it ended before this cell (if currentRange.EndY <= y), set currentRange to the y'th element in the ranges array.
Thus, currentRange will always refer to the range that contains the current cell, or null if the current cell is not in a range.
If the current cell is 1:
If we're in a range, do nothing – the range is continuing into the next column.
If we're not in a range, loop down the column until we hit a 0 or the end of a column, then create a new range stretching from the 1 we just found until the end of the loop.
Then, proceed to the next if (since the current cell is now 0 or the end of the column, and we're not in a range)
If you hit an existing range as you loop ahead to create the new range, you can either stop the new range and let the existing range continue below it (best for fuzzy edges), or close the existing range (see below) and let the new range stretch downwards to take over from the existing range.
If the current cell is 0:
If we're in a range, close the range:
Return a new rectangle stretching from the range's start X and Y to the current Y and the range's end X.
Clear the range from the array of active ranges.
This algorithm is O(x * y) in computation and O(y) in space. I believe that this is the fastest solution to the problem.
You can also rotate this algorithm and scan rows rather than columns (so that ranges would be stretched downwards rather than rightwards), which will be O(x) in storage.
Here is a C# implementation:
class BoxFinder {
class Range {
public Range(int startX, int startY, int endY) {
StartX = startX;
StartY = startY;
EndY = endY;
}
public int StartX { get; private set; }
public int StartY { get; private set; }
public int EndY { get; private set; }
}
public BoxFinder(int[,] data) {
Data = data;
Width = data.GetLength(0);
Height = data.GetLength(1);
ranges = new Range[Height];
}
public int[,] Data { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
private Range[] ranges;
public IEnumerable<Rectangle> GetBoxes() {
for (int x = 0; x < Width; x++) {
Range currentRange = null;
for (int y = 0; y < Height; y++) {
Func<Range, Rectangle> CloseRange = r => {
currentRange = null;
ranges[r.StartY] = null;
return new Rectangle(
r.StartY,
r.StartX,
x - r.StartX,
r.EndY - r.StartY
);
};
if (currentRange == null || currentRange.EndY <= y)
currentRange = ranges[y];
if (Data[x, y] == 1) {
if (currentRange == null) {
int startY = y;
for (; y < Height && Data[x, y] == 1; y++) {
if (ranges[y] != null)
yield return CloseRange(ranges[y]);
//If there are fuzzy edges, break; instead
}
ranges[startY] = new Range(x, startY, y);
if (y == Height)
continue;
//Otherwise, continue to the next if in case a previous range just ended
}
}
//No else; we can get here after creating a range
if(Data[x, y] == 0) {
if (currentRange != null)
yield return CloseRange(currentRange);
}
}
}
}
}
Try something like this:
Create a list containing every desired point. (In your case, the coordinates of every 1)
For each point in this list:
Loop down the Y axis from this point until you hit an undesired point (a 0)
Loop right along the X axis until you hit an X coordinate which has a 0 at any Y value between the point and the ending Y coordinate you got from step 1.
Add the rectangle you just found to the list of rectangles
Remove every point in the rectangle from the original list.
This probably isn't the fastest way to do this, but it should work.
Related
The title says it all. I'm trying to make a collision with the help of the per pixel collision method that I have work for all my enemies. My method works fine with if im not using a list. Here's the code im using:
public bool IntersectPixels(
Matrix transformA, int widthA, int heightA, Color[] dataA,
Matrix transformB, int widthB, int heightB, Color[] dataB)
{
// Calculate a matrix which transforms from A's local space into
// world space and then into B's local space
Matrix transformAToB = transformA * Matrix.Invert(transformB);
// For each row of pixels in A
for (int yA = 0; yA < heightA; yA++)
{
// For each pixel in this row
for (int xA = 0; xA < widthA; xA++)
{
// Calculate this pixel's location in B
Vector2 positionInB =
Vector2.Transform(new Vector2(xA, yA), transformAToB);
// Round to the nearest pixel
int xB = (int)Math.Round(positionInB.X);
int yB = (int)Math.Round(positionInB.Y);
// If the pixel lies within the bounds of B
if (0 <= xB && xB < widthB &&
0 <= yB && yB < heightB)
{
// Get the colors of the overlapping pixels
Color colorA = dataA[xA + yA * widthA];
Color colorB = dataB[xB + yB * widthB];
// If both pixels are not completely transparent,
if (colorA.A != 0 && colorB.A != 0)
{
// then an intersection has been found
return true;
}
}
}
}
// No intersection found
return false;
}
To check the collision I use this bit of code:
public bool Update(Matrix PersonTransform2,Color[] data2)
{
personTransform1 = Matrix.CreateTranslation(new Vector3(new Vector2(position.X, position.Y), 0.0f));
if (game.IntersectPixels(PersonTransform2, texture.Width, texture.Height, data2,
PersonTransform1, texture.Width, texture.Height, data1))
{
return true;
}
return false;
}
My question here is how I can transform this bit of code to be able to work with a list.
You could add an overload and use ToArray() from Linq:
public bool Update(Matrix personTransform2, List<Color> data2)
{
Update(Matrix personTransform2, data2.ToArray())
}
but because of performance (the above creates a copy) I would probably replace the Color[] by List<Color> in both signatures.
On the other end, for these kind of calculations you probably shouldn't be using the List<Color> to start with. If you care about performance work with array's directly.
Mistakes were made... I forgot to put a foreach loop that went through all my list... The code was:
for(int i = 0; i < lista.Count; i++)
{
if (lista[i].Update(blockTransform, data2))
{
touched = true;
}
}
Gonna be honest, I probably have a bad title there, so allow me to explain. (Title suggestions are welcome!)
First and foremost, I have a 'List' aptly named TileList. Tile is a class that contains a few variables, but the important ones are these two: public int xCoor and public int yCoor.
TileList is automatically filled by an outside function. In this case, it's filled with exactly 100 instances of the Tile class, each with a different set of xCoor and yCoor- with xCoors from 0 to 9 as well as yCoors from 0 to 9, basically simulating a 10x10 grid.
As it's filled by an outside function, it's not always 10x10, it can also be 5x15 or 20x20 and so on.
I have a function that grabs a specific tile, named FindTile- that one works perfectly thus far...
But now I need one that selects a diamond shape from the grid, based on a starting point and a radius. And my own attempt, the FindDiamond function.. is very messy and takes forever to load.. and in fact sometimes doesn't even work.
This is basically what I want to do: Grid example
Code is down below:
public class Tile
{
public int xCoor;
public int yCoor;
//more variables
}
public class TileFinder
{
public List<Tile> TileList = new List<Tile>();
//Give back tiles when generated
public void SaveGeneratedTiles(List<Tile> Save)
{
TileList = Save;
}
public Tile FindTile(int tileX, int tileY)
{
for (int i = 1; i < TileList.Count; i++)
{
if (TileList[i].xCoor == tileX && TileList[i].yCoor == tileY)
{
return TileList[i];
}
}
return null;
}
public List<Tile> FindDiamond(int tileX, int tileY, int radius)
{
List<Tile> ReturnList = new List<Tile>();
for (int r = 0; r <= radius; r++)
{
int curx = tileX + r;
int cury = tileY;
if (curx == tileX) { FindTile(curx, cury); }
else
{
while (curx != -r)
{
curx--;
cury--;
ReturnList.Add(FindTile(curx, cury));
}
while (cury != -r)
{
curx++;
cury--;
ReturnList.Add(FindTile(curx, cury));
}
while (curx != r)
{
curx++;
cury++;
ReturnList.Add(FindTile(curx, cury));
}
while (curx != -r)
{
curx--;
cury++;
ReturnList.Add(FindTile(curx, cury));
}
}
}
return ReturnList;
}
}
Frankly, I'm looking for a better way to make this work, that does end in a returned List at the end.. Other parts of my code function off of this, which in fact may also not be the best way, but I'm still blundering my way through this. :)
TL;DR I'm trying to emulate something like Advance Wars, and I need the diamond shape to select ranges for fog of war vision, indirect attacks, etc.
First and foremost, to represent a grid, a 2d array would be a much better choice than a list. A list would work as well but is redundant.
If you know the grid to only contain 10x10 tiles, and you can guarantee their order, finding a specific tile by x and y coordinates should be an o(1) operation, not an o(n) linear search like you currently have in FindTile. Also in that function, why are you starting from index 1 and not 0?
To create a diamond pattern should be very straightforward.
Start with the row (Y coordinate) of the starting point. From there go radius steps backward on the X and radius steps forward on the X. For radius rows below and above the starting point, do the same thing, but for each of those rows, decrease radius by 1.
Something like this? Haven't tested it.
public List<Tile> FindDiamond(int tileX, int tileY, int radius)
{
List<Tile> ReturnList = new List<Tile>();
for (int x = 0; x < radius; x++)
{
for (int y = 0; y < radius-x; y++)
{
ReturnList.Add(FindTile(tileX + x, tileY + y));
ReturnList.Add(FindTile(tileX + x, tileY - y));
ReturnList.Add(FindTile(tileX - x, tileY + y));
ReturnList.Add(FindTile(tileX - x, tileY - y));
}
}
return ReturnList;
}
The "diamond" can be represented as a circle with distances measured using Manhattan Distance.
Manhattan Distance is measured by the sum of absolute differences of the X and Y coordinates of the points in question.
static int ManhattanDistance(int x1, int y1, int x2, int y2) {
return Math.Abs(x1 - x2) + Math.Abs(y1 - y2)
}
This simplifies your FindDiamond method to a single line (if using LINQ).
List<Tile> FindDiamond(int centerX, int centerY, int radius) {
return TileList.Where(tile => ManhattanDistance(tile.xCoor, tile.yCoor, tileX, tileY) <= radius).ToList();
}
FindTile can also be simplified to a one-liner using LINQ:
Tile FindTile(int tileX, int tileY) {
return TileList.FirstOrDefault(tile => tile.xCoor == tileX && tile.yCoor = tileY);
}
If, however you cannot use LINQ for some reason, just loop through TileList in FindDiamond and return all the Tile satisfying the clause inside Where.
I suggest that you improve your design for efficiency though, like #Rotem says. It'll make your code more efficient and quick. FindTile is a method I can imagine being called hundreds of times each frame, so it's best to make the code as efficient as possible. Even with variable width and height the method stated by #Rotem will work.
I also recommend that you create your own Point structure for easy storage and using blah.X and blah.Y instead of using blahX and blahY everywhere. It can also include useful helpers like ManhattanDistance and so on.
A sample Point structure:
struct Point {
public int X, Y;
public int ManhattanDistance(Point other) {
return Point.ManhattanDistance(this, other);
}
public static int ManhattanDistance(Point a, Point b) {
return Math.Abs(a.X- b.X) + Math.Abs(a.Y - b.Y)
}
}
I was originally using a 2d Array of "Tile"s to store a procedural generated map with its various contents.
Each Tile contains a List adjacent which allows every single tile to know which vertices are closest to it, touching it on 8 different sides (straight adjacent and diagonally adjacent).
The general idea was taken from Amit's polygonal map generation, but I attempted to simplify it by using a grid setup instead of voronois, however I've run into more trouble than I originally thought would be possible. My current predicament is figuring out adjacency when I've scrapped 2d Arrays.
This is how I was doing it before changing to a list:
private void ConstructAdjacencyList() {
// Create Adjacency List
for (int x = 0; x < mapWidth; x++) {
for (int y = 0; y < mapHeight; y++) {
// Bool to find position of point
bool omitLeft = false; bool omitRight = false;
bool omitTop = false; bool omitBottom = false;
// Enable bools based on position, reset on each loop
if (x == 0)
omitLeft = true;
else if (x == mapWidth - 1)
omitRight = true;
if (y == 0)
omitTop = true;
else if (y == mapHeight - 1)
omitBottom = true;
// Add entries to list based on bool settings
if (!omitLeft) {
// Left center
islandMap[x,y].adjacent.Add(islandMap[x-1,y]);
if (!omitTop)
islandMap[x,y].adjacent.Add(islandMap[x-1,y-1]);
if (!omitBottom)
islandMap[x,y].adjacent.Add(islandMap[x-1,y+1]);
}
if (!omitTop) // Top Center
islandMap[x,y].adjacent.Add(islandMap[x,y-1]);
if (!omitBottom) // Bottom Center
islandMap[x,y].adjacent.Add(islandMap[x,y+1]);
if (!omitRight) {
// Right Center
islandMap[x,y].adjacent.Add(islandMap[x+1,y]);
if (!omitTop)
islandMap[x,y].adjacent.Add(islandMap[x+1,y-1]);
if (!omitBottom)
islandMap[x,y].adjacent.Add(islandMap[x+1,y+1]);
}
}
} // End Adjacency
Debug.Log ("Adjacencies Built");
}
The x, y values now are held in islandMap.point (A Vector 2d storing the x and y values generated as follows:)
public MapController() {
width = height = (int)Mathf.Sqrt (tileCount);
// Lists for points
var points = new List<Vector2>();
// Build a random set of points.
for (float x = 0; x < width; x++) {
for (float y = 0; y < height; y++) {
points.Add(new Vector2(x,y));
}
}
map = new Map (points, width, height, lakeTreshold);
}
And the Map itself has the following currently:
public class Map {
Func<Vector2, bool> inside; // Contains function to randomly seed area
bool needsMoreRandomness;
public List<Tile> islandMap; // Previously was Tile[,] islandMap
public int mapWidth { get; private set; } // Calculated as Sqrt(totalPoints)
public int mapHeight { get; private set; }
Along with other methods such as the ConstructAdjacencyList() method I'm currently stuck on.
So how can I go on about constructing an adjacency list of surrounding points without relying on array positioning? Could I temporarily reference the entire list from an array, place references to each tile in the entire list in this 2d array, setup adjacencies and then remove the array without losing the information? I believe it would only use references, so it should be fine... Each tile contains an index to store the order with which it was built like so:
foreach (var point in points) {
var p = new Tile { index = islandMap.Count, point = point };
p.border = point.x == 0 || point.x == mapWidth || point.y == 0 || point.y == mapHeight;
islandMap.Add (p);
tileLookup[point] = p;
}
Sorry if this is too long... I just realized it's quite massive -.-
Assuming you haven't done anything to mess with the order of the points, you can treat a 1D list/array as 2D list/array. The example I linked is in C, but the idea language agnostic.
That being said, assuming that you're only going to do this once during the initialization & given the relatively low number of points, you could just as easily brute force it with a function that loops through the point list & picks out the neighbors as needed. My C# is a bit rusty, but I'm talking about something like this:
private List<Vector2> getAdjacenctPointList(List<Vector2> pointsList, Vector2 point){
var adjacencyList = new List<Vector2>();
foreach (var pt in pointList){
var offset = Math.abs(pt.x - point.x) + Math.abs(pt.y - point.y);
if(offset > 0 && offset <= 1.0){
adjacencyList.add(pt);
}
}
return adjacencyList;
}
The final answer I went with which was the least amount of work involved and allowed me to recycle my original 2d Array code posted above was to simply construct a temporary 2d array and store all the references to it in it - create all adjacencies as necessary and then dispose (simply by losing scope) of the 2d Array.
Here's the actual method with the 2d array system in place. First couple statements followed by the nested for loop was all it took:
private void ConstructAdjacencyList() {
Tile[,] tempArray = new Tile[mapWidth, mapHeight];
int count = 0;
// Populate the temp 2D array with list references
for (int x = 0; x < mapWidth; x++) {
for (int y = 0; y < mapHeight; y++) {
tempArray[x,y] = islandMap[count];
count++;
}
}
// Create Adjacency List using our TempArray
for (int x = 0; x < mapWidth; x++) {
for (int y = 0; y < mapHeight; y++) {
// Bool to find position of point
bool omitLeft = false; bool omitRight = false;
bool omitTop = false; bool omitBottom = false;
// Enable bools based on position, reset on each loop
if (x == 0)
omitLeft = true;
else if (x == mapWidth - 1) // Optimize with if else to split checks in half.
omitRight = true;
if (y == 0)
omitTop = true;
else if (y == mapHeight - 1)
omitBottom = true;
// Add entries to list based on bool settings
if (!omitLeft) {
// Left center
tempArray[x,y].adjacent.Add(tempArray[x-1,y]);
if (!omitTop)
tempArray[x,y].adjacent.Add(tempArray[x-1,y-1]);
if (!omitBottom)
tempArray[x,y].adjacent.Add(tempArray[x-1,y+1]);
}
if (!omitTop) // Top Center
tempArray[x,y].adjacent.Add(tempArray[x,y-1]);
if (!omitBottom) // Bottom Center
tempArray[x,y].adjacent.Add(tempArray[x,y+1]);
if (!omitRight) {
// Right Center
tempArray[x,y].adjacent.Add(tempArray[x+1,y]);
if (!omitTop)
tempArray[x,y].adjacent.Add(tempArray[x+1,y-1]);
if (!omitBottom)
tempArray[x,y].adjacent.Add(tempArray[x+1,y+1]);
}
}
} // End Adjacency
Debug.Log ("Adjacencies Built");
}
To ensure this worked out as I wanted it to, I tested it visually by setting one random cube and tested various points all around the area to ensure there were no mistakes and there weren't.
The end result is as expected, depicted below:
Thanks for the help :)
I need to find out the front measure of chest for any individual using Kinect while facing the camera. My current solution is:
When a MultiFrameSource arrives get the color (to display the body in the ui) body (to get the Joints), and bodyIndex frames.
copy the BodyIndexFrame to an byte[] _bodyData by using:
bodyIndexFrame.CopyFrameDataToArray(_bodyData);
I get the Joint objects for: spineShoulder and spineMid. I have assumed that the chest will always be between those points.
I convert both Joints to CameraSpacePoint (x,y,z) and from CameraSpacePoint to DepthSpacePoint (x,y) by using
_sensor.CoordinateMapper.MapCameraPointToDepthSpace(jointPosition);
I still keep a reference to the z value of spineShoulder.
Second assumption => Starting from spineShoulderY to spineMidY I try to find the widest point which is in the player area. in order to do so I will try to find the longest segment between spineShoulderX and the first left region found which does not belong to the player and the longest segment between spineShoulderX and first right side region found which does not belong to the player. Both x segments found must be in the same y coordinate.
/***************
* Returns the distance between 2 points
*/
private static int getDistanceToMid(int pointX, int midX)
{
if (midX > pointX)
{
return (midX - pointX);
}
else if (pointX > midX)
{
return (pointX - midX);
}
else
{
return 0;
}
}
/*********
* Loops through the bodyData array
* It will look for the longest x distance from midX to the last left x value
* which still belongs to a player in the y coordinate
*/
private static int findFarLeftX(byte[] bodyData,int depthWidth, int midX, int y)
{
int farLeftX = -1;
for (int x = midX; x >= 0; --x)
{
int depthIndex = (y * depthWidth) + x;
if (depthIndex > 0 && depthIndex < bodyData.Length)
{
byte player = bodyData[depthIndex];
if (player != 0xff){
if (farLeftX == -1 || farLeftX > x)
{
farLeftX = x;
}
} else{
return farLeftX;
}
}
}
return farLeftX;
}
/*********
* Loops through the bodyData array
* It will look for the longest x distance from midX to the last right x value
* which still belongs to a player in the y coordinate
*/
private static int findFarRightX(byte[] bodyData, int depthWidth, int midX, int y)
{
int farRightX = -1;
for (int x = midX; x < depthWidth; ++x)
{
int depthIndex = (y * depthWidth) + x;
if (depthIndex > 0 && depthIndex < bodyData.Length)
{
byte player = bodyData[depthIndex];
if (player != 0xff)
{
if (farRightX == -1 || farRightX < x)
{
farRightX = x;
} else{
return farRightX;
}
}
}
}
return farRightX;
}
private static BodyMember findElement(byte[] bodyData, int depthHeight, int depthWidth, int startX, int startY, int endY)
{
BodyMember member = new BodyMember(-1, -1, -1, -1);
int totalMaxSum = 0;
int farLeftX = -1;
int farRightX = -1;
int selectedY = -1;
for (int y = startY; y < depthHeight && y <= endY; ++y)
{
int leftX = findFarLeftX(bodyData, depthWidth, startX, y);
int rightX = findFarRightX(bodyData, depthWidth, startX, y);
if (leftX > -1 && rightX > -1)
{
int leftToMid = getDistanceToMid(leftX, startX);
int rightToMid = getDistanceToMid(rightX, startX);
int sum = leftToMid + rightToMid;
if (sum > totalMaxSum)
{
totalMaxSum = sum;
farLeftX = leftX;
farRightX = rightX;
selectedY = y;
}
}
}
member.setFarLeftX(farLeftX);
member.setFarLeftY(selectedY);
member.setFarRightX(farRightX);
member.setFarRightY(selectedY);
return member;
}
findElement will return a BodyMember object which contains farLeftX, farRightX, farLeftY and farRightY.
I create 2 DepthSpacePoint objects:
DepthSpacePoint chestX1 = new DepthSpacePoint();
chestX1.X = bodyMemberObj.getFarLeftX();
chestX1.Y = bodyMemberObj.getFarLeftY();
DepthSpacePoint chestX2 = new DepthSpacePoint();
chestX2.X = bodyMemberObj.getFarRightX();
chestX2.Y = bodyMemberObj.getFarRightY();
In order to get real world coordinates in meters these points must be converted to CameraSpacePoint object. In order to do so I will use the joint's z value that I kept a reference to back in point 4.
CameraSpacePoint chestLeft = _sensor.CoordinateMapper.MapDepthPointToCameraSpace(chestX1,spineShoulderZ);
CameraSpacePoint chestRight = _sensor.CoordinateMapper.MapDepthPointToCameraSpace(chestX1,spineShoulderZ);
Now,If my code and assumptions are right I should be able to get the correct distance in meters for the front chest.
double chestLength = (chestLeft.X > chestRight.X) ? chestLeft - chestRight : chestRight - chestLeft;
However this does not seem to be returning the correct values. I have been looking into a solution for this during weeks but I seem to be stuck.
I have worked with Kinect V2, and can say that skeleton data alone wild be insufficient to get reliable results. Even clothes has impact how Kinect interprets body parts, so you will have to combine results from other sensors data.
Additionally I suggest you to be creative about how you approach this,
for example, you could investigate some possible anatomic correlations about human body, most likely height is a proxy indicator, maybe age from face-recognition and height is another hint, etc.
Are you using using kinect One? Measurements in the old kinects are probably not accurate enough to fit your requirements.
However, from my point of view it would be also worth to do extraction of the moving object inside the frames (if there is only one person in front of the Kinect you will receive this person's contour, but to be sure you can compare its position with Kinect skeleton).
You can tell person to rise their hands for a short period of time. Than use Kinect distance to the persons skeleton and based on the size of the contour and given distance - calculate your final chest measurement just below the shoulders (position taken from the skeleton). I'm also not fully aware how accurately Kinect handles "bigger" people.
Here is described one of the methods: http://onlinelibrary.wiley.com/doi/10.1002/scj.10237/abstract You can also google many other papers with given subject (for free).
What do you think about this idea?
Cheers
I'm struggling to set the values of an element at a specific index within a list to a new value.
The list is of type object Rectangle and i get the following error when I try change any of the values of the rectangle in the list e.g.
Property or indexer 'System.Drawing.Rectangle.Bottom' cannot be
assigned to -- it is read only
I've tried converting the list to an array but i still run into the same issue of the values being read-only.
Basically the app takes in a user defined number of rectangles and draws the rectangles with varying widths and heights but along the same baseline. The code im trying to implement needs to take those rectangles and redraw them vertically from the base upwards, while keeping the same number of rectangles and keeping the same outer shape as the previous rectangles created.
Code:
public void RotateRectangles(List<Rectangle> Rectangles, int startPositionX, int userInput, Graphics DrawingSurface)
{
Graphics RectangleGraphics = DrawingSurface;
try
{
// loop in reverse to compare one rectangle to all the other rectangles in the vector
for (int i = Rectangles.Count - 1; i > -1; --i)
{
bool mustChange = true;
for (int t = Rectangles.Count - 1; t > -1; --t)
{
// only compare if the current position in the vector A if different to the position in vector B.
if (i > t)
{
if (mustChange == true)
{
// If the top Y coordinate of RECT at Position i in vector A is bigger than Y coordinate
// at Position t in vector B
if (Rectangles[i].Top >= Rectangles[t].Top)
{
//adjusting points accordingly
Rectangles[i].Left = (Rectangles[t].Left);
Rectangles[t].Bottom = (Rectangles[i].Top);
}
else
{
// If the Y coordinate is not bigger, then we need to stop checking
mustChange = false;
}
}
}
}
}
// loop forward to compare one rectangle to all the other rectangles in the vector
for (int i = 0; i < Rectangles.Count; ++i)
{
bool forwardChange = true;
for (int t = 0; t < Rectangles.Count; ++t)
{
// If the top Y coordinate of RECT at Position i in vector A is bigger than Y coordinate at Position t
// in vector B AND the two rectangales touch
if (i < t && Rectangles[i].Top <= Rectangles[t].Bottom)
{
if (forwardChange == true)
{
// If the top Y coordinate of RECT at Position i in vector A is bigger than Y coordinate at Position t
// in vector B
if (Rectangles[i].Top > Rectangles[t].Top)
{
//adjusting points accordingly
Rectangles[i].Right = (Rectangles[t].Right);
Rectangles[t].Bottom = (Rectangles[i].Top);
}
else
{
// If the Y coordinate is not bigger, then we need to stop checking
forwardChange = false;
// Addjust the Y position of each rectangle so it does not overlap with the first drawing
for (int z = 0; z < Rectangles.Count; ++z)
{
Rectangles[z].Top = (250 - Rectangles[z].Top);
Rectangles[z].Bottom = (250 - Rectangles[z].Bottom);
}
}
}
}
}
}
for (int z = 0; z < Rectangles.Count; ++z)
{
Rectangle DrawRec = myRectangleClass.MyRectangle(Rectangles[z].Left, Rectangles[z].Top, Rectangles[z].Right, Rectangles[z].Bottom);
RectangleGraphics.DrawRectangle(Pen, DrawRec);
ReadWrite.writeOutput(Rectangles[z].Left, Rectangles[z].Top, Rectangles[z].Right, Rectangles[z].Bottom);
}
}
catch (Exception e)
{
}
}
The parts that are giving the errors are:
Rectangles[i].Left = (Rectangles[t].Left);
Rectangles[t].Bottom = (Rectangles[i].Top);
Rectangles[i].Right = (Rectangles[t].Right);
Rectangles[t].Bottom = (Rectangles[i].Top);
Rectangles[z].Top = (250 - Rectangles[z].Top);
Rectangles[z].Bottom = (250 - Rectangles[z].Bottom);
Please can someone help me out or at least direct me in the right direction
The properties Right, Left, Top, Bottom are read-only. You can use the methods Offset and Inflate, and the properties Location, Size, Width and Height, to adjust the position and size of the rectangle, or you can replace an existing rectangle with a newly created one.
e.g.
Rectangle ri = Rectangles[i];
Rectangle rt = Rectangles[t];
Rectangle[i] = new Rectangle( rt.Left, ri.Bottom, rt.Height, rt.Width );
the problem is less the value type of the rectangle, more that the List[] operator returns a new value object.
List<Rectangle> a;
a[4].X += 4; // does the same like the following code:
var r = a[4]
r.X += 4; // will change r, but not a[4]
so you need to store back the rectangle value in the list
Rectangles[i] = new Rectangle(Rectangles[t].Left, Rectangles[i].Top, Rectangles[i].Width, Rectangles[i].Height);
Assign a new rectangle to the wanted index.