Surface Mesh 3D Question

A forum dedicated to WPF version of LightningChart Ultimate.

Moderator: Arction_LasseP

User avatar
blakeadkins
Posts: 44
Joined: Tue Feb 25, 2014 7:49 pm

Surface Mesh 3D Question

Post by blakeadkins » Fri May 23, 2014 3:29 pm

Currently I'm generating some tubes in 3D and then trying to show differences between the ideal and a measured one. You can see an example attached.

Image

I'm curious if I can use Surface Mesh 3D for this. I want to define the ideal 3D points and then shade the measured surface mesh depending on how different the measured is (gradient colors, etc). Is this possible?

User avatar
ArctionPasi
Posts: 1366
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland

Re: Surface Mesh 3D Question

Post by ArctionPasi » Fri May 23, 2014 5:27 pm

Yes, you can do that. Use surface.Fill = FromSurfacePoints, and set the color for each node. You can use e.g. auxiliary, hidden, ViewXY.IntensitySeries' ValueRangePalette.GetColorByValue method to convert each value into a color.
LightningChart Support Team, PT

User avatar
blakeadkins
Posts: 44
Joined: Tue Feb 25, 2014 7:49 pm

Re: Surface Mesh 3D Question

Post by blakeadkins » Fri May 23, 2014 7:11 pm

How I create the Surface Mesh? The way I'm creating the image above is by using a PointLineSeries3D. I start at angle 0 degrees and sweep until 359. Then I step down in the z direction and sweep the other direction. This is repeated the whole way down. I'm confused as to how to convert that to a surface mesh. Any help would be greatly appreciated!

User avatar
ArctionPasi
Posts: 1366
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland

Re: Surface Mesh 3D Question

Post by ArctionPasi » Mon May 26, 2014 10:30 am

No problem, I'll write you an example.
LightningChart Support Team, PT

User avatar
ArctionPasi
Posts: 1366
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland

Re: Surface Mesh 3D Question

Post by ArctionPasi » Mon May 26, 2014 2:19 pm

By using a code below, you can produce a 3D surface from ring data.

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Arction.WPF.LightningChartUltimate;
using Arction.WPF.LightningChartUltimate.Views.View3D;
using Arction.WPF.LightningChartUltimate.Series3D;


namespace WpfSurfaceMesh3DTube
{
    public struct RingDataPoint
    {
        public double X;
        public double Y;
        public double Z;
        public double Value; 
    } 
    
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        LightningChartUltimate m_chart;

        const int RingCount = 30;
        const int PointsInRing = 361; //360 degrees + 1 step to close the ring 
        const double RingRadius = 10;

        RingDataPoint[][] m_rings; 

        public MainWindow()
        {
            InitializeComponent();

            CreateRings(); 

            CreateChart(); 

        }

        void CreateRings()
        {
            //Create array of rings 
            m_rings = new RingDataPoint[RingCount][];
            
            Random rand = new Random(); 
            
            for (int iRing = 0; iRing < RingCount; iRing++)
            {
                //Create a ring in XZ plane
                m_rings[iRing] = new RingDataPoint[PointsInRing];

                for (int iPoint = 0; iPoint < PointsInRing; iPoint++)
                {
                    double dAngleRadians = (double)iPoint * Math.PI / 180.0;
                    m_rings[iRing][iPoint].X = RingRadius * Math.Cos(dAngleRadians);
                    m_rings[iRing][iPoint].Z = RingRadius * Math.Sin(dAngleRadians);

                    //Let's use ring index as Y 
                    m_rings[iRing][iPoint].Y = iRing;

                    //Let's just generate some Value data. 
                    m_rings[iRing][iPoint].Value = 10 + (double)iRing / (double)RingCount * 20.0 * Math.Sin(4 * dAngleRadians)*1.0;
                }
            } 

        }


        void CreateChart()
        {
            m_chart = new LightningChartUltimate();
            gridMain.Children.Add(m_chart);

            m_chart.BeginUpdate();

            m_chart.ActiveView = ActiveView.View3D;

            //Auxiliary mesh, just to contain a value-range palette that is used for primary mesh coloring 
            SurfaceMeshSeries3D meshAux = new SurfaceMeshSeries3D(m_chart.View3D, Axis3DBinding.Primary, Axis3DBinding.Primary, Axis3DBinding.Primary);
            meshAux.ContourPalette = CreatePalette(meshAux, 10, 30);
            meshAux.ContourLineType = ContourLineType.None;
            meshAux.WireframeType = SurfaceWireframeType.None; 
            m_chart.View3D.SurfaceMeshSeries3D.Add(meshAux); 


            //Primary mesh to present the tube 
            SurfaceMeshSeries3D mesh = new SurfaceMeshSeries3D(m_chart.View3D, Axis3DBinding.Primary, Axis3DBinding.Primary, Axis3DBinding.Primary);
            mesh.Title.ShowInLegendBox = false;  //don't show this series in the legend box
            //mesh.WireframeType = SurfaceWireframeType.WireframeSourcePointColored;
            mesh.WireframeType = SurfaceWireframeType.None;
            mesh.WireframeLineStyle.Width = 1;
            mesh.WireframeLineStyle.Color = Colors.Black;
            mesh.ContourLineType = ContourLineType.None;
            mesh.Fill = SurfaceFillStyle.FromSurfacePoints;
            //mesh.Fill = SurfaceFillStyle.None;
            mesh.ColorSaturation = 90;
            
            //Set the rings as mesh data
            //Create an array of surface points. First dimension PointsInRing, second dimension RingCount
            SurfacePoint[,] meshData = new SurfacePoint[PointsInRing, RingCount];
            for (int iRing = 0; iRing < RingCount; iRing++)
            {
                for (int iPoint = 0; iPoint < PointsInRing; iPoint++)
                {
                    meshData[iPoint, iRing].X = m_rings[iRing][iPoint].X;
                    meshData[iPoint, iRing].Y = m_rings[iRing][iPoint].Y;
                    meshData[iPoint, iRing].Z = m_rings[iRing][iPoint].Z;
                    
                    //Use the auxiliary mesh to convert values into colors
                    meshAux.ContourPalette.GetColorByValue(m_rings[iRing][iPoint].Value, out meshData[iPoint, iRing].Color);
                }
            }
            mesh.Data = meshData; 
            

            m_chart.View3D.SurfaceMeshSeries3D.Add(mesh); 


            //Set axis ranges 
            m_chart.View3D.XAxisPrimary3D.SetRange(-RingRadius * 2, RingRadius * 2);
            m_chart.View3D.YAxisPrimary3D.SetRange(0, RingCount-1); 
            m_chart.View3D.ZAxisPrimary3D.SetRange(-RingRadius * 2, RingRadius * 2);

            //FromSurfacePoints coloring needs adjusting the lights little bit stronger to show the colors nicely. 
            mesh.Material.DiffuseColor = Color.FromArgb(255, 200, 200, 200);
            m_chart.View3D.SetPredefinedLightingScheme(LightingScheme.DirectionalFromCamera);
            m_chart.View3D.Lights[0].DiffuseColor = Color.FromArgb(255, 255, 255, 255);

            m_chart.EndUpdate(); 

        }


        private ValueRangePalette CreatePalette(SeriesBase3D ownerSeries, double minValue, double maxValue)
        {
            ValueRangePalette palette = new ValueRangePalette(ownerSeries);
            palette.MinValue = minValue; 
            palette.Steps.Clear();
            palette.Steps.Add(new PaletteStep(palette, Colors.Blue, minValue + (maxValue-minValue) * 0.0));
            palette.Steps.Add(new PaletteStep(palette, Colors.Yellow, minValue + (maxValue-minValue) * 0.5));
            palette.Steps.Add(new PaletteStep(palette, Colors.Red, minValue + (maxValue-minValue) * 1.0));
            palette.Type = PaletteType.Gradient;

            return palette;
        }

    }
}

3D surface mesh series, filled by &quot;difference&quot; value.
3D surface mesh series, filled by "difference" value.
Surface3DMeshWithIntensityData.jpg (126.52 KiB) Viewed 11565 times
and the same without fill, and using point-colored wireframe instead:
3D surface mesh series, wireframe.
3D surface mesh series, wireframe.
Surface3DMeshWithIntensityDataWireframe.jpg (230.87 KiB) Viewed 11565 times

I hope this helps :geek:
LightningChart Support Team, PT

User avatar
blakeadkins
Posts: 44
Joined: Tue Feb 25, 2014 7:49 pm

Re: Surface Mesh 3D Question

Post by blakeadkins » Wed May 28, 2014 4:15 pm

That is excellent; thank you for the example.

One quick followup question; I notice that you have to add one extra point on the end to "complete the ring". The example still seems to work by taking that extra point out. Does having that extra point ensure that the gradient works properly?

User avatar
ArctionPasi
Posts: 1366
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland

Re: Surface Mesh 3D Question

Post by ArctionPasi » Wed May 28, 2014 7:01 pm

If I set PointsInRing = 360, it doesn't make a complete ring. One 'sector' is missing, and there's a gap in the mesh.

Simplified, if you make circle by using 90 degrees interval, first path point is 0, second 90, third 180, fourth 270 and fifth 360 (0). Then it's a closed path.

Great to know this example is useful :)
LightningChart Support Team, PT