Skip to content

Instantly share code, notes, and snippets.

@Dev-iL
Last active November 10, 2021 08:56
Show Gist options
  • Save Dev-iL/398a38ae03c6ef9ebf935d46884ce74d to your computer and use it in GitHub Desktop.
Save Dev-iL/398a38ae03c6ef9ebf935d46884ce74d to your computer and use it in GitHub Desktop.
Adding an arbitrary .js library to your uifigure during runtime (R2018a)

fancy

This gist demonstrates how we can attach the D3 library, provided as a remote .js file, to our uifigure and use it to create some interesting visualizations. The JS code in this example is taken almost verbatim from the Particles example, found inside the D3 example gallery.

Instructions:

  1. Save the three code files (.m, .js, .css) in the same folder, and execute the MATLAB script.
  2. If there is some error such as
    Error using matlab.internal.webwindow/executeJS (line 730)
    Error executing JavaScript command:
    
        JavaScript error: Uncaught ReferenceError: d3 is not defined at line 1 column 0 in
    
    it only means that the figure didn't finish loading. Usually running the script again helps; if it doesn't, increase the pause durations; if that doesn't help either, just execute the code line by line.

Few notes:

  • I've been experimenting with various examples until I found one that works with minimal adaptations for MATLAB.
  • Some examples require a canvas element. As it happens, spawning a uiaxes creates a canvas. Since canvas implementations differ slightly between browsers, the contextType supported in uifigures (I only tested this on R2018a) is webgl (v1).
  • The delay which we change using setCoalescerMaxDelay(), is set by default to a duration of 10 days in milliseconds. It appears to be needed to synchronize drawnow events between MATLAB and JS. As noted in the code, I have no idea what sinister side-effects there are to changing this value, as it disables some workaround put in place by TMW.
body {
margin: 0;
background: #222;
min-width: 960px;
}
rect {
fill: none;
pointer-events: all;
}
circle {
fill: none;
stroke-width: 2.5px;
}
var width = Math.max(960, innerWidth),
height = Math.max(500, innerHeight);
var i = 0;
var svg = d3.select("body").select("div.figureContainerNode").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("width", width)
.attr("height", height)
.on("ontouchstart" in document ? "touchmove" : "mousemove", particle);
function particle() {
var m = d3.mouse(this);
svg.insert("circle", "rect")
.attr("cx", m[0])
.attr("cy", m[1])
.attr("r", 1e-6)
.style("stroke", d3.hsl((i = (i + 1) % 360), 1, .5))
.style("stroke-opacity", 1)
.transition()
.duration(2000)
.ease(Math.sqrt)
.attr("r", 100)
.style("stroke-opacity", 1e-6)
.remove();
d3.event.preventDefault();
}
% Create a uifigure & let it load:
hFig = uifigure();
drawnow(); pause(0.1);
% This overcomes some "infinite" loop in MATLAB, which appears to be a workaround for "g1658467".
% The consequences of doing this are unclear.
% See also MATLAB\R20###\toolbox\matlab\uitools\uicomponents\components\...
% +matlab\+ui\+internal\+controller\FigureController.m\flushCoalescer()
struct(struct(struct(hFig).Controller).PeerModelInfo).Synchronizer.setCoalescerMaxDelay(5);
% Get the webwindow handle:
hWW = mlapptools.getWebWindow(hFig);
% Load the JS library (in this example, D3) :
hWW.executeJS('require(["https://d3js.org/d3.v5.js"], function(d3){window.d3 = d3;});');
pause(0.1);
% Make sure D3 was added:
disp(hWW.executeJS('d3.version'));
% At the time of writing, the above gives "5.7.0".
% Have fun....
hWW.executeJS(['d3.select("head").append("style")' ...
'.attr("type","text/css").html(`' fileread('ex1.css') '`);']);
% Alternatively to the above:
% mlapptools.addToHead(hWW, ['<style>' regexprep(fileread('ex1.css'),'\s','') '</style>']);
hWW.executeJS(['eval(`' fileread('ex1.js') '`);']);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment