WPF: как применить данные GeneralTransform к геометрии и вернуть новую геометрию?
Имея некоторые данные геометрии и трансформацию, как преобразование может быть применено к геометрии для получения новой геометрии с преобразованием данных?
Ex: у меня есть объект Path, который имеет его Path.Data, установленный для объекта PathGeometry, я хочу преобразовать точки объекта PathGeometry на месте, используя преобразовать и не применять преобразование к PathGeometry, которое будет использоваться во время рендеринга.
P.S. Я знаю, что класс Transform имеет метод Point Transform.Transform(Point p)
, который может быть использован для преобразования точки, но... есть ли способ сразу преобразовать произвольную геометрию?
Изменить: См. Мой вариант для найденного в настоящее время решения
Ответы
Ответ 1
Вы можете попробовать использовать Geometry.Combine. Он применяет преобразование во время комбайна. Один улов заключается в том, что Combine работает только в том случае, если ваша геометрия имеет область, поэтому отдельные строки не будут работать.
Вот образец, который работал у меня.
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);
Ответ 2
Я нашел решение, с помощью которого произвольная tranform может быть применена к геометрии пути, благодаря Тодд Уайт:
В основном Geometry.Combine используется для объединения желаемой геометрии с Geometry.Empty с использованием Union и заданного преобразования. Полученная геометрия преобразуется с данным преобразованием.
PathGeometry geometryTransformed = Geometry.Combine(Geometry.Empty, geometry, GeometryCombineMode.Union, transform);
Ответ 3
Это то, что я нашел, что вы можете сделать, чтобы получить преобразованную геометрию со всей информацией о фигуре:
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);
Ответ 4
Ни одно из быстрых решений на основе Geometry.Combine работает в случае пути, состоящего из одного элемента LineElement.
Поэтому я решил проблему сложным образом, как это (но я также ограничен 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;
}
}
Ответ 5
Я не использовал принятый ответ, так как он возвращал геометрию в формате, отличном от исходного, поэтому я использовал это:
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();
Ответ 6
К сожалению, я не думаю, что есть способ или свойство делать то, что вы просите. По крайней мере, я не могу найти его. (Большой вопрос!)
Похоже, вам придется делать это вручную (как вы сами предлагаете)... это вызов Point Transform.Transform(Point p) для каждой точки вашей PathGeometry... создания новая PathGeometry в этом процессе.
Возможно, это не тот ответ, который вы хотите. (Rueful Grin)
Ответ 7
У меня была та же проблема И нужны строки (не только геометрии с областью).
Я использую PathGeometry, поэтому это может быть не общее решение, которое вы ищете, но это сработало для меня:
pathgeometry.Transform = transform;
PathGeometry transformed = PathGeometry.CreateFromGeometry(pathgeometry);
Ответ 8
Есть две вещи, которые вы должны учитывать:
- Геометрия наследует от Freezable, вы не можете изменить объект геометрии на месте, если он заморожен.
- Вы можете сканировать список фигур и сегментов PathGeometry и преобразовать все точки в них, но некоторые типы, такие как ArcSegment, включают в себя размеры и углы, вы не можете их преобразовать.