How can I put dots on the chart where my datapoints are? - c#

I'm using the Windows.Forms.DataVisualization.Charting class to draw a simple char plotting a simple courb.
My courb is correclty plotted, but the points are not shown. Like dots or crosses where the points are.
I tried using datapoint.BackImage, but that doesn't show anything.
I'm sure that the image is found because I store other images in the exact same folder, and they are correctly read when I use the same path.
The code where I feed the DataPoint:
foreach (MesureTaille mesureTaille in tailles)
{
DataPoint point = new DataPoint(mesureTaille.age, mesureTaille.taille);
point.BackImage = string.Concat(
Application.StartupPath.Remove(Application.StartupPath.IndexOf("\\bin\\Debug")),
"/BackgoundImage/dot.png");
Serie_Age_Taille.Points.Add(point);
}

As DavidG and TaW pointed out, what I needed was Series.MarkerStyle :
// This line sets the dots !
Serie_Age_Taille.MarkerStyle = MarkerStyle.Cross;
foreach (MesureTaille mesureTaille in tailles)
{
// And I don't need to do anything on the DataPoints
DataPoint point = new DataPoint(mesureTaille.age, mesureTaille.taille);
Serie_Age_Taille.Points.Add(point);
}

Related

Make Chart Legend represent two colors

I created a column chart in my application which look like this:
As you can see the positive values are green and the negative values are red. I need to represent this in the legend. I just don't know how.
What I already tried:
I added CustomItems to the Legend. Here is the code:
Legend currentLegend = chart.Legends.FindByName(chart.Series[series].Legend);
if (currentLegend != null)
{
currentLegend.LegendStyle = LegendStyle.Table;
LegendItem li = new LegendItem();
li.Name = series;
li.Color = Color.Red;
li.BorderColor = Color.Transparent;
currentLegend.CustomItems.Add(li);
}
This results in the following representation:
I could live with that. But as soon as I add further series to the chart the order of the elements gets destroyed. Here is an example:
I would like to have one of the two options:
keep the positive and negative color together
or an even better solution could be to have just one tile in the legend which is double colored. Something like this:
Could you please help me solving this issue?
Many thanks in advance!
Yes, you can do that. Note however that you can't really modify the original Legend. So for a perfect result you would need to create a new custom Legend instead.
See here for an example that does that; note especially the positioning..!
But maybe you can get away a little easier; see below!
The first rule to understand is that added LegendItems always go to the end of the list. So you can't keep them together, unless your added Series are at the start. You can do that by using Series.Insert(..) but using those two-color rectangles is much nicer, imo..
To show the graphics you want, simply create them as bitmaps, either on disk or on the fly and store them in the Images collection of the chart:
Legend L = chart1.Legends[0];
Series S = chart1.Series[0];
// either load an image from disk (or resources)
Image img = Image.FromFile(someImage);
// or create it on the fly:
Bitmap bmp = new Bitmap(32, 14);
using (Graphics G = Graphics.FromImage(bmp))
{
G.Clear(Color.Red);
G.FillPolygon(Brushes.LimeGreen, new Point[] { new Point(0,0),
new Point(32,0), new Point(0,14)});
}
Now add it to the chart's NamedImage collection:
chart1.Images.Add(new NamedImage("dia", bmp));
Now you can create as many LegendItems as you need:
LegendItem newItem = new LegendItem();
newItem.ImageStyle = LegendImageStyle.Rectangle;
newItem.Cells.Add(LegendCellType.Image, "dia", ContentAlignment.MiddleLeft);
newItem.Cells.Add(LegendCellType.Text, S.Name, ContentAlignment.MiddleLeft);
And add them to the Legend:
L.CustomItems.Add(newItem);
Unfortunately you can't delete the original item.
What you can do, besides creating a new Legend from scratch, is this:
Clear the text like this:
S.LegendText = " "; // blank, not empty!
As you have set the Colors of all the DataPoints anyway, you can also get rid of the blue rectangle:
S.Color = Color.Transparent;
This will also make all points without colors transparent, so make sure to color them all!
Note that some space in the Legend it still taken!
Here is the result, with a few colored points and your line series added:

Chart - Automatic highlighting Zero Points

So basically I have a graph as below in the image, I am going to be implementing some check boxes to enable and disable each line graph.
My main question is:
As you can see on the graphs there is dips in the data down to ZERO - How do I get these to automatically highlight for the user to see!
I.e. Set a point on the X Axis that displays interval!
One way would be to add a another Series of ChartType Point and add only those Zero Points as red dots..:
// add a new Series:
Series sz = chart1.Series.Add("Zeroes");
sz.ChartType = SeriesChartType.Point;
sz.Color = Color.Red;
sz.BorderWidth = 3;
// add Points wherever the other series is zero or less
foreach (DataPoint dp in chart1.Series[0].Points )
{
if (dp.YValues[0] <= 0) sz.Points.AddXY(dp.XValue, 0);
}
If you need to check on more Series you can repeat the loop for them..

How to add spikes to line on Winform chart?

I am drawing a line on a graph from numbers read from a text file. There is a number on each line of the file which corresponds to the X co-ordinate while the Y co-ordinate is the line it is on.
The requirements have now changed to include "special events" where if the number on the line is followed by the word special a spike will appear like image below:
Currently the only way I can find is to use a line for each spike, however there could be a large of these special events and so needs to be modular. This seems an efficient and bad way to program it.
Is it possible to add the spikes to the same graph line? Or is it possible to use just one additional line and have it broken (invisible) and only show where the spikes are meant to be seen?
I have looked at using bar graphs but due to other items on the graph I cannot.
The DataPoints of a Line Chart are connected so it is not possble to really break it apart. However each segment leading to a DataPoint can have its own color and that includes Color.Transparent which lends itself to a simple trick..
Without adding extra Series or Annotations, your two questions can be solved like this:
To simply add the 'spikes' you show us in the 2nd graph, all you need to do is to insert 2 suitable datapoints, the 2nd being identical to the point the spike is connected to.
To add an unconnected line you need to 'jump' to its beginning by adding one extra point with a transparent color.
Here are two example methods:
void addSpike(Series s, int index, double spikeWidth)
{
DataPoint dp = s.Points[index];
DataPoint dp1 = new DataPoint(dp.XValue + spikeWidth, dp.YValues[0]);
s.Points.Insert(index+1, dp1);
s.Points.Insert(index+2, dp);
}
void addLine(Series s, int index, double spikeDist, double spikeWidth)
{
DataPoint dp = s.Points[index];
DataPoint dp1 = new DataPoint(dp.XValue + spikeDist, dp.YValues[0]);
DataPoint dp2 = new DataPoint(dp.XValue + spikeWidth, dp.YValues[0]);
DataPoint dp0 = dp.Clone();
dp1.Color = Color.Transparent;
dp2.Color = dp.Color;
dp2.BorderWidth = 2; // optional
dp0.Color = Color.Transparent;
s.Points.Insert(index + 1, dp1);
s.Points.Insert(index + 2, dp2);
s.Points.Insert(index + 3, dp0);
}
You can call them like this:
addSpike(chart1.Series[0], 3, 50d);
addLine(chart1.Series[0], 6, 30d, 80d);
Note that they add 2 or 3 DataPoints to the Points collection!
Of course you can set the Color and width (aka BorderWidth) of the extra lines as you wish and also include them in the params list..
If you want to keep the points collection unchanged you also can simply create one 'spikes series' and add the spike points there. The trick is to 'jump' to the new points with a transparent line!

How to change ILNumerics plot marker type?

I have set of random generated points in 3D Scene, and in runtime I want to change the type of point markers to, for example, triangles, as in the picture:
Is it possible? How can I achieve this? Also I need change color for some points.
Scene initialization code below:
ILArray<float> points = ILMath.tosingle(ILMath.randn(3, 1000));
var scene = new ILScene
{
new ILPlotCube(twoDMode: false)
{
new ILPoints
{
Positions = points,
Color = null,
Size = 2
}
}
};
Markers (ILMarker) and 'points' (ILPoints) are very different beasts. Markers are much more flexible configurable, mostly look nicer and are much more expensive to render. They commonly consist out of a border (line shape) and a filled area (triangles shape) and come with a number of predefined looks.
ILPoints on the other hand are designed to be fast and easy. one can easily create millions of points without decreasing the plotting performance. Don't try this with markers! But such points are what they are: filled circles. It's it. No borders, no different shapes.
However, if you want to give it a try - even for the 1000 points in your questions - you can do so. Just use an ILLinePlot instead and configure a marker for it. You may set the line color to Color.Empty to have the markers showing up alone.
new ILLinePlot(points, lineColor: Color.Empty, markerStyle: MarkerStyle.TriangleDown)
In order to get individual colors for individual point markers you would split your markers (points) up into individual set of points. Create an ILLinePlot for each set of points using the scheme described above.
The part of your question dealing with 'dynamic' is also easy: You can change the type of markers as well as any other property at runtime. Here is one simple example which toogles the markers between red triangle markers and white rectangle markers by clicking anywhere on the scene:
ilPanel1.Scene.MouseClick += (_s, _a) => {
if (_a.DirectionUp) return;
var lp = ilPanel1.Scene.First<ILLinePlot>();
if (lp != null) {
if (lp.Marker.Style == MarkerStyle.TriangleDown) {
lp.Marker.Style = MarkerStyle.Rectangle;
lp.Marker.Fill.Color = Color.White;
} else {
lp.Marker.Style = MarkerStyle.TriangleDown;
lp.Marker.Fill.Color = Color.Red;
}
lp.Configure();
ilPanel1.Refresh();
}
};

How to get the center point of a Face or a PlanarFace element in Revit

I'm doing a Revit Macro to get the center point of a part (floor part) to check if it is inside a room or a space.
I couldn't get much of the BoundingBox object which is giving me a point outside the part, so I tried to use the Geometry element internal faces getting the mesh vertices but I'm stuck calculating the mid point.
I'm using a rather naive algorithm shown in the snippet below, but it's giving me false results as it seems to be affected by the initial default of min/max variables.
Any suggestions?
PS: DebugTools is a custom helper class of my own.
public void ZoneDetect()
{
Document doc = this.ActiveUIDocument.Document;
using (Transaction t = new Transaction(doc,"Set Rooms By Region"))
{
t.Start();
FilteredElementCollector fec =
new FilteredElementCollector(doc)
.OfClass(typeof(Part))
.OfCategory(BuiltInCategory.OST_Parts)
.Cast<Part>();
foreach (Part p in fec)
{
Options op = new Options();
op.View=doc.ActiveView;
op.ComputeReferences=true;
GeometryElement gm=p.get_Geometry(op);
Solid so = gm.First() as Solid;
PlanarFace fc=so.Faces.get_Item(0) as PlanarFace;
foreach (PlanarFace f in so.Faces)
{
if (f.Normal == new XYZ(0,0,-1)) fc=f;
}
XYZ max = new XYZ();
XYZ min = new XYZ();
int no = 0;
foreach (XYZ vx in fc.Triangulate().Vertices)
{
// Just for debugging
DebugTools.DrawModelTick(vx,doc,"Max");
doc.Regenerate();
TaskDialog.Show("Point:"+no.ToString(),vx.ToString());
no++;
//Comparing points
if (vx.X>max.X) max=new XYZ (vx.X,max.Y,0);
if (vx.Y>max.Y) max=new XYZ (max.X,vx.Y,0);
if (vx.X<min.X) min=new XYZ (vx.X,min.Y,0);
if (vx.Y<min.Y) min=new XYZ (min.X,vx.Y,0);
}
XYZ mid = new XYZ(max.X-min.X,max.Y-min.Y,0);
DebugTools.DrawModelTick(mid,doc,"Mid");
DebugTools.DrawModelTick(max,doc,"Max");
DebugTools.DrawModelTick(min,doc,"Min");
}
t.Commit();
}
}
It seems like you're looking for the center of gravity of a polygon. An algorithm for that can be found here: Center of gravity of a polygon
Once you have a Face object, you can enumerate its edges to receive a list of vertex points. Use the longest of the EdgeLoops in the face. Collect all the points and make sure that they are in the right order (the start and end points of the edges might need to be swapped).
Daren & Matt thanks a lot for your answers,
Since I'm dealing with rather simple shapes ( mainly rectangles ) I just needed to get a point roughly near the center to test whether it is inside a room, my problem was with the naive algorithm I was using which turned out to be wrong.
I corrected it as follows:
XYZ midSum = Max + Min;
XYZ mid = new XYZ(midSum.X/2 , midSum.Y/2,0);
I will look into refining it using the link you've provided, but as for now I will get into finishing my task in hand.
Many thanks

Categories

Resources