Подтвердить что ты не робот

Инвариантная толщина троса Path независимо от масштаба

Мы используем wpf для разработки приложения типа cad, где рисунки размещаются на холсте с помощью объекта Path. Я просто столкнулся с небольшой проблемой, что, когда я масштабирую/увеличиваю масштаб, все элементы в холсте становятся масштабируемыми, и это поведение, которое я хотел, но это также увеличивает толщину штриха Path.

Есть ли способ, по которому я увеличиваю/масштабирую/масштабирую объекты, но тем не менее поддерживаю ту же толщину пути пути.

Любая помощь в этом отношении будет очень полезна для меня.

4b9b3361

Ответ 1

Лучшим решением было бы использовать один или несколько объектов System.Windows.Media.Geometry для хранения ваших путей, точек и т.д.

Эта геометрия может быть нарисована с помощью Pen, поэтому вы можете действительно изменить толщину штриха при масштабировании, но более гибким является использование свойства Transform.

Используя преобразование, вы можете "масштабировать" фактические координаты геометрического представления, а не визуализацию, поэтому, когда вы его рисуете, вам не нужно возиться с визуальными преобразованиями.

Чтобы вычислить преобразование, я использую следующий код:

public static Matrix TransformShape(Rect fromPosition, Rect toPosition, bool flipVertical) {
    Matrix translateThenScale = Matrix.Identity;
    //we first translate to origin since that just easier
    translateThenScale.Translate(-fromPosition.X, -fromPosition.Y);
    //now we scale the graph to the appropriate dimensions
    translateThenScale.Scale(toPosition.Width / fromPosition.Width, toPosition.Height / fromPosition.Height);
    //then we flip the graph vertically around the viewport middle since in our graph positive is up, not down.
    if (flipVertical)
        translateThenScale.ScaleAt(1.0, -1.0, 0.0, toPosition.Height / 2.0);
    //now we push the graph to the right spot, which will usually simply be 0,0.
    translateThenScale.Translate(toPosition.X, toPosition.Y);

    return translateThenScale;
}

где fromPosition Rect должен содержать нетрансформированные границы, а toPosition Rect должен содержать преобразованные границы. Это также тривиально позволяет масштабировать X и Y отдельно, что часто необходимо для построения графика.

Легко вычислить границы вашей геометрии:

Geometry graphGeom;
//[...]   
//the bounds are modified by the transform, so we want no transform!    
graphGeom.Transform = Transform.Identity; 
Rect graphBounds = graphGeom.Bounds;
//then set the transform again

//or, if the transform is axis-aligned, the following _should_ work:
Rect graphBoundsAlt = graphGeom.Transform.Inverse.TransformBounds(graphGeom.Bounds);

И, конечно же, WPF может рассказать вам, какие ограничения вам необходимо внести, если это необходимо. Объединяя это, вы можете сделать что-то вроде

public void RecomputeTransform(Rect targetRect, bool flipVertical) {
    graphGeom.Transform = Transform.Identity; 
    Rect graphBounds = graphGeom.Bounds;
    Matrix transMat = TransformShape(graphBounds,targetRect,flipVertical);
    graphGeom.Transform = new MatrixTransform(transMat);
}

Преимущество использования этого решения заключается в том, что вам не нужно возиться с RenderTransforms, вы можете свободно использовать преобразования, которые сдвигают и/или масштабируют X и Y независимо, не получая странных искажений в ваших линиях, и вы можете лечить Перо в качестве непрозрачного объекта (т.е. проще настроить из пользовательского интерфейса - если вы выберете ширину пера или еще что-то еще, дальнейшая коррекция не нужна).

Ответ 2

Наконец, я получил обходное решение для вышеупомянутого решения. Я сделал это, установив StrokeThickness в Path относительно масштабирования Canvas.

Вот так:

// scale = scaling factor applied to the canvas
path.StrokeThickness = 1.0 / scale;

Это работает только в том случае, если ScaleX и ScaleY являются однородными.

Ответ 3

Я несколько раз возвращался сюда, но эта идея мне пришла в голову, а как же просто обернуть ее в окне просмотра?

Вы можете попробовать: messing with the Height/Width не должен изменять отношение толщины к размеру. Возможно, это делает другие плохие вещи, которые я еще не обнаружил...

<Viewbox Height="50" Width="50">
    <Path Stroke="Black" Fill="Black" StrokeThickness="0.8" Stretch="Uniform" 
                 Data="M0,0 20,20" />
</Viewbox>

Ответ 4

Я думаю, что вам нужно свойство YourPathObject.RenderedGeometry.Transform