Search code examples
javascriptsliderjsxgraph

How to read slider values dynamically into a function in JSXGraph?


UPDATE from 22.5.2019

I did a simpler example of the "not working" code and also imitated the "working code" by defining K1 and KK locally when drawing the points, but doing this inside a method to have them defined only once and have the same definition for all points. Since I want the points to be drawn on a parabola, I now create points that have a fixed radius from the axis of revolution and a sign, so that I can create two points 180 degrees apart by just switching the sign from +1 to -1 when drawing the parametrized points in the xz plane. Still, nothing gets drawn. Here is a link to the thing I want to see (but the code is ugly).

Below the newest try (with less points being drawn, just to see if it works at all).

    const board = JXG.JSXGraph.initBoard('jxgbox', {
  boundingbox: [-10, 10, 10, -10],
  axis: true,
  showCopyright: true,
  showNavigation: true,
  pan: false,
  grid: false,

  zoom: {
    factorX: 1.25,
    factorY: 1.25,
    wheel: false
  }
});

//create z axis
var zAxis = board.create('axis', [
  [0, 0],
  [-1, -1]
], {
  ticks: {
    majorHeight: 10,
    drawLabels: false
  }
});

//create direction of view for projections
var cam = [4, 4, 30]; // [x,y,z]
var r = 6.0;
var origin = [0, 0, 0];

// Function for parallel projection
var project = function(crd, cam) {
  var d = -crd[2] / cam[2];
  return [1, crd[0] + d * cam[0], crd[1] + d * cam[1]];
};

//create slider for rotating the parabola
var sRadius = board.create('slider', [
  [1, -8.5],
  [6, -8.5],
  [-10, 0, 10]
], {
  name: 'angle',
  needsRegularUpdate: true
  //snapWidth: 1
});

//create slider for adjusting the angular speed
var sOmega = board.create('slider', [
  [1, -7.5],
  [6, -7.5],
  [0, 2, 10]
], {
  name: 'Omega',
  needsRegularUpdate: true
  //snapWidth: 1,
});

//fix parameters
const g = 9.81 //gravitational acceleration
const h0 = 5 //initial height of the water surface

//define radius from the y-axis for I3 and I4
const R34 = Math.sqrt(2);


// Function for parallel projection
var project = function(crd, cam) {
  var d = -crd[2] / cam[2];
  return [1, crd[0] + d * cam[0], crd[1] + d * cam[1]];
};


//function creates points for drawing conic sections
function PPoint2(radius,sign,namep,fixval) {
    this.R=radius;
  this.S=sign;
  this.Namep=namep;
  this.Fixval=fixval
}


//method for drawing each Point
PPoint2.prototype.draw = function(pp) {
    board.create('point', [function() {
    var K1 = sOmega.Value()*sOmega.Value()/g,
            KK = 1/4*sOmega.Value()*sOmega.Value()/g,
        v = sRadius.Value() * Math.PI * 0.5 / 10.0,
            c = [pp.sign*pp.R*Math.sin(v),K1/2*pp.R*pp.R-KK+h0,pp.sign*pp.R*Math.cos(v)];
    //debugger
    return project(c, cam);
  }
], {
  fixed: this.Fixval,
  name: this.Namep,
  visible: true
  })
}


//create and draw points

var p3 = new PPoint2(0,-1,'p_3','false');
var I_1 = new PPoint2(r,1,'I_1','false');

//debugger


p3.draw(p3)
I_1.draw(I_1)

Original question below:

I am doing an illustration of the "bucket argument" (how water takes the shape of a paraboloid in a spinning bucket) using JSXGraph. I would like to A) Have the shape of the parabola be dependent on the angular velocity "Omega" of the bucket. B) Have the parabola be projected from 3D into a 2D image and the user being able to turn the parabola using a slider.

For A) my code uses the slider "Omega" and for B) the slider "angle".

The slider values are read into global variables K1 (coeffiecient of the second order term of the parabola) and KK (constant term of the parabola). Then five points (p3 and I_1-I_4) are drawn and the parabola should be drawn through these points. The points are drawn with the initial slider values, but updating (i.e. sliding) the sliders doesn't make the points move. Also, the parabola is not drawn at all.

How to make the points adjust their positions according to the current slider values? The functionality I want is implemented in this fiddle https://jsfiddle.net/ync3pkx5/1/ (but the code is ugly and KK and K1 are defined locally for each point, but I want them to be global).

HTML

<div id="jxgbox" class="jxgbox" style="width:500px; height:500px">
</div>

JS

    //create drawing board
const board = JXG.JSXGraph.initBoard('jxgbox', {
  boundingbox: [-10, 10, 10, -10],
  axis: true,
  showCopyright: true,
  showNavigation: true,
  pan: false,
  grid: false,

  zoom: {
    factorX: 1.25,
    factorY: 1.25,
    wheel: false
  }
});

//create z axis
var zAxis = board.create('axis', [
  [0, 0],
  [-1, -1]
], {
  ticks: {
    majorHeight: 10,
    drawLabels: false
  }
});

//create direction of view for projections
var cam = [4, 4, 30]; // [x,y,z]
var r = 6.0;
var origin = [0, 0, 0];

// Function for parallel projection
var project = function(crd, cam) {
  var d = -crd[2] / cam[2];
  return [1, crd[0] + d * cam[0], crd[1] + d * cam[1]];
};

//create slider for rotating the parabola
var sRadius = board.create('slider', [
  [1, -8.5],
  [6, -8.5],
  [-10, 0, 10]
], {
  name: 'angle',
  //snapWidth: 1
});

//create slider for adjusting the angular speed (inactive)
var sOmega = board.create('slider', [
  [1, -7.5],
  [6, -7.5],
  [0, 0, 10]
], {
  name: 'Omega',
  //snapWidth: 1,
});

//fix parameters
var g = 9.81 //gravitational acceleration
var h0 = 5 //initial height of the water surface
//peak coordinates of the fixed parabola
var KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g; //constant term in the equation of the parabola
var peak = [0, -KK+h0];
//point for mirroring
var pmirr = board.create('point', [0, h0/2], {
  visible: false
});
//define radius from the y-axis for I3 and I4
var R34 = Math.sqrt(2);

//function for projecting poomntson the parabola
var PProject = function(xx,yy,zz) {
    var K1 = sOmega.Value() * sOmega.Value() / g,
      v = sRadius.Value() * Math.PI * 0.5 / 10.0,
      KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g;
    return project([xx * Math.sin(v), K1/2 * yy * yy-KK+h0, zz * Math.cos(v)], cam);
  }


//p1-p3 are used for drawing the elliptical curves circ1 and prbl2
var p1 = board.create('point', [r, 0], {
  fixed: true,
  name: 'p_1',
  visible: false
});
var p2 = board.create('point', [-r, 0], {
  fixed: true,
  name: 'p_2',
  visible: false
});


var p3 = board.create('point', [
  function() {
    var KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g,
            c =[0,-KK+h0,0];
    //alert(KK);
    //alert(h0);
    return project(c, cam);
  }
], {
  visible: true,
  name: 'p3'
});

//divisor when drawing points A-C for ellipses and points A2-C2
var div = Math.sqrt(2)

//point variables for drawing circles   
var A = board.create('point', [
  function() {
    var c = [r / div, 0, r / div];
    return project(c, cam);
  }
], {
  name: 'A',
  visible: false
});


var B = board.create('point', [
  function() {
    var c = [-r / div, 0, r / div];
    return project(c, cam);
  }
], {
  name: 'B',
  visible: false
});


var C = board.create('point', [
  function() {
    var c = [r / div, 0, -r / div];
    return project(c, cam);
  }
], {
  name: 'C',
  visible: false
});


//I-I4 are points for drawing the rotating parabola
var I = board.create('point', [
  function() {
    var K1 = sOmega.Value() * sOmega.Value() / g,
      v = sRadius.Value() * Math.PI * 0.5 / 10.0,
      KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g;
    return project([r * Math.sin(v), K1/2 * r * r-KK+h0, r * Math.cos(v)], cam);
  }
], {
  visible: true,
  name: 'I'
});

var I2 = board.create('point', [
  function() {
    var K1 = sOmega.Value() * sOmega.Value() / g, 
            v = sRadius.Value() * Math.PI * 0.5 / 10.0,
        KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g;
    return project([-r * Math.sin(v), K1/2 * r * r-KK+h0, -r * Math.cos(v)], cam);
  }
], {
  visible: true,
  name: 'I_2'
});


var I3 = board.create('point', [
  function() {
    var K1 = sOmega.Value() * sOmega.Value() / g,
            v = sRadius.Value() * Math.PI * 0.5 / 10.0,
        KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g;
    return project([R34 * Math.sin(v), K1/2 * R34 * R34-KK+h0, R34 * Math.cos(v)], cam);
  }
], {
  visible: true,
  name: 'I_3'
});

var I4 = board.create('point', [
  function() {
    var K1 = sOmega.Value() * sOmega.Value() / g, 
            v = sRadius.Value() * Math.PI * 0.5 / 10.0,
        KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g;
    return project([-R34 * Math.sin(v), K1/2 * R34 * R34-KK+h0, -R34 * Math.cos(v)], cam);
  }
], {
  visible: true,
  name: 'I_4'
});


//draw circle on surface y=0
var circ1 = board.create('conic', [A, B, C, p2, p1]);
//draw a mirror circle of circ1 w.r.t. to pmirr and a small circle that delimits the parabolas
var circ2 = board.create('mirrorelement', [circ1, pmirr]);

//draw the rotating parabola
var prbl2 = board.create('conic', [I, I2, I3, I4, p3], {
  strokeColor: '#CA7291',
  strokeWidth: 2,
  //trace :true
});

debugger;

//add textbox
var txt1 = board.create('text', [3, 7, 'The blue lines delimit the volume of water when Omega = 0 and the red parabola delimits the volume without water as the bucket is rotating (surface h(r)). The water volume is constant, independent of Omega']);

Here is the fiddle I am working on and would want to get to work https://jsfiddle.net/c8tr4dh3/2/

HTML

 <div id="jxgbox" class="jxgbox" style="width:500px; height:500px">
    </div>

JS

    const board = JXG.JSXGraph.initBoard('jxgbox', {
  boundingbox: [-10, 10, 10, -10],
  axis: true,
  showCopyright: true,
  showNavigation: true,
  pan: false,
  grid: false,

  zoom: {
    factorX: 1.25,
    factorY: 1.25,
    wheel: false
  }
});

//create z axis
var zAxis = board.create('axis', [
  [0, 0],
  [-1, -1]
], {
  ticks: {
    majorHeight: 10,
    drawLabels: false
  }
});

//create direction of view for projections
var cam = [4, 4, 30]; // [x,y,z]
var r = 6.0;
var origin = [0, 0, 0];

// Function for parallel projection
var project = function(crd, cam) {
  var d = -crd[2] / cam[2];
  return [1, crd[0] + d * cam[0], crd[1] + d * cam[1]];
};

//create slider for rotating the parabola
var sRadius = board.create('slider', [
  [1, -8.5],
  [6, -8.5],
  [-10, 0, 10]
], {
  name: 'angle',
  needsRegularUpdate: true
  //snapWidth: 1
});

//create slider for adjusting the angular speed (inactive)
var sOmega = board.create('slider', [
  [1, -7.5],
  [6, -7.5],
  [0, 0, 10]
], {
  name: 'Omega',
  needsRegularUpdate: true
  //snapWidth: 1,
});


//fix parameters
var g = 9.81 //gravitational acceleration
var h0 = 5 //initial height of the water surface
var K1 = sOmega.Value() * sOmega.Value() / g; //coeffficient of the quadratic term of the parabola
var KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g; //constant term in the equation of the parabola
//peak coordinates of the fixed parabola
var peak = [0, -KK+h0];
//slider auxiliary variable
var v = sRadius.Value() * Math.PI * 0.5 / 10.0;


//define radius from the y-axis for I3 and I4
var R34 = Math.sqrt(2);

// Function for parallel projection
var project = function(crd, cam) {
  var d = -crd[2] / cam[2];
  return [1, crd[0] + d * cam[0], crd[1] + d * cam[1]];
};


//function creates points for drawing conic sections
function PPoint(xx, yy,zz,namep,fixval) {
    this.XX=xx;
  this.YY=yy;
  this.ZZ=zz;
  this.Namep=namep;
  this.Fixval=fixval
}


//method for drawing each Point
PPoint.prototype.draw = function(pp) {
    board.create('point', [function() {
    var c = [pp.XX,pp.YY,pp.ZZ];
    //debugger
    return project(c, cam);
  }
], {
  fixed: this.Fixval,
  name: this.Namep,
  visible: true
  })
}

var div=Math.sqrt(2);

//create and draw points
var p3 = new PPoint(0,peak[1],0,'p_3','false');
//debugger
var I_1 = new PPoint(r*Math.sin(v),K1/2*r*r-KK+h0,r*Math.cos(v),'I_1','false');
var I_2 = new PPoint(-r*Math.sin(v),K1/2*r*r-KK+h0,-r*Math.cos(v),'I_2','false');
var I_3 = new PPoint(R34*Math.sin(v),K1/2*R34*R34-KK+h0,R34*Math.cos(v),'I_3','false');
var I_4 = new PPoint(-R34*Math.sin(v),K1/2*R34*R34-KK+h0,-R34*Math.cos(v),'I_4','false');

p3.draw(p3)
I_1.draw(I_1)
I_2.draw(I_2)
I_3.draw(I_3)
//debugger;
I_4.draw(I_4)



//draw the rotating parabola
var prbl = board.create('conic', [[I_1.XX,I_1.YY,I_1.ZZ], [I_2.XX,I_2.YY,I_2.ZZ], [I_3.XX,I_3.YY,I_3.ZZ], [I_4.XX,I_4.YY,I_4.ZZ],[p3.XX,p3.YY,p3.ZZ]], {
  strokeColor: '#CA7291',
  strokeWidth: 2,
  //trace :true
});


//debugger;
//add textbox
var txt1 = board.create('text', [3, 7, 'The blue lines delimit the volume of water when Omega = 0 and the red parabola delimits the volume without water as the bucket is rotating (surface h(r)). The water volume is constant, independent of Omega']);

The blue circles in the first fiddle are not critical, they can be added to the other one later.

Having done some debugging, the parents of the parabola all have "isReal: true" in both fiddles, but in the fiddle that isn't working the parabola itself has "isReal: false" while the fiddle that's working has "isReal: true" for the parabola. Not sure whether that's relevant, though.

In the non-working fiddle, I also tried enclosing the whole code into "board.on('mouse,function(){here all code from line 59 onwards{) to get the points move, but that didn't help; the points aren't drawn at all, not even the initial positions.


Solution

  • It seems that in your updated code posted above there is a very simple error: The value of sign is stored in the property pp.S, but you try to access it as pp.sign. My suggestion is to use the following code:

    function PPoint2(radius,sign,namep,fixval) {
        this.R = radius;
        this.S = sign;
        this.Namep = namep;
        this.Fixval = fixval;
    }
    
    
    //method for drawing each Point
    PPoint2.prototype.draw = function() {
        var pp = this;
        this.point = board.create('point', [function() {
            var K1 = sOmega.Value()*sOmega.Value()/g,
                KK = 1/4*sOmega.Value()*sOmega.Value()/g,
                v = sRadius.Value() * Math.PI * 0.5 / 10.0,
                c = [pp.S*pp.R*Math.sin(v),
                     K1/2*pp.R*pp.R-KK+h0,
                     pp.S*pp.R*Math.cos(v)];
            return project(c, cam);
            }], {
        fixed: this.Fixval,
        name: this.Namep,
        visible: true
        });
    };
    
    //create and draw points
    var p3 = new PPoint2(0,-1,'p_3','false');
    var I_1 = new PPoint2(r,1,'I_1','false');
    
    p3.draw();
    I_1.draw();