Insert Mathbox graph into reactjs application

161 views
Skip to first unread message

Manuel Ángel Hidalgo Pulgarín

unread,
Sep 11, 2018, 6:40:20 AM9/11/18
to MathBox
Hi everyone,

I have created a dynamic graph using mathbox in a html file, and the result is awesome. Now I need to insert this graph into a react component, but I'm not able to get it. I have tried with the property dangerouslySetInnerHTML of React and with react-helmet library, but they don't work for me. Maybe I'm wrong and I have to change my strategy. Anybody could give some help? Please.

Thxs!

Steven Wittens

unread,
Sep 11, 2018, 9:20:09 AM9/11/18
to MathBox
The simplest way is to insert the graph using an iframe tag. Though if you need to communicate between React and Mathbox JS code you'd have to use window.postMessage or something similar to achieve that, so it might not be ideal.

Alternatively, you could render an empty div with React, and then spawn mathbox inside your componentDidMount(). You'd render with a React "ref" on the div, and then pass it in as the containing "element" in the mathbox constructor.

Manuel Ángel Hidalgo Pulgarín

unread,
Sep 12, 2018, 3:41:51 AM9/12/18
to MathBox
I prefer to use the second options, because is nearest to react philosophy. My question now is how I have to import mathbox in my react class, I installed mathbox via npm, so I try with import mathbox from 'mathbox', but I think that I also need to install and import in my react class three.js, don't you? As you can see I'm a little lost with this library. I'm going to show you my code using your second advice, so you can tell me where are my failures now (this code works in a html file perfectly):

import React, { Component } from 'react';
import mathbox from 'mathbox';


export default class ThreeDView extends Component {
componentDidMount() {
var graph = mathbox({
element: this.root,
plugins: ['core', 'controls', 'cursor', 'stats'],
controls: { klass: THREE.OrbitControls }
});
if (graph.fallback) throw 'WebGL not supported';

var three = graph.three;
three.renderer.setClearColor(new THREE.Color(0x000000), 1.0);

var graphData, view;

var a = 1,
b = 0.5;
var xMin = 0,
xMax = 3,
yMin = 0,
yMax = 3,
zMin = 0,
zMax = 3;
var uMin = 0,
uMax = 3,
vMin = 0,
vMax = 3;

// start of updateGraph function ==============================================================
var updateGraphFunc = function() {
view.set('range', [[xMin, xMax], [yMin, yMax], [zMin, zMax]]);
domain.set('range', [[uMin, uMax], [vMin, vMax]]);

// start of color options =============================================
if (graphColorStyle == 'Solid Blue') {
// just a solid blue color
domainColors.set('expr', function(emit, u, v, i, j, t) {
emit(0.5, 0.5, 1.0, 1.0);
});
} else if (graphColorStyle == 'Red U, Green V') {
// increased u/v -> increased red/green
domainColors.set('expr', function(emit, u, v, i, j, t) {
var percentU = (u - uMin) / (uMax - uMin);
var percentV = (v - vMin) / (vMax - vMin);
emit(percentU, percentV, 0.0, 1.0);
});
} else if (graphColorStyle == 'Rainbow Amplitude') {
//
domainColors.set('expr', function(emit, u, v, i, j, t) {
var percent = 1 - Math.abs(Math.sin(5 * u + t) * Math.sin(5 * v + t));
var color = new THREE.Color(0xffffff);
color.setHSL(percent, 1, 0.5);
emit(color.r, color.g, color.b, 1.0);
});
} else if (graphColorStyle == 'Rainbow Along V') {
//
domainColors.set('expr', function(emit, u, v, i, j, t) {
var percent = (v - vMin) / (vMax - vMin);
var color = new THREE.Color(0xfffff);
color.setHSL(percent, 1, 0.5);
emit(color.r, color.g, color.b, 1.0);
});
}
// end of color options =============================================
};
// end of updateGraph function ==============================================================

var updateGraph = function() {
updateGraphFunc();
};

// setting proxy:true allows interactive controls to override base position
var camera = graph.camera({ proxy: true, position: [0, 2, 4] });

// save as variable to adjust later
view = graph.cartesian({
range: [[xMin, xMax], [yMin, yMax], [zMin, zMax]],
scale: [2, 1, 2]
});

// axes
var xAxis = view.axis({ axis: 1, width: 8, detail: 40, color: 'white' });
var xScale = view.scale({ axis: 1, divide: 10, nice: true, zero: true });
var xTicks = view.ticks({ width: 5, size: 15, color: 'white', zBias: 2 });
var xFormat = view.format({
digits: 2,
font: 'Arial',
style: 'normal',
source: xScale
});
var xTicksLabel = view.label({
color: 'white',
zIndex: 0,
offset: [0, -20],
points: xScale,
text: xFormat
});

var yAxis = view.axis({ axis: 3, width: 8, detail: 40, color: 'white' });
var yScale = view.scale({ axis: 3, divide: 5, nice: true, zero: false });
var yTicks = view.ticks({ width: 5, size: 15, color: 'white', zBias: 2 });
var yFormat = view.format({
digits: 2,
font: 'Arial',
style: 'normal',
source: yScale
});
var yTicksLabel = view.label({
color: 'white',
zIndex: 0,
offset: [0, -20],
points: yScale,
text: yFormat
});

var zAxis = view.axis({ axis: 2, width: 8, detail: 40, color: 'white' });
var zScale = view.scale({ axis: 2, divide: 5, nice: true, zero: false });
var zTicks = view.ticks({ width: 5, size: 15, color: 'white', zBias: 2 });
var zFormat = view.format({
digits: 2,
font: 'Arial',
style: 'normal',
source: zScale
});
var zTicksLabel = view.label({
color: 'white',
zIndex: 0,
offset: [20, 0],
points: zScale,
text: zFormat
});

view.grid({ axes: [1, 3], width: 2, divideX: 20, divideY: 20, opacity: 0 });

// need separate range for surface domain values. can't use values from view.

var domain = graph.cartesian({
range: [[uMin, uMax], [vMin, vMax]]
});

var resolution = 64;
graphData = domain.area({
width: resolution,
height: resolution,
// expr: set later
axes: [1, 2], // u,vfalse
channels: 3, // 3D space,
expr: function(emit, u, v, i, j, t) {
emit(u, 1.5 * Math.abs(Math.sin(5 * u + t) * Math.sin(5 * v + t)), v);
}
});

var domainColors = domain.area({
width: resolution,
height: resolution,
// expr: set later
channels: 4 // RGBA
});

var surfaceViewFill = view.surface({
points: graphData,
fill: true,
shaded: false,
lineX: false,
lineY: false,
color: 'white',
colors: domainColors
});

var graphColorStyle = 'Rainbow Amplitude';

updateGraph();
}

render() {
return <div ref={r => (this.root = r)} />;
}
}

Blake La Pierre

unread,
Sep 12, 2018, 4:12:10 AM9/12/18
to MathBox
All the dependencies should be pulled in with an import...but that depends on how you're 'building' the code as I don't think any browsers could use that file as is.

Manuel Ángel Hidalgo Pulgarín

unread,
Sep 12, 2018, 4:22:08 AM9/12/18
to MathBox
The application is running on Chrome.

Manuel Ángel Hidalgo Pulgarín

unread,
Sep 12, 2018, 5:23:46 AM9/12/18
to MathBox
On the other hand, which are all references I have to import in my react class?

Manuel Ángel Hidalgo Pulgarín

unread,
Sep 12, 2018, 6:14:51 AM9/12/18
to MathBox
Besides I'm using webpack to build the code.

Blake La Pierre

unread,
Sep 12, 2018, 1:17:17 PM9/12/18
to mhid...@gmail.com, MathBox
I'm not too familiar with webpack or your setup, but I think the single import is all you need in the ideal situation. Is it not working? Do you see an error message?

On Wed, Sep 12, 2018 at 6:14 AM Manuel Ángel Hidalgo Pulgarín <mhid...@gmail.com> wrote:
Besides I'm using webpack to build the code.

--
You received this message because you are subscribed to a topic in the Google Groups "MathBox" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mathbox/JKTC6e_lLGE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to mathbox+u...@googlegroups.com.
To post to this group, send email to mat...@googlegroups.com.
Visit this group at https://groups.google.com/group/mathbox.
To view this discussion on the web visit https://groups.google.com/d/msgid/mathbox/8fd214f6-979a-4790-a63a-14f6d8202b80%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Manuel Ángel Hidalgo Pulgarín

unread,
Sep 13, 2018, 3:24:46 AM9/13/18
to MathBox
First at all, I have installed mathbox via npm. As you can see in my code, I'm doing an import of mathbox (import mathbox from 'mathbox'), this isn't working, the mathbox imported is not correct, besides I need to import THREE from the library, how do I do it? Thanks for your help.

Blake La Pierre

unread,
Sep 13, 2018, 3:28:05 AM9/13/18
to mhid...@gmail.com, MathBox
Do you have a git repository I could look at?

On Thu, Sep 13, 2018 at 3:24 AM Manuel Ángel Hidalgo Pulgarín <mhid...@gmail.com> wrote:
First at all, I have installed mathbox via npm. As you can see in my code, I'm doing an import of mathbox (import mathbox from 'mathbox'), this isn't working, the mathbox imported is not correct, besides I need to import THREE from the library, how do I do it? Thanks for your help.

--
You received this message because you are subscribed to a topic in the Google Groups "MathBox" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mathbox/JKTC6e_lLGE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to mathbox+u...@googlegroups.com.
To post to this group, send email to mat...@googlegroups.com.
Visit this group at https://groups.google.com/group/mathbox.

Manuel Ángel Hidalgo Pulgarín

unread,
Sep 13, 2018, 3:49:37 AM9/13/18
to MathBox
It's a private repo, but you can see my code below. 

import React, { Component } from 'react';
import mathBox from 'mathbox';


export default class ThreeDView extends Component {
componentDidMount() {
var mathbox = mathBox({
element: this.root,
plugins: ['core', 'controls', 'cursor', 'stats'],
controls: { klass: THREE.OrbitControls }
});

if (mathbox.fallback) throw 'WebGL not supported';

var three = mathbox.three;

Blake La Pierre

unread,
Sep 13, 2018, 4:20:17 AM9/13/18
to mhid...@gmail.com, MathBox
Have you tried typescript?

Based on your line:

var three = mathbox.three;

I would say that mathbox already has the appropriate code to cause some 'builders' to pull in three automatically.

Have you tried importing three as well?

--
You received this message because you are subscribed to a topic in the Google Groups "MathBox" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mathbox/JKTC6e_lLGE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to mathbox+u...@googlegroups.com.
To post to this group, send email to mat...@googlegroups.com.
Visit this group at https://groups.google.com/group/mathbox.

Blake La Pierre

unread,
Sep 13, 2018, 4:21:22 AM9/13/18
to mhid...@gmail.com, MathBox
It looks like three is listed as a dependency at https://github.com/unconed/mathbox/blob/master/package.json so I would expect it to just work if you're using npm.

Blake La Pierre

unread,
Sep 13, 2018, 4:24:35 AM9/13/18
to mhid...@gmail.com, MathBox
I'm not exactly sure what you're trying to do, but maybe you could hack it by putting a <script > tag to the three.js code in the final html.

Manuel Ángel Hidalgo Pulgarín

unread,
Sep 13, 2018, 5:08:28 AM9/13/18
to MathBox
I need to import the THREE variable that is exported by mathbox. The dependency three.js is managed by npm, so I don't need to import it. My first test was a html file with the next code and it works perfectly, but now I have to integrate this funcionality into my react application.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>3D Grapher</title>
<script src="mathbox-bundle.js"></script>

<meta name="viewport" content="initial-scale=1, maximum-scale=1">
</head>
<body>
<script>

var mathbox = mathBox({
plugins: ['core', 'controls', 'cursor', 'stats'],
controls: {klass: THREE.OrbitControls}
});
if (mathbox.fallback) throw "WebGL not supported"

var three = mathbox.three;
console.log(mathbox);
three.renderer.setClearColor(new THREE.Color(0x000000), 1.0);

  var graphData, view;
  
  var a = 1, b = 0.5;
  var xMin = 0, xMax = 3, yMin = 0yMax = 3, zMin = 0, zMax = 3;
  var uMin = 0, uMax = 3, vMin = 0, vMax = 3;
  
  // start of updateGraph function ==============================================================
  var updateGraphFunc = function()
  {
    view.set("range", [[xMin, xMax], [yMin, yMax], [zMin,zMax]]);
    domain.set("range", [[uMin, uMax], [vMin, vMax]] );
    
    // start of color options =============================================
    if (graphColorStyle == "Solid Blue")
    {
      // just a solid blue color      
      domainColors.set("expr",
        function (emit, u,v, i,j, t)
        {
          emit( 0.5, 0.5, 1.0, 1.0 );
        }
      );
    }
    else if (graphColorStyle == "Red U, Green V")
    {
      // increased u/v -> increased red/green     
      domainColors.set("expr",
        function (emit, u,v, i,j, t)
        {
          var percentU = (u - uMin) / (uMax - uMin);
          var percentV = (v - vMin) / (vMax - vMin);
          emit(percentU, percentV, 0.0, 1.0);
    
        }
      );
    }
    else if (graphColorStyle == "Rainbow Amplitude")
    {
      //    
      domainColors.set("expr",
        function (emit, u,v, i,j, t)
        {
          var percent = 1 - Math.abs((Math.sin(5*u + t) * Math.sin(5*v + t)));
          var color = new THREE.Color( 0xffffff );
          color.setHSL( percent, 1, 0.5 );
          emit( color.r, color.g, color.b, 1.0 );
        }
      );
    }
    else if (graphColorStyle == "Rainbow Along V")
    {
      //    
      domainColors.set("expr",
        function (emit, u,v, i,j, t)
        {
          var percent = (v - vMin) / (vMax - vMin);
          var color = new THREE.Color( 0xfffff );
          color.setHSL( percent, 1, 0.5 );
          emit( color.r, color.g, color.b, 1.0 );
        }
      );
    }
    // end of color options =============================================
    
  }
  // end of updateGraph function ==============================================================
  
  var updateGraph = function() { updateGraphFunc(); };
  

  // setting proxy:true allows interactive controls to override base position
  var camera = mathbox.camera( { proxy: true, position: [0, 2, 4] } );

   // save as variable to adjust later
view = mathbox.cartesian(
   {
range: [[xMin, xMax], [yMin, yMax], [zMin,zMax]],
scale: [2,1,2],
}
  );

  // axes
  var xAxis = view.axis( {axis: 1, width: 8, detail: 40, color:"white"} );
var xScale = view.scale( {axis: 1, divide: 10, nice:true, zero:true} );
var xTicks = view.ticks( {width: 5, size: 15, color: "white", zBias:2} );
var xFormat = view.format( {digits: 2, font:"Arial", style: "normal", source: xScale} );
var xTicksLabel = view.label( {color: "white", zIndex: 0, offset:[0,-20], points: xScale, text: xFormat} );
  
  var yAxis = view.axis( {axis: 3, width: 8, detail: 40, color:"white"} );
var yScale = view.scale( {axis: 3, divide: 5, nice:true, zero:false} );
var yTicks = view.ticks( {width: 5, size: 15, color: "white", zBias:2} );
var yFormat = view.format( {digits: 2, font:"Arial", style: "normal", source: yScale} );
var yTicksLabel = view.label( {color: "white", zIndex: 0, offset:[0,-20], points: yScale, text: yFormat} );
  
  var zAxis = view.axis( {axis: 2, width: 8, detail: 40, color:"white"} );
var zScale = view.scale( {axis: 2, divide: 5, nice:true, zero:false} );
var zTicks = view.ticks( {width: 5, size: 15, color: "white", zBias:2} );
var zFormat = view.format( {digits: 2, font:"Arial", style: "normal", source: zScale} );
var zTicksLabel = view.label( {color: "white", zIndex: 0, offset:[20,0], points: zScale, text: zFormat} );
  
  view.grid( {axes:[1,3], width: 2, divideX: 20, divideY: 20, opacity:0} );
  
  // need separate range for surface domain values. can't use values from view.
  
var domain = mathbox.cartesian(
   {
range: [[uMin, uMax], [vMin, vMax]]
}
  );
  
  var resolution = 64;
  graphData = domain.area({
    width: resolution, height: resolution,
// expr: set later
    axes: [1,2], // u,vfalse
channels: 3, // 3D space,
    expr: function (emit, u, v, i, j, t)
      {
       emit(u, 1.5 * Math.abs((Math.sin(5*u + t) * Math.sin(5*v + t))), v);
      }
});

  var domainColors = domain.area({
    width: resolution, height: resolution,
    // expr: set later
   channels: 4, // RGBA
    
});
  
  var surfaceViewFill = view.surface({
   points: graphData,
   fill: true,
    shaded: false,
    lineX: false,
    lineY: false,
    color: "white",
    colors: domainColors,
});
  
  // var surfaceViewLine = view.surface({
  // points: graphData,
  // fill: false, shaded: false, lineX: true, lineY: true,
// color: "black",
// });
  
// GUI controls
  
  // var gui = new dat.GUI();
  
  // var xFuncGUI = gui.add( this, 'xFunctionText' ).name('x = f(u,v) = ');
  // var yFuncGUI = gui.add( this, 'yFunctionText' ).name('y = g(u,v) = ');
  // var zFuncGUI = gui.add( this, 'zFunctionText' ).name('z = h(u,v) = ');
  
  // var folder0 = gui.addFolder('Parameters');
  // var aGUI = folder0.add( this, 'a' ).min(0).max(5).step(0.01).name('a = ');
  // var bGUI = folder0.add( this, 'b' ).min(0).max(5).step(0.01).name('b = ');
  // folder0.open();
  
  
  // var folder2 = gui.addFolder('Parameter Range');
  // var uMinGUI = folder2.add( this, 'uMin' ).onChange( updateGraphFunc );
  // var uMaxGUI = folder2.add( this, 'uMax' ).onChange( updateGraphFunc );
  // var vMinGUI = folder2.add( this, 'vMin' ).onChange( updateGraphFunc );
  // var vMaxGUI = folder2.add( this, 'vMax' ).onChange( updateGraphFunc );
  // folder2.open();
  
  // var folderP = gui.addFolder("Preset Equations");
  // folderP.open();
  
  // var presetFunc1 = function() {
  //  xFuncGUI.setValue("u"); yFuncGUI.setValue("v"); zFuncGUI.setValue("random()");
  //  uMinGUI.setValue(-1); uMaxGUI.setValue(1);
  //  vMinGUI.setValue(-1); vMaxGUI.setValue(1);
  //  updateGraph();
  //  };
  // var preset1GUI = folderP.add( this, "presetFunc1" ).name("Square");  
  
  // var folder1 = gui.addFolder('Window Range');
  // var xMinGUI = folder1.add( this, 'xMin' );
  // var xMaxGUI = folder1.add( this, 'xMax' );
  // var zMinGUI = folder1.add( this, 'zMin' ).name("yMin");
  // var zMaxGUI = folder1.add( this, 'zMax' ).name("yMax");
  // var yMinGUI = folder1.add( this, 'yMin' ).name("zMin");
  // var yMaxGUI = folder1.add( this, 'yMax' ).name("zMax");
  // folder1.close();
  
  var graphColorStyle = "Rainbow Amplitude";
  // var graphColorStyleList = ["Solid Blue", "Red U, Green V", "Rainbow Amplitude", "Rainbow Along V"];
  // var graphColorGUI = gui.add( this, "graphColorStyle", graphColorStyleList ).name('Graph style').onChange( updateGraphFunc );
  
  // gui.add( this, 'updateGraph' ).name("Update Graph");
  
  // gui.open();
  
  
  updateGraph();
  
  </script>
</body>
</html>

Blake La Pierre

unread,
Sep 13, 2018, 9:14:34 AM9/13/18
to MathBox
Oh, are you talking about for this line:

>new THREE.Color(0x000000)

?

try new three.Color(...

or var THREE = three;

Manuel Ángel Hidalgo Pulgarín

unread,
Sep 17, 2018, 3:39:19 AM9/17/18
to MathBox
I've created a test project using create-react-app, you can found it here https://github.com/mhidalgop/matbox-react-test an you also can pull it. This is an example of my problem, I'm not able to import the mathbox library into a react application. In this case, I'm trying to import mathbox from index.html, but mathBox and THREE are not being included into window object. Thxs!

Christopher Chudzicki

unread,
Oct 16, 2018, 9:45:58 PM10/16/18
to MathBox
For anyone else stumbling onto this, I just made a PR to Manuel's repo:


This presents a way to get MathBox working in React. (The only way I could figure out.) I'm currently using React + MathBox for a total re-write of math3d.org. The repo is:


and there's a (fairly-polished) prototype on Heroku:

Reply all
Reply to author
Forward
0 new messages