VirtualCanvas
VirtualCanvas copied to clipboard
How to display accurately when size is really big?
Hi. I tried the demo and it works really well and smoothly with the 100000x100000 Canvas size the demo shows. But when I tried using it with the Extent of the PriorityQuadTree being really big I noticed that it does not display accurately at all. It seems to me it works smoothly up until around being 10million pixels wide. If the extent goes above this and I use an offset to show this distance then I loose the ability to show pixel accurate offset change or movement (panning, moving elements). It seems to me that it is getting more and more inaccurate as the offset value gets bigger. For example when I use an Extent width and offset around 700 million pixels I need to move the offset around 65pixels to make it show this change visually.
So my question is, is it possible to use this virtual canvas somehow to show offset movement with pixel perfect accuracy even with the extent and offset being as huge as a few hundred million pixels?
Interesting, this is a great question and I have not tested the really huge canvas sizes (I mean 100,000 is really huge in my book, but hey :-) So sounds like you might be hitting some sort of scale rounding errors or something. Perhaps if you debug into how the scale is being applied you might find where those rounding errors are coming from.
Could you please have a look at it when you have some free time? I would really like to know what you think about this problem. You can use this code to demonstrate the issue:
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
double maxX = 700000000;
double maxY = 100000;
var index = new DemoSpatialIndex();
index.Extent = new Rect(0, 0, maxX, maxY);
int width = 1000;
Random r = new Random(Environment.TickCount);
for (int i = 0; i < maxX; i += width)
{
double w = width;
double h = 100;
double x = i;
double y = 10;
Rect bounds = new Rect(x, y, w, h);
DemoShape newRect = new DemoShape()
{
Bounds = bounds,
IsVisible = true,
Fill = Brushes.Green,
Stroke = GetRandomColor(r),
StrokeThickness = 0,
Type = ShapeType.Rect
};
if (i % 2000 == 0) newRect.Fill = Brushes.Red;
index.Insert(newRect);
}
this.Diagram.Index = index;
this.Diagram.ScrollExtent = index.Extent;
this.Diagram.MoveTo(new Point(699998000, 0), true);
this.WindowState = WindowState.Normal;
}
At this distance the vertical black spaces between the rectangles is the first symptom that can be noticed.
Besides this the VirtualCanvas will not respond visually unless a movement of around 65 pixels occurs
(panning or moving an element):
Also some visual artifacts are left behind when an element is moved with the mouse:
And thank you for the answer, I will look into it myself too and try to find what causes this behavior.
Wow very cool test. Can you reproduce this without VirtualCanvas on just a regular Canvas, I wonder if your width is exceeding the precision of a single precision floating point number? note that WPF uses single-precision floating point for much of its internal rendering... And the significand precision then is 23 bits which is 8,388,608, much less than your width of 700,000,000. So perhaps this explains things. The visual artifacts, for example, have to be coming from the WPF rendering engine.
I think your theory is right. I have just confirmed that the exact same thing happens when the regular canvas is used. That one also looses it's accuracy around 10,000,000 pixels. Thank you for the explanation. It is a sad thing. I guess we can do nothing to improve it then...
Ok, thanks for confirming. I'm not too surprised because most GPU's are optimized for single precision floats and WPF wants to get the most out of your GPU.
If it is really because of the GPU using single precision, is it possible that using software rendering could make a difference?
hi, I wanted to share with you a bit of an update on this topic and also a few thoughts about it.
I experimented around and managed to prove that you were right: The inaccuracy comes from the rendering, and only the rendering itself nothing else. Because of this, I also managed to create a very simple workaround for this problem, the sad thing is it uses a regular WPF canvas not your virtual one. The interesting question now is: Can your virtual canvas get rid of this problem with a similar workaround now that the default canvas can? I am sure it needs to be rewired inside pretty badly for this and I don't know enough details about how it works to determine it myself. (perhaps it can be done with losing some functionality?) I am interested to hear what you think about it.
Here I have placed 200 pixel wide rectangles next to each other starting at the 709,994,000th pixel position.
You can see that all the rectangles are rendered in the correct places accurately:
Here I placed the mouse on the 3rd displayed pixel , which is the 709,994,000th one altogether.
(I also added some magnification to the screenshot to see it better)
Besides that you can read what your virtual canvas and demoDiagram reports. Everything is in the correct position pixel perfectly:
When I said this is done using a very simple workaround I really meant it: I simply placed a regular Canvas behind your virtual one. Right before yours would start displaying inaccurately I automatically switch to the regular canvas which is controlled by your virtual canvas. So I am still using your canvas's panning to move around for example. And all that the regular canvas does is it moves it's elements to the view port before they are displayed. So the child elements are placed really far but at the same time the canvas's size will always stay small. The elements that are far away are shifted to be near before we can see them. And when the elements are near rendering is accurate.
Of course the only problem is: when I switch to the regular canvas I loose all the good stuff yours have like virtualizing. But at least far elements too display accurately now.