WPF: How to apply a GeneralTransform to a Geometry data and return the new geometry?
Solution 1
You could try and use Geometry.Combine. It applies a transform during the combine. One catch is that Combine only works if your Geometry has area, so single lines will not work.
Here is a sample that worked for me.
PathGeometry geometry = new PathGeometry();
geometry.Figures.Add(new PathFigure(new Point(10, 10), new PathSegment[] { new LineSegment(new Point(10, 20), true), new LineSegment(new Point(20, 20), true) }, true));
ScaleTransform transform = new ScaleTransform(2, 2);
PathGeometry geometryTransformed = Geometry.Combine(geometry, geometry, GeometryCombineMode.Intersect, transform);
Solution 2
I've found a solution with which arbitrary tranform can be applied to a path geometry, thanks to Todd White's answer:
Basically Geometry.Combine is used to combine the desired geometry with Geometry.Empty using Union, and the desired transform is given. The resulting geometry is transformed with the given transform.
PathGeometry geometryTransformed = Geometry.Combine(Geometry.Empty, geometry, GeometryCombineMode.Union, transform);
Solution 3
This is what I found you can do to get a transformed geometry with all of the figure information intact:
var geometry = new PathGeometry();
geometry.Figures.Add(new PathFigure(new Point(10, 10), new PathSegment[] { new LineSegment(new Point(10, 20), true), new LineSegment(new Point(20, 20), true) }, true));
geometry.Transform = new ScaleTransform(2, 2);
var transformedGeometry = new PathGeometry ();
// this copies the transformed figures one by one into the new geometry
transformedGeometry.AddGeometry (geometry);
Solution 4
I didn't use accepted answer since it was returning geometry in format different from the original one, so I used this:
Geometry inputGeometry = new PathGeometry();
var inputGeometryClone = inputGeometry.Clone(); // we need a clone since in order to
// apply a Transform and geometry might be readonly
inputGeometryClone.Transform = new TranslateTransform(); // applying some transform to it
var result = inputGeometryClone.GetFlattenedPathGeometry();
Solution 5
None of the quick solutions based on Geometry.Combine works in the case of path made of a single LineElement. So I solved the problem the hard way, like this (But I am also limited to PathGeometry):
public static class GeometryHelper
{
public static PointCollection TransformPoints(PointCollection pc, Transform t)
{
PointCollection tp = new PointCollection(pc.Count);
foreach (Point p in pc)
tp.Add(t.Transform(p));
return tp;
}
public static PathGeometry TransformedGeometry(PathGeometry g, Transform t)
{
Matrix m = t.Value;
double scaleX = Math.Sqrt(m.M11 * m.M11 + m.M21 * m.M21);
double scaleY = (m.M11 * m.M22 - m.M12 * m.M21) / scaleX;
PathGeometry ng = g.Clone();
foreach (PathFigure f in ng.Figures)
{
f.StartPoint = t.Transform(f.StartPoint);
foreach (PathSegment s in f.Segments)
{
if (s is LineSegment)
(s as LineSegment).Point = t.Transform((s as LineSegment).Point);
else if (s is PolyLineSegment)
(s as PolyLineSegment).Points = TransformPoints((s as PolyLineSegment).Points, t);
else if (s is BezierSegment)
{
(s as BezierSegment).Point1 = t.Transform((s as BezierSegment).Point1);
(s as BezierSegment).Point2 = t.Transform((s as BezierSegment).Point2);
(s as BezierSegment).Point3 = t.Transform((s as BezierSegment).Point3);
}
else if (s is PolyBezierSegment)
(s as PolyBezierSegment).Points = TransformPoints((s as PolyBezierSegment).Points, t);
else if (s is QuadraticBezierSegment)
{
(s as QuadraticBezierSegment).Point1 = t.Transform((s as QuadraticBezierSegment).Point1);
(s as QuadraticBezierSegment).Point2 = t.Transform((s as QuadraticBezierSegment).Point2);
}
else if (s is PolyQuadraticBezierSegment)
(s as PolyQuadraticBezierSegment).Points = TransformPoints((s as PolyQuadraticBezierSegment).Points, t);
else if (s is ArcSegment)
{
ArcSegment a = s as ArcSegment;
a.Point = t.Transform(a.Point);
a.Size = new Size(a.Size.Width * scaleX, a.Size.Height * scaleY); // NEVER TRIED
}
}
}
return ng;
}
}
Comments
-
Pop Catalin almost 2 years
Having some Geometry data and a Transform how can the transform be applied to the Geometry to get a new Geometry with it's data transformed ?
Ex: I Have a Path object that has it's Path.Data set to a PathGeometry object, I want to tranform the points of the PathGeometry object in place using a transform, and not apply a transform to the PathGeometry that will be used at render time.
P.S. I know that the Transform class has a method
Point Transform.Transform(Point p)
that can be used to transform a Point but...is there a way to transform a arbitrary geometry at once?Edit: See my repply for a currently found solution
-
Pop Catalin over 15 yearsyou can transform anything, even sizes and angles, besides, I know that you can transform points individually, and also my geometry is not frozen. I wanted to know if there's a framework way to apply transformations to an geometry object as a whole.
-
Goran over 13 yearsThe only problem is that the combined geometry is not the same as the original geometry. Differences aren't big but might be important.
-
Goran over 13 yearsNot sure why this was down voted as it seems the only correct answer.
-
ManIkWeet over 5 yearsThis appears to be the correct way to do it, as no StandardFlatteningTolerance appears to be applied.
-
Jürgen Böhm about 2 yearsThe code for ArcSegment seems to be very dubious, as the rotation angle is left unmodified. But if you rotate a true ellipse the major axis rotates with it, so the rotation angle must change.
-
Jürgen Böhm about 2 yearsIt seems that even Microsoft shuns the complications of transforming an ellipse ArcSegment into another such. Instead they approximate it by Bezier curves and transform these: referencesource.microsoft.com/#PresentationCore/Core/CSharp/… Look for 'AddToFigure'.