wpflinecap

Retaining flat LineCap property for a line whilst filling in missing corners


I'm currently trying to simulate the tracks of a railway using given coordinates. Previously I had been using the 'Rounded' value for the StrokeStart and StrokeEnd LineCap property, however the added radius you get with that value was causing problems when it came to adding track joints. I switched to the 'flat' value for LineCap, which works much better apart from the fact that the 'corners' of my track aren't filled in, like so:

Missing corners https://i.sstatic.net/wFnt2.jpg

I've tried to counteract this by rendering all my tracks first with the 'Round' value, and then rendering them again over the top with the 'Flat' value, which does seem to fill in the corners, but leads to other problems, such as overlapping colours when a track is occupied, and the rounded tracks I rendered first making an appearance at the end of the track:

Overlapping colours https://i.sstatic.net/iHxMn.jpg

Problems at the end of the track https://i.sstatic.net/9U2op.jpg

My XAML is just a simple line formed from a list of coordinates with various bindings from my view model, and converters to scale my coordinates down slightly. This is obviously the tracks rendered first with the Rounded EndCaps:


<Line  X1="{Binding X1, Converter={StaticResource ScaleXCoordConverter}}" Y1="{Binding Y1, Converter={StaticResource ScaleYCoordConverter}}"
                                                   X2="{Binding X2, Converter={StaticResource ScaleXCoordConverter}}" Y2="{Binding Y2, Converter={StaticResource ScaleYCoordConverter}}"
                                                   Stroke="{Binding DataContext.LineColor, RelativeSource={RelativeSource AncestorType=ContentPresenter, AncestorLevel=2}}" 
                                                   StrokeThickness="2" StrokeEndLineCap="Round" StrokeStartLineCap="Round">
                                                <Line.InputBindings>
                                                    <MouseBinding Gesture="LeftClick" Command="{Binding DataContext.OccupyTrackCommand, RelativeSource={RelativeSource AncestorType=ContentPresenter, AncestorLevel=2}}"/>
                                                </Line.InputBindings>

                                            </Line>

Has anyone had any luck creating a custom line that solves these problems, where you can use the 'Flat' value of the LineCap whilst retaining corners with no problems? EDIT: Just to clarify I suppose I'm asking for a custom implementation of line or line cap to solve all these issues, but any other solution is very welcome.


Solution

  • The solution I came up with is quite specific to my project, but I thought I'd post it anyway in case it helped anyone in future who comes across this thread.

    private void SetLineCaps() {
    
            if (LineCoords.Count == 1) {
                LineCoords[0].StartLineCap = "Flat";
                LineCoords[0].EndLineCap = "Flat";
                return;
            }
            foreach (var currentCoord in LineCoords) {
                foreach (var compareCoord in LineCoords) {
    
    
                    if(currentCoord == compareCoord) continue;
                    if ((compareCoord.X1 == currentCoord.X1 && compareCoord.Y1 == currentCoord.Y1)
                        || (compareCoord.X2 == currentCoord.X1 && compareCoord.Y2 == currentCoord.Y1)) {
                        currentCoord.StartLineCap = "Round";
                    }
                    if ((compareCoord.X1 == currentCoord.X2 && compareCoord.Y1 == currentCoord.Y2 )
                        || (compareCoord.X2 == currentCoord.X2 && compareCoord.Y2 == currentCoord.Y2)){ 
    
                        currentCoord.EndLineCap = "Round";
                    }
                }
            }
        }
    

    Essentially what I ended up having to do, is checking the start and end point of each of my lines, and checking to see if the start or end touches any other point of that specific list of lines. For instance if I have a track with 3 lines, it would check to see if any line ends are touching, and if they are, it would set that particular line cap to 'Round', otherwise it keeps it 'Flat'. I then bounded my StartLineCap and EndLineCap to these values, and everything worked as expected. See my other posts about z-index if you wish to see how I dealt with the bleeding colours.