AB4D Forum

Full Version: Polylines
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I need to plot a 3D representation of a graph line (measurement) in an 3D view of an measurement instrument. I have tried doing this with a meshgeometry3d to draw a ribbon (extruding the line to form a flat ribbon) that follows the points of the graph line, however, this has issues that it disappears when rotated. I've therefore started looking at the PolyLineVisual3D class - this is what I need as it will be visible in all orientations of the view. What i need to do is draw one or more of these dynamically and add points to each line dynamically. I'd ideally like to do this via binding. So I'd bind to a ObservableCollection<something> and for each something I'd be able to add points as I receive data. The points would then be plotted in the three d view. I'm having some issues. Firstly I tried a very simple approach:

<visuals:PolyLineVisual3D Positions="{Binding PointsCollection}" LineColor="Red" EndLineCap="ArrowAnchor" LineThickness="10"/>

and then using a timer I simply add some points to the points collection at a rate of 10 per second:


This works but its very slow and gets rapidly slower taking several seconds once there are more than about 50 points. With the ribbon approach I described above it copes fine with a large number of points.

Secondly, I can't really work out how to bind multiple polylines. I need to bind a somethingcollection in the xaml to an ObservableCollection<something> in the view model.

What is the best way to solve this problem?

PolyLineVisual3D.Positions takes Point3DCollection type so you cannot bind ObservableCollection<Point3D> to it.

Point3DCollection does not implement INotifyProperty changed, but it is a DependencyObject so it has changed notifications baked in.

If you set the Point3DCollection to PolyLineVisual3D.Positions, you can add points to the collection and the PolyLineVisual3D will be automatically updated. For example the following simple code can be used to test that:

PolyLineVisual1.Positions = Positions;

_dispatcherTimer = new DispatcherTimer(TimeSpan.FromSeconds(0.1), DispatcherPriority.Normal, delegate (object sender, EventArgs args)
    Positions.Add(new Point3D(_index * 10, _index, _index * 10));
}, this.Dispatcher);


This also works without any performance problems for quite some time - for me it worked very well with around 1000 positions. Of course, this cannot go forever. Therefore I am curious why you get performance problems with 50 positions - could you investigate this with performance profiler?

Though, after some positions were added, the arrow begin to become smaller and smaller. The cause of this is that the arrow cannot be smaller than one line segment.

Another problem with this approach is that it only works when you set the PolyLineVisual1.Positions in code.

Instead of this I tried to define the Binding in XAML. I tried many different ways, but I was not able to make this work - the initial line was shown (using the positions defined at the time of binding), but then the values were not updated. I am not an expert for data binding so I do not know why this happens.

WPF 3D is not very well suited for MVVM. I think that your application will work best if you would put some gluing code into the View class (though this is not very common). But I am not an expert on MVVM, so there might be some other better way to do that.
Quick update, the performance issues I was getting were due to some poor test code! I was adding a lot more points than I thought. I have since got this working well using some "gluing code" i.e. not using binding as like you say that doesn't appear to work.

I have another question regarding the polylines. When I draw many lines close together there are times when they interfere with one another, i.e. cross the same points. When this happens it seems they render a different colour. I'm using a dark blue colour and where they interfere they seem to be a light blue. This would be ok except if there are enough interference points, they seem to dominate when zoomed out. The result of this is that if I plot a lot of graph lines close to each other and zoom out, the lines appear to be entirely in the new blue colour, something that is quite noticeable. Is this something someon can explain?
The problem with lines is caused by the EmissiveMaterial that is by default used to render 3D lines.

The EmissiveMaterial is "emitting" the color regardless of the lights in the scene. This is done by adding the color to the scene.

But when the same line is rendered over the same pixels multiple times (the line intersects with itself), the color is also added multiple times.

Because colors are actually byte numbers, the colors can change when the same color is added multiple times.

For example if you add dark blue (0,0,80) two times you get a lighter blue (0,0,160). If the color is added 4 times then you get full blue (0,0,255).

This works well for red, green, blue and white color because those colors are not changed when they are added to the final pixel multiple times. But other colors have problems when they intersect with itself.

It is not possible to change how EmissiveMaterials are rendered with WPF 3D.

But Ab3d.PowerToys allows to use standard DiffuseMaterial instead of EmissiveMaterial. This can be changed with setting Ab3d.Utilities.LinesUpdater.Instance.IsEmissiveMaterialUsed to false.

This prevents the problems with colors when lines intersects with itself, but using DiffuseMaterial will be affected by light positions and will be shaded based on the angle of the line surface and direction of lights.

A real solution to this problem is to use Ab3d.DXEngine to render 3D lines. There the line shader code always preserves the line color and is not adding color when the line intersects with itself.