Question

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

Was it helpful?

Solution

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);

OTHER TIPS

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);

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); 

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();

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;
}
}

Unfortunately, I don't think there is a method or property to do what you are asking. At least, I can't find one. (Great question!)

It seems like you would have to do it manually (as you suggest yourself) ... that is call Point Transform.Transform(Point p) for every point in your PathGeometry ... creating a new PathGeometry in the process.

Probably isn't the answer you want. (Rueful Grin)

I've had the same problem AND need lines as well (not only geometries with area).

I'm only using PathGeometry, so this may not be the general solution you are looking for, but this worked for me:

pathgeometry.Transform = transform;
PathGeometry transformed =  PathGeometry.CreateFromGeometry(pathgeometry);

There are two things you have to consider:

  1. Geometry inherits from Freezable, you can't modify the geometry object in-place if it's frozen.
  2. You can scan the PathGeometry list of figures and segments and transform all the points in them but some types, like ArcSegment includes sizes and angles, you can't transform them.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top