0

I am trying to create a 3D-scatter plot of some data with Highcharts. To make it easier to understand the 3D position of the data points, specially when using a still picture of the plot instead of the interactive draggable version, I would like to create projections of the data on each of the plot boundary planes.

It is not difficult to assign the corresponding position of these projections (example: http://jsfiddle.net/worg6jLz/1/), but I would like them to appear as real projections, not just as circles (or actually spheres) regardless of the perspective.

Is there a way to transform the plot markers according to their corresponding plane of projection, so that their shape is consistent with such projection regardless of the viewpoint?

Thanks.

$(function () {

    // Give the points a 3D feel by adding a radial gradient
    Highcharts.getOptions().colors = $.map(Highcharts.getOptions().colors, function (color) {
        return {
            radialGradient: {
                cx: 0.4,
                cy: 0.3,
                r: 0.5
            },
            stops: [
                [0, color],
                [1, Highcharts.Color(color).brighten(-0.2).get('rgb')]
            ]
        };
    });

    // Set up the chart
    var chart = new Highcharts.Chart({
        chart: {
            renderTo: 'container',
            margin: 100,
            type: 'scatter',
            options3d: {
                enabled: true,
                alpha: 10,
                beta: 30,
                depth: 450,
                viewDistance: 5,
                fitToPlot: false,
                frame: {
                    bottom: { size: 1, color: 'rgba(0,0,0,0.02)' },
                    back: { size: 1, color: 'rgba(0,0,0,0.04)' },
                    side: { size: 1, color: 'rgba(0,0,0,0.06)' }
                }
            }
        },
        title: {
            text: 'Draggable box'
        },
        subtitle: {
            text: 'Click and drag the plot area to rotate in space'
        },
        plotOptions: {
            scatter: {
                width: 10,
                height: 10,
                depth: 10
            }
        },
        yAxis: {
            min: 0,
            max: 10,
            title: null
        },
        xAxis: {
            min: 0,
            max: 10,
            gridLineWidth: 1
        },
        zAxis: {
            min: 0,
            max: 10,
            showFirstLabel: false
        },
        legend: {
            enabled: false
        },
        series: [{
            name: 'Data',
            marker: {radius: 7,
                                symbol: 'circle',
                        fillColor: {
                        radialGradient: { cx: 0.4, cy: 0.3, r: 0.5 },
                        stops: [[0, 'rgba(195,195,255,1)'],
                               [1, 'rgba(0,0,255,1)']]}
                      },
            colorByPoint: false,
            color: 'blue',
            data: [[9,9,1],[8,8,2],[7,7,3],[6,6,4],[5,5,5],[4,4,6],[3,3,7],[2,2,8],[1,1,9]]
        },
        {
            name: 'Proj.A',
            marker: {radius: 7,
                                symbol: 'circle'},
            colorByPoint: false,
            color: 'rgba(155,155,155,1)',
            data: [[0,9,1],[0,8,2],[0,7,3],[0,6,4],[0,5,5],[0,4,6],[0,3,7],[0,2,8],[0,1,9]]
        },
        {
            name: 'Proj.B',
            marker: {radius: 7,
                                symbol: 'circle'},
            colorByPoint: false,
            color: 'rgba(155,155,155,1)',
            data: [[9,0,1],[8,0,2],[7,0,3],[6,0,4],[5,0,5],[4,0,6],[3,0,7],[2,0,8],[1,0,9]]
        },
        {
            name: 'Proj.C',
            marker: {radius: 7,
                                symbol: 'circle'},
            colorByPoint: false,
            color: 'rgba(155,155,155,1)',
            data: [[9,9,10],[8,8,10],[7,7,10],[6,6,10],[5,5,10],[4,4,10],[3,3,10],[2,2,10],[1,1,10]]
        }]
    });


    // Add mouse events for rotation
    $(chart.container).bind('mousedown.hc touchstart.hc', function (eStart) {
        eStart = chart.pointer.normalize(eStart);

        var posX = eStart.pageX,
            posY = eStart.pageY,
            alpha = chart.options.chart.options3d.alpha,
            beta = chart.options.chart.options3d.beta,
            newAlpha,
            newBeta,
            sensitivity = 5; // lower is more sensitive

        $(document).bind({
            'mousemove.hc touchdrag.hc': function (e) {
                // Run beta
                newBeta = beta + (posX - e.pageX) / sensitivity;
                chart.options.chart.options3d.beta = newBeta;

                // Run alpha
                newAlpha = alpha + (e.pageY - posY) / sensitivity;
                chart.options.chart.options3d.alpha = newAlpha;

                chart.redraw(false);
            },
            'mouseup touchend': function () {
                $(document).unbind('.hc');
            }
        });
    });

});

Bonus: An additional interesting feature, though not the main purpose of this post, would be to draw lines linking the data points with their projections when the mouse pointer is placed over them.

ZZR
  • 49
  • 4
  • 1
    I am not sure how you would like your markers to look. Could you post some drawing showing your idea? I have added the feature to draw lines between your points and their projections. Look at this example: http://jsfiddle.net/worg6jLz/3/ – Grzegorz Blachliński Aug 02 '16 at 12:03
  • Thanks Grzegorz. Regarding the markers, what I meant them to be is the equivalent of shadows projected perpendicularly to the surface. They would be 2D ellipses, whose shape should actually change according to the viewpoint. I read it is possible to transform objects in that way but I do not know if that can be done dynamically. The projection lines you added look good. I was wondering whether it may be possible to make them appear individually when moving the cursor over the markers, as an alternative option when many data points are present and that many lines might become confusing. – ZZR Aug 02 '16 at 14:34
  • I have added shadows to your chart using polygon series, look at this example http://jsfiddle.net/worg6jLz/13/ – Grzegorz Blachliński Aug 03 '16 at 10:42
  • Thank you very much Grzegorz. That is exactly what I wanted. It seems it's a rather complex solution. I'll experiment with it a little bit to be able to apply the concept to the sets of data and customized features I want to use, but it should work great. It is not essential, and I don't want to ask too much, but I wonder whether the other option I mentioned, regarding projection lines being shown only for the data point the cursor is over at any given time, would also be feasible and reasonably simple to implement. Thanks again. – ZZR Aug 03 '16 at 14:54
  • I actually managed to introduce several modifications to be able to handle larger amounts of data points, as well as group all 3 projections together, so that the projections for each data series can be enabled/disabled individually on the legend if needed. This is the result: http://jsfiddle.net/sen79xem/. I noticed a couple of weird issues, though they do not have real significance. The first one is that the size of the projections depends on the browser used (Chrome or IE, for instance). The second one is that some of the legend names appear repeated when exporting the plot. No big deal. – ZZR Aug 03 '16 at 20:06
  • @ZZR I am trying to achieve something like http://jsfiddle.net/sen79xem/ using Highcharts and ReactJs. You can guide me on this? can it be done without jQuery? – Daniel Nov 03 '20 at 12:49

0 Answers0