How To Know When AdaptativeGridView Will Use One, Two, Three (or More) Columns - c#

I`m using an AdaptativeGridView, with DesiredWidth of 488.
I thought that when the grid had ActualWidth of 684, for exemple, it would use two columns, because 684 is 196 pixels distant from the DesiredWidth (488), as 684/2 (if use two columns) result in 342, witch is only 146 pixels distant from DesiredWidth. But, different from the expected, the grid keep only one column, until the ActualWidth reach some value that I don't understand how it is defined.
I tried to calculate the definition of columns number, trying to check if the number of pixels is more close from the target (DesiredWidth) using only one or using two columns, but the logic didn't work. I need to understand how DesiredWidth logic works.
Here is my logic:
var twoColumns = Math.Abs(AppWebsiteGridView.DesiredWidth - (AppWebsiteGridView.ActualWidth / 2));
var oneColumn = Math.Abs(AppWebsiteGridView.ActualWidth - AppWebsiteGridView.DesiredWidth);
var threeColumns = Math.Abs(AppWebsiteGridView.DesiredWidth - (AppWebsiteGridView.ActualWidth / 3));
var numberOfItems = AppWebsiteGridView.Items.Count;
if (AppWebsiteGridView.Items[numberOfItems - 1] is SignInModel lastItem && AppWebsiteGridView.Items[numberOfItems - 2] is SignInModel lastButOneItem
&& AppWebsiteGridView.Items[numberOfItems - 3] is SignInModel lastButTwoItem)
{
if (threeColumns < twoColumns)
{
lastItem.IsEndLine = false;
lastButOneItem.IsEndLine = false;
lastButTwoItem.IsEndLine = false;
}
else if (twoColumns < oneColumn)
{
lastItem.IsEndLine = false;
lastButOneItem.IsEndLine = false;
lastButTwoItem.IsEndLine = true;
}
else
{
lastItem.IsEndLine = false;
lastButOneItem.IsEndLine = true;
lastButTwoItem.IsEndLine = true;
}
}

Related

Find points with maximum visibility in a room-as-grid

I have a 2D grid of cells, like so:
I want to find the "centroids," or the places in each room that can see the most other cells. For example, "centroid" 1 can see 500 other cells, "centroid" 2 can see 400 other cells (NOT included in the first 500), and so on (if there's a better name for this let me know).
I'm currently doing this with the code below.
public void SetCentroids(CellWalkableState[,] grid)
{
centroids = new List<((int, int), int)>();
List<(int, int)> cellsCopy = new List<(int, int)>();
for (int i = 0; i < cells.Count; i++)
{
cellsCopy.Add(cells[i]);
}
Debug.Log(DateTime.Now.ToString("o") + " - Setting centroids for room with " + cells.Count + " cells");
var perCellInView = cellsCopy.AsParallel().Select(x => (x, StaticClass.FindInView(x, grid))).ToList();
var force_start = perCellInView.First();
Debug.Log(DateTime.Now.ToString("o") + " - got in view");
var perCellInViewOrdered = perCellInView.AsParallel().OrderByDescending(xxx => xxx.Item2.Count);
var force_start_1 = perCellInViewOrdered.First();
Debug.Log(DateTime.Now.ToString("o") + " - sorted");
List<(int, int)> roomCellsAdded = new List<(int, int)>();
while(roomCellsAdded.Count < (cells.Count*0.9))
{
if(cellsCopy.Count == 0)
{
Debug.LogError("something is wrong here.");
}
var centroid = perCellInViewOrdered.First().x;
var centroidCells = perCellInViewOrdered.First().Item2;
if(centroidCells.Count == 0)
{
Debug.Log("this shouldnt be happening");
break;
}
roomCellsAdded.AddRange(centroidCells);
centroids.Add((centroid, centroidCells.Count));
Debug.Log(DateTime.Now.ToString("o") + " - added centroids, " + roomCellsAdded.Count + " cells in view");
var loopPerCellInView = perCellInView.AsParallel().Where(x => centroids.Select(y => y.Item1).Contains(x.x) == false).Select(x => (x.x, x.Item2.Except(roomCellsAdded).ToList())).ToList();
Debug.Log(DateTime.Now.ToString("o") + " - excluded");
perCellInViewOrdered = loopPerCellInView.AsParallel().OrderByDescending(xxx => xxx.Item2.Count);
Debug.Log(DateTime.Now.ToString("o") + " - resorted");
}
}
public static List<(int, int)> FindInView((int,int) start, CellWalkableState[,] grid)
{
List<(int, int)> visible = new List<(int, int)>() { start };
bool alive = true;
int r = 1;
var length_x = grid.GetLength(0);
var length_y = grid.GetLength(1);
List<(int, int)> searched = new List<(int, int)>();
List<double> angles = new List<double>();
while(alive)
{
//alive = false;
int newR = r;
int count = CountFromR(newR);
var angleInc = 360.0 / count;
var rNexts = Enumerable.Repeat(1, count).ToArray();
for (int i = 0; i < count; i++)
{
var angle = angleInc * i;
if(angles.Contains(angle) == false)
{
angles.Add(angle);
float cos = Mathf.Cos((float)(Mathf.Deg2Rad * angle));
float sin = Mathf.Sin((float)(Mathf.Deg2Rad * angle));
var b = r;
var p = i % (r * 2);
var d = math.sqrt(math.pow(b, 2) + math.pow(p, 2));
var dScaled = d / r;
bool keepGoing = true;
while(keepGoing)
{
var rCur = dScaled * (rNexts[i]);
var loc = (start.Item1 + Mathf.RoundToInt(rCur * cos), start.Item2 + Mathf.RoundToInt(rCur * sin));
if (searched.Contains(loc) == false)
{
searched.Add(loc);
if (loc.Item1 >= 0 && loc.Item1 < length_x && loc.Item2 >= 0 && loc.Item2 < length_y)
{
if (grid[loc.Item1, loc.Item2] == CellWalkableState.Interactive || grid[loc.Item1, loc.Item2] == CellWalkableState.Walkable)
{
visible.Add(loc);
}
else
{
keepGoing = false;
}
}
else
{
keepGoing = false; // invalid, stop
}
}
else
{
if (visible.Contains(loc) == false)
{
keepGoing = false; // can stop, because we can't see past this place
}
}
if(keepGoing)
{
rNexts[i]++;
}
}
}
}
angles = angles.Distinct().ToList();
searched = searched.Distinct().ToList();
visible = visible.Distinct().ToList();
if(rNexts.All(x => x <= r))
{
alive = false;
}
else
{
r = rNexts.Max();
}
}
return visible;
}
static int CountFromR(int r)
{
return 8 * r;
}
The "short" summary of the code above is that each location first determines what cells around itself it can see. That becomes a list of tuples, List<((int,int), List<(int,int)>)>, where the first item is the location and the second is all cells it views. That main list is sorted by the count of the sublist, such that the item with the most cells-it-can-vew is first. That's added as a centroid, and all cells it can view are added to a second ("already handled") list. A modified "main list" is formed, with each sublist now excluding anything in the second list. It loops doing this until 90% of the cells have been added.
Some output:
2021-04-27T15:24:39.8678545-04:00 - Setting centroids for room with 7129 cells
2021-04-27T15:45:26.4418515-04:00 - got in view
2021-04-27T15:45:26.4578551-04:00 - sorted
2021-04-27T15:45:27.3168517-04:00 - added centroids, 4756 cells in view
2021-04-27T15:45:27.9868523-04:00 - excluded
2021-04-27T15:45:27.9868523-04:00 - resorted
2021-04-27T15:45:28.1058514-04:00 - added centroids, 6838 cells in view
2021-04-27T15:45:28.2513513-04:00 - excluded
2021-04-27T15:45:28.2513513-04:00 - resorted
2021-04-27T15:45:28.2523509-04:00 - Setting centroids for room with 20671 cells
This is just too slow for my purposes. Can anyone suggest alternate methods of doing this? For all of the cells essentially the only information one has is whether they're "open" or one can see through them or not (vs something like a wall).
An approximate solution with fixed integer slopes
For a big speedup (from quadratic in the number of rooms to linear), you could decide to check just a few integer slopes at each point. These are equivalence classes of visibility, i.e., if cell x can see cell y along such a line, and cell y can see cell z along the same line, then all 3 cells can see each other. Then you only need to compute each "visibility interval" along a particular line once, rather than per-cell.
You would probably want to check at least horizontal, vertical and both 45-degree diagonal slopes. For horizontal, start at cell (1, 1), and move right until you hit a wall, let's say at (5, 1). Then the cells (1, 1), (2, 1), (3, 1) and (4, 1) can all see each other along this slope, so although you started at (1, 1), there's no need to repeat the computation for the other 3 cells -- just add a copy of this list (or even a pointer to it, which is faster) to the visibility lists for all 4 cells. Keep heading right, and repeat the process as soon as you hit a non-wall. Then begin again on the next row.
Visibility checking for 45-degree diagonals is slightly harder, but not much, and checking for other slopes in which we advance 1 horizontally and some k vertically (or vice versa) is about the same. (Checking for for general slopes, like for every 2 steps right go up 3, is perhaps a bit trickier.)
Provided you use pointers rather than list copies, for a given slope, this approach spends amortised constant time per cell: Although finding the k horizontally-visible neighbours of some cell takes O(k) time, it means no further horizontal processing needs to be done for any of them. So if you check a constant number of slopes (e.g., the four I suggested), this is O(n) time overall to process n cells. In contrast, I think your current approach takes at least O(nq) time, where q is the average number of cells visible to a cell.

Xamarin find child fast with point

I would like the search for a child on a grid to be super fast. The code I've created now is probably not the fastest way. I've tried implementing the 'GetChildElements()' but it doesn't return any of the children where my code does. Any suggestions on how to make this lighting fast?
touches contains any relatve touched Point(s) within the grid.
private void GetChildWithFrequency(object sender, Point[] touches)
{
Layout<View> grid = (Layout<View>)sender;
foreach (Key child in grid.Children)
{
if (touches[0].X >= child.X &&
touches[0].X <= child.X + child.Width &&
touches[0].Y >= child.Y &&
touches[0].Y <= child.Y + child.Height)
{
child.LastX = touches[0].X;
if (LastChild != null)
{
LastChild.Selected = false;
}
LastChild = child;
child.Selected = true;
break;
}
}
}
if you have a regular grid of cells and each row is R high and each column is C wide, then a touch at X,Y
Row = Y / R;
Col = X / C;
for instance, if R = 50 and C = 50, then a touch at (120,410) would be
Row = 410 / 50 ==> 8
Col = 120 / 50 ==> 2
if you Grid does not start at 0,0 you will need to adjust for that

Chart: Red X Error when trying to display multiple series in an indexed chart

I have an Issue with C# charting.
This is using System.Windows.Forms.DataVisualization.Charting
My series are configured to be indexed as such:
{
Name = name,
Color = color,
IsVisibleInLegend = false,
IsXValueIndexed = true,
ChartType =
System.Windows.Forms.DataVisualization.Charting.SeriesChartType.FastLine
};
I have 3 series in this chart
I am adding data to the series as it arrives via an Event Handler
for (int i = 0; i < samples; i++)
{
double time_point = DData[i * 4]
chart1.Series[0].Points[lastSample + i].XValue = time_point;
chart1.Series[0].Points[lastSample + i].YValues[0] = DData[i * 4 + 1];
chart1.Series[1].Points[lastSample + i].XValue = time_point;
chart1.Series[1].Points[lastSample + i].YValues[0] = DData[i * 4 + 2];
chart1.Series[2].Points[lastSample + i].XValue = time_point;
chart1.Series[2].Points[lastSample + i].YValues[0] = DData[i * 4 + 3];
}
lastSample = (lastSample + samples) % maxPlotPoints;
chart1.Invalidate();
The issue is as follows:
If I turn on each series individually (via chart1.Series[0].Enabled), they all work fine
If I turn on more than 1 series at a time, a big red X appears instead oft eh chart, and I have to restart the application to resume streaming charts. This either happens immediately or after a few seconds.
If I set time_point to some other number, like 0, this issue doesn't happen, and all 3 charts can be displayed simultaneously
Next, I understand that this happens when each series has a different X-value for the same Point[] location. But I am explicitly setting all 3 series to use the same exact time_point
My next assumption was that the event handler was executing the tread before the previous thread finishes.
So I added a lock around the graphing call, it did not help
private Object thisLock = new Object();
lock (thisLock)
{
}
MY questions are:
Does anyone know if there is another reason why this may be caused?
Is it at all possible to use just the X-indexes from the first series for the chart but to display all 3 series simultaneuously?
EDIT: This fix did not work, the problem disappeared, then came back after a while
The comment above by TaW helped me fix this issue.
This really seems to be a problem with Microsoft charts
I originally though it was due to EventHandler firing multiple times before it finishes, and I opted out to use a Queue that would be filled with an EventHandler and Dequeued on a separate thread.
The problem was still there.
The solution to the problem was to simply set IsXValueIndexed to false
then to set it to true before drawing the chart
Below is the code used
In case anyone is wondering what this does:
It graphs 3 Values simultaneously on the chart with increasing time.
The chart updates from left to right instead of scrolling (think EKG machine in a hospital)
There is a fixed total number of points that is set to maxPlotPoints
The EventHandler receives some Data packet (made up here) that contains a certain number of points which = samplePoints.
It constructs a DataPoints struct and adds it to a queue
A separate thread MainTask dequeues an element from DataPoints and uses it to populate a 100 point section of each of the three series. That section then increments to the next 100 points, and so on.
This is useful if you want to continuously chart a fixed number of points
Unfortunately, I can't seem to mark the answer above from TaW as correct because it's in a comment. But I would be happy to try if you can explain how to do this. Thanks
private struct DataPoint
{
public double timePoint;
public double X;
public double Y;
public double Z;
}
private struct DataPoints
{
public DataPoints(int samples)
{
dataPoints = new DataPoint[samples];
}
public DataPoint[] dataPoints;
}
Queue queue = new Queue();
int lastSample = 0;
static int maxPlotPoints = 1000;
static int samplePoints = 100;
public ChartForm(UdpMgrControl RefUdpMgr, byte GyroChartNum, string ConfigFile)
{
for ( Row = 0; Row < 3; Row++)
{
var series1 = new System.Windows.Forms.DataVisualization.Charting.Series
{
Name = name,
Color = Color.Blue,
IsVisibleInLegend = false,
IsXValueIndexed = true,
ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.FastLine
};
//Fill in chart with blank points
for (int i = 0; i < maxPlotPoints; i++)
{
series1.Points.AddY(0);
}
chart1.Series.Add(series1);
}
chart1.Series[0].Enabled = true;
chart1.Series[1].Enabled = true;
chart1.Series[2].Enabled = true;
running = true;
Thread main_thread = new Thread(new ThreadStart(this.MainTask));
}
private void OutputHandler(byte[] DData)
{
if (running)
{
DataPoints data_element = new DataPoints(samplePoints);
for (int i = 0; i < samplePoints; i++)
{
data_element.dataPoints[i].X = DData[i].X;
data_element.dataPoints[i].Y = DData[i].Y;
data_element.dataPoints[i].Z = DData[i].Z;
data_element.dataPoints[i].timePoint = DData[i].timePoint;
}
queue.Enqueue(data_element);
}
}
private void MainTask()
{
while (running)
{
try
{
if (queue.Count > 0)
{
chart1.Series[0].IsXValueIndexed = false;
chart1.Series[1].IsXValueIndexed = false;
chart1.Series[2].IsXValueIndexed = false;
DataPoints data_element = (DataPoints)queue.Dequeue();
for (int i = 0; i < data_element.dataPoints.Length; i++)
{
chart1.Series[0].Points[lastSample + i].XValue = data_element.dataPoints[i].timePoint;
chart1.Series[0].Points[lastSample + i].YValues[0] = data_element.dataPoints[i].X;
chart1.Series[1].Points[lastSample + i].XValue = data_element.dataPoints[i].timePoint;
chart1.Series[1].Points[lastSample + i].YValues[0] = data_element.dataPoints[i].Y;
chart1.Series[2].Points[lastSample + i].XValue = data_element.dataPoints[i].timePoint;
chart1.Series[2].Points[lastSample + i].YValues[0] = data_element.dataPoints[i].Z;
}
//Adjust the next location to end of first
lastSample = (lastSample + samples) % maxPlotPoints;
chart1.Series[0].IsXValueIndexed = true;
chart1.Series[1].IsXValueIndexed = true;
chart1.Series[2].IsXValueIndexed = true;
GC_UpdateDataGrids();
chart1.Invalidate();
}
else
{
Thread.Sleep(10);
}
}
catch (Exception error)
{
LogErrors.AddErrorMsg(error.ToString());
}
}
}

DataGridView shows columns that were set to non-visible

I have 3 datagridviews, and each of them has a separate binding source. All three binding sources however get data from the same datatable.
bindingSource1.DataSource = mytable;
bindingSource2.DataSource = mytable;
bindingSource3.DataSource = mytable;
dataGridView1.DataSource = bindingSource1;
dataGridView2.DataSource = bindingSource2;
dataGridView3.DataSource = bindingSource3;
I control what the user sees with the following logic: Show 10 first columns on the first grid, the next 10 on the second, and the next 10 on the third.
for (int i = 0; i < mytable.Columns.Count; i++)
{
dataGridView1.Columns[i].Visible = i < 10;
dataGridView2.Columns[i].Visible = (i >= 10 && i < 20);
dataGridView3.Columns[i].Visible = (i >= 20 && i < 30);
}
This works fine when I have many columns in the datatable.
Problem If I have less than 10 columns in my datatable, normally they should only be shown in the first datagridview. This does happen, but also the first column of the datatable is always shown in datagrid 2 and 3. I have stepped through my loop to see whether a condition is wrong, and I found it to be correct. Therefore, I am pretty sure it must be one of the events that follow this. I have two events registed for my grids that could be the cuplrits: RowPostPaint and CellPainting. I commented out everything that I was doing in these events and still get this problem. I also have some others, like DataError, CellValueChanged(empty inside), Scroll etc, but I think they are irrelevant.
So I am wondering whether there is another event which I haven't registered, which could be doing this by itself.
You're seeing this behavior because of a default behavior that happens when binding the data source. To fix, handle each DataGridView.DataBindingComplete event. We will use the same event handler for each:
dataGridView1.DataBindingComplete += DataGridView_DataBindingComplete;
dataGridView2.DataBindingComplete += DataGridView_DataBindingComplete;
dataGridView3.DataBindingComplete += DataGridView_DataBindingComplete;
In that event handler, we will set the visible columns as before. But we will also set the row headers' visibility as well as scroll bars - in case no columns are visible.
private void DataGridView_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
DataGridView view = sender as DataGridView;
bool anyVisible = false;
int max = 0, min = 0;
if (view == this.dataGridView1)
{
min = 0;
max = 10;
}
else if (view == this.dataGridView2)
{
min = 10;
max = 20;
}
else if (view == this.dataGridView3)
{
min = 20;
max = 30;
}
for (int i = 0; i < this.table.Columns.Count; i++)
{
view.Columns[i].Visible = i >= min && i < max;
anyVisible = anyVisible || view.Columns[i].Visible;
}
view.RowHeadersVisible = anyVisible;
view.ScrollBars = anyVisible ? ScrollBars.Both : ScrollBars.None;
}

c# how to check if pieces are ordered?

Picture 1.
Picture 2.
I made method for checking if all pieces are in base or goal if yes it returns true,now i need another method.
If its like on Picture 1. i need to change number of throws to 3 ,if its ordered like on Picture 2 i can allow only 1 throw.
I got 4 goalPositions and 4 piecePositions,and need to check if pieces are ordered on them properly from 54-51 path positions(path is array of 55 fields 0-54) ,if yes return true if not return false.
I am new to C# never had chance to work with order checking till now.
I was trying to do it with 3 int lists goalPositions (populated with 51,52,53,54 path positions),piecePositions(populated with pieces positions with getPosition() method),and piecesOnGoal (reserved for counting pieces on goal positions). but no luck with that.
I'll add some code. Part of Player class with that lists and method for checking pieces in goal or base
class Player {
protected PieceSet[] pieces;
Color color;
int numberOfThrows;
Dice dice;
public List<int> goalPositions;
public List<int> piecePositions;
public List<int> piecesOnGoal;
public enum Color
{
Yellow, Green, Blue, Red
}
public Player(Color color)
{
int[] path = new int[55];
this.color = color;
dice = new Dice();
numberOfThrows = 3;
switch (color)
{
case Color.Yellow:
path = BoardHelper.getYellowPath();
break;
case Color.Green:
path = BoardHelper.getGreenPath();
break;
case Color.Blue:
path = BoardHelper.getBluePath();
break;
case Color.Red:
path = BoardHelper.getRedPath();
break;
}
pieces = new PieceSet[4];
pieces[0] = new PieceSet(path, 0);
pieces[1] = new PieceSet(path, 1);
pieces[2] = new PieceSet(path, 2);
pieces[3] = new PieceSet(path, 3);
piecePositions = new List<int>(4);
piecePositions.Add(pieces[0].getPosition());
piecePositions.Add(pieces[1].getPosition());
piecePositions.Add(pieces[2].getPosition());
piecePositions.Add(pieces[3].getPosition());
goalPositions = new List<int>(4);
goalPositions.Add(51);
goalPositions.Add(52);
goalPositions.Add(53);
goalPositions.Add(54);
piecesOnGoal =new List<int>();
}
public bool isAllPiecesInBaseOrGoal()
{
if ((pieces[0].getPosition() < 4 || pieces[0].getPosition() > 50) &&
(pieces[1].getPosition() < 4 || pieces[1].getPosition() > 50) &&
(pieces[2].getPosition() < 4 || pieces[2].getPosition() > 50) &&
(pieces[3].getPosition() < 4 || pieces[3].getPosition() > 50))
return true;
else
return false;
}
And this is how I was thinking to solve my problem: Check if goalPositions contains piecePositions. If yes, add that position into piecesOnGoal. Now I need somehow to check if these piecesOnGoal are ordered. If yes, return true. If not, false.
I am open for any suggestion.
public bool isAllPiecesAreOrderedInGoal()
{
for (int i = 0; i < 4; i++)
{
if (goalPositions.Contains(piecePositions[i]))
{
piecesOnGoal.Add(piecePositions[i]);
}
}
}
Any help is appreciated.
I would suggest a method that checks if any move is possible. A move is possible if there are pieces outside the home or goal or if there are pieces in the goal which have an empty spot before them:
bool IsMovePossible()
{
// check every piece
for(int iPiece = 0; iPiece < 4; ++iPiece)
{
// if it is outside of home or goal, there is a possible move
if(piecePositions[iPiece] > 3 && piecePositions[iPiece] < goalPositions.First())
return true;
// if it is in the goal, check the next position
if(piecePositions[iPiece] >= goalPositions.First()
&& piecePositions[iPiece] < goalPositions.Last()
&& !piecePositions.Any(p => p == piecePositions[iPiece] + 1))
return true;
}
// we have found no piece with a possible move
return false;
}
Then, decide how often the user can roll the dice based on this method's return value. Note that the last check (piecePositions.Any) could be done more efficiently if you maintain an inverted map (i.e. for every position, store which piece is on that position). But checking four pieces should be fine.

Categories