For some kind of mask dialog I want to be able to invert a geometry in C#. For example I would like to invert a rectange. I've got this working for filled rectangles but the same doesn't work for non filled ones.
For example if I have this rectangle on a canvas:
And I invert this rectangle with the following code:
RectangleGeometry line = new RectangleGeometry(_myRectangle);
RectangleGeometry geo = new RectangleGeometry(_myCanvasRectangle);
PathGeometry intersect = Geometry.Combine(line, geo, GeometryCombineMode.Xor, null);
drawingContext.DrawGeometry(
new SolidColorBrush(Color.FromArgb(99, _myObjectColor.R, _myObjectColor.G, _myObjectColor.B)),
new Pen(new SolidColorBrush(_myObjectColor), _myActualLineWidth),
intersect
);
I get the following result (where the gray region is the filled region)
Does anybody how I could achieve a result where only the rectangle (the black line in the first image) is spared and the rest is returned (filled with gray)?
Thanks guys!
So I found the solution myself:
It seems geometries under C# are always filled "shapes". But you can get the outline (even with desired stroke thickness !) by using the GetWidenedPathGeometry method!
My example looks like this:
RectangleGeometry outerRect = new RectangleGeometry(_myRectangle);
RectangleGeometry geo = new RectangleGeometry(_myCanvasRectangle);
PathGeometry outerRectLine = outerRect.GetWidenedPathGeometry(new Pen(new SolidColorBrush(Colors.White), _myActualLineWidth));
PathGeometry intersect = Geometry.Combine(geo, outerRectLine, GeometryCombineMode.Exclude, null);
drawingContext.DrawGeometry(
new SolidColorBrush(System.Windows.Media.Color.FromArgb(99, _myObjectColor.R, _myObjectColor.G, _myObjectColor.B)),
new System.Windows.Media.Pen(new SolidColorBrush(Colors.White), ActualLineWidth),
intersect
);
Hope this helps someone else!
Related
How to complete a square using 4 lines?
<- Please see the picture.
the square
trying to find a min' square
Hi, I work with EMGUcv and I am supposed to identify 4 lines from which to complete an exact square. Maybe someone has an idea? I tried to find the least square but it is not exact and it is difficult to work with the results of it.
Thanks.
Image<Bgr, Byte> Clean_Image = new Image<Bgr, Byte>(Original).CopyBlank();
Method.ERectangle ERRectangle = null;//<------my Rectangle object..
if (Pointlist.Count > 0)
{
ERRectangle = new Method.ERectangle(Emgu.CV.CvInvoke.MinAreaRect(Pointlist.ToArray()));
Size s = new Size((int)Method.LineSize(ERRectangle.MainRectangle.Points[0], ERRectangle.MainRectangle.Points[2]),(int)Method.LineSize(ERRectangle.MainRectangle.Points[1], ERRectangle.MainRectangle.Points[3]));
Method.ERectangle rRect = new Method.ERectangle(new RotatedRect(ERRectangle.Center, s, (float)ERRectangle.ContourAngle-(float)45));
//I tried to find a square and align with the lines.. A possible idea but not accurate
Emgu.CV.CvInvoke.Polylines(Clean_Image, rRect.MainRectangle.Points.ToArray(), true, new MCvScalar(255, 0, 0), 7);
}
I have loaded a set of vector data into my application whose bounds extend from {-165835.328125, 6582072.5} to {-64674.02734375, 6609767} giving a Width of 101161.30078125 and a Height of 27694.5.
I want to show this data on my FrameworkElement which defaults to 663 pixels wide and 468 pixels high and obviously resize it when the element is resized. As far as I could judge in order scale my DrawingGroup I need to create a ScaleTransform and add it to a TransformGroup which I can then set as the Transform of the DrawingGroup - I figure I also need an offset in order to bring the origin location of my data to the top left corner of the view, although in retrospect I might be better off aligning the centres.
This is the code I have at the moment:
var transformWidth = this.drawingGroup.Bounds.Width;
var scale = width / transformWidth;
var left = this.drawingGroup.Bounds.Left * -1;
var top = this.drawingGroup.Bounds.Top * -1;
var translateTransform = new TranslateTransform(left, top);
var scaleTransform = new ScaleTransform();
scaleTransform.CenterX = (drawingGroup.Bounds.Width / 2);
scaleTransform.CenterY = (drawingGroup.Bounds.Height / 2);
scaleTransform.ScaleX = scale;
scaleTransform.ScaleY = scale;
var tg = new TransformGroup()
{
Children = new TransformCollection() { translateTransform, scaleTransform }
};
var oldTransform = this.drawingGroup.Transform;
this.drawingGroup.Transform = tg;
context.DrawDrawing(this.drawingGroup);
this.drawingGroup.Transform = oldTransform;
This is rough on multiple levels but the main one bothering me right now is that it doesn't work. I can adjust the data on input ( as in if I translate the points manually when generating the geometry ) and it will draw only slightly wrong so I think the geometry exists but is outside the visible area, as far as I can tell the translation just isn't doing anything. I tried pushing the transforms onto the DrawingContext too but that didn't seem to work either ( I would quite like to be able to maintain the original coordinates on the DrawingGroup so I can relate things back to them later, hence the possibly unnecessary Transform switcheroo ) and when I inspect the child collection of the DrawingGroup their geometry seems untranslated after the translate was applied- I don't know whether or not that is correct.
What do I need to do to get my data visible in my FrameworkElement?
The Bounds property takes the current transform into account.
Reset the Transform property before accessing the Bounds:
drawingGroup.Transform = Transform.Identity;
var bounds = drawingGroup.Bounds;
var scale = width / bounds.Width;
var matrix = new Matrix();
matrix.Translate(-bounds.X, -bounds.Y);
matrix.Scale(scale, scale);
drawingGroup.Transform = new MatrixTransform(matrix);
As a note, in order to keep the thickness of the Drawings' Pens, better transform the underlying Geometries instead of the Drawings.
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:
I want to draw a Line between 2 rows while using drag and drop. The function of this is simply visual, so that the user knows, where he is dropping the row. The line should look like the excel onces. Here my code:
Pen _marqueePen = new Pen(Color.Gray, 2);
float[] dashValues = {1f,1f};
_marqueePen.DashPattern = dashValues;
But this looks like that
I want to look it like that:
I'm WinForms and the C1 Flexgrid control.
You can use a Custom Pen like this:
using (Pen pen = new Pen(Color.Gray, 4f) )
{
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Custom;
pen.DashPattern = new float[] { 0.25F, 0.25F };
// now draw your stuff..
}
Note the doc on MSDN:
The elements in the dashArray array set the length of each dash
and space in the dash pattern. The first element sets the length of a dash,
the second element sets the length of a space, the third element sets
the length of a dash, and so on. Consequently, each element should be a
non-zero positive number.
The length of each dash and space in the dash pattern is the product
of the element value in the array and the width of the Pen.
You can pick any pen width and any dash&gap lengths as long as you keep their relation in mind.. So if you want the finest dashes, make sure they multiply to 1.0 pixels!
Here is the resulting line:
Some options:
You could use a PNG graphic that mimics that excel behaviour and then draw it on the control (you'll have to tile your image vertically).
Draw three lines with your code, with offset of y-axis & x-axis one pixel.
That looks to me more like a rectangle filed with HatchBrush having HatchStyle.Percent50 and height of 3.
You could try
Rectangle rect = new Rectangle(0, 0, 500, 3) //you will use the values here from your cursor but height will be 3
HatchBrush brush = new HatchBrush(HatchStyle.Percent50, Color.Black);
g.FillRectangle(brush, rect);
I am currently doing a project in which I've managed to identify the peak I want. However, I wanted to do more like circling the particular point with a label attached to it. Is it possible to do that in Zedgraph?
I've attached a snippet of my code which only include a text label to that point, and I wanted to do more so people will identify the point more easily.
PointPair pt = myCurve.Points[i-1];
const double offset = 0.8;
TextObj text = new TextObj("P", pt.X, pt.Y + offset,
CoordType.AxisXYScale, AlignH.Left, AlignV.Center);
text.ZOrder = ZOrder.A_InFront;
text.FontSpec.Border.IsVisible = false;
text.FontSpec.Fill.IsVisible = false;
text.FontSpec.Fill = new Fill( Color.FromArgb( 100, Color.White ) );
myPane.GraphObjList.Add(text);
Any help is appreciated! Thanks!
Make a LineItem as follows
LineItem line = new LineItem("Point", new double[] {pt.x}, new double[] {pt.y}, Color.Black, SymbolType.Circle);
line.Symbol.Size = 20;
line.Symbol.Fill = new Fill(Color.Transparent);
myPane.CurveList.Add(line);
This should create a large empty circle centered around your point. Obviously, you can adjust color and size as you see fit, and the ZOrder if you need to. You might want to adjust your legend so it doesn't include this point. Alternatively, you can name this line with your label and leave it in the legend as a way of tagging it. The only other way for a label is to do what you're doing, as I'm not sure of a way to associate labels directly to a line.