Имея некоторые данные геометрии и преобразование, как можно применить преобразование к геометрии, чтобы получить новую геометрию с преобразованными данными?
Пример: у меня есть объект Path, для которого Path.Data задан объект PathGeometry, я хочу преобразовать точки объекта PathGeometry на месте с помощью преобразования, а не применять преобразование к PathGeometry, которое будет использоваться во время рендеринга.
P.S. Я знаю, что в классе Transform есть метод Point Transform.Transform(Point p), который можно использовать для преобразования Point, но ... есть ли способ сразу же преобразовать произвольную геометрию?
Редактировать: См. Мой ответ о найденном на данный момент решение





Вы должны учитывать две вещи:
Вы можете трансформировать что угодно, даже размеры и углы, к тому же я знаю, что вы можете трансформировать точки индивидуально, а также моя геометрия не замораживается. Я хотел знать, есть ли в рамках фреймворка способ применения преобразований к геометрическому объекту в целом.
К сожалению, я не думаю, что есть способ или свойство делать то, о чем вы просите. По крайней мере, не могу найти. (Отличный вопрос!)
Похоже, вам придется делать это вручную (как вы предлагаете сами) ... то есть вызывать Преобразование точки.Преобразование (точка p) для каждой точки в вашей PathGeometry ... создавая новую PathGeometry в процессе.
Вероятно, это не тот ответ, который вам нужен. (Скорбная усмешка)
Не уверен, почему это было отклонено, поскольку это кажется единственно правильным ответом.
Вы можете попробовать использовать Geometry.Combine. Он применяет преобразование во время комбинирования. Одна загвоздка заключается в том, что Combine работает только в том случае, если ваша Geometry имеет площадь, поэтому отдельные линии не будут работать.
Вот образец, который у меня сработал.
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);
Единственная проблема в том, что комбинированная геометрия не совпадает с исходной геометрией. Различия невелики, но могут быть важны.
Я нашел решение, с помощью которого произвольная трансформация может быть применена к геометрии пути, благодаря ответу Тодд Уайт:
В основном Geometry.Combine используется для объединения желаемой геометрии с Geometry.Empty с помощью Union, и дается желаемое преобразование. Результирующая геометрия преобразуется заданным преобразованием.
PathGeometry geometryTransformed = Geometry.Combine(Geometry.Empty, geometry, GeometryCombineMode.Union, transform);
У меня была такая же проблема, и мне нужны линии (не только геометрия с площадью).
Я использую только PathGeometry, поэтому это может быть не то общее решение, которое вы ищете, но у меня это сработало:
pathgeometry.Transform = transform;
PathGeometry transformed = PathGeometry.CreateFromGeometry(pathgeometry);
Вы действительно получаете преобразованную геометрию? Применение CreateFromGeometry к PathGeometry возвращает ту же геометрию (те же точки). Вы не трансформируете геометрию, а просто добавляете свойство трансформации.
Я не использовал принятый ответ, поскольку он возвращал геометрию в формате, отличном от исходного, поэтому я использовал это:
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();
Я обнаружил, что вы можете сделать преобразованную геометрию со всей информацией о фигуре без изменений:
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);
Кажется, это правильный способ сделать это, поскольку СтандартРазверстиеТолерантность не применяется.
Ни одно из быстрых решений на основе 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;
}
}
Если вы действительно найдете способ сделать это ... Я тоже им воспользуюсь. Хе-хе.