I need to do some hand made formatting in c# with only spaces. Here's what I display now:
Conc2_CO ( Y? = 170.2; Y? = 2; delta = -15)
atns_UreaMassFlowDemand ( Y? = 0; Y? = 0; delta = 0)
And this is what I'd like to have:
Conc2_CO ( Y? = 170.2; Y? = 2; delta = -15)
atns_UreaMassFlowDemand ( Y? = 0; Y? = 0; delta = 0)
I tried playing with string length using new string(' ', x) but this is a big pain and seems to work randomly since all the characters don't have the same length (i.e: l is shorter than w)... Is there some better option?
Edit:
The resulting string is construct with the concatenation of the name (left) and the informations I add (right) in a library so I can't use string.format() since I can only play with the right part.
I need to display this information on the ZedGraph Legend using WinForms (but I doubt this will change anything).
Edit 2
Using paddings, this is what I've got:
Which is not what I want.
You can change your font in order to use a monospace font.
This way you will be able to use the String.Format and work with paddings.
I was delete may previous answer.
Try this:
string s = String.Format("{0,-12}({1,8})", "Example", 223);
{0,-12} -0 - value index, -12 align to left 12 chars
{1,8} - 1 - value index, 8 align to right 8 chars
More:http://msdn.microsoft.com/pl-pl/library/system.string.format%28v=vs.110%29.aspx see: The format item
Related
I am very new to C# sharp and this question may not be very well formulated, but here it goes.
I am currently generating a level consisting of more game objects. The map is generated by parsing a text file, this being my dictionary. Currently the code I have works perfectly if the index of the object does not go over 9, because of the char declaration.
I would like to know an approach on how to change this into an int maybe or into a string format (A-Z).
I attached a photo of my current "Map Generator", and highlighted the 10, which is currently read as 1 and 0.
private void CreateLevel() {
Tiles = new Dictionary<Point, TileScript>();
string[] mapData = ReadLevelText();
int mapX = mapData[0].ToCharArray().Length;
int mapY = mapData.Length;
Vector3 maxTile = Vector3.zero;
// Calculates the world start point, this is the top left corner of the screen, rememeber to use on the sprite the top left pivot point
Vector3 worldStart = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height));
for (int y = 0; y < mapY; y++) // the Y position
{
char[] newTiles = mapData[y].ToCharArray();
for (int x = 0; x < mapX; x++) // the X position
{
// Places the tile into the level
PlaceTile(newTiles[x].ToString(), x, y, worldStart);
}
}
maxTile = Tiles[new Point(mapX-1, mapY-1)].transform.position;
cameraMovement.SetLimit(new Vector3(maxTile.x + TileSize, maxTile.y - TileSize));
}
private void PlaceTile(string tileType, int x, int y, Vector3 worldStart) {
int tileIndex = int.Parse(tileType);
// Creates a new tile and makes a reference to that tile in the newTile variable
TileScript newTile = Instantiate(tilePrefabs[tileIndex]).GetComponent<TileScript>();
// Uses the new tile variable to change the position of the tile
newTile.Setup(new Point(x, y), new Vector3(worldStart.x + (TileSize * x), worldStart.y - (TileSize * y), 0));
Tiles.Add(new Point(x,y), newTile);
}
The data format you are using is not possible to parse.
Think about it, there is no way of telling which of those digits are supposed to be interpreted as a pair "10" or a single "1" or even three "101".
In these scenarios, I would recommend redoing your file format to use characters instead of digit, giving you many more symbols to work with, and/or I strongly recommend using a delimiter like a , character between each of your. The file format CSV (Comma Separated Values) does exactly this.
You could create your levels in a spreadsheet program like Excel, Numbers (mac), LibreOffice, or an online spreadsheet tool like Google Sheets. You would do this by filling in the values in the cells, and then when you are done you would export / "save as" a .csv file.
You can parse the .csv file using a .NET library like CsvHelper (which I highly recommend using for reading CSV data in the real world)
Or, if you want to do it the quick and dirty way, you would read the file into memory using something like File.ReadAllLines, Trim() each line from the file, and then use Split(',') to split each line into its values. You can then iterate through your 2D array, and map it to another value if you prefer to do that!
I'd create an array of string to hold symbols, first of all -- probably "A"-"Z". Then, change the char[] to unsigned int[] (and other changes to support that, like the ToCharArray() calls).
Then, when you place the tiles, instead of passing newTiles[x].ToString(), pass symbols[newTiles[x]].
(Truthfully, I'd probably make the symbol array char, and change the signature of PlaceTile(), if that's an option).
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.
In the homework I am doing:
It is stated: "You may NOT use a number to access a tile - if you are using a number to find a tile, then it is most likely your design is not object oriented."
So essentially, my question is this:
If one were to not use an index to get an object from a list of objects, would the only solution be to use a foreach loop, every time you needed to retrieve a specific object?
If not, could you please provide an example (e.x. using a "for loop in an object oriented way", or other solutions instead of using an index)?
My concern is the amount of bloat (in lines) that might be created when not using for loops - and the affect that might have on speed.
e.g., I have the following:
for (int i = dimensions-1; i >= 0; i--)
{
map += "-";
for (int j = 0; j < dimensions; j++)
{
//For 4X4Y is 24. 3X4Y is 19. and so on.
//For 4X3Y is 23. 3X3Y is 18. and so on.
//As you can see: j * dimensions represents a "generalized" x value. adding it by i, represents its y value.
Tile currentTile = _game.TileSet[(j * dimensions) + i];
if (currentTile.Visitor == null)
map += "X-"; //X implies no robot
else
map += "R-"; //R implies robot
}
}
Yes this code is kind of ugly, but my list is formatted in such a way that I can't just use one foreach loop, and split the line on each x amount of tiles per line, as the list is stored vertically, and then horizontally, as opposed to horizontally, and then vertically (which would allow a simple foreach to suffice).
My concern is that if this is "not object oriented" - then using a foreach loop, would require a LOT more iterations.
Anyway, if anyone has an answer to the question I would really appreciate your thoughts.
Thanks.
Edit: Instead of using X and Y values, in a 2D List/Array, the list is 1d, and we are required to use association by having a list of connecting tiles, 4 elements, each element representing a typical compass direction in which the adjacent tile is located. There are no properties - however there is one method which can return an adjacent tile at a specified direction GetTileAtDirection(Direction).
The tileSet is a list of 25 elements, with 0 through 4 being the first 5 elements, X = 0, Y = 0 X = 0, Y = 1 X = 0, Y = 2 X = 0, Y = 3 X = 0, Y = 4. For the next five elements, X is incremented by one, representing the next five values in this "Y line"...
Note that there are no X and Y variables in which each tile can be accessed. This is a requirement in the homework.
If one were to not use an index to get an object from a list of
objects, would the only solution be to use a foreach loop, every time
you needed to retrieve a specific object?
No. I think the idea is that each tile should have a mapping of Direction to Tile, and GetTileAtDirection simply looks it up. The code that creates the tiles would need to assign the relationships, something like this:
nextTile = new Tile();
nextTile.AdjacentTiles[Direction.Left] = previousTile;
previousTile.AdjacentTiles[Direction.Right] = nextTile;
and so on. Of course, this code would probably be looking up the indices in TileSet, but the key is that an individual tile object can be completely independent from it -- the Tile class wouldn't even need a reference to a Game object or TileSet.
I have a long string, ex: "Please help me to solve this problem."
This string is so long to fit in a width of 100 pixels.
I need to get a substring of this string and substring will fit in 100 pixels. Ex: substring "Please help me to sol" is fit in 100 pixels.
Please help me how to estimate a substring like this. Thanks.
My application is Win Forms and C#.
As usual, the Win32 API has a function designed exactly for this: GetTextExtentExPoint
P/invoke declaration: http://pinvoke.net/default.aspx/gdi32/GetTextExtentExPoint.html
Looks like you could use TextRenderer::MeasureText().
this should work... not really tested and no optimisations, though
var g = Graphics.FromImage(someimage); // or any from hwnd etc... should use your panel/whatever
var f = new Font("YOURFONT", 12f, FontStyle.Regular, GraphicsUnit.Pixel); // replace with your vals
var yourtext = "yourtextyourtextyourtextyourtextyourtext";
while (g.MeasureString(yourtext, f).Width > 100)
yourtext = yourtext.Remove(yourtext.Length - 2, 1);
If all you are looking for is a good estimate, you might first measure the width of a representative string to determine the average character width of your font once. A good representative might not be just the alphabet; you might look for a character frequency histogram to get a better average width.
string Representative = "abcdefghijklmnopqrstuvwxyz";
float CharacterWidth;
using(Bitmap b = new Bitmap(0, 0))
using(Graphics g = Graphics.FromImage(b))
using(Font f = /* some font definition */)
{
CharacterWidth = g.MeasureString(Representative, f).Width / Representative.Length;
}
Then use that to estimate how many characters would fit within N pixels.
string Text = ...
int DisplayWidth = 100;
int FitLength = Math.Min(Text.Length, (int)(DisplayWidth / CharacterWidth));
string FitText = Text.Substring(0, FitLength);