Dynamic Colorizer for SurfaceSeries3D

7 posts, 1 answers
  1. Joe
    Joe avatar
    65 posts
    Member since:
    Nov 2017

    Posted 05 Jun Link to this post

    I've got a RadCartesianChart3D that uses a SurfaceSeries3D to show a colorized surface I've attached a sample image to show how it looks now ("Current_Surface.png")

    1. I colorize this surface using  a SurfaceSeries3dValueGradientColorizer.
    2. I build the colorizer in code-behind one single time.  It consists of 64 hard-coded color values that I use to colorize the my surface.
    3. The gradient stops are evenly distributed throughout the 0.0 to 1.0 range.  I just manually set each GradientStop's "Offset" value to be (1.0 / 64.0) apart

    This all works very well and looks great.  It produces the image I referred to above.

    Here is the XAML I use. 

    <tk:RadCartesianChart3D.Series>
         <tk:SurfaceSeries3D ItemsSource="{Binding Points}"
                             XValueBinding="X"
                             YValueBinding="Y"
                             ZValueBinding="Z"
                             x:Name="Series"                                   
                             >
     
             <tk:SurfaceSeries3D.Colorizer>
                 <tk:SurfaceSeries3DValueGradientColorizer x:Name="Colorizer" IsAbsolute="False" />
             </tk:SurfaceSeries3D.Colorizer>
         </tk:SurfaceSeries3D>
    </tk:RadCartesianChart3D.Series>

    And here is how my XAML builds the GradientStops

    // "Colors" is an array of 64 custom colors.
    // Return an evenly distributed array of them for gradient stops.
     
     var step = 1.0 / Colors.Length;
     var stops = Colors.Select((color, index) => new GradientStop(color, index * step));
     return new GradientStopCollection(stops);

     

    But now I have a new requirement.  I need to add a RadSlider that allows the user to dynamically change the colors instantaneously.  The slider sets "cut-off" values distribution of the colors, limiting the range to a subset of 1.0.  The visual effect is to highlight certain values.  Basically I am emulating something that is already done  a different application of ours (that app uses OpenGL).  

    To illustrate, I have also attached an image ("Desired_Surface.png") of this alternate application (the one that already has this slider) and the same surface showing.     You can see the the user has adjusted the bottom slider upwards and this has totally compressed the color distribution.  (You can even see the compressed distribution in the slider itself, though I don't need that). 

    My problem is that I cannot see how to emulate this with SurfaceSeries3D without using Deferred Dragging .   The user needs to see the colors change immediately as he/she adjusted either of the sliders indicators;  But the only way I can see to implement this is far too slow for anything but deferred dragging.

    First I tried binding the Colorizer's GradientStop property.  As you no doubt already know, that is not allowed by WPF

     

    <tk:SurfaceSeries3D.Colorizer>
        <tk:SurfaceSeries3DValueGradientColorizer IsAbsolute="False" GradientStops="{Binding GradientStops}"/>

     

    Then I tried declaring all 64 gradient stops directly in XAML and binding each one's GradientStop.Offset to a backing view-model property but that is also not allowed (because Gradient stops must be Freezable and always sorted)

    <tk:SurfaceSeries3D.Colorizer>
        <tk:SurfaceSeries3DValueGradientColorizer IsAbsolute="False">
     
            <tk:SurfaceSeries3DValueGradientColorizer.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="sc# 1.0,    0.0,    0.0, 0.5625" Offset="{Binding Offset_0}"/>
                    <GradientStop Color="sc# 1.0,    0.0,    0.0, 0.6250" Offset="{Binding Offset_1}"/>

    So the only way I could get this working was to completely rebuild the entire Colorizer every single time the user adjusts the slider.  I construct 64 new Gradient stops with the new Offset values and put them into a new GradientStop collection.  Unfortunately, this is far, far too laggy and slow.  It's too much work to do for a slider drag.  So I am left with using deferred dragging.  But that's not what my boss wants.

    Is there any other approach I might take to speed up the adjustment of the colors? 

     


  2. Martin Ivanov
    Admin
    Martin Ivanov avatar
    2240 posts

    Posted 07 Jun Link to this post

    Hello Joe,

    I've prepared a small example base on your description. Can you please give it a try and let me know if this helps? On my side, there is no performance hit. Probably the data points count is different than yours.

    Regards,
    Martin Ivanov
    Progress Telerik
    Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
  3. Joe
    Joe avatar
    65 posts
    Member since:
    Nov 2017

    Posted 07 Jun in reply to Martin Ivanov Link to this post

    Hi Martin,

    Thanks for the reply.  Yes your example does work quite smoothly.  I think my problem is the sheer number of points.  In your example, there are 8,000.  However if I make the simple change of making "maxX" be 1000 (instead of 100) that yields, 80,000 points.  Do that and you will start to see my problem.

     

    Now try taking it to 200,000 points. That's how many my surface has.

    There's not much I can do about that.  In fact, it could have a whole lot more points;  The files I'm reading have X and Y dimensions of approximately 4,000 x 3,000 pixels.  However I sample it to get it down to the "reasonable" size of 200,000 to use in the SurfaceSeries3D.

    (This is used in an application for the measurement of micron-level differences in surfaces so anything lower than 200,000 and I start to lose too much detail)

    I don't generally expect SurfaceSeries3D to achieve OpenGL-level of performance but I was hoping there was some alternate way I could code this a little "closer to the metal" while still using ChartView3D and SurfaceSeries3D to get all their benefit.

  4. Joe
    Joe avatar
    65 posts
    Member since:
    Nov 2017

    Posted 08 Jun Link to this post

    Hi Martin,

    I thought of a workaround that would probably be a lot easier but I cannot make it work.

    Instead of trying to dynamically update a surface with 200,000 points, instead I could use two SurfaceSeries3D objects.  One would be the normal high-res version with a lot of points and another would be a low-res version with only, say, 10,000 or 20,000 points.  Normally I keep the high-res version visible and the low-rest version hidden.  But while the slider is dragging, I only show the "low-res" version and only apply Colorizer updates to *that*.  I would wait until dragging is completed to re-show the high-res" version with the final colorizer.

    This would be acceptable for my task.

    Unfortunately,  SurfaceSeries3D appears to completely ignore its own Visibility setting.  It doesn't matter what I set it to, the surface gets drawn.

    So how can I make ChartView3d show and hide individual series?



  5. Martin Ivanov
    Admin
    Martin Ivanov avatar
    2240 posts

    Posted 12 Jun Link to this post

    Hello Joe,

    I've tested the 3D surface series with 200,000 data points and the performance is find, until you start dragging the slider. In this case multiple value changes are fired from the slider and the colorizer's GradientStops are reset each time. This starts re-drawing of the chart (probably several times within a single second) which leads to the slow performance. 

    Your idea with the low res chart sounds quite nice. However, the Visibility approach won't work. This is because the ModelVisual3D elements used to display the 3D objects doesn't support visibility. If you hide the series control initially, the 3D visuals won't be rendered. But runtime changes doesn't affect them because they are rendered in a separate UI element than the series itself and there is not an easy approach to hide them entirely.

    Instead of the Visibility, you can remove the ItemsSource of the res series re-add it again when you stop dragging the slider. You can see this approach shown in the attached project.

    An alternative way to go is to use RadSlider and set its IsDeferredDraggingEnabled property to True. This way the SelectionChanged event will fire only when you start dragging and the GradientStops will be updated only once.

    Regards,
    Martin Ivanov
    Progress Telerik
    Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
  6. Joe
    Joe avatar
    65 posts
    Member since:
    Nov 2017

    Posted 12 Jun in reply to Martin Ivanov Link to this post

    Hi Martin,

    I ended up doing just what you suggest. My DragStarted handler simply changes the contents of the current ItemsSource to use the low-res version and then my DragCompleted handler puts it back.   I didn't post that back here because I was hoping you would be able to tell me about some secret, code-to-the-metal approach that would let me go even faster.  But this is quite good so thank you.

    At first I figured merely changing visibility would be faster.  But now that I think of it, I imagine there must be little if any speed difference.   The thing that appears to slow down the high-res version is not so much the setting of the points but rather their first-time rendering with new color settings;  And one way or another, the points of the high-res image have to be expensively-rendered with the new settings once the slider is done dragging.  So I think this is good.   Thanks
  7. Answer
    Martin Ivanov
    Admin
    Martin Ivanov avatar
    2240 posts

    Posted 12 Jun Link to this post

    Hi Joe,

    I am afraid this is the most of the native 3D engine that we can provide. I hope this solution is working out for you.

    Regards,
    Martin Ivanov
    Progress Telerik
    Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
Back to Top