Click here to Skip to main content
15,886,724 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
How do I scale a WPF GeometryDrawing where the GeometryDrawing path points are effectively changed? What I want to do is that I have a DrawingGroup of GeometryDrawings and I want to resize one of these GeometryDrawings.

I know I can put this Geometrydrawing in a separate DrawingGroup and use ScaleTransform to scale this GeometryDrawing but:

1. I can’t set specific widths and heights I have to use a scale factor, so it will closely be the width I specify but not exactly the width I specify;
2. The GeometryDrawing doesn’t change but is zoomed like an effect, I would like to make the ScaleTransform (and other transforms) effective, really change the pathpoints of the GeometryDrawing.

Have tried to change the pathpoints myself but the endresult is not what I expected. The path is translated when resized and the path is corrupted.

Best regards,

Rémy

C#
private void SetScaleAndTranslateTransform(object obj, Point newPos, Size newSize)
{
    double translateX = 0;
    double translateY = 0;
    double scaleWidth = 1;
    double scaleHeight = 1;

    if (obj is System.Windows.Media.GeometryDrawing)
    {
        System.Windows.Media.GeometryDrawing geometryDrawing = (System.Windows.Media.GeometryDrawing)obj;
        translateX = newPos.X - geometryDrawing.Bounds.X;
        translateY = newPos.Y - geometryDrawing.Bounds.Y;
        scaleWidth = newSize.Width / geometryDrawing.Bounds.Width;
        scaleHeight = newSize.Height / geometryDrawing.Bounds.Height;
    }
    else if (obj is System.Windows.Media.DrawingGroup)
    {
        System.Windows.Media.DrawingGroup drawingGroup = (System.Windows.Media.DrawingGroup)obj;
        translateX = newPos.X - drawingGroup.Bounds.X;
        translateY = newPos.Y - drawingGroup.Bounds.Y;
        scaleWidth = newSize.Width / drawingGroup.Bounds.Width;
        scaleHeight = newSize.Height / drawingGroup.Bounds.Height;
    }

    SetScaleAndTranslateTransform(obj, translateX, translateY, scaleWidth, scaleHeight);

    CreateSelectionImage(null);
}

private void SetScaleAndTranslateTransform(object obj, double translateX, double translateY, double scaleWidth, double scaleHeight)
{
    if (obj is System.Windows.Media.GeometryDrawing)
    {
        System.Windows.Media.GeometryDrawing geometryDrawing = (System.Windows.Media.GeometryDrawing)obj;

        Geometry geoMetry = geometryDrawing.Geometry;
        PathGeometry pathGeometry = (PathGeometry)geoMetry;
        foreach (PathFigure pathFigure in pathGeometry.Figures)
        {
            pathFigure.StartPoint = new Point(pathFigure.StartPoint.X + translateX, pathFigure.StartPoint.Y - translateY);
            foreach(PathSegment pathSegment in pathFigure.Segments)
            {
                PointCollection pointCollectionOriginal = new PointCollection();
                if (pathSegment is PolyBezierSegment)
                {
                    pointCollectionOriginal = ((PolyBezierSegment)pathSegment).Points;
                }
                else if (pathSegment is LineSegment)
                {
                    pointCollectionOriginal.Add(((LineSegment)pathSegment).Point);
                }
                else if (pathSegment is PolyLineSegment)
                {
                    pointCollectionOriginal = ((PolyLineSegment)pathSegment).Points;
                }

                PointCollection pointCollectionNew = new PointCollection();
                foreach (Point point in pointCollectionOriginal)
                {
                    Point pointNew = new Point();
                    pointNew.X = point.X * scaleWidth;
                    pointNew.Y = point.Y * scaleHeight;
                    pointCollectionNew.Add(pointNew);
                }

                if (pathSegment is PolyBezierSegment)
                {
                    ((PolyBezierSegment)pathSegment).Points = pointCollectionNew;
                }
                else if (pathSegment is LineSegment)
                {
                    ((LineSegment)pathSegment).Point = pointCollectionNew[0];
                }
                else if (pathSegment is PolyLineSegment)
                {
                    ((PolyLineSegment)pathSegment).Points = pointCollectionNew;
                }
            }
        }
    }
    else if (obj is System.Windows.Media.DrawingGroup)
    {
        System.Windows.Media.DrawingGroup drawingGroup = (System.Windows.Media.DrawingGroup)obj;
        foreach(object objChild in drawingGroup.Children)
        {
            SetScaleAndTranslateTransform(objChild, translateX, translateY, scaleWidth, scaleHeight);
        }
    }
}


With HUGE help from DanBecause this is the code I use in my code:

C#
public static void TransformPoints(this PathFigure figure, Transform t)
{
    figure.StartPoint = t.Transform(figure.StartPoint);

    foreach (var segment in figure.Segments)
    {
        if (segment is LineSegment)
        {
            var lineSegment = segment as LineSegment;
            Point newPoint = t.Transform(lineSegment.Point);
            lineSegment.Point = newPoint;
        }
        else if (segment is PolyLineSegment)
        {
            var polyLineSegment = segment as PolyLineSegment;
            PointCollection pointCollection = new PointCollection();
            foreach (var point in polyLineSegment.Points)
            {
                pointCollection.Add(t.Transform(point));
            }
            polyLineSegment.Points = pointCollection;
        }
        else if (segment is PolyBezierSegment)
        {
            var polyBezierSegment = segment as PolyBezierSegment;
            PointCollection pointCollection = new PointCollection();
            foreach (var point in polyBezierSegment.Points)
            {
                pointCollection.Add(t.Transform(point));
            }
            polyBezierSegment.Points = pointCollection;
        }
        else
        {
            throw new Exception(segment.GetType().ToString());
        }
    }
}

private void SetScaleAndTranslateTransform(object obj, Point newPos, Size newSize)
{
    double translateX = 0;
    double translateY = 0;
    double scaleWidth = 1;
    double scaleHeight = 1;

    if (obj is System.Windows.Media.GeometryDrawing)
    {
        System.Windows.Media.GeometryDrawing geometryDrawing = (System.Windows.Media.GeometryDrawing)obj;
        translateX = newPos.X - geometryDrawing.Bounds.X;
        translateY = newPos.Y - geometryDrawing.Bounds.Y;
        scaleWidth = newSize.Width / geometryDrawing.Bounds.Width;
        scaleHeight = newSize.Height / geometryDrawing.Bounds.Height;
    }
    else if (obj is System.Windows.Media.DrawingGroup)
    {
        System.Windows.Media.DrawingGroup drawingGroup = (System.Windows.Media.DrawingGroup)obj;
        translateX = newPos.X - drawingGroup.Bounds.X;
        translateY = newPos.Y - drawingGroup.Bounds.Y;
        scaleWidth = newSize.Width / drawingGroup.Bounds.Width;
        scaleHeight = newSize.Height / drawingGroup.Bounds.Height;
    }

    SetScaleAndTranslateTransform(obj, translateX, translateY, scaleWidth, scaleHeight);
}

private void SetScaleAndTranslateTransform(object obj, double translateX, double translateY, double scaleWidth, double scaleHeight)
{
    if (obj is System.Windows.Media.GeometryDrawing)
    {
        System.Windows.Media.GeometryDrawing geometryDrawing = (System.Windows.Media.GeometryDrawing)obj;

        Geometry geoMetry = geometryDrawing.Geometry;
        PathGeometry pathGeometry = (PathGeometry)geoMetry;
        foreach (PathFigure pathFigure in pathGeometry.Figures)
        {
            Business.PathFigureExtensions.TransformPoints(pathFigure, new ScaleTransform(scaleWidth, scaleHeight));
            Business.PathFigureExtensions.TransformPoints(pathFigure, new TranslateTransform(translateX, translateY));
        }
    }
    else if (obj is System.Windows.Media.DrawingGroup)
    {
        System.Windows.Media.DrawingGroup drawingGroup = (System.Windows.Media.DrawingGroup)obj;
        foreach (object objChild in drawingGroup.Children)
        {
            SetScaleAndTranslateTransform(objChild, translateX, translateY, scaleWidth, scaleHeight);
        }
    }
}
Posted
Updated 10-Sep-15 12:06pm
v2

1 solution

I can't say I entirely understand what you mean by "really change the pathpoints", but the Transform property on the Geometry will scale the Geometry before it is rendered and the Geometry will be rendered as though it were the scaled size (not a zoomed version of the original size). So, for example, the pen thickness will not increase (or decrease) like it would with a visual transform. I think this is what you are looking for.

So perhaps:
C#
private void SetScaleAndTranslateTransform(object obj, double translateX, double translateY, double scaleWidth, double scaleHeight)
{
    var geometryDrawing = obj as System.Windows.Media.GeometryDrawing;
    if(geometryDrawing != null)
    {
        geometryDrawing.Transform = new MatrixTransform(scaleWidth, 0, 0, scaleHeight, translateX, translate);
        return;
    }

    var drawingGroup = obj as System.Windows.Media.DrawingGroup;
    if(drawingGroup == null) return;

    foreach(object objChild in drawingGroup.Children)
        SetScaleAndTranslateTransform(objChild, translateX, translateY, scaleWidth, scaleHeight);
}


Keep in mind you could also create the transform once and assign it to the appropriate Geometry instances and then simply modifying that transform will affect all of them without having to use recursion.

Updated:
Below is a quick example illustrating what I mean when I say to transform the points.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication3
{
	public static class PathFigureExtensions
	{
		public static IEnumerable<Point> EnumeratePoints(this PathFigure figure)
		{
			yield return figure.StartPoint;
			foreach (var segment in figure.Segments)
			{
				var lineSegment = segment as LineSegment;
				if (lineSegment != null)
					yield return lineSegment.Point;
				else
				{
					var polyLineSegment = segment as PolyLineSegment;
					if (polyLineSegment == null) continue;
					foreach (var point in polyLineSegment.Points)
						yield return point;
				}
			}
		}

		public static IEnumerable<Point> EnumeratePoints(this PathFigure figure, Transform t)
		{
            //Here I am transforming the points using the geometry's transform.
			return EnumeratePoints(figure).Select(p => t.Transform(p));
		}
	}

	/// <summary>
	/// Interaction logic for MainWindow.xaml
	/// </summary>
	public partial class MainWindow : Window
	{
		public MainWindow()
		{
			InitializeComponent();

			var figures = new[]{
				new PathFigure(
				new Point(0,0),
				new[]{
					new PolyLineSegment(
						new []{
							new Point(50,0),
							new Point(50,50),
							new Point(0,50),
						}, true) as PathSegment
				}, true)
			};

			UntransformedGeometry = new PathGeometry(figures);
			UntransformedGeometryPoints = new PointCollection(UntransformedGeometry.Figures.First().EnumeratePoints());

			TransformedGeometry = new PathGeometry(figures);
			TransformedGeometry.Transform = new ScaleTransform(2, 2);
			TransformedGeometryPoints = new PointCollection(UntransformedGeometry.Figures.First().EnumeratePoints(TransformedGeometry.Transform));
			DataContext = this;
		}

		public PathGeometry UntransformedGeometry {get;private set;}
		public PathGeometry TransformedGeometry { get; private set; }
		public PointCollection UntransformedGeometryPoints { get; private set; }
		public PointCollection TransformedGeometryPoints { get; private set; }

	}
}


Here's the xaml I will use to demonstrate:
XML
<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel Margin="5">
        <Path Data="{Binding UntransformedGeometry}" Stroke="Red" Fill="Gray" StrokeThickness="1" UseLayoutRounding="true"/>
        <Label Content="{Binding UntransformedGeometryPoints}"/>
        <Path Data="{Binding TransformedGeometry}" Stroke="Blue" Fill="Gray" StrokeThickness="1" UseLayoutRounding="true"/>
        <Label Content="{Binding TransformedGeometryPoints}"/>
    </StackPanel>
</Window>


If you run this you will see a square with the dimensions you gave, a label with its points, another square twice as large as the original square, and another label showing the points transformed. The second square has the same apparent stroke thickness as the original square because it is logically transformed rather than visually.

I also demonstrate how the PathGeometry doesn't own the figures used to create it (I use the same PathFigure to create both geoetries); this is just one of many reasons why applying a transform to a PathFigure, or Geometry, etc. should not change the underlying values. I think this is also how a developer expect properties to behave; I could see having a method that given a transform produced a new Geometry that had the points resulting from applying the transform (and the transform not applied to it). You could exchange your GeometryDrawing's Geometry with that if you like (making it "effective").

This was a quick and dirty example. I could see you extending the various PathSegment types and PathFigure to expose this functionality on each and finally doing so to PathGeometry.
 
Share this answer
 
v7
Comments
LiQuick 30-Aug-15 5:14am    
The GeometryDrawing doesn't have a Transform definition (Edit: have found that the Geometry does have a transform!). What I want to do is translate, scale, rotate, etc. and be able to read the new pathpoints. So if I have a rectangle with points 0,0 50,0 50,50 0,50 and I rescale it with a 200% scale transform that the geometrydrawing has points 0,0 100,0 100,100, 0,100 (not only visually but also really has those points).

Edit: Furthermore how can I make this transform effective? I can have only one transform on the Geometry, can I apply the transform so I can "add" a new transform to it. Example of above rectangle when I put an transform on it of 200% (2) I get the right size but adding another scale transform I lose the first one and it only takes the last transform.
DanBecause 30-Aug-15 14:00pm    
My apologies. I meant the transform property on the geometry. So geometryDrawing.Geometry.Transform
DanBecause 30-Aug-15 14:02pm    
To do multiple transforms you could use a TransformGroup.
DanBecause 30-Aug-15 14:08pm    
Why don't you transform the path points as you read them using the Transform method of the Transform? I sense the opportunity for some handy extension methods.
LiQuick 31-Aug-15 2:59am    
Can you elaborate on this? How can I "transform the path points as you read them using the Transform method of the Transform"?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900