Converting Path & StrokeWidth to Geometry - c#

I have a bit of code that takes a number of Points and creates multiple LineSegments to build up a Path.
System.Windows.Shapes.Path pathSegment = new System.Windows.Shapes.Path();
PathFigure pathFig = new PathFigure();
PathGeometry pathGeo = new PathGeometry();
pathFig.StartPoint = new Point(pointData[0].X, pointData[0].Y);
for (int loop = 1; loop < pointData.Count; loop++)
{
LineSegment ls = new LineSegment();
ls.Point = new Point(pointData[loop].X, pointData[loop].Y);
pathFig.Segments.Add(ls);
}
pathGeo.Figures.Add(pathFig);
pathSegment.Data = pathGeo;
pathSegment.Stroke = brush;
pathSegment.StrokeThickness = 22;
This creates my line with a width of 22px (roughly). Now if you look at the actual Data for this you can only see the LineSegement elements, which essentially gives you an output like this, where the real line is in black and the actual displayed line is in grey (excuse the dodgy mspaint sketch):
Now I want to perform a StrokeContains on the Geometry to see if a specified Point is within the entire pathSegment above (grey area). What it actually does though is check against the LineSegments (the black line).
Is there a better way to build up the Path? or is there a way of converting the pathSegment, including the StrokeWidth to a new Path?

It should work if you use the proper Pen Thickness in the StrokeContains call:
Point point = ...
Pen pen = new Pen { Thickness = pathSegment.StrokeThickness };
bool contains = pathSegment.Data.StrokeContains(pen, point);
Alternatively you could simply do a hit test on the Path:
bool contains = pathSegment.InputHitTest(point) != null;

Related

Stack polygons on top of each other, join by diagonal lines

I want to do a perfectly simple thing. I want to draw two quadrilateral polygons, which have the top and bottom lines as parallel diagonals. I also want to draw these polygons stacked on top of each other.
However, after I draw the two polygons, there is a tiny but visible gap between them. It looks like this:
Here's the code that I use (I'm with Silverlight, which means WriteableBitmap):
var bmp = new System.Windows.Media.Imaging.WriteableBitmap((int)this.displayImage.Width, (int)this.displayImage.Height);
var polygon = new System.Windows.Shapes.Polygon()
{
StrokeThickness = 0,
Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Black),
Points = new System.Windows.Media.PointCollection()
{
new System.Windows.Point(10, 10),
new System.Windows.Point(100, 40),
new System.Windows.Point(100, 130),
new System.Windows.Point(10, 100)
}
};
var polygon2 = new System.Windows.Shapes.Polygon()
{
StrokeThickness = 0,
Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Black),
Points = new System.Windows.Media.PointCollection()
{
new System.Windows.Point(10, 100),
new System.Windows.Point(100, 130),
new System.Windows.Point(100, 220),
new System.Windows.Point(10, 190)
}
};
bmp.Render(polygon, null);
bmp.Invalidate();
bmp.Render(polygon2, null);
bmp.Invalidate();
displayImage.Source = bmp;
Here displayImage is an Image control on the main grid.
What do I do to get rid of this gap?
P.S. If possible, I'd like solutions that do not use the WriteableBitmapEx library, as I don't want to drag an entire library into my project for such a small task. Also, if possible, I'd like to leave the polygons' size and coordinates the same.

Unable to Remove Child in WPF

I have a uniform grid that has rectangles dynamically added to it. I want to remove a particular rectangle, but I am getting the following error when trying to pass it to the Remove method:
Cannot convert from 'System.Drawing.Rectangle' to 'System.Windows.UIElement'
My code is:
Rectangle swatch = (Rectangle)ug_Thumbnails.FindName("s_" + _instance);
ug_Thumbnails.Children.Remove(swatch);
I tried casting, and got an error saying that you couldn't do it.
EDIT: Per request, here's the code to create the rectangle:
System.Windows.Shapes.Rectangle swatch = new System.Windows.Shapes.Rectangle();
swatch.Width = 50;
swatch.Height = 50;
swatch.Margin = new Thickness(0, 5, 5, 0);
swatch.StrokeThickness = 1;
swatch.Stroke = System.Windows.Media.Brushes.Gray;
swatch.Name = "s_" + name.ToString();
double groupsize = 100 / colors.Count();
DrawingBrush blackBrush = new DrawingBrush();
DrawingGroup checkersDrawingGroup = new DrawingGroup();
List<SolidColorBrush> brushes = colors;
double location = 0;
for (int i = 0; i < colors.Count(); i++)
{
GeometryDrawing drawing = new GeometryDrawing(brushes[i], null,
new RectangleGeometry(new Rect(location, 0, groupsize, groupsize)));
checkersDrawingGroup.Children.Add(drawing);
location += groupsize;
}
blackBrush.Drawing = checkersDrawingGroup;
swatch.Fill = blackBrush;
swatch.MouseUp += new MouseButtonEventHandler(loadSwatchResources);
ug_Thumbnails.Children.Add(swatch);
You need to use the Rectangle in System.Windows.Shapes when trying to reference a rectangle in WPF. This is specifically for rectangles in WPF and as such is a bit different than the System.Drawing rectangle class. You should be able to cast to this version of rectangle since it derives from FrameworkElement. See http://msdn.microsoft.com/en-us/library/system.windows.shapes.rectangle(v=vs.110).aspx for more info.

How to draw vertical line on mschart that fills graph but isn't infinite?

I am trying to draw a vertical line that is anchored to a point. I tried to use the height of my Y axis which is fixed to draw the line, but it wasn't centered correctly. So right now I have an infinite line, but that I want is the line to just fill the graph like so
VerticalLineAnnotation lineannot = new VerticalLineAnnotation();
lineannot.AnchorDataPoint = chart.Series[item].Points.Last();
lineannot.LineColor = Color.Red;
lineannot.Width = 3;
lineannot.Visible = true;
lineannot.IsInfinitive = true;
chart.Annotations.Add(lineannot);
IsInfinitive is complemented by ClipToChartArea; you can set the line to be clipped to a ChartArea like this:
lineannot.ClipToChartArea = chart.ChartAreas[item].Name;
assuming item is the right area name or index..
Note that ClipToChartArea takes the name of the chart area!
This is the simplest way to do it.
It is also possible to control an annotation's position and size directly:
// Note that directly after adding points this will return NaN:
double maxDataPoint = chart1.ChartAreas[0].AxisY.Maximum;
double minDataPoint = chart1.ChartAreas[0].AxisY.Minimum;
LineAnnotation annotation2 = new LineAnnotation();
annotation2.IsSizeAlwaysRelative = false;
annotation2.AxisX = chart1.ChartAreas[0].AxisX;
annotation2.AxisY = chart1.ChartAreas[0].AxisY;
annotation2.AnchorY = minDataPoint;
annotation2.Height = maxDataPoint - minDataPoint;;
annotation2.Width = 0;
annotation2.LineWidth = 2;
annotation2.StartCap = LineAnchorCapStyle.None;
annotation2.EndCap = LineAnchorCapStyle.None;
annotation2.AnchorX = 21; // <- your point
annotation2.LineColor = Color.Pink; // <- your color
chart1.Annotations.Add(annotation2);

Extra lines appear when EdgeMode.Aliased option is used in WPF

I have encountered a very odd problem today using WPF. Here is the code I have used to draw 10000 LineGeometry objects.
// Draw 10000 lines
var g = new GeometryGroup();
var x = 0;
var y = 0;
var n = 1;
while (n < 10000)
{
x = x + 20;
if (x > 600)
{
x = 0;
y = y + 20;
}
var l = new LineGeometry
{
StartPoint = new Point(x, y),
EndPoint = new Point(x, y + 15)
};
g.Children.Add(l);
n++;
}
var drawing = new GeometryDrawing {Geometry = g};
var drawingGroup = new DrawingGroup();
drawingGroup.Children.Add(drawing);
var myPen = new Pen {Thickness = 1, Brush = Brushes.Yellow};
drawing.Pen = myPen;
var myImage = new Image {Stretch = Stretch.None, Margin = new Thickness(10)};
var myDrawingImage = new DrawingImage {Drawing = drawingGroup};
myImage.Source = myDrawingImage;
canvas.Children.Add(myImage);
Now as you can see the result is not crisp and I used the code below to achieve better results.
RenderOptions.SetEdgeMode(myImage, EdgeMode.Aliased);
The image gets crisp but has another side effects as you can see in the image below.
Some lines are now shown.
Some extra odd lines are shown.
I have included the full code so you can experiment this for yourself.
Note:
I'm using ZoomBorder class around the Canvas canvas.
Pan & Zoom Image
Have you tried using
RenderOptions.SetBitmapScalingMode(myImage, BitmapScalingMode.HighQuality);
instead?
You can also set it on your Canvas control as well..
<Canvas RenderOptions.BitmapScalingMode="HighQuality"> ...
This is not a real answer but I want add something to that odd behavior of WPF in hope somebody can come up with a real explanation.
I added a single line from (0,0)-(20000,20000). Creating 8000 line objects (n < 8000) produces the expected result like this:
Now, creating 9000 line objects will totally mess it up, but see for yourself:
Without setting EdgeMode.Aliased it looks fine, even with high counts of objects.

(Composite) Geometry confusion in c#

I'm trying to create 1 complex composite shape on an InkCanvas, but I must be doing something wrong, as what I was expecting to happen, is not. I've tried several different incarnations of accomplishing this.
So I have this method.
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
Stroke stroke = e.Stroke;
// Close the "shape".
StylusPoint firstPoint = stroke.StylusPoints[0];
stroke.StylusPoints.Add(new StylusPoint() { X = firstPoint.X, Y = firstPoint.Y });
// Hide the drawn shape on the InkCanvas.
stroke.DrawingAttributes.Height = DrawingAttributes.MinHeight;
stroke.DrawingAttributes.Width = DrawingAttributes.MinWidth;
// Add to GeometryGroup. According to http://msdn.microsoft.com/en-us/library/system.windows.media.combinedgeometry.aspx
// a GeometryGroup should work better at Unions.
_revealShapes.Children.Add(stroke.GetGeometry());
Path p = new Path();
p.Stroke = Brushes.Green;
p.StrokeThickness = 1;
p.Fill = Brushes.Yellow;
p.Data = _revealShapes.GetOutlinedPathGeometry();
selectionInkCanvas.Children.Clear();
selectionInkCanvas.Children.Add(p);
}
But this is what I get:
http://img72.imageshack.us/img72/1286/actual.png
So where am I going wrong?
TIA,
Ed
The problem is that the Geometry returned by stroke.GetGeometry() is a path around the stroke, so the area you're filling with yellow is just the middle of the stroke. You can see this more clearly if you make the lines thicker:
_revealShapes.Children.Add(stroke.GetGeometry(new DrawingAttributes() { Width = 10, Height = 10 }));
You can do what you want if you convert the list of stylus points to a StreamGeometry yourself:
var geometry = new StreamGeometry();
using (var geometryContext = geometry.Open())
{
var lastPoint = stroke.StylusPoints.Last();
geometryContext.BeginFigure(new Point(lastPoint.X, lastPoint.Y), true, true);
foreach (var point in stroke.StylusPoints)
{
geometryContext.LineTo(new Point(point.X, point.Y), true, true);
}
}
geometry.Freeze();
_revealShapes.Children.Add(geometry);

Categories

Resources