I've stumbled upon converting Ellipse to PathGeometry.
I've tried to use ArcSegment to present Ellipse, but still don't know how to convert Ellipse size to ArcSegment size. I'm trying to segment Ellipse into two part, but there are other approaches, i.e. Bezier curves.
Need clues how to convert Ellipse to ArcSegment collection.
The whole process could be looking like that:
public static PathFigure ToFigures(this Ellipse ellipse)
{
var pathFigure = new PathFigure {IsClosed = true};
var arcSegment1 = new ArcSegment();
var arcSegment2 = new ArcSegment();
pathFigure.Segments.Add(arcSegment1);
pathFigure.Segments.Add(arcSegment2);
return pathFigure;
}
You could directly use an EllipseGeometry for the Path Data:
path = new Path
{
Data = new EllipseGeometry { RadiusX = 100, RadiusY = 50 }
};
Using the size of the Ellipse it would look like this:
path = new Path
{
Data = new EllipseGeometry
{
RadiusX = ellipse.ActualWidth / 2,
RadiusY = ellipse.ActualHeight / 2
}
};
Related
I am capturing InkStrokes and have a need to create a scaled bitmap image of the strokes in the background. The captured images need to be of uniform size regardless of how big the bounding box of the ink.
For example, if original ink stroke is drawn and the bounding box top/left is 100,100 and size is 200,200 on the ink canvas, I want the ink to start at 0,0 of the new rendered bitmap that is 50,50 size (ignore impact of stroke width right now).
I have figured out how to scale the ink strokes (thanks StackOverflow) but not how to move the strokes. Right now, it seems I have to create a bitmap the size of the InkCanvas, render the scaled ink, then crop bigger image to the correct size.
I've tried using the InkStroke.PointTranslate via
var scaleMatrix = Matrix3x2.CreateScale(scale);
scaleMatrix.Translation = -offset; // top/left of ink stroke bounding box
stroke.PointTransform = scaleMatrix;
But the coordinates do not come out correct.
Any help much appreciated.
You can combine transformations by multiplying matrices. This works for me
var strokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
var boundingBox = inkCanvas.InkPresenter.StrokeContainer.BoundingRect;
var matrix1 = Matrix3x2.CreateTranslation((float)-boundingBox.X, (float)-boundingBox.Y);
var matrix2 = Matrix3x2.CreateScale(0.5f);
var builder = new InkStrokeBuilder();
var newStrokeList = new List<InkStroke>();
foreach (var stroke in strokes)
{
newStrokeList.Add(builder.CreateStrokeFromInkPoints
(stroke.GetInkPoints(), matrix1 * matrix2));
}
//Add the translated and scaled strokes to the inkcanvas
inkCanvas.InkPresenter.StrokeContainer.AddStrokes(newStrokeList);
Maybe I was still doing something wrong, but it appears you cannot use InkStrokeBuilder.CreateStrokeFromInkPoints with more than one kind of transform. I tried all kinds of combinations/approaches, and just could not get it to work.
Here is my solution...
private static IList<InkStroke> GetScaledAndTransformedStrokes(IList<InkStroke> strokeList, float scale)
{
var builder = new InkStrokeBuilder();
var newStrokeList = new List<InkStroke>();
var boundingBox = strokeList.GetBoundingBox();
foreach (var singleStroke in strokeList)
{
var translateMatrix = new Matrix(1, 0, 0, 1, -boundingBox.X, -boundingBox.Y);
var newInkPoints = new List<InkPoint>();
var originalInkPoints = singleStroke.GetInkPoints();
foreach (var point in originalInkPoints)
{
var newPosition = translateMatrix.Transform(point.Position);
var newInkPoint = new InkPoint(newPosition, point.Pressure, point.TiltX, point.TiltY, point.Timestamp);
newInkPoints.Add(newInkPoint);
}
var newStroke = builder.CreateStrokeFromInkPoints(newInkPoints, new Matrix3x2(scale, 0, 0, scale, 0, 0));
newStrokeList.Add(newStroke);
}
return newStrokeList;
}
I ended up having to apply my own translate transform then use the builder.CreateStrokeFromInkPoints with a scale matrix applied to get the results I wanted. GetBoundingBox is my own extension:
public static class RectExtensions
{
public static Rect CombineWith(this Rect r, Rect rect)
{
var top = (r.Top < rect.Top) ? r.Top : rect.Top;
var left = (r.Left < rect.Left) ? r.Left : rect.Left;
var bottom = (r.Bottom < rect.Bottom) ? rect.Bottom : r.Bottom;
var right = (r.Right < rect.Right) ? rect.Right : r.Right;
var newRect = new Rect(new Point(left, top), new Point(right, bottom));
return newRect;
}
}
I have the scenario as follows, I am drawing an arc between two points in WPF, I need to fill the arc, but I want to fill the arc sector touching the center, but WPF fills up differently. How do I get the arc filled up the way I want?
PathGeometry pathGeometry = new PathGeometry();
PathFigure pathFigure = new PathFigure();
pathFigure.StartPoint = start;
ArcSegment arc = new ArcSegment(end, new Size(radiusX, radiusY), 0.0, large, d, true); //large & d corresponds to size & direction
pathFigure.Segments.Add(arc);
pathGeometry.Figures.Add(pathFigure);
SolidColorBrush fill = new SolidColorBrush(color);
drawingContext.DrawGeometry(fill, pen, pathGeometry);
My code produces the output as this :
whereas my requirement is this one:
Is there a way of getting this done? TIA.
Sure, you just need at add a line segment to go along with your arc segment.
PathGeometry pathGeometry = new PathGeometry();
PathFigure pathFigure = new PathFigure();
pathFigure.StartPoint = start;
ArcSegment arc = new ArcSegment(end, new Size(radiusX, radiusY), 0.0, large, d, true); //large & d corresponds to size & direction
pathFigure.Segments.Add(arc);
//line segment takes the path to the origin
LineSegment line = new LineSegment(new Point(originX, originY), true);
pathFigure.Segments.Add(line );
pathGeometry.Figures.Add(pathFigure);
SolidColorBrush fill = new SolidColorBrush(color);
drawingContext.DrawGeometry(fill, pen, pathGeometry);
How to draw a bezier with two colors? like the picture below?
I already have drawn the bezier, I can fill it with any color, however I cant the gradient working.
this is what im doing, im using a Path to create the bezier by the way.
private void test()
{
System.Windows.Media.GradientStop GradientStop1 = new System.Windows.Media.GradientStop();
System.Windows.Media.GradientStop GradientStop2 = new System.Windows.Media.GradientStop();
System.Windows.Media.LinearGradientBrush p_Fill; p_Fill = new System.Windows.Media.LinearGradientBrush(Colors.Blue, Colors.Red, new Point(0, 0.5), new Point(1, 0.5));
p_Fill.GradientStops.Add(GradientStop1);
p_Fill.GradientStops.Add(GradientStop2);
Bez.Fill = p_Fill;
}
This is how it should be
This is what I get
If you need a sharp cut between the 2 colored halves, you have to need more GradientStops:
var grad3 = new System.Windows.Media.GradientStop()
{Offset = 0.5, Color=Colors.Blue};
var grad4 = new System.Windows.Media.GradientStop()
{Offset = 0.5, Color=Colors.Red};
GradientStop2.Offset = 1;
p_Fill.GradientStops.Add(GradientStop1);
p_Fill.GradientStops.Add(grad3);
p_Fill.GradientStops.Add(grad4);
p_Fill.GradientStops.Add(GradientStop2);
Addtionally, you have to set the Brush for the Stroke, not the Fill. The StrokeThickness determines the thickness of the curve:
Bez.Stroke = p_Fill;
Bez.StrokeThickness = new Thickness(10);
I would like to dynamically add a Triangle to my canvas. I can do this but only in one very specific way that does not work for my application. What I would like to do is supply a point and a size and get back a triangle.
var poly = shape as Polygon;
Polygon p = new Polygon
{
//Width = size,
//Height = size,
Fill = new SolidColorBrush(Colors.Red),
ManipulationMode = ManipulationModes.All,
RenderTransform = new CompositeTransform()
};
int w = 200;
int h = 200;
Point start = new Point(400, 200);
var right = new Point(start.X + w, start.Y);
var top = new Point(start.X + (w / 2), start.Y - (h));
poly.Points.Add(point);
poly.Points.Add(right);
poly.Points.Add(top);
poly.Points.Add(point);
Then I add the shape to my canvas child controls and and set the X Y on the shapes RenderTransform. Nothing appears. If however I just do this:
Polygon p = new Polygon
{
//Width = size,
//Height = size,
Fill = new SolidColorBrush(Colors.Red),
ManipulationMode = ManipulationModes.All,
RenderTransform = new CompositeTransform()
};
p.Points.Add(new Point(300, 200));
p.Points.Add(new Point(400, 125));
p.Points.Add(new Point(400, 275));
p.Points.Add(new Point(300, 200));
It renders a Triangle just fine. However if you supply a width and height to the above code it will stop rendering.
Can I just create a triangle of a certain size, without having to set actual points on the canvas at first, much like you do an Ellipse or a Rectangle?
What is the best solution for getting the middle point of an ArcSegment in a path and label it in WPF?
This should work:
//the given arc (or any other segments)
var arc = new ArcSegment(
point: new Point(200, 100),
size: new Size(100, 50),
rotationAngle: 90,
isLargeArc: true,
sweepDirection: SweepDirection.Counterclockwise,
isStroked: true);
//compose one or more segments into a collection
var pathcoll = new PathSegmentCollection();
pathcoll.Add(arc);
//create a figure based on the set of segments
var figure = new PathFigure();
figure.Segments = pathcoll;
//compose a collection of figures
var figcoll = new PathFigureCollection();
figcoll.Add(figure);
//create a path-geometry using the figures collection
var geom = new PathGeometry(figcoll);
double fraction = 0.5; //the relative point of the curve
Point pt; //the absolute point of the curve
Point tg; //the tangent point of the curve
geom.GetPointAtFractionLength(
fraction,
out pt,
out tg);
Hope it helps.
Cheers