Okay guys,
I am having a LOT trouble with this. I simply cannot figure out a way to implement tile picking in a hexagonal map in XNA. I have looked this up prior to asking this question, and all the answers involve complicated algorithms and diagrams my puny mind simply cannot comprehend. So my question for you guys is: How would i be able to hover over tiles, and select them if i wanted to?
If you need any reference as to how my program looks so far, just check out this link, its literally the same except i have a smaller map on mine.
http://www.xnaresources.com/default.asp?page=Tutorial:TileEngineSeries:3
Thanks!
This is code i had stored but never used and its for hex grid where one edge is looking up, so by some minor tweaks it could work in your example. It's not my code, not sure who wrote it.
Hexagon[][] hexagons = new Hexagon[100][100];
double hexagonHeight = 30;
double hexagonWidth = 40;
double halfWidth = hexagonWidth / 2;
// Find rough coordinates of Hexagon at mousepoint
private Hexagon getSelectedHexagon(MouseEvent mouse)
{
// These will represent which box the mouse is in, not which hexagon!
int row = (int) (mouse.y / hexagonHeight);
int column;
boolean rowIsOdd = row % 2 != 0;
// Is the row an even number?
if (rowIsOdd) // No: Calculate normally
column = (int) (mouse.x / hexagonWidth);
else // Yes: Offset mouse.x to match the offset of the row
column = (int) ((mouse.x + halfWidth) / hexagonWidth);
// column is more complex because it has to
// take into account that every other row
// is offset by half the width of a hexagon
return hexagons[row][column];
}
edit: i just found author
Hexagonal Grids, how do you find which hexagon a point is in?
Related
I have a candlestick chart which automatically updates with real time prices from a cryptocurrency exchange in the .NET forms. The goal is to make the bot preform actions when the price on chart passes one of the lines drawn by the user. So far I've come to the point of enabling line-drawing for users thanks to this article.
Could anyone please point me towards a method of detecting collision between the chart candles and the drawn lines? I feel like there must be an easier way than what I'm thinking of currently, just can't seem to figure out the way to it.
Using the exact solution for the line drawing as in the article, also posted code for the line-drawing below:
int index1 = 1;
int index2 = 4;
DataPoint left = chart.Series[0].Points[index1];
DataPoint right = chart.Series[0].Points[index2];
//Init the annotation
LineAnnotation line = new LineAnnotation();
line.AxisX = chart.ChartAreas[0].AxisX;
line.AxisY = chart.ChartAreas[0].AxisY;
line.IsSizeAlwaysRelative = false;
//Each point in a candlestick series has several y values, 0=high, 1=low, 2=open, 3=close
line.Y = left.YValues[1]; //low
line.X = left.XValue;
//If your data is indexed (your x values are Strings or you've set Series.IsXValueIndexed to true), use the data point index(+1) as the line X coordinate.
//line.X = index1 + 1;
//Use the width and height properties to determine the end position of the annotation.
line.Height = right.YValues[1] - left.YValues[1];
line.Width = right.XValue - left.XValue;
//Again, use the index if necessary
//line.Width = index2 - index1;
chart.Annotations.Add(line);
Just looking for a point in the direction of an easier solution, not the solution itself :) Thanks in advance!
So it sounds like you are asking is if a Point (Geometry) is above or below a line.
Here are the assumption (which you can change later to fit your needs):
an external resource is giving you a specific value (Y) at a specific point in time (X), which will call the Integral point XY.
The user has drawn a line which gives you a starting point (x1, y1) and an end point (x2, y2).
The graphs X component is in minutes, with each tick horizontally is 1 minute.
The graphs Y component is in dollars, with each tick is $25.
The user has drawn a line from (1:00pm, $50) to (1:05pm, $75).
We get an Integral Point XY at 1:10pm of $125.
What is the value of the line at 1:10pm so you can compare it to the Integral Point XY.
Based on my comments of Trigonometry..
We know the adjacent length is: 1:05 - 1:00 = 5
We know the opposite length is: 75 - 25 = 50
Using the formula: atan(opposite / adjacent) = angle
We calculate that the angle is: atan(50 / 5) = 1.47112767rad (radians)
Now we simply reverse our math:
We know the adjacent length is: 1:10 - 1:00 = 10
We know our Angle in Radians: 1.47112767
Using the formula: adjacent * tan(angle) = opposite
We calculate that the opposite is: 10 * tan(1.47112767) = ~$99.999999 or $100
$125 is above $100, do what you want.
I use the C# Chart in WinForms to plot a variety of variables in real time using the "line" chart type. That works well for analog values, but it's less than ideal for on/off flags.
I'd like to plot multiple flags as horizontal bars that are filled when the value is '1" and clear when the value is '0'.
Before I start coding a solution from scratch, do you have any suggestion on how I could take advantage of any features of the "chart" object to implement this more effectively?
EDIT: I am playing with the Area type, and it seems to be promising.
EDIT 2: That didn't work, because the area in the Area type always starts at the bottom of the chart, hiding the other rows. I am now trying the Range Column type
There are several ways to tackle this.: StackedBars, AreaChart, Annotations but I think by far the simplest is using a LineChartType.
The first issue is: How to create the gaps? The simplest way is to draw them as lines but with Color.Transparent. So instead of using the flag value as our y-value we use it to set the color..
So we could use a function like this:
void AddFlagLine(Chart chart, int series, int flag, int x)
{
Series s = chart.Series[series];
int px = s.Points.AddXY(x, series);
s.Points[px].Color = s.Color;
if (px > 0) s.Points[px - 1].Color = flag == 1 ? s.Color : Color.Transparent;
}
It takes the index of your Series and uses the flag to determine the color; note that the color of a line segment is controlled by the color of the end point.
So if you want to have the line going out from the new point to have its flag color, you need to set it when adding the next one..
This is simple enough and for lines as thick as 1-10 it works fine. But if you want larger widths things get a bit ugly..:
The rounded caps start to get bigger and bigger until they actually touch, flling the gaps more or less.
Unfortunately there seems to be no way to controls the caps-style of the lines. There are many CustomAttributes including DashStyles but not this one. So we have to resort to owner-drawing. This is rather simple for line charts. Here is an example:
The xxxPaint event looks like this:
private void chart_PostPaint(object sender, ChartPaintEventArgs e)
{
Graphics g = e.ChartGraphics.Graphics;
Axis ax = chart.ChartAreas[0].AxisX;
Axis ay = chart.ChartAreas[0].AxisY;
for (int si = 0; si < chart.Series.Count; si++ )
{
Series s = chart.Series[si];
for (int pi = 1; pi < s.Points.Count - 1; pi++)
{
DataPoint dp = s.Points[pi];
int y = (int) ay.ValueToPixelPosition(dp.YValues[0]+1); ///*1*
int x0 = (int)ax.ValueToPixelPosition(ax.Minimum);
int x1 = (int)ax.ValueToPixelPosition(s.Points[pi-1].XValue); ///*2*
int x2 = (int)ax.ValueToPixelPosition(dp.XValue);
x1 = Math.Max(x1, x0);
x2 = Math.Max(x2, x0);
using (Pen pen = new Pen(dp.Color, 40) ///*3*
{ StartCap = System.Drawing.Drawing2D.LineCap.Flat,
EndCap = System.Drawing.Drawing2D.LineCap.Flat })
{
g.DrawLine(pen, x1, y, x2, y);
}
}
}
A few notes:
1 : I have decided to move the the series up by one; this is up to you just as using or turning off the y-axis labels or replacing them by custom labels..
2 : Here we use the previous point's x-position!
3 : Note that instead of hard coding a width of 40 pixels you really should decide on a calculated width. This is an example that almost fills up the area:
int width = (int)( ( ay.ValueToPixelPosition(ay.Minimum) -
ay.ValueToPixelPosition(ay.Maximum)) / (chart7.Series.Count + 2));
You can twist is to fill more or less by adding less or more than 2.
I have turned all BorderWidths to 0 so only the drawn lines show.
I got it:
It turned out to actually be pretty easy; I used the Range Column type.
A) Set-up (done once):
plotChart.Series[chanNo].ChartType = SeriesChartType.RangeColumn;
plotChart.Series[chanNo].CustomProperties = "PointWidth=" + noOfFlags;
PointWidth is required to set the relative width of each rectangle so that it fills the entire width of one data point (if too small, there are gaps in the horizontal bar; if too large, there is overlap). noOfFlags is the number of flags shown (in the example shown above, noOfFlags = 4). (By the way the MSDN documentation is wrong: PointWidth is not limited to 2.)
B) Plotting (done for each new data point):
baseLine--;
int barHeight = flagHigh ? 1 : 0;
plotChart.Series[chanNo].Points.AddXY(pointX, baseLine, baseLine + barHeight);
flagHigh is a bool that is equal to the flag being monitored.
baseLine is decremented for each trace. In the example above, baseLine starts at 4, and is decremented down to 0.
Note that for each data point, RangeColumn requires 2 "Y" values: one for the bottom of the rectangle, one for the top; in the code, I set the bottom Y to the bottom of the row that I use for that particular flag, and the top to 1 above the bottom, to give me a height of 1.
Okay so, I have looked at a lot of question from people like my self who are beginners in programming. And most of the time their questions are hard to answer or understand what the question really is. I will do my best to be as specific as possible about my problems so the above mentioned doesn't happen.
First off I have been following the tutorials over at http://xnagpa.net/xna4rpg.php. And my tile engine is based off the one that Jamie McMahon makes in his rpg tutorial series. Just so you know what the general structure of my tile engine is like.
Secondly I will try to explain what I'm trying to do inside the tile engine. I recently found an article about how the original Civilization generated their maps. http://forums.civfanatics.com/showthread.php?t=498630
And I rather like this approach to generating a "world" style map if you will. ie: oceans, continents, islands ect. So I decided to try to take this and implement it into my tile engine. It works for the most part. The parts that I added to the tile engine are supposed to randomly pick a location in the specified map layer (y,x) and then from that location generate a chunk of land(or tiles) and then replace the tiles in the map layer with the tiles created in the chunk of land. (ill show the code in a minute) and then do that for a desired amount of either number of landmasses(chunks) or continue creating chunks of land until the number of land tiles is equal to a desired amount of land tiles.
My Problem:
My program does what its supposed to (as mentioned above) except it only ever makes one landmass.(Chunk of land tiles) It does everything else just fine but it for some reason will not make more than one landmass. Now I suspect that it actually is making the other landmasses but somehow the way the tile engine is set up to display map layers is causing the landmass's to be covered up with water. Maybe its a layering issue. But It shouldn't be because the landmass's are all part of the same layer. So I'm completely baffled as to why its doing this.
public void GenerateLandChunks(MapLayer layer)
{
Tile tile = new Tile(0, 3);
Random random = new Random();
int x = random.Next(8, layer.Width - 10);
int y = random.Next(10, layer.Height - 20);
int length = random.Next(10, 70);
for (int i = 0; i < length; i++)
{
if (length != 0 && x > 8 || x < layer.Width - 10 && y > 10 || y < layer.Height - 20)
{
layer.SetTile(y, x, tile);
layer.SetTile(y, x + 1, tile);
layer.SetTile(y + 1, x, tile);
}
x = random.Next(x - 1, x + 2);
y = random.Next(y - 1, y + 2);
}
}
This is my method for generating the actual chunks it does what I want it to. (ABOVE)
MapLayer randomLayer = new MapLayer(100, 100);
for (int y = 0; y < randomLayer.Height; y++)
{
for (int x = 0; x < randomLayer.Width; x++)
{
Tile tile = new Tile(1, 3);
randomLayer.SetTile(x, y, tile);
}
}
int landMasses = 5;
for (int i = 0; i < landMasses; i++)
{
randomLayer.GenerateLandChunks(randomLayer);
}
This is where I create the map layer. I initially set the entire map to water tiles(tile (1,3)) then I tell it to generate 5 landmasses.
It seems like this should work. And it does but like I said only for the first one. It doesn't display the other 4 land masses.
My Question:
Is there anything you can see here that I'm doing wrong in order to accomplish what I'm trying to do?
If you need more of the code to understand whats going on let me know and ill post what ever you need. And like I said everything other than what I have posted is the exact same as it is in Jamie McMahon's tile engine.
I'm sorry if I have come off as unclear or if my question is hard to answer. I tried to make it as straight forward as possible. Thank you for your time.
So much text for such a simple answer. The problem is that a new Random object is generated every time, and so the "random" values are the same every time. That is how random number generators work, the numbers are not actually random, but "pseudorandom", and if you use the same random function and the same seed you will get the same progression of seemingly random numbers.
Default Random constructor seeds the generator with some value based on time, but if two generators are created with very small time period then that time value will be the same.
Move the Random object outside the function, so that it is used for all random generations, or move the loop inside (but Random outside the loop).
I am drawing a chart which I populate with the data I obtain from different procedures. I want to make two buttons to zoom in and out. I saw that I can use different functions from AxisX.ScaleView and I am playing a bit with those. I am almost there but I have a problem at the moment of drawing the chart:If you see the image 1, this is the chart after executing the different procedures and drawing it for the first time. When I do a zoom in and a zoom out, the last bars (Week 22 from image 2) are cut in half and doesn't go to its original size.
Does anyone have any idea how can I manipulate the start and end position for the Axis X in order to make the zoom? Does anyone know how to get the initian values of start and end of the Chart Area? I place the code of my function to make the zoom of the chart:
private void setSize(int zoom)
{
int blockSize = (Convert.ToInt32(tbZoom.Text) + zoom) / 100;
// set view range to [0,max]
chartReport.ChartAreas[0].AxisX.Minimum = 0;
chartReport.ChartAreas[0].AxisX.Maximum = chartReport.Series[0].Points.Count;
// enable autoscroll
chartReport.ChartAreas[0].CursorX.AutoScroll = true;
chartReport.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
// let's zoom to [0,blockSize] (e.g. [0,100])
chartReport.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
chartReport.ChartAreas[0].AxisX.ScaleView.SizeType = DateTimeIntervalType.Number;
int actualHeight = chartReport.Height;
int actualWidth = chartReport.Width;
int position = 0;
int size = blockSize;
chartReport.ChartAreas[0].AxisX.ScaleView.Zoom(position, size);
// disable zoom-reset button (only scrollbar's arrows are available)
chartReport.ChartAreas[0].AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll;
// set scrollbar small change to blockSize (e.g. 100)
chartReport.ChartAreas[0].AxisX.ScaleView.SmallScrollSize = blockSize;
tbZoom.Text = (blockSize * 100).ToString();
}
Your first line is setting the maximum of the axis wrong: chartReport.ChartAreas[0].AxisX.Maximum = chartReport.Series[0].Points.Count; sets it to 22, when it really should be 23 (based on the first image).
If your data will always look like this, simply add 1:
chartReport.ChartAreas[0].AxisX.Maximum = chartReport.Series[0].Points.Count + 1;
Unfortunately, using the automatic min/max values won't give you the actual values until the chart is actually drawn. If your chart has few DataPoints this isn't a problem, as you can just call chartReport.Refresh(); or something similar and then get the values from the axes. But, if you have a lot of points, the Refresh() will take a long time which is undesirable. In my extensive use of the charts, I wound up setting the axis ranges myself so I have full control, rather than using the automatic min/max values.
I load multiple meshs from .x files in different mesh variables.
Now I would like to calculate the bounding sphere across all the meshes I have loaded (and which are being displayed)
Please guide me how this could be achieved.
Can VertexBuffers be appended togather in one variable and the boundingSphere be computed using that? (if yes how are they vertexBuffers added togather)
Otherwise what alternative would you suggest!?
Thankx
Its surprisingly easy to do this:
You need to, firstly, average all your vertices. This gives you the center position.
This is done as follows in C++ (Sorry my C# is pretty rusty but it should give ya an idea):
D3DXVECTOR3 avgPos;
const rcpNum = 1.0f / (float)numVerts; // Do this here as divides are far more epxensive than multiplies.
int count = 0;
while( count < numVerts )
{
// Instead of adding everything up and then dividing by the number (which could lead
// to overflows) I'll divide by the number as I go along. The result is the same.
avgPos.x += vert[count].pos.x * rcpNum;
avgPos.y += vert[count].pos.y * rcpNum;
avgPos.z += vert[count].pos.z * rcpNum;
count++;
}
Now you need to go through every vert and work out which vert is the furthest away from the center point.
Something like this would work (in C++):
float maxSqDist = 0.0f;
int count = 0;
while( count < numVerts )
{
D3DXVECTOR3 diff = avgPos - vert[count].pos;
// Note we may as well use the square length as the sqrt is very expensive and the
// maximum square length will ALSO be the maximum length and yet we only need to
// do one sqrt this way :)
const float sqDist = D3DXVec3LengthSq( diff );
if ( sqDist > maxSqDist )
{
maxSqDist = sqDist;
}
count++;
}
const float radius = sqrtf( maxSqDist );
And you now have your center position (avgPos) and your radius (radius) and, thus, all the info you need to define a bounding sphere.
I have an idea, what I would do is that I would determine the center of every single mesh object, and then determine the center of the collection of mesh objects by using the aforementioned information ...