Creating an interactive graphic using JSXGraph

24 views
Skip to first unread message

Jim Pettigrew

unread,
Oct 22, 2021, 10:41:30 PM10/22/21
to numbas...@googlegroups.com
Hi All,

Inspired by Christian's revamping of the JSXGraph extension, I decided to rewrite a question I'd created a little while ago using the Geogebra extension.


So far, rewriting the question using the JSXGraph extension has been reasonably straightforward. The sticking point has been in enabling two-way communication between Numbas variables/user input and the JSXGraph visualisation.

Also, I have wanted to steer well clear of the problems I've encountered using Geogebra-driven questions that were not created using Numbas variables. (On this score, Christian notes in the JSXGraph docs that "You should create the board in a question variable, so that its state can be saved and restored in case the student leaves and resumes their attempt.".) 

I have read Christian's blog post outlining his improvements to the extension and looked - I believe comprehensively - at the demos he has created. This all looks great and is probably enough to switch my default extension from Geogebra to JSXGraph for the development of interactive Numbas questions.

But I have been unable, using either the JME or JessieCode approach, to replicate all of the features I need (or want) for the movable syringe graphic.

Below is the JavaScript code used to create the graphic.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%'
JXG.Options.grid.gridX=1;
JXG.Options.grid.gridY=0.2;
JXG.Options.point.snapSizeX = 0.2;

var div = Numbas.extensions.jsxgraph.makeBoard('1000px','250px',
{boundingBox: [-1,2.5,250,0.5],
 axis: false,
 showNavigation: false,
 grid: false
});

var board = div.board;

var im = board.create('image', ['resources/question-resources/1mLSyringeImage_no_plunger_end.svg', [-0.1,1],[132.87,-1]]);
// remove highlighting of image
im.hasPoint = function(){return false;}

var pcl = board.create('line', [[132.4,1.5],[232.4,1.5]], {straightFirst:false, straightLast:false, strokeWidth: 0, highlightStrokeWidth: 0});

var mp = board.create('glider', [132.4+100*initialVol,1.5,pcl], {snapToGrid: true, withLabel:false, showInfobox: false, size: 4, fillColor: '#00F', strokeWidth: 0, highlightStrokeWidth: 0, highlightFillColor: '#99ccff', highlightStrokeColor: '#99ccff', highlightFillOpacity: 1, highlightStrokeOpacity: 1}); // var mp = board.create('point', [132.3, 1.5]);

var pel = board.create('line', [[function(){ return mp.X()-120.4;},1.3],[function(){ return mp.X()-120.4;},1.7]], {straightFirst:false, straightLast:false, strokeColor: '#000', strokeWidth: 1.0, highlightStrokeWidth: 0, highlightFillColor: '#000', highlightStrokeColor: '#000', highlightFillOpacity: 1, highlightStrokeOpacity: 1});

// Rectangular region of syringe containing fluid
var rp1 = board.create('point', [12,1.3],{visible: false}),
    rp2 = board.create('point', [function(){ return mp.X()-120.3;},1.3],{visible: false}),
    rp3 = board.create('point', [function(){ return mp.X()-120.3;},1.7],{visible: false}),
    rp4 = board.create('point', [12,1.7],{visible: false}),
    rrect = board.create('polygon',[rp1,rp2,rp3,rp4],{fillColor: '#ff99ff', fillOpacity: 0.25, highlightFillColor: '#ff99ff', highlightFillOpacity: 0.25, highlightStrokeColor: '#000', highlightStrokeOpacity: 1, borders: {strokeWidth: 0, highlightStrokeWidth: 0}});

// Rectangular region at 'open' end of plunger
var repp1 = board.create('point', [function(){ return mp.X()-0.4;},1.28],{visible: false}),
    repp2 = board.create('point', [function(){ return mp.X()+0.4;},1.28],{visible: false}),
    repp3 = board.create('point', [function(){ return mp.X()+0.4;},1.72],{visible: false}),
    repp4 = board.create('point', [function(){ return mp.X()-0.4;},1.72],{visible: false}),
    eprect = board.create('polygon',[repp1,repp2,repp3,repp4],{fillColor: '#fff', fillOpacity: 0, highlightFillColor: '#fff', highlightFillOpacity: 0, borders: {color: '#000',strokeWidth: 0.5, highlightStrokeWidth: 0, highlightStrokeColor: '#000'}});

var poutlinelower = board.create('line', [[123,1.43],[function(){ return mp.X()-0.4;},1.43]], {straightFirst:false, straightLast:false, strokeColor: '#000', strokeWidth: 1.0, highlightStrokeWidth: 0, highlightFillColor: '#000', highlightStrokeColor: '#000', highlightFillOpacity: 1, highlightStrokeOpacity: 1});
var poutlineupper = board.create('line', [[123,1.57],[function(){ return mp.X()-0.4;},1.57]], {straightFirst:false, straightLast:false, strokeColor: '#000', strokeWidth: 1.0, highlightStrokeWidth: 0, highlightFillColor: '#000', highlightStrokeColor: '#000', highlightFillOpacity: 1, highlightStrokeOpacity: 1});

var pinlinelower = board.create('line', [[function(){ return mp.X()-120.4;},1.43],[122,1.43]], {straightFirst:false, straightLast:false, strokeColor: '#000', strokeWidth: 1.0, highlightStrokeWidth: 0, strokeOpacity: 0.15, highlightFillColor: '#000', highlightStrokeColor: '#000', highlightFillOpacity: 1, highlightStrokeOpacity: 0.15});
var pinlineupper = board.create('line', [[function(){ return mp.X()-120.4;},1.57],[122,1.57]], {straightFirst:false, straightLast:false, strokeColor: '#000', strokeWidth: 1.0, highlightStrokeWidth: 0, strokeOpacity: 0.15, highlightFillColor: '#000', highlightStrokeColor: '#000', highlightFillOpacity: 1, highlightStrokeOpacity: 0.15});

return div;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

There are features in here that I cannot work out how to replicate using the JME or JessieCode approach - eg switching off the image highlighting using im.hasPoint = function(){return false;}.

So so my question is: is it possible to create a JSXGraph in the robust way using a Numbas variable (what I'm calling the JME or JessieCode approach) that preserves access to all of JSXGraph's commands and functionality?

I'm thinking something like an object passed as a string to JSXGraph, eg (heuristically) 'disable_image_highlighting': [eval('im.hasPoint = function(){return false;}'].

Any help or advice is much appreciated.

Cheers,

Jim

Christian Lawson-Perfect

unread,
Nov 2, 2021, 6:47:49 AM11/2/21
to numbas...@googlegroups.com
Hi Jim,
Since Jim's question isn't public, I've replied to him privately with a working JessieCode version of his diagram. For everyone else's benefit:
I don't think you need to override the 'hasPoint' function. There's a 'highlightFillOpacity' attribute which you can set to 1 so that the image doesn't change when it's highlighted. But I think that you really just want to turn off the highlighting behaviour for that image, so set highlight: false.
It's not at all clear from the JSXGraph documentation, but I don't think that overriding methods is something you should do in JavaScript, either. There are lots and lots of options, so there's almost always one for whatever behaviour you want. The reason that some of the internal methods aren't available in JessieCode is precisely because it becomes very hard to reason about the board if you change them.

--
You received this message because you are subscribed to the Google Groups "Numbas Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to numbas-users...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/numbas-users/CAJ9nevHj_igBx7qsYbusk46QJmS-QGjDGO2ZvCLex2E51cGaUw%40mail.gmail.com.
Reply all
Reply to author
Forward
0 new messages