Search code examples
javascriptmultidimensional-array3dplotly.js

Getting X, Y and Z reference planes in plotly.js


I'm learning a bit of plotly mixing it with HTML/JavaScript, so I'm using the plotly.js CDN

I pretend to draw a set of randomly generated dots into a X, Y, and Z coordinate space, I have successfully generated all the dots but I'm facing an issues trying to show in the same drawing all three coordinates axis as a reference planes...

I have successfully been able to show the XY plane and dots

But I'm still failing to shows the XZ and YZ references planes as I'm doing in here but with lines

I have even attempt to use chatGPT but it's totally lost trying to resolve the issue...

Here share the whole JS script code I'm using , hope I get someone that can help!!!!

<script>
    // Array to store the generated points
    const dots = [];

    // Generate 100 points with random x, y, z coordinates
    for (let i = 0; i < 100; i++) {
      const x = Math.random() * 20 - 10;
      const y = Math.random() * 20 - 10;
      const z = Math.random() * 20 - 10;
      dots.push({ x, y, z });
    }

    // Extract x, y, and z coordinates separately for the chart
    const xData = dots.map(point => point.x);
    const yData = dots.map(point => point.y);
    const zData = dots.map(point => point.z);

    // Configure the 3D chart trace
    const trace = {
      x: xData,
      y: yData,
      z: zData,
      mode: 'markers',
      marker: {
        size: 5,
        color: 'rgb(75, 192, 192)',
      },
      type: 'scatter3d',
    };

    // Plane x-y (z = 0)
    const dataPlaneXY = {
        x: [-10, 10, 10, -10], 
        y: [-10, -10, 10, 10], 
        z: [0, 0, 0, 0], 
        type: 'mesh3d',
        colorscale: 'Viridis', 
        opacity: 0.5, 
        lighting: {
            specular: 0.2,
            ambient: 0.8,
            diffuse: 0.5,
            fresnel: 0.2,
        },
    };

    // Plane x-z (y = 0)
    const dataPlaneXZ = {
        x: [-10, 10, 10, -10], 
        y: [0, 0, 0, 0], 
        z: [-10, -10, 10, 10], 
        type: 'mesh3d',
        colorscale: 'Viridis', 
        opacity: 0.5, 
        lighting: {
            specular: 0.2,
            ambient: 0.8,
            diffuse: 0.5,
            fresnel: 0.2,
        },
    };

    // Plane y-z (x = 0)
    const dataPlaneYZ = {
        x: [0, 0, 0, 0], 
        y: [-10, 10, 10, -10], 
        z: [-10, -10, 10, 10], 
        type: 'mesh3d',
        colorscale: 'Viridis', 
        opacity: 0.5, 
        lighting: {
            specular: 0.2,
            ambient: 0.8,
            diffuse: 0.5,
            fresnel: 0.2,
        },
    };

    // Data for the chart, including the lines for the reference planes
    const data = [trace, dataPlaneXY, dataPlaneXZ, dataPlaneYZ];

    // Configure the layout of the 3D chart
    const layout = {
      scene: {
        xaxis: { title: 'X Coordinate', range: [-10, 10], showaxeslines: true },
        yaxis: { title: 'Y Coordinate', range: [-10, 10], showaxeslines: true },
        zaxis: { title: 'Z Coordinate', range: [-10, 10], showaxeslines: true },
        aspectmode: 'cube', //  ensures the axes have the same scale
        showaxeslines: true, // Show axis lines
      },
    };
    // Draw the 3D chart
    Plotly.newPlot('coordinateChart', data, layout);
  </script>

Thanks a lot for @kikon for they answer ( please read it!!! )

If someone is interested here the result of his answer


Solution

  • A mesh3d is a set of triangles; the properties x, y and z specify a set of points; one has also to specify how these points are to be grouped in triangles.

    The safest and clearest way to do that is to set the properties i, j and k. If one sets:

    i: [0, 1],
    j: [1, 2],
    k: [2, 3],
    

    there will be two triangles: the first formed by the points 0,1,2 and the second by points 1,2,3 - coordinates taken from the x,y,z arrays.

    Here's a solution, based on that (note that I changed the order of two coordinates also):

    const dots = [];
    
    // Generate 100 points with random x, y, z coordinates
    for (let i = 0; i < 100; i++) {
       const x = Math.random() * 20 - 10;
       const y = Math.random() * 20 - 10;
       const z = Math.random() * 20 - 10;
       dots.push({ x, y, z });
    }
    
    // Extract x, y, and z coordinates separately for the chart
    const xData = dots.map(point => point.x);
    const yData = dots.map(point => point.y);
    const zData = dots.map(point => point.z);
    
    // Configure the 3D chart trace
    const trace = {
       x: xData,
       y: yData,
       z: zData,
       mode: 'markers',
       marker: {
          size: 5,
          color: 'rgb(75, 192, 192)',
       },
       type: 'scatter3d',
    };
    
    // Plane x-y (z = 0)
    const dataPlaneXY = {
       x: [-10, 10, -10, 10],
       y: [-10, -10, 10, 10],
       z: [0, 0, 0, 0],
       i: [0, 1],
       j: [1, 2],
       k: [2, 3],
       type: 'mesh3d',
       colorscale: 'Viridis',
       opacity: 0.5,
       lighting: {
          specular: 0.2,
          ambient: 0.8,
          diffuse: 0.5,
          fresnel: 0.2,
       },
    };
    
    // Plane x-z (y = 0)
    const dataPlaneXZ = {
       x: [-10, 10, -10, 10],
       y: [0, 0, 0, 0],
       z: [-10, -10, 10, 10],
       i: [0, 1],
       j: [1, 2],
       k: [2, 3],
       type: 'mesh3d',
       colorscale: 'Viridis',
       opacity: 0.5,
       lighting: {
          specular: 0.2,
          ambient: 0.8,
          diffuse: 0.5,
          fresnel: 0.2,
       },
    };
    
    // Plane y-z (x = 0)
    const dataPlaneYZ = {
       x: [0, 0, 0, 0],
       y: [-10, 10, -10, 10],
       z: [-10, -10, 10, 10],
       i: [0, 1],
       j: [1, 2],
       k: [2, 3],
       type: 'mesh3d',
       colorscale: 'Viridis',
       opacity: 0.5,
       lighting: {
          specular: 0.2,
          ambient: 0.8,
          diffuse: 0.5,
          fresnel: 0.2,
       },
    };
    
    // Data for the chart, including the lines for the reference planes
    const data = [trace, dataPlaneXY, dataPlaneXZ, dataPlaneYZ];
    
    // Configure the layout of the 3D chart
    const layout = {
       scene: {
          xaxis: { title: 'X Coordinate', range: [-10, 10], showaxeslines: true },
          yaxis: { title: 'Y Coordinate', range: [-10, 10], showaxeslines: true },
          zaxis: { title: 'Z Coordinate', range: [-10, 10], showaxeslines: true },
          aspectmode: 'cube', //  ensures the axes have the same scale
          showaxeslines: true, // Show axis lines
       },
    };
    // Draw the 3D chart
    Plotly.newPlot('coordinateChart', data, layout);
    <div id='coordinateChart' style="height:450px; width:100%"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/plotly.js/2.24.3/plotly.min.js" integrity="sha512-OB3KqMv8ZelkEhNOv1J6PB0aYRkn8oota0+LoGXIVD3hv1Pu9ebxFJXBopRlGkYLTLEUM7aX9zBepBzGcZzH5A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

    Now, for an alternative solution, and an explanation of why did the xy plane work without the i, j, k: if they are not specified, then the Delaunay triangulation is used - see the docs on alphahull and delaunayaxis properties.

    So, it turns out that at least for the 4 coplanar points that you have Delaunay triangulation works fine, but you have to set the delaunayaxis to the axis perpendicular to their plane. And yes, the default is z and that's why it worked with no addition for the xy plane:

    const dots = [];
    
    // Generate 100 points with random x, y, z coordinates
    for (let i = 0; i < 100; i++) {
       const x = Math.random() * 20 - 10;
       const y = Math.random() * 20 - 10;
       const z = Math.random() * 20 - 10;
       dots.push({ x, y, z });
    }
    
    // Extract x, y, and z coordinates separately for the chart
    const xData = dots.map(point => point.x);
    const yData = dots.map(point => point.y);
    const zData = dots.map(point => point.z);
    
    // Configure the 3D chart trace
    const trace = {
       x: xData,
       y: yData,
       z: zData,
       mode: 'markers',
       marker: {
          size: 5,
          color: 'rgb(75, 192, 192)',
       },
       type: 'scatter3d',
    };
    
    // Plane x-y (z = 0)
    const dataPlaneXY = {
       x: [-10, 10, 10, -10],
       y: [-10, -10, 10, 10],
       z: [0, 0, 0, 0],
       delaunayaxis: 'z', //default
       type: 'mesh3d',
       colorscale: 'Viridis',
       opacity: 0.5,
       lighting: {
          specular: 0.2,
          ambient: 0.8,
          diffuse: 0.5,
          fresnel: 0.2,
       },
    };
    
    // Plane x-z (y = 0)
    const dataPlaneXZ = {
       x: [-10, 10, 10, -10],
       y: [0, 0, 0, 0],
       z: [-10, -10, 10, 10],
       delaunayaxis: 'y',
       type: 'mesh3d',
       colorscale: 'Viridis',
       opacity: 0.5,
       lighting: {
          specular: 0.2,
          ambient: 0.8,
          diffuse: 0.5,
          fresnel: 0.2,
       },
    };
    
    // Plane y-z (x = 0)
    const dataPlaneYZ = {
       x: [0, 0, 0, 0],
       y: [-10, 10, 10, -10],
       z: [-10, -10, 10, 10],
       delaunayaxis: 'x',
       type: 'mesh3d',
       colorscale: 'Viridis',
       opacity: 0.5,
       lighting: {
          specular: 0.2,
          ambient: 0.8,
          diffuse: 0.5,
          fresnel: 0.2,
       },
    };
    
    // Data for the chart, including the lines for the reference planes
    const data = [trace, dataPlaneXY, dataPlaneXZ, dataPlaneYZ];
    
    // Configure the layout of the 3D chart
    const layout = {
       scene: {
          xaxis: { title: 'X Coordinate', range: [-10, 10], showaxeslines: true },
          yaxis: { title: 'Y Coordinate', range: [-10, 10], showaxeslines: true },
          zaxis: { title: 'Z Coordinate', range: [-10, 10], showaxeslines: true },
          aspectmode: 'cube', //  ensures the axes have the same scale
          showaxeslines: true, // Show axis lines
       },
    };
    // Draw the 3D chart
    Plotly.newPlot('coordinateChart', data, layout);
    <div id='coordinateChart' style="height:450px"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/plotly.js/2.24.3/plotly.min.js" integrity="sha512-OB3KqMv8ZelkEhNOv1J6PB0aYRkn8oota0+LoGXIVD3hv1Pu9ebxFJXBopRlGkYLTLEUM7aX9zBepBzGcZzH5A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>