PeterM
PeterM

Reputation: 2692

Long lines don't render correctly

I have a project with a canvas that the user can zoom and pan using the mouse. The origin is marked with a blue cross running through the canvas. As the user zooms in the line width of the markers is reduced to keep the them about 1 pixel wide on screen. However, after zooming in about 10x the origin lines abruptly disappear. It seems like the rendering engine is deciding that the line is too small to be displayed before applying the scaling transform.

What is strange is that when I do the same operation to a rectangle instead of a line I get the expected behavior (see the ScaleBox rectangle object in the code below). Any ideas on what is causing the problem and how to fix it would be welcome.

Here is the relevant XAML:

    <Canvas x:Name="MouseCapture" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="WhiteSmoke" >
    <Canvas x:Name="ZoomPan">
        <Canvas.RenderTransform >
            <MatrixTransform x:Name="_zoomPanTransform" />
        </Canvas.RenderTransform>
        <Rectangle x:Name="ScaleBox" Width="100" Height="100" Canvas.Left="-50" Canvas.Top="-50" Stroke="Black"/>
        <Line x:Name="YOrigin" X1="-5000" X2="5000" Y1="0" Y2="0" Stroke="Blue" />
        <Line x:Name="XOrigin" X1="0" X2="0" Y1="-5000" Y2="5000" Stroke="Blue" />
    </Canvas>
</Canvas>

And the code behind:

Private Sub ZoomControl_PreviewMouseWheel(sender As Object, e As System.Windows.Input.MouseWheelEventArgs) Handles MouseCapture.PreviewMouseWheel
    Dim scale As Double
    If e.Delta > 0 Then
        scale = 0.95
    Else
        scale = 1 / 0.95
    End If
    Dim center As Point = e.GetPosition(MouseCapture)
    Dim zoom As New ScaleTransform(scale, scale, center.X, center.Y)
    _zoomPanTransform.Matrix *= zoom.Value
    UpdateLineWidth()
End Sub

Private Sub UpdateLineWidth()
    ScaleBox.StrokeThickness = 1 / _zoomPanTransform.Matrix.M11
    XOrigin.StrokeThickness = 1 / _zoomPanTransform.Matrix.M11
    YOrigin.StrokeThickness = 1 / _zoomPanTransform.Matrix.M11
End Sub

Here is a screenshot when everything is working.

Here is a screenshot as 10x Zoom. The origin lines have disappeared abruptly, but the black box still shows up as expected. (Note that only one side of the black box is visible because the rest is outside the visible region because of the zoom.)

edit: I figured out the problem see below

Upvotes: 0

Views: 874

Answers (2)

PeterM
PeterM

Reputation: 2692

The problem was that the line was too long. If I reduce the length of the origin lines dynamically so that they are roughly the same length as the canvas, everything displays as expected. If the line gets to be 100x or so longer than the canvas they abruptly disappear. This is probably due to some rounding errors or optimization scheme.

Upvotes: 0

Clemens
Clemens

Reputation: 128061

I don't quite understand why you would put these crosshair lines in the transformed Canvas anyway.

Instead of drawing them into the transformed canvas and fix their effective line width on every change of the transformation, you should simply place them in another canvas, which is not transformed, e.g. in the parent Canvas here:

<Canvas x:Name="MouseCapture" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="WhiteSmoke" >
    <Canvas x:Name="ZoomPan">
        <Canvas.RenderTransform >
            <MatrixTransform x:Name="_zoomPanTransform" />
        </Canvas.RenderTransform>
        <Rectangle x:Name="ScaleBox" Width="100" Height="100" Canvas.Left="-50" Canvas.Top="-50" Stroke="Black"/>
    </Canvas>
    <Line x:Name="YOrigin" X1="-5000" X2="5000" Y1="0" Y2="0" Stroke="Blue" />
    <Line x:Name="XOrigin" X1="0" X2="0" Y1="-5000" Y2="5000" Stroke="Blue" />
</Canvas>

In general, when you want to transform geometric shapes while keeping their stroke thickness, you should go for the Path element and set Path.Data to a Geometry with an appropriate Geometry.Transform, like in the following example. Note that the transform is defined as static resource (with key GlobalTransform), so that in can be reused by multiple geometries.

<Path Stroke="Blue">
    <Path.Data>
        <LineGeometry StartPoint="0,-100" EndPoint="0,100" Transform="{StaticResource GlobalTransform}"/>
    </Path.Data>
</Path>

Upvotes: 1

Related Questions