").addClass(errClass).css("position", "absolute")
+ .css("top", el.offsetTop)
+ .css("left", el.offsetLeft)
+ // setting width can push out the page size, forcing otherwise
+ // unnecessary scrollbars to appear and making it impossible for
+ // the element to shrink; so use max-width instead
+ .css("maxWidth", el.offsetWidth)
+ .css("height", el.offsetHeight);
+ errorDiv.text(err.message);
+ $el.after(errorDiv);
+
+ // Really dumb way to keep the size/position of the error in sync with
+ // the parent element as the window is resized or whatever.
+ var intId = setInterval(function() {
+ if (!errorDiv[0].parentElement) {
+ clearInterval(intId);
+ return;
+ }
+ errorDiv
+ .css("top", el.offsetTop)
+ .css("left", el.offsetLeft)
+ .css("maxWidth", el.offsetWidth)
+ .css("height", el.offsetHeight);
+ }, 500);
+ }
+ }
+ },
+ clearError: function(el) {
+ var $el = $(el);
+ var display = $el.data("restore-display-mode");
+ $el.data("restore-display-mode", null);
+
+ if (display === "inline" || display === "inline-block") {
+ if (display)
+ $el.css("display", display);
+ $(el.nextSibling).filter(".htmlwidgets-error").remove();
+ } else if (display === "block"){
+ $el.css("visibility", "inherit");
+ $(el.nextSibling).filter(".htmlwidgets-error").remove();
+ }
+ },
+ sizing: {}
+ };
+
+ // Called by widget bindings to register a new type of widget. The definition
+ // object can contain the following properties:
+ // - name (required) - A string indicating the binding name, which will be
+ // used by default as the CSS classname to look for.
+ // - initialize (optional) - A function(el) that will be called once per
+ // widget element; if a value is returned, it will be passed as the third
+ // value to renderValue.
+ // - renderValue (required) - A function(el, data, initValue) that will be
+ // called with data. Static contexts will cause this to be called once per
+ // element; Shiny apps will cause this to be called multiple times per
+ // element, as the data changes.
+ window.HTMLWidgets.widget = function(definition) {
+ if (!definition.name) {
+ throw new Error("Widget must have a name");
+ }
+ if (!definition.type) {
+ throw new Error("Widget must have a type");
+ }
+ // Currently we only support output widgets
+ if (definition.type !== "output") {
+ throw new Error("Unrecognized widget type '" + definition.type + "'");
+ }
+ // TODO: Verify that .name is a valid CSS classname
+
+ // Support new-style instance-bound definitions. Old-style class-bound
+ // definitions have one widget "object" per widget per type/class of
+ // widget; the renderValue and resize methods on such widget objects
+ // take el and instance arguments, because the widget object can't
+ // store them. New-style instance-bound definitions have one widget
+ // object per widget instance; the definition that's passed in doesn't
+ // provide renderValue or resize methods at all, just the single method
+ // factory(el, width, height)
+ // which returns an object that has renderValue(x) and resize(w, h).
+ // This enables a far more natural programming style for the widget
+ // author, who can store per-instance state using either OO-style
+ // instance fields or functional-style closure variables (I guess this
+ // is in contrast to what can only be called C-style pseudo-OO which is
+ // what we required before).
+ if (definition.factory) {
+ definition = createLegacyDefinitionAdapter(definition);
+ }
+
+ if (!definition.renderValue) {
+ throw new Error("Widget must have a renderValue function");
+ }
+
+ // For static rendering (non-Shiny), use a simple widget registration
+ // scheme. We also use this scheme for Shiny apps/documents that also
+ // contain static widgets.
+ window.HTMLWidgets.widgets = window.HTMLWidgets.widgets || [];
+ // Merge defaults into the definition; don't mutate the original definition.
+ var staticBinding = extend({}, defaults, definition);
+ overrideMethod(staticBinding, "find", function(superfunc) {
+ return function(scope) {
+ var results = superfunc(scope);
+ // Filter out Shiny outputs, we only want the static kind
+ return filterByClass(results, "html-widget-output", false);
+ };
+ });
+ window.HTMLWidgets.widgets.push(staticBinding);
+
+ if (shinyMode) {
+ // Shiny is running. Register the definition with an output binding.
+ // The definition itself will not be the output binding, instead
+ // we will make an output binding object that delegates to the
+ // definition. This is because we foolishly used the same method
+ // name (renderValue) for htmlwidgets definition and Shiny bindings
+ // but they actually have quite different semantics (the Shiny
+ // bindings receive data that includes lots of metadata that it
+ // strips off before calling htmlwidgets renderValue). We can't
+ // just ignore the difference because in some widgets it's helpful
+ // to call this.renderValue() from inside of resize(), and if
+ // we're not delegating, then that call will go to the Shiny
+ // version instead of the htmlwidgets version.
+
+ // Merge defaults with definition, without mutating either.
+ var bindingDef = extend({}, defaults, definition);
+
+ // This object will be our actual Shiny binding.
+ var shinyBinding = new Shiny.OutputBinding();
+
+ // With a few exceptions, we'll want to simply use the bindingDef's
+ // version of methods if they are available, otherwise fall back to
+ // Shiny's defaults. NOTE: If Shiny's output bindings gain additional
+ // methods in the future, and we want them to be overrideable by
+ // HTMLWidget binding definitions, then we'll need to add them to this
+ // list.
+ delegateMethod(shinyBinding, bindingDef, "getId");
+ delegateMethod(shinyBinding, bindingDef, "onValueChange");
+ delegateMethod(shinyBinding, bindingDef, "onValueError");
+ delegateMethod(shinyBinding, bindingDef, "renderError");
+ delegateMethod(shinyBinding, bindingDef, "clearError");
+ delegateMethod(shinyBinding, bindingDef, "showProgress");
+
+ // The find, renderValue, and resize are handled differently, because we
+ // want to actually decorate the behavior of the bindingDef methods.
+
+ shinyBinding.find = function(scope) {
+ var results = bindingDef.find(scope);
+
+ // Only return elements that are Shiny outputs, not static ones
+ var dynamicResults = results.filter(".html-widget-output");
+
+ // It's possible that whatever caused Shiny to think there might be
+ // new dynamic outputs, also caused there to be new static outputs.
+ // Since there might be lots of different htmlwidgets bindings, we
+ // schedule execution for later--no need to staticRender multiple
+ // times.
+ if (results.length !== dynamicResults.length)
+ scheduleStaticRender();
+
+ return dynamicResults;
+ };
+
+ // Wrap renderValue to handle initialization, which unfortunately isn't
+ // supported natively by Shiny at the time of this writing.
+
+ shinyBinding.renderValue = function(el, data) {
+ Shiny.renderDependencies(data.deps);
+ // Resolve strings marked as javascript literals to objects
+ if (!(data.evals instanceof Array)) data.evals = [data.evals];
+ for (var i = 0; data.evals && i < data.evals.length; i++) {
+ window.HTMLWidgets.evaluateStringMember(data.x, data.evals[i]);
+ }
+ if (!bindingDef.renderOnNullValue) {
+ if (data.x === null) {
+ el.style.visibility = "hidden";
+ return;
+ } else {
+ el.style.visibility = "inherit";
+ }
+ }
+ if (!elementData(el, "initialized")) {
+ initSizing(el);
+
+ elementData(el, "initialized", true);
+ if (bindingDef.initialize) {
+ var rect = el.getBoundingClientRect();
+ var result = bindingDef.initialize(el, rect.width, rect.height);
+ elementData(el, "init_result", result);
+ }
+ }
+ bindingDef.renderValue(el, data.x, elementData(el, "init_result"));
+ evalAndRun(data.jsHooks.render, elementData(el, "init_result"), [el, data.x]);
+ };
+
+ // Only override resize if bindingDef implements it
+ if (bindingDef.resize) {
+ shinyBinding.resize = function(el, width, height) {
+ // Shiny can call resize before initialize/renderValue have been
+ // called, which doesn't make sense for widgets.
+ if (elementData(el, "initialized")) {
+ bindingDef.resize(el, width, height, elementData(el, "init_result"));
+ }
+ };
+ }
+
+ Shiny.outputBindings.register(shinyBinding, bindingDef.name);
+ }
+ };
+
+ var scheduleStaticRenderTimerId = null;
+ function scheduleStaticRender() {
+ if (!scheduleStaticRenderTimerId) {
+ scheduleStaticRenderTimerId = setTimeout(function() {
+ scheduleStaticRenderTimerId = null;
+ window.HTMLWidgets.staticRender();
+ }, 1);
+ }
+ }
+
+ // Render static widgets after the document finishes loading
+ // Statically render all elements that are of this widget's class
+ window.HTMLWidgets.staticRender = function() {
+ var bindings = window.HTMLWidgets.widgets || [];
+ forEach(bindings, function(binding) {
+ var matches = binding.find(document.documentElement);
+ forEach(matches, function(el) {
+ var sizeObj = initSizing(el, binding);
+
+ var getSize = function(el) {
+ if (sizeObj) {
+ return {w: sizeObj.getWidth(), h: sizeObj.getHeight()}
+ } else {
+ var rect = el.getBoundingClientRect();
+ return {w: rect.width, h: rect.height}
+ }
+ };
+
+ if (hasClass(el, "html-widget-static-bound"))
+ return;
+ el.className = el.className + " html-widget-static-bound";
+
+ var initResult;
+ if (binding.initialize) {
+ var size = getSize(el);
+ initResult = binding.initialize(el, size.w, size.h);
+ elementData(el, "init_result", initResult);
+ }
+
+ if (binding.resize) {
+ var lastSize = getSize(el);
+ var resizeHandler = function(e) {
+ var size = getSize(el);
+ if (size.w === 0 && size.h === 0)
+ return;
+ if (size.w === lastSize.w && size.h === lastSize.h)
+ return;
+ lastSize = size;
+ binding.resize(el, size.w, size.h, initResult);
+ };
+
+ on(window, "resize", resizeHandler);
+
+ // This is needed for cases where we're running in a Shiny
+ // app, but the widget itself is not a Shiny output, but
+ // rather a simple static widget. One example of this is
+ // an rmarkdown document that has runtime:shiny and widget
+ // that isn't in a render function. Shiny only knows to
+ // call resize handlers for Shiny outputs, not for static
+ // widgets, so we do it ourselves.
+ if (window.jQuery) {
+ window.jQuery(document).on(
+ "shown.htmlwidgets shown.bs.tab.htmlwidgets shown.bs.collapse.htmlwidgets",
+ resizeHandler
+ );
+ window.jQuery(document).on(
+ "hidden.htmlwidgets hidden.bs.tab.htmlwidgets hidden.bs.collapse.htmlwidgets",
+ resizeHandler
+ );
+ }
+
+ // This is needed for the specific case of ioslides, which
+ // flips slides between display:none and display:block.
+ // Ideally we would not have to have ioslide-specific code
+ // here, but rather have ioslides raise a generic event,
+ // but the rmarkdown package just went to CRAN so the
+ // window to getting that fixed may be long.
+ if (window.addEventListener) {
+ // It's OK to limit this to window.addEventListener
+ // browsers because ioslides itself only supports
+ // such browsers.
+ on(document, "slideenter", resizeHandler);
+ on(document, "slideleave", resizeHandler);
+ }
+ }
+
+ var scriptData = document.querySelector("script[data-for='" + el.id + "'][type='application/json']");
+ if (scriptData) {
+ var data = JSON.parse(scriptData.textContent || scriptData.text);
+ // Resolve strings marked as javascript literals to objects
+ if (!(data.evals instanceof Array)) data.evals = [data.evals];
+ for (var k = 0; data.evals && k < data.evals.length; k++) {
+ window.HTMLWidgets.evaluateStringMember(data.x, data.evals[k]);
+ }
+ binding.renderValue(el, data.x, initResult);
+ evalAndRun(data.jsHooks.render, initResult, [el, data.x]);
+ }
+ });
+ });
+
+ invokePostRenderHandlers();
+ }
+
+
+ function has_jQuery3() {
+ if (!window.jQuery) {
+ return false;
+ }
+ var $version = window.jQuery.fn.jquery;
+ var $major_version = parseInt($version.split(".")[0]);
+ return $major_version >= 3;
+ }
+
+ /*
+ / Shiny 1.4 bumped jQuery from 1.x to 3.x which means jQuery's
+ / on-ready handler (i.e., $(fn)) is now asyncronous (i.e., it now
+ / really means $(setTimeout(fn)).
+ / https://jquery.com/upgrade-guide/3.0/#breaking-change-document-ready-handlers-are-now-asynchronous
+ /
+ / Since Shiny uses $() to schedule initShiny, shiny>=1.4 calls initShiny
+ / one tick later than it did before, which means staticRender() is
+ / called renderValue() earlier than (advanced) widget authors might be expecting.
+ / https://github.com/rstudio/shiny/issues/2630
+ /
+ / For a concrete example, leaflet has some methods (e.g., updateBounds)
+ / which reference Shiny methods registered in initShiny (e.g., setInputValue).
+ / Since leaflet is privy to this life-cycle, it knows to use setTimeout() to
+ / delay execution of those methods (until Shiny methods are ready)
+ / https://github.com/rstudio/leaflet/blob/18ec981/javascript/src/index.js#L266-L268
+ /
+ / Ideally widget authors wouldn't need to use this setTimeout() hack that
+ / leaflet uses to call Shiny methods on a staticRender(). In the long run,
+ / the logic initShiny should be broken up so that method registration happens
+ / right away, but binding happens later.
+ */
+ function maybeStaticRenderLater() {
+ if (shinyMode && has_jQuery3()) {
+ window.jQuery(window.HTMLWidgets.staticRender);
+ } else {
+ window.HTMLWidgets.staticRender();
+ }
+ }
+
+ if (document.addEventListener) {
+ document.addEventListener("DOMContentLoaded", function() {
+ document.removeEventListener("DOMContentLoaded", arguments.callee, false);
+ maybeStaticRenderLater();
+ }, false);
+ } else if (document.attachEvent) {
+ document.attachEvent("onreadystatechange", function() {
+ if (document.readyState === "complete") {
+ document.detachEvent("onreadystatechange", arguments.callee);
+ maybeStaticRenderLater();
+ }
+ });
+ }
+
+
+ window.HTMLWidgets.getAttachmentUrl = function(depname, key) {
+ // If no key, default to the first item
+ if (typeof(key) === "undefined")
+ key = 1;
+
+ var link = document.getElementById(depname + "-" + key + "-attachment");
+ if (!link) {
+ throw new Error("Attachment " + depname + "/" + key + " not found in document");
+ }
+ return link.getAttribute("href");
+ };
+
+ window.HTMLWidgets.dataframeToD3 = function(df) {
+ var names = [];
+ var length;
+ for (var name in df) {
+ if (df.hasOwnProperty(name))
+ names.push(name);
+ if (typeof(df[name]) !== "object" || typeof(df[name].length) === "undefined") {
+ throw new Error("All fields must be arrays");
+ } else if (typeof(length) !== "undefined" && length !== df[name].length) {
+ throw new Error("All fields must be arrays of the same length");
+ }
+ length = df[name].length;
+ }
+ var results = [];
+ var item;
+ for (var row = 0; row < length; row++) {
+ item = {};
+ for (var col = 0; col < names.length; col++) {
+ item[names[col]] = df[names[col]][row];
+ }
+ results.push(item);
+ }
+ return results;
+ };
+
+ window.HTMLWidgets.transposeArray2D = function(array) {
+ if (array.length === 0) return array;
+ var newArray = array[0].map(function(col, i) {
+ return array.map(function(row) {
+ return row[i]
+ })
+ });
+ return newArray;
+ };
+ // Split value at splitChar, but allow splitChar to be escaped
+ // using escapeChar. Any other characters escaped by escapeChar
+ // will be included as usual (including escapeChar itself).
+ function splitWithEscape(value, splitChar, escapeChar) {
+ var results = [];
+ var escapeMode = false;
+ var currentResult = "";
+ for (var pos = 0; pos < value.length; pos++) {
+ if (!escapeMode) {
+ if (value[pos] === splitChar) {
+ results.push(currentResult);
+ currentResult = "";
+ } else if (value[pos] === escapeChar) {
+ escapeMode = true;
+ } else {
+ currentResult += value[pos];
+ }
+ } else {
+ currentResult += value[pos];
+ escapeMode = false;
+ }
+ }
+ if (currentResult !== "") {
+ results.push(currentResult);
+ }
+ return results;
+ }
+ // Function authored by Yihui/JJ Allaire
+ window.HTMLWidgets.evaluateStringMember = function(o, member) {
+ var parts = splitWithEscape(member, '.', '\\');
+ for (var i = 0, l = parts.length; i < l; i++) {
+ var part = parts[i];
+ // part may be a character or 'numeric' member name
+ if (o !== null && typeof o === "object" && part in o) {
+ if (i == (l - 1)) { // if we are at the end of the line then evalulate
+ if (typeof o[part] === "string")
+ o[part] = tryEval(o[part]);
+ } else { // otherwise continue to next embedded object
+ o = o[part];
+ }
+ }
+ }
+ };
+
+ // Retrieve the HTMLWidget instance (i.e. the return value of an
+ // HTMLWidget binding's initialize() or factory() function)
+ // associated with an element, or null if none.
+ window.HTMLWidgets.getInstance = function(el) {
+ return elementData(el, "init_result");
+ };
+
+ // Finds the first element in the scope that matches the selector,
+ // and returns the HTMLWidget instance (i.e. the return value of
+ // an HTMLWidget binding's initialize() or factory() function)
+ // associated with that element, if any. If no element matches the
+ // selector, or the first matching element has no HTMLWidget
+ // instance associated with it, then null is returned.
+ //
+ // The scope argument is optional, and defaults to window.document.
+ window.HTMLWidgets.find = function(scope, selector) {
+ if (arguments.length == 1) {
+ selector = scope;
+ scope = document;
+ }
+
+ var el = scope.querySelector(selector);
+ if (el === null) {
+ return null;
+ } else {
+ return window.HTMLWidgets.getInstance(el);
+ }
+ };
+
+ // Finds all elements in the scope that match the selector, and
+ // returns the HTMLWidget instances (i.e. the return values of
+ // an HTMLWidget binding's initialize() or factory() function)
+ // associated with the elements, in an array. If elements that
+ // match the selector don't have an associated HTMLWidget
+ // instance, the returned array will contain nulls.
+ //
+ // The scope argument is optional, and defaults to window.document.
+ window.HTMLWidgets.findAll = function(scope, selector) {
+ if (arguments.length == 1) {
+ selector = scope;
+ scope = document;
+ }
+
+ var nodes = scope.querySelectorAll(selector);
+ var results = [];
+ for (var i = 0; i < nodes.length; i++) {
+ results.push(window.HTMLWidgets.getInstance(nodes[i]));
+ }
+ return results;
+ };
+
+ var postRenderHandlers = [];
+ function invokePostRenderHandlers() {
+ while (postRenderHandlers.length) {
+ var handler = postRenderHandlers.shift();
+ if (handler) {
+ handler();
+ }
+ }
+ }
+
+ // Register the given callback function to be invoked after the
+ // next time static widgets are rendered.
+ window.HTMLWidgets.addPostRenderHandler = function(callback) {
+ postRenderHandlers.push(callback);
+ };
+
+ // Takes a new-style instance-bound definition, and returns an
+ // old-style class-bound definition. This saves us from having
+ // to rewrite all the logic in this file to accomodate both
+ // types of definitions.
+ function createLegacyDefinitionAdapter(defn) {
+ var result = {
+ name: defn.name,
+ type: defn.type,
+ initialize: function(el, width, height) {
+ return defn.factory(el, width, height);
+ },
+ renderValue: function(el, x, instance) {
+ return instance.renderValue(x);
+ },
+ resize: function(el, width, height, instance) {
+ return instance.resize(width, height);
+ }
+ };
+
+ if (defn.find)
+ result.find = defn.find;
+ if (defn.renderError)
+ result.renderError = defn.renderError;
+ if (defn.clearError)
+ result.clearError = defn.clearError;
+
+ return result;
+ }
+})();
diff --git a/reference/libs/rglWebGL-binding-1.3.1/rglWebGL.js b/reference/libs/rglWebGL-binding-1.3.1/rglWebGL.js
new file mode 100644
index 0000000..52c8fe6
--- /dev/null
+++ b/reference/libs/rglWebGL-binding-1.3.1/rglWebGL.js
@@ -0,0 +1,79 @@
+/* el is the div, holding the rgl object as el.rglinstance,
+ which holds x as el.rglinstance.scene
+ x is the JSON encoded rglwidget.
+*/
+
+
+HTMLWidgets.widget({
+
+ name: 'rglWebGL',
+
+ type: 'output',
+
+ factory: function(el, width, height) {
+ el.width = width;
+ el.height = height;
+ var rgl = new rglwidgetClass(),
+ onchangeselection = function(e) {
+ for (var i = 0; i < rgl.scene.crosstalk.sel_handle.length; i++)
+ rgl.clearBrush(except = e.rglSubsceneId);
+ rgl.selection(e, false);
+ },
+ onchangefilter = function(e) {
+ rgl.selection(e, true);
+ };
+
+ return {
+ renderValue: function(x) {
+ var i, pel, player, groups,
+ inShiny = (typeof Shiny !== "undefined");
+
+ x.crosstalk.group = groups = [].concat(x.crosstalk.group);
+ x.crosstalk.id = [].concat(x.crosstalk.id);
+ x.crosstalk.key = [].concat(x.crosstalk.key);
+ x.crosstalk.sel_handle = new Array(groups.length);
+ x.crosstalk.fil_handle = new Array(groups.length);
+ x.crosstalk.selection = [];
+ for (i = 0; i < groups.length; i++) {
+ x.crosstalk.sel_handle[i] = new crosstalk.SelectionHandle(groups[i], {sharedId: x.crosstalk.id[i]});
+ x.crosstalk.sel_handle[i].on("change", onchangeselection);
+ x.crosstalk.fil_handle[i] = new crosstalk.FilterHandle(groups[i], {sharedId: x.crosstalk.id[i]});
+ x.crosstalk.fil_handle[i].on("change", onchangefilter);
+ }
+ if (inShiny) {
+ // Shiny calls this multiple times, so we need extra cleanup
+ // between
+ rgl.sphere = undefined;
+ }
+ rgl.initialize(el, x);
+ rgl.initGL();
+
+ /* We might have been called after (some of) the players were rendered.
+ We need to make sure we respond to their initial values. */
+
+ if (typeof x.players !== "undefined") {
+ var players = [].concat(x.players);
+ for (i = 0; i < players.length; i++) {
+ pel = document.getElementById(players[i]);
+ if (pel) {
+ player = pel.rglPlayer;
+ if (player && (!player.initialized || inShiny)) {
+ rgl.Player(pel, player);
+ player.initialized = true;
+ }
+ }
+ }
+ }
+ rgl.drag = 0;
+ rgl.drawScene();
+ },
+
+ resize: function(width, height) {
+ el.width = width;
+ el.height = height;
+ el.rglinstance.resize(el);
+ el.rglinstance.drawScene();
+ }
+ };
+ }
+});
diff --git a/reference/libs/rglwidgetClass-1.3.1/animation.src.js b/reference/libs/rglwidgetClass-1.3.1/animation.src.js
new file mode 100644
index 0000000..cdca347
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/animation.src.js
@@ -0,0 +1,153 @@
+/**
+ * Methods related to animations
+ * @name ___METHODS_FOR_ANIMATION___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+ */
+ /**
+ * Binary search
+ * @param x - x coordinates in increasing order
+ * @param newx - value to find, assumed to be in the range of x
+ * @result index of largest x value below newx
+ */
+ rglwidgetClass.bisect = function(x, newx) {
+ var lo = 0, hi = x.length - 1, mid;
+ while (lo < hi - 1) {
+ mid = Math.round((lo + hi)/2);
+ if (x[mid] < newx)
+ lo = mid;
+ else
+ hi = mid;
+ }
+ return lo;
+ };
+
+ /**
+ * Step interpolation (constant outside bounds)
+ * @param x - x coordinates in increasing order
+ * @param v - values at x; either a vector or matrix
+ * @param newx - value at which to evaluate
+ */
+ rglwidgetClass.step = function(x, v, newx) {
+ var n, lo;
+ if (newx <= x[0])
+ return v[0];
+ n = x.length;
+ if (newx >= x[n-1])
+ return v[n-1];
+ lo = this.bisect(x, newx);
+ return v[lo];
+ };
+
+ /**
+ * Linear interpolation (constant outside bounds)
+ * @param x - x coordinates in increasing order
+ * @param v - values at x; either a vector or matrix
+ * @param newx - value at which to evaluate
+ */
+ rglwidgetClass.lerp = function(x, v, newx) {
+ var i, n, lo, hi, alpha, result;
+ if (newx <= x[0])
+ return v[0];
+ n = x.length;
+ if (newx >= x[n-1])
+ return v[n-1];
+ lo = this.bisect(x, newx);
+ if (newx === x[lo])
+ return v[lo];
+ hi = lo + 1;
+ if (newx === x[hi])
+ return v[hi];
+ alpha = (newx - x[lo])/(x[hi] - x[lo]);
+ result = v[lo];
+ n = result.length;
+ if (typeof n !== "undefined") {
+ for (i = 0; i < n; i++)
+ result[i] = (1 - alpha)*result[i] + alpha*v[hi][i];
+ } else
+ result = (1 - alpha)*result + alpha*v[hi];
+ return result;
+ };
+
+ /**
+ * Spherical linear interpolation (constant outside bounds)
+ * @param x - x coordinates in increasing order
+ * @param v - a matrix of unit quaternions
+ * @param newx - value at which to evaluate
+ */
+ rglwidgetClass.slerp = function(x, v, newx) {
+ var n, lo, hi, alpha, result,
+ p0, p1, dot, Omega, alpha0, alpha1, len;
+ if (newx <= x[0])
+ return v[0];
+ if (newx >= x[n-1])
+ return v[n-1];
+ lo = this.bisect(x, newx);
+ if (newx === x[lo])
+ return v[lo];
+ hi = lo + 1;
+ if (newx === x[hi])
+ return v[hi];
+ p0 = v[lo];
+ p1 = v[hi];
+ dot = p0[0]*p1[0] +
+ p0[1]*p1[1] +
+ p0[2]*p1[2] +
+ p0[3]*p1[3];
+ if (dot < 0) {
+ p1 = [-p1[0], -p1[1], -p1[2], -p1[3]];
+ dot = -dot;
+ }
+ if (dot >= 1)
+ result = p1;
+ else {
+ alpha = (newx - x[lo])/(x[hi] - x[lo]);
+ Omega = Math.acos(dot);
+ alpha0 = Math.sin((1 - alpha)*Omega);
+ alpha1 = Math.sin(alpha*Omega);
+ result = [alpha0*p0[0] + alpha1*p1[0],
+ alpha0*p0[1] + alpha1*p1[1],
+ alpha0*p0[2] + alpha1*p1[2],
+ alpha0*p0[3] + alpha1*p1[3]];
+ }
+ len = Math.sqrt(result[0]*result[0] +
+ result[1]*result[1] +
+ result[2]*result[2] +
+ result[3]*result[3]);
+ return [result[0]/len,
+ result[1]/len,
+ result[2]/len,
+ result[3]/len];
+ };
+
+ /**
+ * Rotate using unit quaternion
+ * @param q - a single unit quaternion
+ */
+ rglwidgetClass.rotateByQuaternion = function(M, q) {
+
+ var xx = q[0]*q[0],
+ xy = q[0]*q[1],
+ xz = q[0]*q[2],
+ xw = q[0]*q[3],
+ yy = q[1]*q[1],
+ yz = q[1]*q[2],
+ yw = q[1]*q[3],
+ zz = q[2]*q[2],
+ zw = q[2]*q[3],
+ matrix = new CanvasMatrix4();
+ matrix.m11 = 1 - 2*(yy + zz);
+ matrix.m12 = 2*(xy + zw);
+ matrix.m13 = 2*(xz - yw);
+
+ matrix.m21 = 2*(xy - zw);
+ matrix.m22 = 1 - 2*(xx + zz);
+ matrix.m23 = 2*(yz + xw);
+
+ matrix.m31 = 2*(xz + yw);
+ matrix.m32 = 2*(yz - xw);
+ matrix.m33 = 1 - 2*(xx + yy);
+
+ M.multRight(matrix);
+ };
diff --git a/reference/libs/rglwidgetClass-1.3.1/axes.src.js b/reference/libs/rglwidgetClass-1.3.1/axes.src.js
new file mode 100644
index 0000000..485fa13
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/axes.src.js
@@ -0,0 +1,441 @@
+ /**
+ * Methods related to axes
+ * @name ___METHODS_FOR_AXES___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+ */
+
+ /**
+ * Choose edges for ticks
+ * @param { Matrix } prmv - projection-model-view matrix
+ */
+ rglwidgetClass.prototype.getTickEdges = function(prmv){
+ var vertices = [[0,0,0,1], [0,0,1,1],
+ [0,1,0,1], [0,1,1,1],
+ [1,0,0,1], [1,0,1,1],
+ [1,1,0,1], [1,1,1,1]],
+ dim, i, j, k, edges, hull, step, result = [], proj = [],
+
+ // Filter to edges that are on sides that would
+ // be shown with a filled backing.
+
+ has_back = function(edge) {
+ var normals = [[], []],
+ verts = [vertices[edge[0]],
+ vertices[edge[1]]],
+ normal, m, n;
+ n = 0;
+ for (m=0; m<3; m++) {
+ if (verts[0][m] === verts[1][m]) {
+ normals[n] = [0,0,0,1];
+ normals[n][m] = 2*verts[0][m] - 1;
+ n++;
+ }
+ }
+ for (n=0; n<2; n++) {
+ normal = rglwidgetClass.multVM(normals[n], self.normMatrix);
+ if (normal[2] < 0 ||
+ (normal[2] === 0 && normal[0] < 0))
+ return true;
+ }
+ return false;
+ }, self = this;
+
+ for (i = 0; i < vertices.length; i++) {
+ proj[i] = rglwidgetClass.multVM(vertices[i], prmv);
+ proj[i][0] = proj[i][0]/proj[i][3];
+ proj[i][1] = proj[i][1]/proj[i][3];
+ proj[i][2] = i;
+ }
+ hull = rglwidgetClass.chull(proj.slice());
+ for (i = 0; i < hull.length; i++)
+ hull[i] = hull[i][2];
+ hull.push(hull[0]);
+ for (dim = 0; dim < 3; dim++) {
+ edges = [];
+ step = Math.pow(2, 2-dim);
+ for (i = 0; i < 4; i++) {
+ j = (dim === 0) ? i : (dim === 1) ? i + 2*(i>1) : 2*i;
+ for (k = 0; k < hull.length - 1; k++) {
+ if ((hull[k] === j && hull[k+1] === j + step) ||
+ (hull[k] === j+step && hull[k+1] === j))
+
+ edges.push([j, j+step], [j+step, j]);
+ }
+ }
+
+ edges = edges.filter(has_back);
+
+ // Find the edge with a vertex closest
+ // to the bottom left corner
+ if (edges.length) {
+ var best, best2, val = Infinity, newval;
+ for (i = 0; i < edges.length; i++) {
+ j = edges[i][0];
+ newval = proj[j][0] + proj[j][1];
+ if (newval < val) {
+ best = j;
+ best2 = edges[i][1];
+ val = newval;
+ }
+ }
+ if (typeof best !== "undefined") {
+ result[dim] = vertices[best].slice(0,3);
+ result[dim][dim] = undefined;
+ } else
+ result[dim] = undefined;
+ }
+ }
+ return result;
+ };
+
+ /**
+ * Choose tick locations
+ * @param { Object } obj - The bboxdeco
+ */
+ rglwidgetClass.prototype.getTickLocations = function(obj){
+ var dim, i, limits, locations = [], result = [[],[],[]], value,
+ len, delta, range, bbox = obj.bbox;
+ obj.needsAxisCallback = false;
+ for (dim = 0; dim < 3; dim++) {
+ limits = bbox.slice(2*dim, 2*dim + 2);
+ range = limits[1] - limits[0];
+ switch(obj.axes.mode[dim]) {
+ case "custom":
+ for (i=0; i < obj.vertices.length; i++) {
+ value = (obj.vertices[i][dim] - limits[0])/range;
+ if (typeof value !== "undefined" &&
+ !isNaN(value))
+ result[dim].push(value);
+ }
+ break;
+ case "fixedstep":
+ len = Math.floor(range/obj.axes.step[dim]);
+ delta = obj.axes.step[dim];
+ for (i = 0; i < len; i++)
+ result[dim].push(i*delta);
+ break;
+ case "fixednum":
+ len = obj.axes.nticks[dim];
+ delta = (len > 1) ? range/(len-1) : 0;
+ for (i = 0; i < len; i++)
+ result[dim].push(i*delta/range);
+ break;
+ case "pretty":
+ locations = this.R_pretty(limits[0], limits[1], 5,
+ 2, // min_n
+ 0.75, // shrink_sml
+ [1.5, 2.75], // high_u_fact
+ 0, // eps_correction
+ 0); // return_bounds)
+ for (i = locations.lo; i <= locations.up; i++) {
+ value = (i*locations.unit - limits[0])/range;
+ if (0 < value && value < 1)
+ result[dim].push(value);
+ }
+ break;
+ case "user":
+ obj.needsAxisCallback = true;
+ break;
+ }
+ }
+ return result;
+ };
+
+ /**
+ * Set tick vertices
+ * @param { Object } ticks - the tick object
+ * @param { Array } edges - Which edges get the ticks?
+ */
+ rglwidgetClass.prototype.getTickVertices = function(ticks) {
+ var dim, i, j, vertices = [], locations,
+ edges = ticks.edges, edge;
+ for (dim = 0; dim < 3; dim++) {
+ locations = ticks.locations[dim];
+ if (locations.length)
+ for (i = 0; i < locations.length; i++)
+ if (typeof edges[dim] !== "undefined") {
+ edge = edges[dim].slice();
+ edge[dim] = locations[i];
+ vertices.push(edge);
+ edge = edge.slice();
+ for (j = 0; j < 3; j++)
+ if ((dim < 2 && j === 1 - dim) ||
+ (dim === 2 && j === 0))
+ edge[j] += 2*(edge[j] - 0.5)/ticks.axes.marklen[dim];
+ vertices.push(edge);
+ }
+ }
+ ticks.vertices = vertices;
+ ticks.vertexCount = vertices.length;
+ ticks.values = new Float32Array(rglwidgetClass.flatten(vertices));
+ ticks.initialized = false;
+ };
+
+ /**
+ * Set tick label positions
+ * @param { Object } obj - the bbox object
+ */
+ rglwidgetClass.prototype.placeTickLabels = function(obj) {
+ var ticks = obj.ticks, labels = obj.labels, i,j,k,
+ vertices = [], tickvertices = ticks.vertices,
+ vertex, locations, dim, edges = obj.ticks.edges;
+ j = 0;
+ for (dim = 0; dim < 3; dim++) {
+ if (typeof edges[dim] === "undefined")
+ continue;
+ locations = ticks.locations[dim];
+ if (locations.length)
+ for (i = 0; i < locations.length; i++) {
+ if (isNaN(locations[i]))
+ continue;
+ while (j < tickvertices.length &&
+ tickvertices[j][dim] !== locations[i]) j++;
+ if (j >= tickvertices.length)
+ break;
+ vertex = tickvertices[j].slice();
+ for (k = 0; k < 3; k++)
+ vertex[k] += 2*(tickvertices[j+1][k] - vertex[k]);
+ vertices.push(vertex);
+ j += 2;
+ }
+ }
+ labels.vertices = vertices;
+ labels.centers = labels.vertices;
+ labels.initialized = false;
+ };
+
+ /**
+ * Set tick labels
+ * @param { Object } obj - the bbox object
+ */
+ rglwidgetClass.prototype.setTickLabels = function(obj) {
+ var ticks = obj.ticks, mode, locations, labels = [],
+ start = 0, nticks, dim, i, limits, range, values, max,
+ edges = obj.ticks.edges;
+ for (dim = 0; dim < 3; dim++) {
+ if (typeof edges[dim] === "undefined")
+ continue;
+ mode = obj.axes.mode[dim];
+ nticks = obj.axes.nticks[dim]; // used on input only for custom!
+ if (mode === "custom")
+ labels = labels.concat(obj.texts.slice(start, start + nticks));
+ else {
+ limits = obj.bbox.slice(2*dim, 2*(dim+1));
+ range = limits[1] - limits[0];
+ locations = ticks.locations[dim];
+ max = -Infinity;
+ values = [];
+ for (i = 0; i < locations.length; i++) {
+ values.push(limits[0] + range*locations[i]);
+ max = Math.max(max, Math.abs(values[i]));
+ }
+ for (i = 0; i < locations.length; i++) {
+ if (Math.abs(values[i])/max < Math.pow(10, -5))
+ values[i] = 0;
+ labels.push(rglwidgetClass.signif(values[i], 4).toString());
+ }
+ obj.axes.nticks[dim] = locations.length;
+ }
+ start += nticks;
+ }
+ obj.labels.texts = labels;
+ };
+
+ /**
+ * Set bboxdeco bbox and center vector
+ * @param { Object } obj - the bbox object
+ */
+ rglwidgetClass.prototype.setBbox = function(obj, subscene) {
+ var i, expand, center = [], bbox;
+ if (!obj.initialized)
+ this.initBBox(obj);
+
+ bbox = [].concat(subscene.par3d.bbox);
+ for (i = 0; i < 3; i++) {
+ expand = obj.axes.expand[i];
+ center[i] = (bbox[2*i] + bbox[2*i + 1])/2;
+ bbox[2*i] = center[i] - expand*(bbox[2*i + 1] - center[i]);
+ bbox[2*i+1] = center[i] + expand*(bbox[2*i + 1] - center[i]);
+ }
+ obj.bbox = bbox;
+ obj.center = center;
+ };
+
+ rglwidgetClass.prototype.setBBoxMatrices = function(obj) {
+ var saved = {normMatrix: new CanvasMatrix4(this.normMatrix),
+ mvMatrix: new CanvasMatrix4(this.mvMatrix)},
+ bboxNorm, bboxMV, bbox = obj.bbox, scale;
+
+ bboxNorm = new CanvasMatrix4();
+ scale = [bbox[1]-bbox[0], bbox[3]-bbox[2], bbox[5]-bbox[4]];
+ bboxNorm.scale(1/scale[0], 1/scale[1], 1/scale[2]);
+ bboxNorm.multRight(saved.normMatrix);
+ this.normMatrix = bboxNorm;
+
+ bboxMV = new CanvasMatrix4();
+ bboxMV.scale(scale[0], scale[1], scale[2]);
+ bboxMV.translate(bbox[0], bbox[2], bbox[4]);
+ bboxMV.multRight(saved.mvMatrix);
+ this.mvMatrix = obj.mvMatrix = bboxMV;
+
+ if (this.prmvMatrix === null)
+ saved.prmvMatrix = null;
+ else
+ saved.prmvMatrix = new CanvasMatrix4(this.prmvMatrix);
+
+ this.setprmvMatrix();
+ obj.prmvMatrix = this.prmvMatrix;
+
+ return saved;
+ };
+
+ rglwidgetClass.prototype.restoreBBoxMatrices = function(saved) {
+ this.normMatrix = saved.normMatrix;
+ this.mvMatrix = saved.mvMatrix;
+ this.prmvMatrix = saved.prmvMatrix;
+ };
+
+ rglwidgetClass.prototype.getMarginParameters = function(bboxdeco, material) {
+ // Assume we've run this.setBbox(bboxdeco, subscene);
+ var bbox = bboxdeco.bbox,
+ edge = [].concat(material.edge),
+ saved, edges, i,
+ at = material.margin, line, level, trans, scale;
+
+ if (material.floating) {
+ saved = this.setBBoxMatrices(bboxdeco);
+ edges = this.getTickEdges(this.prmvMatrix)[at];
+ this.restoreBBoxMatrices(saved);
+ if (typeof edges !== "undefined")
+ for (i = 0; i < 3; i++) {
+ if (edges[i] < 1) edges[i] = -1;
+ edge[i] = edge[i]*edges[i];
+ } else
+ return undefined;
+ }
+ switch(at) {
+ case 0: line = 1;
+ level = 2;
+ break;
+ case 1: line = 0;
+ level = 2;
+ break;
+ case 2: line = 0;
+ level = 1;
+ break;
+ }
+ scale = [edge[0]*(bbox[1]-bbox[0])/bboxdeco.axes.marklen[0],
+ edge[1]*(bbox[3]-bbox[2])/bboxdeco.axes.marklen[1],
+ edge[2]*(bbox[5]-bbox[4])/bboxdeco.axes.marklen[2]];
+ trans = [edge[0] === 1 ? bbox[1] : bbox[0],
+ edge[1] === 1 ? bbox[3] : bbox[2],
+ edge[2] === 1 ? bbox[5] : bbox[4]];
+ return {at: at, line: line, level: level, trans: trans, scale: scale};
+ };
+
+ rglwidgetClass.prototype.fixVertex = function(orig, parms, center, bbox) {
+ var vertex = [0,0,0];
+ if (rglwidgetClass.missing(orig[0]))
+ vertex[parms.at] = center[parms.at];
+ else if (orig[0] === "-Inf")
+ vertex[parms.at] = bbox[2*parms.at];
+ else if (orig[0] === "Inf")
+ vertex[parms.at] = bbox[2*parms.at + 1];
+ else
+ vertex[parms.at] = orig[0];
+ vertex[parms.line] = parms.scale[parms.line]*orig[1] +
+ parms.trans[parms.line];
+ vertex[parms.level] = parms.scale[parms.level]*orig[2] +
+ parms.trans[parms.level];
+ return vertex;
+ };
+
+ rglwidgetClass.prototype.fixNormal = function(orig, parms) {
+ var vertex = [0,0,0];
+ vertex[parms.at] = orig[0];
+ vertex[parms.line] = orig[1]/parms.scale[parms.line];
+ vertex[parms.level] = orig[2]/parms.scale[parms.level];
+ return vertex;
+ };
+
+ rglwidgetClass.prototype.marginVecToDataVec = function(obj, subscene) {
+ var bboxdeco = this.getBBoxDeco(subscene),
+ center, bbox, parms, parmsjson,
+ orig = obj.orig,
+ vertices = [], normals = [],
+ centers = [], i, vertex;
+ if (typeof orig === "undefined") {
+ orig = {vert: obj.vertices,
+ norm: obj.normals,
+ cent: obj.centers,
+ doNormals: typeof obj.normals !== "undefined",
+ doCenters: typeof obj.centers !== "undefined",
+ parms: ""
+ };
+ obj.orig = orig;
+ }
+
+ if (typeof bboxdeco !== "undefined") {
+ this.setBbox(bboxdeco, subscene);
+ center = bboxdeco.center;
+ bbox = bboxdeco.bbox;
+ parms = this.getMarginParameters(bboxdeco, obj.material);
+ if (typeof parms === "undefined")
+ return false; /* axis is not currently shown */
+
+ parmsjson = JSON.stringify(parms);
+ if (parmsjson === orig.parms)
+ return true; /* nothing has changed */
+
+ orig.parms = parmsjson;
+
+ for (i=0; i < orig.vert.length; i++) {
+ vertex = this.fixVertex(orig.vert[i], parms, center, bbox);
+ vertices.push(vertex);
+ }
+ obj.vertices = vertices;
+ if (orig.doNormals) {
+ for (i=0; i < orig.norm.length; i++) {
+ vertex = this.fixNormal(orig.norm[i], parms);
+ normals.push(vertex);
+ }
+ obj.normals = normals;
+ }
+ if (orig.doCenters) {
+ for (i=0; i < orig.cent.length; i++) {
+ vertex = this.fixVertex(orig.cent[i], parms, center, bbox);
+ centers.push(vertex);
+ }
+ obj.centers = centers;
+ }
+
+ obj.initialized = false;
+ return true;
+ } else {
+ console.warn("bboxdeco not found");
+ return false;
+ }
+ };
+
+ rglwidgetClass.prototype.doAxisCallback = function(obj, edges) {
+ var i, j, code, axis, fn;
+ for (i = 0; i < 3; i++) {
+ if (obj.axes.mode[i] === "user") {
+ axis = ["x", "y", "z"][i];
+ if (typeof obj.callbacks !== "undefined" &&
+ typeof (code = obj.callbacks[axis]) !== "undefined") {
+ if (typeof edges[i] !== "undefined")
+ for (j = 0; j < 3; j++)
+ if (typeof edges[i][j] !== "undefined")
+ axis = axis + (edges[i][j] > 0 ? "+" : "-");
+
+ /* jshint evil:true */
+ fn = Function('"use strict";return (' + code + ')')();
+ /* jshint evil:false */
+ fn.call(this, axis);
+ }
+ }
+ }
+ };
diff --git a/reference/libs/rglwidgetClass-1.3.1/buffer.src.js b/reference/libs/rglwidgetClass-1.3.1/buffer.src.js
new file mode 100644
index 0000000..13d2e1a
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/buffer.src.js
@@ -0,0 +1,182 @@
+/**
+ * Methods related to buffered data
+ * @name ___METHODS_FOR_BUFFERS___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+ */
+ /**
+ * Detect rglBuffered object
+ * @param { Object } obj - vertices or similar
+ */
+ rglwidgetClass.prototype.isBuffered = function(obj) {
+ return typeof obj === "string";
+ };
+
+ /* The next two functions are taken from
+
+ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
+
+ They were written by Mozilla Contributors and dedicated
+ to the public domain under CC0. */
+
+ /* Array of bytes to Base64 string decoding */
+ rglwidgetClass.prototype.b64ToUint6 = function(nChr) {
+ return nChr > 64 && nChr < 91 ? nChr - 65 :
+ nChr > 96 && nChr < 123 ? nChr - 71 :
+ nChr > 47 && nChr < 58 ? nChr + 4 :
+ nChr === 43 ? 62 :
+ nChr === 47 ? 63 :
+ 0;
+ };
+
+ /* jshint bitwise:false */
+ rglwidgetClass.prototype.base64DecToArr = function(sBase64, nBlocksSize) {
+ var sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""),
+ nInLen = sB64Enc.length,
+ nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2,
+ taBytes = new Uint8Array(nOutLen);
+ for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
+ nMod4 = nInIdx & 3;
+ nUint24 |= this.b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 6 * (3 - nMod4);
+ if (nMod4 === 3 || nInLen - nInIdx === 1) {
+ for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
+ taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
+ }
+ nUint24 = 0;
+ }
+ }
+ return taBytes;
+ };
+ /* jshint bitwise:true */
+
+ rglwidgetClass.prototype.getArrayBuffer = function(base64) {
+ return this.base64DecToArr(base64, 4).buffer;
+ };
+
+ rglwidgetClass.prototype.getBufferedData = function(v) {
+ return this.readAccessor(parseInt(v, 10), this.scene.buffer);
+ };
+
+ rglwidgetClass.prototype.readAccessor = function(acc, buf) {
+ var typeSignedByte = 5120,
+ typeUnsignedByte = 5121,
+ typeSignedShort = 5122,
+ typeUnsignedShort = 5123,
+ typeSignedInt = 5124,
+ typeUnsignedInt = 5125,
+ typeFloat = 5126,
+ typeDouble = 5130,
+ accessor = buf.accessors[acc],
+ bufferView = buf.bufferViews[accessor.bufferView],
+ buffer = buf.buffers[bufferView.buffer],
+ bytes,
+ lens = {
+ SCALAR: 1,
+ VEC2: 2,
+ VEC3: 3,
+ VEC4: 4,
+ MAT2: 4,
+ MAT3: 9,
+ MAT4: 16
+ },
+ rowsizes = {
+ SCALAR: 1,
+ VEC2: 2,
+ VEC3: 3,
+ VEC4: 4,
+ MAT2: 2,
+ MAT3: 3,
+ MAT4: 4
+ },
+ offset = 0,
+ len = lens[accessor.type],
+ rowsize = rowsizes[accessor.type],
+ count = len * accessor.count,
+ nrows = count / rowsize,
+ values, arr = [], row, i, j, k;
+
+ if (typeof buffer.bytes === "string")
+ buffer.bytes = this.getArrayBuffer(buffer.bytes);
+
+ bytes = buffer.bytes;
+
+ if (typeof accessor.byteOffset !== "undefined")
+ offset += accessor.byteOffset;
+
+ if (typeof bufferView.byteOffset !== "undefined")
+ offset += bufferView.byteOffset;
+
+ switch (accessor.componentType) {
+ case typeSignedByte:
+ values = new Int8Array(buffer.bytes, offset, count);
+ break;
+
+ case typeUnsignedByte:
+ values = new Uint8Array(buffer.bytes, offset, count);
+ break;
+
+ case typeSignedShort:
+ values = new Int16Array(buffer.bytes, offset, count);
+ break;
+
+ case typeUnsignedShort:
+ values = new Uint16Array(buffer.bytes, offset, count);
+ break;
+
+ case typeSignedInt:
+ values = new Int32Array(buffer.bytes, offset, count);
+ break;
+
+ case typeUnsignedInt:
+ values = new Uint32Array(buffer.bytes, offset, count);
+ break;
+
+ case typeFloat:
+ values = new Float32Array(buffer.bytes, offset, count);
+ break;
+
+ case typeDouble:
+ values = new Float64Array(buffer.bytes, offset, count);
+ break;
+ }
+
+ /* This is all very inefficient, but is convenient
+ to work with the old code. */
+ k = 0;
+ for (i = 0; i < nrows; i++) {
+ row = [];
+ for (j = 0; j < rowsize; j++) {
+ if (accessor.normalized) {
+ switch(accessor.componentType) {
+ case typeSignedByte:
+ row.push(Math.max(values[k++]/127, -1.0));
+ break;
+ case typeSignedShort:
+ row.push(Math.max(values[k++]/32767, -1.0));
+ break;
+ case typeUnsignedByte:
+ row.push(values[k++]/255);
+ break;
+ case typeUnsignedShort:
+ row.push(values[k++]/65535);
+ break;
+ }
+ } else
+ row.push(values[k++]);
+ }
+ arr.push(row);
+ }
+ return arr;
+ };
+
+ rglwidgetClass.prototype.expandBufferedFields = function(obj) {
+ /* this list needs to match the one in convertScene.R */
+ var fields = ["vertices", "normals", "indices",
+ "texcoords", "colors", "centers"], i, field;
+ for (i = 0; i < fields.length; i++) {
+ field = obj[fields[i]];
+ if (this.isBuffered(field))
+ obj[fields[i]] = this.getBufferedData(field);
+ }
+ };
diff --git a/reference/libs/rglwidgetClass-1.3.1/controls.src.js b/reference/libs/rglwidgetClass-1.3.1/controls.src.js
new file mode 100644
index 0000000..ffc7be8
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/controls.src.js
@@ -0,0 +1,591 @@
+
+ /**
+ * Change the displayed subset
+ * @param { Object } el - Element of the control; not used.
+ * @param { Object } control - The subset control data.
+ */
+ rglwidgetClass.prototype.subsetSetter = function(el, control) {
+ if (typeof control.subscenes === "undefined" ||
+ control.subscenes === null)
+ control.subscenes = this.scene.rootSubscene;
+ var value = Math.round(control.value),
+ subscenes = [].concat(control.subscenes),
+ fullset = [].concat(control.fullset),
+ i, j, subsceneid,
+ adds = [], deletes = [];
+ if (rglwidgetClass.missing(value))
+ value = control.value = 0;
+ if (control.accumulate)
+ for (i=0; i <= value; i++)
+ adds = adds.concat(control.subsets[i]);
+ else
+ adds = adds.concat(control.subsets[value]);
+ deletes = fullset.filter(function(x) { return adds.indexOf(x) < 0; });
+ for (i = 0; i < subscenes.length; i++) {
+ subsceneid = subscenes[i];
+ if (typeof this.getObj(subsceneid) === "undefined")
+ this.alertOnce("typeof object is undefined");
+ for (j = 0; j < adds.length; j++)
+ this.addToSubscene(adds[j], subsceneid);
+ for (j = 0; j < deletes.length; j++)
+ this.delFromSubscene(deletes[j], subsceneid);
+ }
+ };
+
+ /**
+ * Change the requested property
+ * @param { Object } el - Element of the control; not used.
+ * @param { Object } control - The property setter control data.
+ */
+ rglwidgetClass.prototype.propertySetter = function(el, control) {
+ var value = control.value,
+ values = [].concat(control.values),
+ svals = [].concat(control.param),
+ direct = values[0] === null,
+ entries = [].concat(control.entries),
+ ncol = entries.length,
+ nrow = values.length/ncol,
+ properties = rglwidgetClass.repeatToLen(control.properties, ncol),
+ objids = rglwidgetClass.repeatToLen(control.objids, ncol),
+ property, objid = objids[0],
+ obj = this.getObj(objid),
+ propvals, i, j, v1, v2, p, entry, gl, needsBinding,
+ newprop, newid,
+
+ getPropvals = function() {
+ if (property === "userMatrix")
+ return obj.par3d.userMatrix.getAsArray();
+ else if (property === "scale" || property === "FOV" || property === "zoom")
+ return [].concat(obj.par3d[property]);
+ else
+ return [].concat(obj[property]);
+ },
+
+ putPropvals = function(newvals) {
+ if (newvals.length === 1)
+ newvals = newvals[0];
+ if (property === "userMatrix")
+ obj.par3d.userMatrix.load(newvals);
+ else if (property === "scale" || property === "FOV" || property === "zoom")
+ obj.par3d[property] = newvals;
+ else
+ obj[property] = newvals;
+ };
+
+ if (direct && typeof value === "undefined")
+ return;
+
+ if (control.interp) {
+ values = values.slice(0, ncol).concat(values).
+ concat(values.slice(ncol*(nrow-1), ncol*nrow));
+ svals = [-Infinity].concat(svals).concat(Infinity);
+ for (i = 1; i < svals.length; i++) {
+ if (value <= svals[i]) {
+ if (svals[i] === Infinity)
+ p = 1;
+ else
+ p = (svals[i] - value)/(svals[i] - svals[i-1]);
+ break;
+ }
+ }
+ } else if (!direct) {
+ value = Math.round(value);
+ }
+
+ for (j=0; j
value - svals[j-1])
+ j = j - 1;
+ }
+ break;
+ }
+ }
+
+ obj = this.getObj(control.objid);
+ // First, make sure color attributes vary in original
+ if (typeof obj.vOffsets !== "undefined") {
+ varies = true;
+ for (k = 0; k < ncol; k++) {
+ attrib = attributes[k];
+ if (typeof attrib !== "undefined") {
+ ofs = obj.vOffsets[ofss[attrib]];
+ if (ofs < 0) {
+ switch(attrib) {
+ case "alpha":
+ case "red":
+ case "green":
+ case "blue":
+ obj.colors = [obj.colors[0], obj.colors[0]];
+ break;
+ }
+ varies = false;
+ }
+ }
+ }
+ if (!varies)
+ this.initObjId(control.objid);
+ }
+ propvals = obj.values;
+ aliases = obj.alias;
+ if (typeof aliases === "undefined")
+ aliases = [];
+ for (k=0; k= 0) {
+ if (ofs < 3) {
+ if (obj.normals[vertex][ofs] !== newval) { // Assume no aliases here...
+ obj.normals[vertex][ofs] = newval;
+ obj.initialized = false;
+ }
+ } else {
+ if (obj.offsets[vertex][0] !== newval) {
+ obj.offsets[vertex][0] = newval;
+ obj.initialized = false;
+ }
+ }
+ continue;
+ }
+ }
+ // Not a plane setting...
+ ofs = obj.vOffsets[ofss[attrib]];
+ if (ofs < 0)
+ this.alertOnce("Attribute '"+attrib+"' not found in object "+control.objid);
+ else {
+ stride = obj.vOffsets.stride;
+ ofs = ofs + pos[attrib];
+ entry = vertex*stride + ofs;
+ propvals[entry] = newval;
+ if (typeof alias !== "undefined")
+ for (a = 0; a < alias.length; a++)
+ propvals[alias[a]*stride + ofs] = newval;
+ }
+ }
+ if (typeof obj.buf !== "undefined") {
+ var gl = this.gl || this.initGL();
+ gl.bindBuffer(gl.ARRAY_BUFFER, obj.buf);
+ gl.bufferData(gl.ARRAY_BUFFER, propvals, gl.STATIC_DRAW);
+ }
+ };
+
+ /**
+ * Change the requested vertex properties by age
+ * @param { Object } el - Element of the control; not used.
+ * @param { Object } control - The age setter control data.
+ */
+ rglwidgetClass.prototype.ageSetter = function(el, control) {
+ var objids = [].concat(control.objids),
+ nobjs = objids.length,
+ time = control.value,
+ births = [].concat(control.births),
+ ages = [].concat(control.ages),
+ steps = births.length,
+ j = Array(steps),
+ p = Array(steps),
+ i, k, l, age, j0, propvals, stride, ofs, objid, obj,
+ attrib, dim, varies, alias, aliases, a, d,
+ attribs = ["colors", "alpha", "radii", "vertices",
+ "normals", "origins", "texcoords",
+ "x", "y", "z",
+ "red", "green", "blue"],
+ ofss = ["cofs", "cofs", "radofs", "vofs",
+ "nofs", "oofs", "tofs",
+ "vofs", "vofs", "vofs",
+ "cofs", "cofs", "cofs"],
+ dims = [3,1,1,3,
+ 3,2,2,
+ 1,1,1,
+ 1,1,1],
+ pos = [0,3,0,0,
+ 0,0,0,
+ 0,1,2,
+ 0,1,2];
+ /* Infinity doesn't make it through JSON */
+ ages[0] = -Infinity;
+ ages[ages.length-1] = Infinity;
+ for (i = 0; i < steps; i++) {
+ if (births[i] !== null) { // NA in R becomes null
+ age = time - births[i];
+ for (j0 = 1; age > ages[j0]; j0++);
+ if (ages[j0] === Infinity)
+ p[i] = 1;
+ else if (ages[j0] > ages[j0-1])
+ p[i] = (ages[j0] - age)/(ages[j0] - ages[j0-1]);
+ else
+ p[i] = 0;
+ j[i] = j0;
+ }
+ }
+ // First, make sure color attributes vary in original
+ for (l = 0; l < nobjs; l++) {
+ objid = objids[l];
+ obj = this.getObj(objid);
+ varies = true;
+ if (typeof obj.vOffsets === "undefined")
+ continue;
+ for (k = 0; k < attribs.length; k++) {
+ attrib = control[attribs[k]];
+ if (typeof attrib !== "undefined") {
+ ofs = obj.vOffsets[ofss[k]];
+ if (ofs < 0) {
+ switch(attribs[k]) {
+ case "colors":
+ case "alpha":
+ case "red":
+ case "green":
+ case "blue":
+ obj.colors = [obj.colors[0], obj.colors[0]];
+ break;
+ }
+ varies = false;
+ }
+ }
+ }
+ if (!varies)
+ this.initObjId(objid);
+ }
+ for (l = 0; l < nobjs; l++) {
+ objid = objids[l];
+ obj = this.getObj(objid);
+ if (typeof obj.vOffsets === "undefined")
+ continue;
+ aliases = obj.alias;
+ if (typeof aliases === "undefined")
+ aliases = [];
+ propvals = obj.values;
+ stride = obj.vOffsets.stride;
+ for (k = 0; k < attribs.length; k++) {
+ attrib = control[attribs[k]];
+ if (typeof attrib !== "undefined") {
+ ofs = obj.vOffsets[ofss[k]];
+ if (ofs >= 0) {
+ dim = dims[k];
+ ofs = ofs + pos[k];
+ for (i = 0; i < steps; i++) {
+ alias = aliases[i];
+ if (births[i] !== null) {
+ for (d=0; d < dim; d++) {
+ propvals[i*stride + ofs + d] = p[i]*attrib[dim*(j[i]-1) + d] + (1-p[i])*attrib[dim*j[i] + d];
+ if (typeof alias !== "undefined")
+ for (a=0; a < alias.length; a++)
+ propvals[alias[a]*stride + ofs + d] = propvals[i*stride + ofs + d];
+ }
+ }
+ }
+ } else
+ this.alertOnce("\'"+attribs[k]+"\' property not found in object "+objid);
+ }
+ }
+ obj.values = propvals;
+ if (typeof obj.buf !== "undefined") {
+ var gl = this.gl || this.initGL();
+ gl.bindBuffer(gl.ARRAY_BUFFER, obj.buf);
+ gl.bufferData(gl.ARRAY_BUFFER, obj.values, gl.STATIC_DRAW);
+ }
+ }
+ };
+
+ /**
+ * Bridge to old style control
+ * @param { Object } el - Element of the control; not used.
+ * @param { Object } control - The bridge control data.
+ */
+ rglwidgetClass.prototype.oldBridge = function(el, control) {
+ var attrname, global = window[control.prefix + "rgl"];
+ if (global)
+ for (attrname in global)
+ this[attrname] = global[attrname];
+ window[control.prefix + "rgl"] = this;
+ };
+
+ /**
+ * Set up a player control
+ * @param { Object } el - The player control element
+ * @param { Object } control - The player data.
+ */
+ rglwidgetClass.prototype.Player = function(el, control) {
+ var
+ self = this,
+ components = [].concat(control.components),
+ buttonLabels = [].concat(control.buttonLabels),
+
+ Tick = function() { /* "this" will be a timer */
+ var i,
+ nominal = this.value,
+ slider = this.Slider,
+ labels = this.outputLabels,
+ output = this.Output,
+ step;
+ if (typeof slider !== "undefined" && nominal !== slider.value)
+ slider.value = nominal;
+ if (typeof output !== "undefined") {
+ step = Math.round((nominal - output.sliderMin)/output.sliderStep);
+ if (labels !== null) {
+ output.innerHTML = labels[step];
+ } else {
+ step = step*output.sliderStep + output.sliderMin;
+ output.innerHTML = step.toPrecision(output.outputPrecision);
+ }
+ }
+ for (i=0; i < this.actions.length; i++) {
+ this.actions[i].value = nominal;
+ }
+ self.applyControls(el, this.actions, false);
+ self.drawScene();
+ },
+
+ OnSliderInput = function() { /* "this" will be the slider */
+ this.rgltimer.value = Number(this.value);
+ this.rgltimer.Tick();
+ },
+
+ addSlider = function(min, max, step, value) {
+ var slider = document.createElement("input");
+ slider.type = "range";
+ slider.min = min;
+ slider.max = max;
+ slider.step = step;
+ slider.value = value;
+ slider.oninput = OnSliderInput;
+ slider.sliderActions = control.actions;
+ slider.sliderScene = this;
+ slider.className = "rgl-slider";
+ slider.id = el.id + "-slider";
+ el.rgltimer.Slider = slider;
+ slider.rgltimer = el.rgltimer;
+ el.appendChild(slider);
+ },
+
+ addLabel = function(labels, min, step, precision) {
+ var output = document.createElement("output");
+ output.sliderMin = min;
+ output.sliderStep = step;
+ output.outputPrecision = precision;
+ output.className = "rgl-label";
+ output.id = el.id + "-label";
+ el.rgltimer.Output = output;
+ el.rgltimer.outputLabels = labels;
+ el.appendChild(output);
+ },
+
+ addButton = function(which, label, active) {
+ var button = document.createElement("input"),
+ onclicks = {Reverse: function() { this.rgltimer.reverse();},
+ Play: function() { this.rgltimer.play();
+ this.value = this.rgltimer.enabled ? this.inactiveValue : this.activeValue; },
+ Slower: function() { this.rgltimer.slower(); },
+ Faster: function() { this.rgltimer.faster(); },
+ Reset: function() { this.rgltimer.reset(); },
+ Step: function() { this.rgltimer.step(); }
+ };
+ button.rgltimer = el.rgltimer;
+ button.type = "button";
+ button.value = label;
+ button.activeValue = label;
+ button.inactiveValue = active;
+ if (which === "Play")
+ button.rgltimer.PlayButton = button;
+ button.onclick = onclicks[which];
+ button.className = "rgl-button";
+ button.id = el.id + "-" + which;
+ el.appendChild(button);
+ };
+
+ if (typeof control.reinit !== "undefined" && control.reinit !== null) {
+ control.actions.reinit = control.reinit;
+ }
+ el.rgltimer = new rgltimerClass(Tick, control.start, control.interval, control.stop,
+ control.step, control.value, control.rate, control.loop, control.actions);
+ for (var i=0; i < components.length; i++) {
+ switch(components[i]) {
+ case "Slider": addSlider(control.start, control.stop,
+ control.step, control.value);
+ break;
+ case "Label": addLabel(control.labels, control.start,
+ control.step, control.precision);
+ break;
+ default:
+ addButton(components[i], buttonLabels[i], control.pause);
+ }
+ }
+ el.rgltimer.Tick();
+ };
+
+ /**
+ * Apply all registered controls
+ * @param { Object } el - DOM element of the control
+ * @param { Object } x - List of actions to apply
+ * @param { boolean } [draw=true] - Whether to redraw after applying
+ */
+ rglwidgetClass.prototype.applyControls = function(el, x, draw) {
+ var self = this, reinit = x.reinit, i, control, type;
+ for (i = 0; i < x.length; i++) {
+ control = x[i];
+ type = control.type;
+ self[type](el, control);
+ }
+ if (typeof reinit !== "undefined" && reinit !== null) {
+ reinit = [].concat(reinit);
+ for (i = 0; i < reinit.length; i++)
+ self.getObj(reinit[i]).initialized = false;
+ }
+ if (typeof draw === "undefined" || draw)
+ self.drawScene();
+ };
+
+ /**
+ * Handler for scene change
+ * @param { Object } message - What sort of scene change to do?
+ */
+ rglwidgetClass.prototype.sceneChangeHandler = function(message) {
+ var self = document.getElementById(message.elementId).rglinstance,
+ objs = message.objects, mat = message.material,
+ root = message.rootSubscene,
+ initSubs = message.initSubscenes,
+ redraw = message.redrawScene,
+ skipRedraw = message.skipRedraw,
+ deletes, subs, allsubs = [], i,j;
+ if (typeof message.delete !== "undefined") {
+ deletes = [].concat(message.delete);
+ if (typeof message.delfromSubscenes !== "undefined")
+ subs = [].concat(message.delfromSubscenes);
+ else
+ subs = [];
+ for (i = 0; i < deletes.length; i++) {
+ for (j = 0; j < subs.length; j++) {
+ self.delFromSubscene(deletes[i], subs[j]);
+ }
+ delete self.scene.objects[deletes[i]];
+ }
+ }
+ if (typeof objs !== "undefined") {
+ Object.keys(objs).forEach(function(key){
+ key = parseInt(key, 10);
+ self.scene.objects[key] = objs[key];
+ self.initObjId(key);
+ var obj = self.getObj(key),
+ subs = [].concat(obj.inSubscenes), k;
+ allsubs = allsubs.concat(subs);
+ for (k = 0; k < subs.length; k++)
+ self.addToSubscene(key, subs[k]);
+ });
+ }
+ if (typeof mat !== "undefined") {
+ self.scene.material = mat;
+ }
+ if (typeof root !== "undefined") {
+ self.scene.rootSubscene = root;
+ }
+ if (typeof initSubs !== "undefined")
+ allsubs = allsubs.concat(initSubs);
+ allsubs = self.unique(allsubs);
+ for (i = 0; i < allsubs.length; i++) {
+ self.initSubscene(allsubs[i]);
+ }
+ if (typeof skipRedraw !== "undefined") {
+ root = self.getObj(self.scene.rootSubscene);
+ root.par3d.skipRedraw = skipRedraw;
+ }
+ if (redraw)
+ self.drawScene();
+ };
diff --git a/reference/libs/rglwidgetClass-1.3.1/draw.src.js b/reference/libs/rglwidgetClass-1.3.1/draw.src.js
new file mode 100644
index 0000000..3e38e1a
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/draw.src.js
@@ -0,0 +1,1373 @@
+ /**
+ * Methods related to drawing
+ * @name ___METHODS_FOR_DRAWING___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+ */
+
+ /**
+ * Start drawing
+ * @returns { boolean } Previous state
+ */
+ rglwidgetClass.prototype.startDrawing = function() {
+ var value = this.drawing;
+ this.drawing = true;
+ return value;
+ };
+
+ /**
+ * Stop drawing and check for context loss
+ * @param { boolean } saved - Previous state
+ */
+ rglwidgetClass.prototype.stopDrawing = function(saved) {
+ this.drawing = saved;
+ if (!saved && this.gl && this.gl.isContextLost())
+ this.restartCanvas();
+ };
+
+ /**
+ * Update the triangles used to display a plane
+ * @param { number } id - id of the plane
+ * @param { Object } bbox - bounding box in which to display the plane
+ */
+ rglwidgetClass.prototype.planeUpdateTriangles = function(obj, bbox) {
+ var perms = [[0,0,1], [1,2,2], [2,1,0]],
+ x, xrow, elem, A, d, nhits, i, j, k, u, v, w, intersect, which, v0, v2, vx, reverse,
+ face1 = [], face2 = [], normals = [],
+ nPlanes = obj.normals.length, idx, center;
+ obj.bbox = bbox;
+ obj.vertices = [];
+ obj.centers = [];
+ obj.initialized = false;
+ for (elem = 0; elem < nPlanes; elem++) {
+// Vertex Av = normal.getRecycled(elem);
+ x = [];
+ A = obj.normals[elem];
+ d = obj.offsets[elem][0];
+ nhits = 0;
+ for (i=0; i<3; i++)
+ for (j=0; j<2; j++)
+ for (k=0; k<2; k++) {
+ u = perms[0][i];
+ v = perms[1][i];
+ w = perms[2][i];
+ if (A[w] !== 0.0) {
+ intersect = -(d + A[u]*bbox[j+2*u] + A[v]*bbox[k+2*v])/A[w];
+ if (bbox[2*w] < intersect && intersect < bbox[1+2*w]) {
+ xrow = [];
+ xrow[u] = bbox[j+2*u];
+ xrow[v] = bbox[k+2*v];
+ xrow[w] = intersect;
+ x.push(xrow);
+ face1[nhits] = j + 2*u;
+ face2[nhits] = k + 2*v;
+ nhits++;
+ }
+ }
+ }
+
+ if (nhits > 3) {
+ /* Re-order the intersections so the triangles work */
+ for (i=0; i i+1) {
+ rglwidgetClass.swap(x, i+1, which);
+ rglwidgetClass.swap(face1, i+1, which);
+ rglwidgetClass.swap(face2, i+1, which);
+ }
+ }
+ }
+ if (nhits >= 3) {
+ /* Put in order so that the normal points out the FRONT of the faces */
+ v0 = [x[0][0] - x[1][0] , x[0][1] - x[1][1], x[0][2] - x[1][2]];
+ v2 = [x[2][0] - x[1][0] , x[2][1] - x[1][1], x[2][2] - x[1][2]];
+ /* cross-product */
+ vx = rglwidgetClass.xprod(v0, v2);
+ reverse = rglwidgetClass.dotprod(vx, A) > 0;
+
+ for (i=0; i 0) {
+ clipplanedata = new Float32Array(4*n);
+ for (i=0; i < clipplaneids.length; i++) {
+ clip = this.getObj(clipplaneids[i]);
+ for (j=0; j < clip.offsets.length; j++) {
+ clipplanedata.set(clip.IMVClip[j], clipcheck);
+ clipcheck += 4;
+ }
+ }
+
+ // Leftovers are initialized to zero, which is fine
+ gl.uniform4fv(obj.clipLoc, clipplanedata);
+ }
+ };
+
+ /**
+ * Do code for lighting
+ * @param { object } obj - Object to work with
+ * @param { object } subscene - Subscene to work with
+ */
+ rglwidgetClass.prototype.doLighting = function(obj, subscene) {
+ var gl = this.gl, i, j, n, light,
+ ambient, specular, diffuse, lightDir, viewpoint, finite,
+ ambient0, specular0;
+
+ gl.uniform3fv( obj.emissionLoc, obj.emission);
+ gl.uniform1f( obj.shininessLoc, obj.shininess);
+ while ((typeof subscene.lights === "undefined" ||
+ subscene.lights.length === 0) &&
+ typeof subscene.parent !== "undefined")
+ subscene = this.getObj(subscene.parent);
+
+ if (typeof subscene.lights === "undefined")
+ return;
+
+ n = subscene.lights.length;
+
+ ambient = new Float32Array(3*n);
+ specular = new Float32Array(3*n);
+ diffuse = new Float32Array(3*n);
+ lightDir = new Float32Array(3*n);
+ viewpoint = new Int32Array(n);
+ finite = new Int32Array(n);
+
+ for (i=0; i < n; i++) {
+ light = this.getObj(subscene.lights[i]);
+ if (!light.initialized) this.initObj(light);
+ ambient0 = this.componentProduct(light.ambient, obj.ambient);
+ specular0 = this.componentProduct(light.specular, obj.specular);
+ for (j=0; j < 3; j++) {
+ ambient[3*i + j] = ambient0[j];
+ specular[3*i + j] = specular0[j];
+ diffuse[3*i + j] = light.diffuse[j];
+ lightDir[3*i + j] = light.lightDir[j];
+ }
+ viewpoint[i] = light.viewpoint;
+ finite[i] = light.finite;
+ }
+
+ for (i = n; i < obj.nlights; i++) {
+ for (j = 0; j < 3; j++) {
+ ambient[3*i + j] = 0.0;
+ specular[3*i + j] = 0.0;
+ diffuse[3*i + j] = 0.0;
+ }
+ }
+
+ gl.uniform3fv( obj.ambientLoc, ambient);
+ gl.uniform3fv( obj.specularLoc, specular);
+ gl.uniform3fv( obj.diffuseLoc, diffuse);
+ gl.uniform3fv( obj.lightDirLoc, lightDir);
+ gl.uniform1iv( obj.viewpointLoc, viewpoint);
+ gl.uniform1iv( obj.finiteLoc, finite);
+ };
+
+ /**
+ * Do code for colors
+ * @param { object } obj - Object to work with
+ */
+ rglwidgetClass.prototype.doColors = function(obj) {
+ var gl = this.gl;
+ if (obj.colorCount === 1) {
+ gl.disableVertexAttribArray( this.colLoc );
+ gl.vertexAttrib4fv( this.colLoc, new Float32Array(obj.onecolor));
+ return false;
+ } else {
+ gl.enableVertexAttribArray( this.colLoc );
+ gl.vertexAttribPointer(this.colLoc, 4, gl.FLOAT, false, 4*obj.vOffsets.stride, 4*obj.vOffsets.cofs);
+ return true;
+ }
+ };
+
+ /**
+ * Do code for normals
+ * @param { object } obj - Object to work with
+ */
+ rglwidgetClass.prototype.doNormals = function(obj) {
+ var gl = this.gl;
+ if (obj.vOffsets.nofs >= 0) {
+ gl.enableVertexAttribArray( obj.normLoc );
+ gl.vertexAttribPointer(obj.normLoc, 3, gl.FLOAT, false, 4*obj.vOffsets.stride, 4*obj.vOffsets.nofs);
+ return true;
+ } else
+ return false;
+ };
+
+ /**
+ * Do code for vNormal
+ * @param { object } obj - Object to work with
+ */
+ rglwidgetClass.prototype.doNormMat = function(obj) {
+ var gl = this.gl;
+
+ gl.uniformMatrix4fv( obj.normMatLoc, false, new Float32Array(this.normMatrix.getAsArray()) );
+ };
+
+ /**
+ * Do code for textures
+ * @param { object } obj - Object to work with
+ */
+ rglwidgetClass.prototype.doTexture = function(obj) {
+ var gl = this.gl,
+ is_sphere = obj.type === "sphere";
+ gl.enableVertexAttribArray( obj.texLoc );
+ if (is_sphere)
+ gl.vertexAttribPointer(obj.texLoc, 2, gl.FLOAT, false, 4*this.sphere.vOffsets.stride, 4*this.sphere.vOffsets.tofs);
+ else
+ gl.vertexAttribPointer(obj.texLoc, 2, gl.FLOAT, false, 4*obj.vOffsets.stride, 4*obj.vOffsets.tofs);
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, obj.texture);
+ gl.uniform1i( obj.sampler, 0);
+ return true;
+ };
+
+ /**
+ * Do code for user attributes
+ * @param { object } obj - Object to work with
+ */
+ rglwidgetClass.prototype.doUserAttributes = function(obj) {
+ if (typeof obj.userAttributes !== "undefined") {
+ var gl = this.gl;
+ for (var attr in obj.userAttribSizes) { // Not all attributes may have been used
+ gl.enableVertexAttribArray( obj.userAttribLocations[attr] );
+ gl.vertexAttribPointer( obj.userAttribLocations[attr], obj.userAttribSizes[attr],
+ gl.FLOAT, false, 4*obj.vOffsets.stride, 4*obj.userAttribOffsets[attr]);
+ }
+ }
+ };
+
+ /**
+ * Do code for user uniforms
+ * @param { object } obj - Object to work with
+ */
+ rglwidgetClass.prototype.doUserUniforms = function(obj) {
+ var gl = this.gl, attr;
+ if (typeof obj.userUniforms !== "undefined") {
+ for (attr in obj.userUniformLocations) {
+ var loc = obj.userUniformLocations[attr];
+ if (loc !== null) {
+ var uniform = obj.userUniforms[attr];
+ if (typeof uniform !== "undefined") {
+ var dim = rglwidgetClass.arrayDim(uniform);
+ if (dim.length === 0)
+ gl.uniform1f(loc, uniform);
+ else if (dim.length === 1) {
+ uniform = new Float32Array(uniform);
+ switch(uniform.length) {
+ case 2: gl.uniform2fv(loc, uniform); break;
+ case 3: gl.uniform3fv(loc, uniform); break;
+ case 4: gl.uniform4fv(loc, uniform); break;
+ default: console.warn("bad uniform length");
+ }
+ } else if (dim.length === 2 && dim[0] === 4 && dim[1] === 4)
+ gl.uniformMatrix4fv(loc, false, new Float32Array(rglwidgetClass.flatten(uniform)));
+ else if (dim.length === 2) {
+ uniform = new Float32Array(rglwidgetClass.flatten(uniform));
+ switch(dim[[1]]) {
+ case 1: gl.uniform1fv(loc, uniform); break;
+ case 2: gl.uniform2fv(loc, uniform); break;
+ case 3: gl.uniform3fv(loc, uniform); break;
+ case 4: gl.uniform4fv(loc, uniform); break;
+ default: console.warn("bad uniform column count");
+ }
+ } else
+ console.warn("unsupported uniform shape");
+ }
+ }
+ }
+ }
+ if (typeof obj.userTextures !== "undefined") {
+ var has_texture = rglwidgetClass.isSet(obj.flags, rglwidgetClass.f_has_texture),
+ texnum = has_texture - 1;
+ for (attr in obj.userTextures) {
+ var texture = obj.userTextures[attr];
+ if (texture.sampler !== null) {
+ texnum += 1;
+ gl.activeTexture(gl.TEXTURE0 + texnum);
+ gl.bindTexture(gl.TEXTURE_2D, texture.texture);
+ gl.uniform1i( texture.sampler, texnum);
+ }
+ }
+ }
+ };
+
+ /**
+ * Load indices for complex drawing
+ * @param { object } obj - Object to work with
+ * @param { numeric } pass - Which pass of drawing?
+ * @param { array } indices - Indices to draw
+ */
+ rglwidgetClass.prototype.doLoadIndices = function(obj, pass, indices) {
+ var gl = this.gl,
+ f = obj.f[pass],
+ type = obj.type,
+ fat_lines = rglwidgetClass.isSet(obj.flags, rglwidgetClass.f_fat_lines),
+ fnew, step;
+ switch(type){
+ case "points":
+ step = 1;
+ break;
+ case "abclines":
+ case "lines":
+ if (fat_lines)
+ step = 6;
+ else
+ step = 2;
+ break;
+ case "linestrip":
+ if (fat_lines)
+ step = 6;
+ else
+ step = 1;
+ break;
+ case "sphere":
+ case "planes":
+ case "triangles":
+ step = 3;
+ break;
+ case "text":
+ case "sprites":
+ case "quads":
+ case "surface":
+ step = 6;
+ break;
+ default:
+ console.error("loadIndices for "+type);
+ return 0;
+ }
+ if (obj.index_uint)
+ fnew = new Uint32Array(step * indices.length);
+ else
+ fnew = new Uint16Array(step * indices.length);
+ for (var i = 0; i < indices.length; i++) {
+ for (var j = 0; j < step; j++) {
+ fnew[step*i + j] = f[step*indices[i] + j];
+ }
+ }
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, fnew, gl.DYNAMIC_DRAW);
+ return fnew.length;
+ };
+
+ /**
+ * Do code for depth masking
+ * @param { boolean } mask - whether to mask
+ */
+ rglwidgetClass.prototype.doMasking = function(mask) {
+ var gl = this.gl;
+ gl.depthMask(mask);
+ };
+
+ /**
+ * Do code for alpha blending
+ * @param { boolean } blend - Whether to blend.
+ * @param { integer } objid - Object id
+ */
+ rglwidgetClass.prototype.doBlending = function(blend, objid) {
+ var gl = this.gl, blendfunc, obj,
+ blends = {zero: gl.ZERO,
+ one: gl.ONE,
+ src_color: gl.SRC_COLOR,
+ one_minus_src_color: gl.ONE_MINUS_SRC_COLOR,
+ dst_color: gl.DST_COLOR,
+ one_minus_dst_color: gl.ONE_MINUS_DST_COLOR,
+ src_alpha: gl.SRC_ALPHA,
+ one_minus_src_alpha: gl.ONE_MINUS_SRC_ALPHA,
+ dst_alpha: gl.DST_ALPHA,
+ one_minus_dst_alpha: gl.ONE_MINUS_DST_ALPHA,
+ constant_color: gl.CONSTANT_COLOR,
+ one_minus_constant_color: gl.ONE_MINUS_CONSTANT_COLOR,
+ constant_alpha: gl.CONSTANT_ALPHA,
+ one_minus_constant_alpha: gl.ONE_MINUS_CONSTANT_ALPHA,
+ src_alpha_saturate: gl.SRC_ALPHA_SATURATE};
+ if (blend) {
+ obj = this.getObj(objid);
+ blendfunc = this.getMaterial(obj, "blend");
+ gl.blendFuncSeparate(blends[blendfunc[0]],
+ blends[blendfunc[1]],
+ gl.ONE, gl.ONE);
+ gl.enable(gl.BLEND);
+ } else {
+ gl.disable(gl.BLEND);
+ }
+ };
+
+ /**
+ * Set up for fog in the subscene
+ * @param { object } obj - background object
+ * @param { object } subscene - which subscene
+ */
+ rglwidgetClass.prototype.doFog = function(obj, subscene) {
+ var gl = this.gl, fogmode, color,
+ observer = subscene.par3d.observer[2],
+ sintheta = Math.sin(subscene.par3d.FOV*Math.PI/180/2),
+ parms = [this.frustum.near - 2*observer,
+ this.frustum.far - 2*observer,
+ this.fogScale,
+ (1-sintheta)/(1+sintheta)];
+ if (typeof this.fogType === "undefined")
+ this.fogType = "none";
+ if (typeof this.fogScale === "undefined")
+ parms[2] = 1;
+ if (sintheta === 0)
+ parms[3] = 1/3;
+ switch(this.fogType){
+ case "none": fogmode = 0; break;
+ case "linear":
+ fogmode = 1; break;
+ case "exp":
+ fogmode = 2; break;
+ case "exp2":
+ fogmode = 3;
+ break;
+ default: console.error("Unknown fogtype "+this.fogType);
+ }
+ gl.uniform1i(obj.uFogMode, fogmode);
+ color = this.fogColor;
+ gl.uniform3f(obj.uFogColor, color[0], color[1], color[2]);
+ gl.uniform4f(obj.uFogParms, parms[0], parms[1], parms[2], parms[3]);
+ };
+
+ /* The draw methods are called twice. When
+ this.opaquePass is true, they should draw opaque parts
+ of the scene, and return the list of transparent
+ pieces. Here context is the context array on input,
+ modified when the matrices are changed.
+ When this.opaquePass is false, the context argument
+ contains a "piece", i.e. an ordered list of parts
+ of the object to draw. */
+
+ /**
+ * Draw simple object
+ * @param { object } obj - Object to draw
+ * @param { object } subscene - which subscene
+ * @param { array } context - Which context are we in?
+ */
+ rglwidgetClass.prototype.drawSimple = function(obj, subscene, context) {
+ var
+ fl,
+ is_transparent,
+ type = obj.type,
+ gl = this.gl || this.initGL(),
+ count,
+ pass, mode, pmode,
+ enabled = {};
+
+ if (!obj.initialized)
+ this.initObj(obj);
+
+ if (this.texturesLoading)
+ return[];
+
+ count = obj.vertexCount;
+ if (!count)
+ return [];
+
+ fl = obj.defFlags;
+ is_transparent = fl.is_transparent || obj.someHidden;
+
+ if (is_transparent && this.opaquePass)
+ return this.getPieces(context, obj.id, 0, obj);
+
+ this.doDepthTest(obj);
+
+ this.doMasking(this.getMaterial(obj, "depth_mask"));
+
+ gl.useProgram(obj.prog);
+
+ this.doPolygonOffset(obj);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, obj.buf);
+
+ gl.uniformMatrix4fv( obj.prMatLoc, false, new Float32Array(this.prMatrix.getAsArray()) );
+ gl.uniformMatrix4fv( obj.mvMatLoc, false, new Float32Array(this.mvMatrix.getAsArray()) );
+
+ this.doClipping(obj, subscene);
+
+ if (fl.needs_vnormal)
+ this.doNormMat(obj);
+
+ if (fl.is_lit)
+ this.doLighting(obj, subscene);
+
+ if (fl.has_fog)
+ this.doFog(obj, subscene);
+
+ this.doUserAttributes(obj);
+
+ this.doUserUniforms(obj);
+
+ gl.enableVertexAttribArray( this.posLoc );
+ enabled.posLoc = true;
+
+ if (fl.has_texture || obj.type === "text")
+ enabled.texLoc = this.doTexture(obj);
+
+ enabled.colLoc = this.doColors(obj);
+ enabled.normLoc = this.doNormals(obj);
+
+ if (fl.fixed_size) {
+ gl.uniform3f( obj.textScaleLoc, 0.75/this.vp.width, 0.75/this.vp.height, 1.0);
+ }
+
+ if (fl.fixed_quads) {
+ gl.enableVertexAttribArray( obj.ofsLoc );
+ enabled.ofsLoc = true;
+ gl.vertexAttribPointer(obj.ofsLoc, 3, gl.FLOAT, false, 4*obj.vOffsets.stride, 4*obj.vOffsets.oofs);
+ }
+
+ for (pass = 0; pass < obj.passes; pass++) {
+ pmode = obj.pmode[pass];
+ if (pmode === "culled")
+ continue;
+
+ mode = fl.fat_lines && (fl.is_lines || pmode === "lines") ? "TRIANGLES" : this.mode4type[type];
+
+ if (fl.is_twosided) {
+ gl.uniform1i(obj.frontLoc, pass !== 0);
+ if (fl.has_normals) {
+ gl.uniformMatrix4fv(obj.invPrMatLoc, false, new Float32Array(this.invPrMatrix.getAsArray()));
+ }
+ }
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.ibuf[pass]);
+ if (!this.opaquePass) {
+ if (type === "sphere" && obj.fastTransparency)
+ count = this.doLoadIndices(obj, pass, this.sphere.fastpieces[0].indices);
+ else
+ count = this.doLoadIndices(obj, pass, context.indices);
+ } else {
+ count = obj.f[pass].length;
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, obj.f[pass], gl.STATIC_DRAW);
+ }
+ if (!fl.is_lines && pmode === "lines" && !fl.fat_lines) {
+ mode = "LINES";
+ } else if (pmode === "points") {
+ mode = "POINTS";
+ }
+
+ if ((fl.is_lines || pmode === "lines") && fl.fat_lines) {
+ gl.enableVertexAttribArray(obj.pointLoc);
+ enabled.pointLoc = true;
+ gl.vertexAttribPointer(obj.pointLoc, 2, gl.FLOAT, false, 4*obj.vOffsets.stride, 4*obj.vOffsets.pointofs);
+ gl.enableVertexAttribArray(obj.nextLoc );
+ enabled.nextLoc = true;
+ gl.vertexAttribPointer(obj.nextLoc, 3, gl.FLOAT, false, 4*obj.vOffsets.stride, 4*obj.vOffsets.nextofs);
+ gl.uniform1f(obj.aspectLoc, this.vp.width/this.vp.height);
+ gl.uniform1f(obj.lwdLoc, this.getMaterial(obj, "lwd")/this.vp.height);
+ }
+
+ gl.vertexAttribPointer(this.posLoc, 3, gl.FLOAT, false, 4*obj.vOffsets.stride, 4*obj.vOffsets.vofs);
+
+ gl.drawElements(gl[mode], count, obj.index_uint ? gl.UNSIGNED_INT : gl.UNSIGNED_SHORT, 0);
+ }
+ this.disableArrays(obj, enabled);
+ return [];
+ };
+
+ /**
+ * Draw planes object
+ * @param { object } obj - Object to draw
+ * @param { object } subscene - which subscene
+ * @param { array } context - Which context are we in?
+ */
+ rglwidgetClass.prototype.drawPlanes = function(obj, subscene, context) {
+ if (this.opaquePass && (obj.bbox !== subscene.par3d.bbox || !obj.initialized)) {
+ this.planeUpdateTriangles(obj, subscene.par3d.bbox);
+ }
+ return this.drawSimple(obj, subscene, context);
+ };
+
+ /**
+ * @param { object } obj - object to draw
+ * @param { object } subscene
+ * @param { array } context
+ * @description
+ * Draw spheres in a subscene
+ *
+ * Drawing spheres happens in six ways:
+ * 1 opaquepass, not transparent: transform and draw this.sphere count times
+ * 2 opaquepass, transparent, not fast: transform & collect sphere pieces count times
+ * 3 opaquepass, transparent, fast: order the centres into separate pieces, order this.sphere once
+ * 4 not opaquepass, not transparent: do nothing
+ * 5 not opaquepass, transparent, not fast: transform for one sphere, draw one merged piece
+ * 6 not opaquepass, transparent, fast: transform for one sphere, draw this.sphere in fixed order.
+ **/
+
+ rglwidgetClass.prototype.drawSpheres = function(obj, subscene, context) {
+ var flags = obj.flags,
+ is_transparent = rglwidgetClass.isSet(flags, rglwidgetClass.f_is_transparent),
+ sphereMV, baseofs, ofs, sscale, i,
+ count, nc, scount, scale, indices, sphereNorm,
+ enabled = {}, drawing,
+ saveNorm = new CanvasMatrix4(this.normMatrix),
+ saveMV = new CanvasMatrix4(this.mvMatrix),
+ savePRMV = null,
+ result = [], idx, margin = obj.material.margin;
+
+ if (typeof margin !== "undefined")
+ if (!this.marginVecToDataVec(obj, subscene))
+ return [];
+
+ if (!obj.initialized)
+ this.initObj(obj);
+
+ count = obj.vertexCount;
+ if (!count)
+ return [];
+
+ is_transparent = is_transparent || obj.someHidden;
+
+ if (!this.opaquePass && !is_transparent)
+ return [];
+
+ if (this.prmvMatrix !== null)
+ savePRMV = new CanvasMatrix4(this.prmvMatrix);
+
+ scale = subscene.par3d.scale;
+ sphereNorm = new CanvasMatrix4();
+ sphereNorm.scale(scale[0], scale[1], scale[2]);
+ sphereNorm.multRight(saveNorm);
+ this.normMatrix = sphereNorm;
+
+ if (this.opaquePass) {
+ context = context.slice();
+ context.push(obj.id);
+ }
+
+ drawing = this.opaquePass !== is_transparent;
+ if (drawing) {
+ nc = obj.colorCount;
+ if (nc === 1) {
+ this.sphere.onecolor = obj.onecolor;
+ }
+ }
+
+ this.initShapeFromObj(this.sphere, obj);
+
+ if (!this.opaquePass && obj.fastTransparency && typeof this.sphere.fastpieces === "undefined") {
+ this.sphere.fastpieces = this.getPieces(context.context, obj.id, 0, this.sphere);
+ this.sphere.fastpieces = this.sortPieces(this.sphere.fastpieces);
+ this.sphere.fastpieces = this.mergePieces(this.sphere.fastpieces);
+ }
+
+ if (this.opaquePass)
+ scount = count;
+ else {
+ indices = context.indices;
+ if (obj.fastTransparency)
+ scount = indices.length; /* Each item gives the center of a whole sphere */
+ else
+ scount = 1; /* Each item is a fragment of the sphere, at location subid */
+ }
+ for (i = 0; i < scount; i++) {
+ sphereMV = new CanvasMatrix4();
+ if (this.opaquePass)
+ idx = i;
+ else if (obj.fastTransparency)
+ idx = indices[i];
+ else
+ idx = context.subid;
+ if (typeof idx === "undefined")
+ console.error("idx is undefined");
+ baseofs = idx*obj.vOffsets.stride;
+ ofs = baseofs + obj.vOffsets.radofs;
+ sscale = obj.values[ofs];
+
+ sphereMV.scale(sscale/scale[0], sscale/scale[1], sscale/scale[2]);
+ sphereMV.translate(obj.values[baseofs],
+ obj.values[baseofs+1],
+ obj.values[baseofs+2]);
+ sphereMV.multRight(saveMV);
+ this.mvMatrix = sphereMV;
+ this.setnormMatrix2();
+ this.setprmvMatrix();
+ if (drawing) {
+ if (nc > 1) {
+ this.sphere.onecolor = obj.values.slice(baseofs + obj.vOffsets.cofs, baseofs + obj.vOffsets.cofs + 4);
+ }
+ this.drawSimple(this.sphere, subscene, context);
+ } else
+ result = result.concat(this.getSpherePieces(context, i, obj));
+ }
+ if (drawing)
+ this.disableArrays(obj, enabled);
+ this.normMatrix = saveNorm;
+ this.mvMatrix = saveMV;
+ this.prmvMatrix = savePRMV;
+
+ return result;
+ };
+
+ /**
+ * Prepare clipplanes for drawing
+ * @param { object } obj - clip planes object
+ */
+ rglwidgetClass.prototype.drawClipplanes = function(obj) {
+ var count = obj.offsets.length,
+ IMVClip = [];
+ for (var i=0; i < count; i++) {
+ IMVClip[i] = rglwidgetClass.multMV(this.invMatrix, obj.vClipplane.slice(4*i, 4*(i+1)));
+ }
+ obj.IMVClip = IMVClip;
+ return [];
+ };
+
+ /**
+ * Prepare linestrip for drawing
+ * @param { object } obj - line strip object
+ * @param { object } subscene
+ * @param { array } context
+ */
+ rglwidgetClass.prototype.drawLinestrip = function(obj, subscene, context) {
+ var origIndices, i, j, margin = obj.material.margin;
+
+ if (typeof margin !== "undefined")
+ if (!this.marginVecToDataVec(obj, subscene))
+ return [];
+
+ if (this.opaquePass)
+ return this.drawSimple(obj, subscene, context);
+ origIndices = context.indices.slice();
+ for (i=0; i < origIndices.length; i++) {
+ j = origIndices[i];
+ if (j < obj.centers.length - 1) {
+ context.indices = [j, j+1];
+ this.drawSimple(obj, subscene, context);
+ }
+ }
+ context.indices = origIndices;
+ return [];
+ };
+
+ /**
+ * Draw a sprites object in a subscene
+ * @param { object } obj - object to draw
+ * @param { object } subscene
+ * @param { object } context
+ */
+ rglwidgetClass.prototype.drawSprites = function(obj, subscene, context) {
+ var flags = obj.flags,
+ is_transparent = rglwidgetClass.isSet(flags, rglwidgetClass.f_is_transparent),
+ sprites3d = rglwidgetClass.isSet(flags, rglwidgetClass.f_sprites_3d),
+ fixed_size = rglwidgetClass.isSet(flags, rglwidgetClass.f_fixed_size),
+ rotating = rglwidgetClass.isSet(flags, rglwidgetClass.f_rotating),
+ i,j,
+ origMV = new CanvasMatrix4( this.mvMatrix ),
+ origPRMV = null,
+ origPR,
+ pos, radius, userMatrix,
+ result = [], margin = obj.material.margin;
+
+ if (typeof margin !== "undefined")
+ if (!this.marginVecToDataVec(obj, subscene))
+ return [];
+
+ if (!sprites3d)
+ return this.drawSimple(obj, subscene, context);
+
+ if (!obj.initialized)
+ this.initObj(obj);
+
+ if (!obj.vertexCount)
+ return [];
+
+ is_transparent = is_transparent || obj.someHidden;
+
+ var norigs = obj.vertices.length,
+ savenorm = new CanvasMatrix4(this.normMatrix),
+ iOrig, adj, offset;
+
+ userMatrix = obj.userMatrix;
+
+ if (this.opaquePass) {
+ context = context.slice();
+ context.push(obj.id);
+ } else
+ norigs = 1;
+
+ if (this.prmvMatrix !== null)
+ origPRMV = new CanvasMatrix4( this.prmvMatrix );
+
+ offset = obj.offset;
+
+ if (fixed_size && !rotating) {
+ origPR = this.prMatrix;
+ this.prMatrix = new CanvasMatrix4();
+ }
+
+ for (iOrig=0; iOrig < norigs; iOrig++) {
+ if (this.opaquePass)
+ j = iOrig;
+ else
+ j = context.subid;
+ pos = [].concat(obj.vertices[j]).concat(1.0);
+ radius = obj.radii.length > 1 ? obj.radii[j][0] : obj.radii[0][0];
+ this.mvMatrix = new CanvasMatrix4(userMatrix);
+ adj = this.getAdj(obj, j, offset);
+ this.mvMatrix.translate(1 - 2*adj[0], 1 - 2*adj[1], 1 - 2*adj[2]);
+ this.mvMatrix.scale(radius, radius, radius);
+
+ if (fixed_size) {
+ var viewport = subscene.par3d.viewport,
+ winwidth = viewport.width*this.canvas.width,
+ winheight = viewport.height*this.canvas.height,
+ scalex = 27/winwidth, scaley = 27/winheight,
+ scale = Math.sqrt(scalex * scaley);
+ if (!rotating) {
+ pos = rglwidgetClass.multVM(pos, origMV);
+ pos = rglwidgetClass.multVM(pos, origPR);
+ this.mvMatrix.scale(scalex, scaley, scale);
+ } else {
+ scale = 4.0 * scale * subscene.par3d.zoom;
+ this.mvMatrix.scale(scale, scale, scale);
+ }
+ this.mvMatrix.translate(pos[0]/pos[3], pos[1]/pos[3], pos[2]/pos[3]);
+ if (rotating)
+ this.mvMatrix.multRight(origMV);
+ } else {
+ if (!rotating) {
+ pos = rglwidgetClass.multVM(pos, origMV);
+ this.mvMatrix.translate(pos[0]/pos[3], pos[1]/pos[3], pos[2]/pos[3]);
+ } else {
+ this.mvMatrix.translate(pos[0]/pos[3], pos[1]/pos[3], pos[2]/pos[3]);
+ this.mvMatrix.multRight(origMV);
+ }
+ }
+ this.setnormMatrix2();
+ this.setprmvMatrix();
+
+ j = iOrig % obj.shapefirst.length;
+ var first = obj.shapefirst[j];
+
+ for (i=0; i < obj.shapelens[j]; i++)
+ if (this.opaquePass)
+ result = result.concat(this.drawObjId(obj.objects[first + i], subscene.id, context.concat(j)));
+ else
+ this.drawObjId(obj.objects[i], subscene.id, context);
+ }
+ this.normMatrix = savenorm;
+ this.mvMatrix = origMV;
+ if (fixed_size && !rotating)
+ this.prMatrix = origPR;
+ if (origPRMV !== null)
+ this.prmvMatrix = origPRMV;
+ return result;
+ };
+
+ /**
+ * Draw object that might be in margin
+ * @param { Object } obj - text object to draw
+ * @param { Object } subscene - subscene holding it
+ * @param { Object } context - context for drawing
+ */
+ rglwidgetClass.prototype.drawMarginal = function(obj, subscene, context) {
+ var margin = obj.material.margin;
+
+ if (typeof margin !== "undefined")
+ if (!this.marginVecToDataVec(obj, subscene))
+ return [];
+
+ return this.drawSimple(obj, subscene, context);
+ };
+
+ /**
+ * Draw bounding box and decorations
+ * @param { Object } obj - bboxdeco to draw
+ * @param { Object } subscene - subscene holding it
+ * @param { Object } context - context for drawing
+ */
+ rglwidgetClass.prototype.drawBBox = function(obj, subscene, context) {
+ var flags = obj.flags,
+ is_transparent = rglwidgetClass.isSet(flags, rglwidgetClass.f_is_transparent),
+ scale, bbox, indices,
+ enabled = {}, drawing,
+ result = [], idx, center, edges,
+ saved;
+
+ if (!obj.initialized)
+ this.initBBox(obj);
+
+ is_transparent = is_transparent || obj.someHidden;
+
+ if (!this.opaquePass && !is_transparent)
+ return result;
+
+ this.setBbox(obj, subscene);
+
+ saved = this.setBBoxMatrices(obj);
+
+ bbox = obj.bbox;
+ center = obj.center;
+
+ scale = [bbox[1]-bbox[0], bbox[3]-bbox[2], bbox[5]-bbox[4]];
+
+ if (!obj.cube.initialized) {
+ this.initObj(obj.cube);
+ }
+
+ if (this.opaquePass) {
+ context = context.slice();
+ context.push(obj.id);
+ }
+
+ drawing = this.opaquePass !== is_transparent;
+ this.cube.onecolor = obj.cube.onecolor;
+ this.initShapeFromObj(this.cube, obj.cube);
+
+ if (!this.opaquePass)
+ indices = context.indices;
+
+ if (this.opaquePass)
+ idx = 0;
+ else
+ idx = context.subid;
+ if (typeof idx === "undefined")
+ console.error("idx is undefined");
+
+ if (drawing) {
+ this.drawSimple(this.cube, subscene, context);
+ } else
+ result = result.concat(this.getCubePieces(context, obj));
+
+ if (!obj.ticks.initialized) {
+ obj.ticks.locations = this.getTickLocations(obj);
+ obj.ticks.edges = undefined;
+ }
+ edges = this.getTickEdges(this.prmvMatrix);
+ if (obj.needsAxisCallback)
+ this.doAxisCallback(obj, edges);
+ if (!obj.ticks.edges || edges.toString() !== obj.ticks.edges.toString()) {
+ obj.ticks.edges = edges;
+ this.getTickVertices(obj.ticks);
+ this.placeTickLabels(obj);
+ this.setTickLabels(obj);
+ }
+ if (!obj.ticks.initialized) {
+ this.initObj(obj.ticks);
+ this.initObj(obj.labels);
+ }
+
+ if (drawing) {
+ this.drawSimple(obj.ticks, subscene, context);
+ this.drawSimple(obj.labels, subscene, context);
+
+ this.disableArrays(obj, enabled);
+ } else {
+ result = result.concat(this.drawSimple(obj.ticks, subscene, context));
+ result = result.concat(this.drawSimple(obj.labels, subscene, context));
+ }
+
+ this.restoreBBoxMatrices(saved);
+
+ return result;
+ };
+
+ /**
+ * Use ids to choose object to draw
+ * @param { numeric } id - object to draw
+ * @param { numeric } subscene
+ * @param { array } context
+ */
+ rglwidgetClass.prototype.drawObjId = function(id, subsceneid, context) {
+ if (typeof id !== "number")
+ this.alertOnce("drawObjId id is "+typeof id);
+
+ return this.drawObj(this.getObj(id), this.getObj(subsceneid), context);
+ };
+
+ /**
+ * Draw an object in a subscene
+ * @param { object } obj - object to draw
+ * @param { object } subscene
+ * @param { array } context
+ */
+ rglwidgetClass.prototype.drawObj = function(obj, subscene, context) {
+ switch(obj.type) {
+ case "abclines":
+ case "surface":
+ return this.drawSimple(obj, subscene, context);
+ case "points":
+ case "lines":
+ case "triangles":
+ case "quads":
+ case "text":
+ return this.drawMarginal(obj, subscene, context);
+ case "linestrip":
+ return this.drawLinestrip(obj, subscene, context);
+ case "planes":
+ return this.drawPlanes(obj, subscene, context);
+ case "spheres":
+ return this.drawSpheres(obj, subscene, context);
+ case "clipplanes":
+ return this.drawClipplanes(obj);
+ case "sprites":
+ return this.drawSprites(obj, subscene, context);
+ case "light":
+ return [];
+ case "bboxdeco":
+ return this.drawBBox(obj, subscene, context);
+ }
+
+ console.error("drawObj for type = "+obj.type);
+ };
+
+ /**
+ * Draw the background for a subscene
+ * @param { number } id - id of background object
+ * @param { number } subsceneid - id of subscene
+ */
+ rglwidgetClass.prototype.drawBackground = function(id, subsceneid, context) {
+ var gl = this.gl || this.initGL(),
+ obj = this.getObj(id),
+ subscene,
+ bg, i, savepr, saveinvpr, savemv, savenorm, m, bbox, result = [],
+ savedm = gl.getParameter(gl.DEPTH_WRITEMASK),
+ savedt = gl.isEnabled(gl.DEPTH_TEST),
+ saveblend = gl.isEnabled(gl.BLEND);
+
+ if (!obj.initialized)
+ this.initObj(obj);
+
+ if (obj.colors.length) {
+ bg = obj.colors[0];
+ gl.depthMask(true);
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ gl.clearColor(bg[0], bg[1], bg[2], bg[3]);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ this.fogColor = bg;
+ } else {
+ this.fogColor = [0,0,0,0];
+ obj.colors = [[0,0,0,0]];
+ }
+
+ this.fogType = obj.fogtype;
+ this.fogScale = obj.fogscale;
+ gl.disable(gl.BLEND);
+ gl.disable(gl.DEPTH_TEST);
+ gl.depthMask(false);
+ if (typeof obj.quad !== "undefined") {
+ savepr = this.prMatrix;
+ saveinvpr = this.invPrMatrix;
+ savemv = this.mvMatrix;
+ this.prMatrix = new CanvasMatrix4();
+ this.invPrMatrix = new CanvasMatrix4();
+ this.mvMatrix = new CanvasMatrix4();
+ for (i=0; i < obj.quad.length; i++)
+ result = result.concat(this.drawObjId(obj.quad[i], subsceneid));
+ this.prMatrix = savepr;
+ this.invPrMatrix = saveinvpr;
+ this.mvMatrix = savemv;
+
+ } else if (obj.sphere) {
+ subscene = this.getObj(subsceneid);
+ savemv = this.mvMatrix;
+ savenorm = this.normMatrix;
+ bbox = subscene.par3d.bbox;
+ var center = [(bbox[0] + bbox[1])/2,
+ (bbox[2] + bbox[3])/2,
+ (bbox[4] + bbox[5])/2, 1],
+ scale = subscene.par3d.scale,
+ ranges = [bbox[1] - bbox[0],
+ bbox[3] - bbox[2],
+ bbox[5] - bbox[4]],
+ avgscale = rglwidgetClass.vlen(ranges)/Math.sqrt(3),
+ aspect = [ranges[0]*scale[0]/avgscale,
+ ranges[1]*scale[1]/avgscale,
+ ranges[2]*scale[2]/avgscale],
+ maxaspect = Math.max(aspect[0], aspect[1], aspect[2]),
+ zoom = subscene.par3d.zoom;
+ m = new CanvasMatrix4();
+ m.rotate(90, 1, 0, 0);
+ m.scale(zoom*2.0*maxaspect*ranges[0]/aspect[0],
+ zoom*2.0*maxaspect*ranges[1]/aspect[1],
+ zoom*2.0*maxaspect*ranges[2]/aspect[2]);
+ m.translate(center[0], center[1], center[2]);
+ m.multRight(savemv);
+ center = rglwidgetClass.multVM(center, savemv);
+ m.translate(-center[0], -center[1], -center[2]);
+ m.scale(1, 1, 0.25/zoom);
+ m.translate(center[0], center[1], center[2]);
+ this.mvMatrix = m;
+ this.initShapeFromObj(this.sphere, obj);
+ this.sphere.onecolor = obj.colors.length > 1 ? obj.colors[1] : obj.colors[0];
+
+ this.normMatrix = new CanvasMatrix4();
+
+ this.setnormMatrix2();
+ this.setprmvMatrix();
+
+ result = result.concat(this.drawSimple(this.sphere, subscene, context));
+ this.mvMatrix = savemv;
+ this.normMatrix = savenorm;
+ }
+ gl.depthMask(savedm);
+ if (savedt)
+ gl.enable(gl.DEPTH_TEST);
+ if (saveblend)
+ gl.enable(gl.BLEND);
+ return result;
+ };
+
+ /**
+ * Draw a subscene
+ * @param { number } subsceneid - id of subscene
+ * @param { array } context
+ */
+ rglwidgetClass.prototype.drawSubscene = function(subsceneid, context) {
+ var sub = this.getObj(subsceneid),
+ objects = this.scene.objects,
+ clipids = sub.clipplanes,
+ subids = sub.objects,
+ subscene_has_faces = false,
+ subscene_needs_sorting = false,
+ flags, i, obj, result = [];
+
+ if (sub.par3d.skipRedraw)
+ return result;
+
+ if (this.opaquePass) {
+ for (i=0; i < subids.length; i++) {
+ obj = objects[subids[i]];
+ flags = obj.flags;
+ if (typeof flags !== "undefined") {
+ subscene_has_faces = subscene_has_faces ||
+ (rglwidgetClass.isSet(flags, rglwidgetClass.f_is_lit) &&
+ !rglwidgetClass.isSet(flags, rglwidgetClass.f_fixed_quads));
+ obj.is_transparent = obj.someHidden ||
+ rglwidgetClass.isSet(flags, rglwidgetClass.f_is_transparent);
+ subscene_needs_sorting = subscene_needs_sorting ||
+ obj.is_transparent ||
+ rglwidgetClass.isSet(flags, rglwidgetClass.f_depth_sort);
+ }
+ }
+ }
+
+ this.setViewport(subsceneid);
+
+ this.setprMatrix(subsceneid);
+ this.setInvPrMatrix();
+ this.setmvMatrix(subsceneid);
+ this.setnormMatrix2();
+ this.setprmvMatrix();
+ this.invMatrix = new CanvasMatrix4(this.mvMatrix);
+ this.invMatrix.invert();
+
+ if (this.opaquePass) {
+ context = context.slice();
+ context.push(subsceneid);
+
+ this.doBlending(false);
+ this.subsceneid = subsceneid;
+ if (typeof this.sphere !== "undefined") // reset this.sphere.fastpieces; it will be recreated if needed
+ this.sphere.fastpieces = undefined;
+ if (typeof sub.backgroundId !== "undefined")
+ result = result.concat(this.drawBackground(sub.backgroundId, subsceneid, context));
+ }
+
+ if (subids.length) {
+
+ if (clipids.length > 0) {
+ for (i = 0; i < clipids.length; i++)
+ this.drawObjId(clipids[i], subsceneid);
+ }
+
+ subids = sub.opaque.concat(sub.transparent);
+ if (this.opaquePass) {
+ for (i = 0; i < subids.length; i++)
+ result = result.concat(this.drawObjId(subids[i], subsceneid, context));
+ subids = sub.subscenes;
+ for (i = 0; i < subids.length; i++)
+ result = result.concat(this.drawSubscene(subids[i], context));
+ }
+ }
+ return result;
+ };
+
+ /**
+ * Set the context for drawing transparently
+ * @param { array } context
+ */
+ rglwidgetClass.prototype.setContext = function(context) {
+ var result = [], objid, obj, type;
+ context = context.slice();
+ context.reverse();
+ while (context.length > 0) {
+ objid = context.pop();
+ obj = this.getObj(objid);
+ type = obj.type;
+ switch (type) {
+ case "subscene":
+ this.drawSubscene(objid, false);
+ break;
+ case "sprites":
+ result = result.concat(context.pop());
+ break;
+ case "spheres":
+ // this.initSphereFromObj(obj); // FIXME: not needed?
+ break;
+ case "bboxdeco":
+ result = result.concat(context.pop());
+ break;
+ default:
+ console.error("bad type '", type, "' in setContext");
+ }
+ }
+ return result;
+ };
+
+ /**
+ * Draw the transparent pieces of a scene
+ * @param {object} pieces
+ */
+ rglwidgetClass.prototype.drawPieces = function(pieces) {
+ var i, prevcontext = [], context;
+ for (i = 0; i < pieces.length; i++) {
+ context = pieces[i].context.slice();
+ if (context !== prevcontext) {
+ prevcontext = context.slice();
+ context = this.setContext(context);
+ this.doBlending(true, pieces[i].objid);
+ }
+ this.drawObjId(pieces[i].objid, this.subsceneid,
+ pieces[i]);
+ }
+ };
+
+ /**
+ * Draw the whole scene
+ */
+ rglwidgetClass.prototype.drawScene = function() {
+ var wasDrawing = this.startDrawing(),
+ pieces;
+ if (!wasDrawing) {
+ if (this.select.state !== "inactive")
+ this.selectionChanged();
+
+ this.doStartScene();
+ this.opaquePass = true;
+ pieces = this.drawSubscene(this.scene.rootSubscene, []);
+ this.opaquePass = false;
+ pieces = this.sortPieces(pieces);
+ pieces = this.mergePieces(pieces);
+ this.drawPieces(pieces);
+ }
+ this.stopDrawing(wasDrawing);
+ };
diff --git a/reference/libs/rglwidgetClass-1.3.1/init.src.js b/reference/libs/rglwidgetClass-1.3.1/init.src.js
new file mode 100644
index 0000000..3e4c448
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/init.src.js
@@ -0,0 +1,1338 @@
+ /**
+ * Methods related to initialization
+ * @name ___METHODS_FOR_INITIALIZATION___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+ */
+
+ /**
+ * Initial test for WebGL
+ */
+ rglwidgetClass.prototype.initGL0 = function() {
+ if (!window.WebGLRenderingContext){
+ this.alertOnce("Your browser does not support WebGL. See http://get.webgl.org");
+ return;
+ }
+ };
+
+ /**
+ * Initialize WebGL
+ * @returns { Object } the WebGL context
+ */
+ rglwidgetClass.prototype.initGL = function() {
+ var self = this, success = false;
+ if (this.gl) {
+ if (!this.drawing && this.gl.isContextLost())
+ this.restartCanvas();
+ else
+ return this.gl;
+ }
+ // if (!this.isInBrowserViewport()) return; Return what??? At this point we know this.gl is null.
+ this.canvas.addEventListener("webglcontextrestored",
+ this.onContextRestored, false);
+ this.canvas.addEventListener("webglcontextlost",
+ this.onContextLost, false);
+ this.gl = this.canvas.getContext("webgl", this.webGLoptions) ||
+ this.canvas.getContext("experimental-webgl", this.webGLoptions);
+ success = !!(this.gl && this.gl instanceof WebGLRenderingContext);
+ if (!success)
+ this.alertOnce("Your browser does not support WebGL. See http://get.webgl.org");
+ this.index_uint = this.gl.getExtension("OES_element_index_uint");
+ var save = this.startDrawing();
+ Object.keys(this.scene.objects).forEach(function(key){
+ self.initObjId(parseInt(key, 10));
+ });
+ this.stopDrawing(save);
+ return this.gl;
+ };
+
+ /**
+ * Resize the display to match element
+ * @param { Object } el - DOM element to match
+ */
+ rglwidgetClass.prototype.resize = function(el) {
+ this.canvas.width = el.width;
+ this.canvas.height = el.height;
+ };
+
+ /**
+ * Initialize the sphere object
+ */
+ rglwidgetClass.prototype.initSphere = function(sections, segments) {
+ var v = [], phi = [], theta = [], it = [], centers = [],
+ i, j, k, ind, result = {};
+
+ for (i = 0; i <= sections; i++) {
+ phi.push(i/sections - 0.5);
+ }
+
+ for (j = 0; j <= segments; j++) {
+ theta.push(2*j/segments);
+ for (i = 0; i <= sections; i++) {
+ /* These are [x,y,z,s,t]: */
+ v.push([Math.sin(Math.PI*theta[j]) * Math.cos(Math.PI*phi[i]),
+ Math.sin(Math.PI*phi[i]),
+ Math.cos(Math.PI*theta[j]) * Math.cos(Math.PI*phi[i]),
+ theta[j]/2,
+ phi[i] + 0.5]);
+ // console.log("xyzst="+v[v.length-1]);
+ }
+ }
+ result.values = new Float32Array(rglwidgetClass.flatten(v));
+ result.vertexCount = v.length;
+
+ for (j = 0; j < segments; j++) {
+ for (i = 0; i < sections; i++) {
+ ind = i + (sections + 1)*j;
+ if (i > 0) // Not south pole
+ it.push([ind,
+ ind + sections + 1,
+ ind + 1]);
+ if (i < sections - 1) // Not north pole
+ it.push([ind + sections + 1,
+ ind + sections + 2,
+ ind + 1]);
+ }
+ }
+ result.it = new Uint16Array(rglwidgetClass.flatten(it));
+
+ for (i = 0; i < it.length; i++) {
+ centers.push([0,0,0]);
+ for (j = 0; j < 3; j++) { // x,y,z
+ for (k = 0; k < 3; k++) {// vertices
+ centers[i][j] += v[it[i][k]][j]/3;
+ }
+ }
+ }
+ result.centers = centers;
+
+ result.vOffsets = {vofs:0, cofs:-1, nofs:0, radofs:-1, oofs:-1,
+ tofs:3, nextofs:-1, pointofs:-1, stride:5};
+
+ result.f = [];
+ result.indices = {};
+
+ result.colorCount = 1;
+ result.type = "sphere";
+ this.sphere = result;
+ this.initShapeGL(this.sphere);
+ };
+
+ /**
+ * Initialize the cube object
+ */
+ rglwidgetClass.prototype.initCube = function() {
+ var v = [[0, 0, 0], [1, 0, 0],
+ [0, 1, 0], [1, 1, 0],
+ [0, 0, 1], [1, 0, 1],
+ [0, 1, 1], [1, 1, 1]],
+ ib = [[0, 2, 3, 1],
+ [2, 6, 7, 3],
+ [1, 3, 7, 5],
+ [0, 4, 6, 2],
+ [0, 1, 5, 4],
+ [4, 5, 7, 6]],
+ centers = [], i, j, k,
+ i0, i1, i2,
+ normal, result = {};
+
+ for (i = 0; i < ib.length; i++) {
+ centers.push([0,0,0]);
+ for (j = 0; j < 3; j++) { // x,y,z
+ for (k = 0; k < 4; k++) {// vertices
+ centers[i][j] += v[ib[i][k]][j]/4;
+ }
+ }
+ }
+ result.centers = centers;
+ result.values = new Float32Array(6*4*3*2);
+ result.vertexCount = 24;
+ result.vertices = new Array(24);
+ result.normals = new Array(24);
+ for (i=0; i < 6; i++) {
+ for (j=0; j < 4; j++) {
+ i0 = ib[i][j];
+ result.vertices[4*i + j] = v[i0];
+ i1 = ib[i][(j + 1) % 4];
+ i2 = ib[i][(j + 2) % 4];
+ if (j === 0)
+ normal = rglwidgetClass.normalize(rglwidgetClass.xprod(rglwidgetClass.vdiff(v[i1], v[i0]),
+ rglwidgetClass.vdiff(v[i2], v[i0])));
+ result.normals[4*i + j] = normal;
+ for (k=0; k < 3; k++) {
+ result.values[i*24 + j*6 + k] = v[i0][k];
+ result.values[i*24 + j*6 + 3 + k] = normal[k];
+ }
+ }
+ for (j=0; j<4; j++)
+ ib[i][j] = 4*i + j;
+ }
+ result.ib = new Uint16Array(rglwidgetClass.flatten(ib));
+
+ result.vOffsets = {vofs:0, cofs:-1, nofs:3, radofs:-1, oofs:-1,
+ tofs:-1, nextofs:-1, pointofs:-1, stride:6};
+
+ result.f = [];
+ result.indices = {};
+
+ result.colorCount = 1;
+ result.type = "quads";
+ this.cube = result;
+ this.initShapeGL(this.cube);
+ };
+
+
+ /**
+ * Do the gl part of initializing the sphere and cube
+ */
+ rglwidgetClass.prototype.initShapeGL = function(shape) {
+ var gl = this.gl || this.initGL();
+ if (gl.isContextLost()) return;
+ shape.buf = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, shape.buf);
+ gl.bufferData(gl.ARRAY_BUFFER, shape.values, gl.STATIC_DRAW);
+ shape.ibuf = [gl.createBuffer(), gl.createBuffer()];
+ return;
+ };
+
+ /* Initialize common sphere object from spheres object
+ */
+ rglwidgetClass.prototype.initShapeFromObj = function(shape, obj) {
+ var i, pass, f, mode, self = this,
+ /* This function selects things that would be
+ the back, ignoring perspective -- this is what
+ we want for the bounding box decoration. */
+ is_back = function(i) {
+ var normal = [].concat(shape.normals[i]),
+ pt = shape.vertices[i];
+ normal.push(-rglwidgetClass.dotprod(normal, pt));
+ normal = rglwidgetClass.multVM(normal, self.normMatrix);
+ return normal[2] < 0 || (normal[2] === 0 && normal[0] < 0);
+ };
+ shape.ofsLoc = obj.ofsLoc;
+ shape.texLoc = obj.texLoc;
+ shape.texture = obj.texture;
+ shape.sampler = obj.sampler;
+ shape.uFogMode = obj.uFogMode;
+ shape.uFogColor = obj.uFogColor;
+ shape.uFogParms = obj.uFogParms;
+ shape.userAttribLocations = obj.userAttribLocations;
+ shape.userUniformLocations = obj.userUniformLocations;
+ shape.normLoc = obj.normLoc;
+ shape.invPrMatLoc = obj.invPrMatLoc;
+ shape.clipLoc = obj.clipLoc;
+ shape.nextLoc = obj.nextLoc;
+ shape.pointLoc = obj.pointLoc;
+ shape.aspectLoc = obj.aspectLoc;
+ shape.lwdLoc = obj.lwdLoc;
+ shape.prog = obj.prog;
+ shape.material = obj.material;
+ shape.flags = obj.flags;
+ shape.defFlags = obj.defFlags;
+ shape.someHidden = obj.someHidden;
+ shape.fastTransparency = obj.fastTransparency;
+ shape.nlights = obj.nlights;
+ shape.emission = obj.emission;
+ shape.emissionLoc = obj.emissionLoc;
+ shape.shininess = obj.shininess;
+ shape.shininessLoc = obj.shininessLoc;
+ shape.ambient = obj.ambient;
+ shape.ambientLoc = obj.ambientLoc;
+ shape.specular = obj.specular;
+ shape.specularLoc = obj.specularLoc;
+ shape.diffuse = obj.diffuse;
+ shape.diffuseLoc = obj.diffuseLoc;
+ shape.lightDir = obj.lightDir;
+ shape.lightDirLoc = obj.lightDirLoc;
+ shape.viewpoint = obj.viewpoint;
+ shape.viewpointLoc = obj.viewpointLoc;
+ shape.finite = obj.finite;
+ shape.finiteLoc = obj.finiteLoc;
+ shape.prMatLoc = obj.prMatLoc;
+ shape.mvMatLoc = obj.mvMatLoc;
+ shape.normMatLoc = obj.normMatLoc;
+ shape.frontLoc = obj.frontLoc;
+ shape.index_uint = false;
+ shape.is_transparent = obj.is_transparent;
+ shape.ignoreExtent = obj.ignoreExtent;
+ if (shape.passes !== obj.passes ||
+ JSON.stringify( shape.pmode) !== JSON.stringify(obj.pmode)) {
+ shape.passes = obj.passes;
+ shape.pmode = obj.pmode;
+ for (pass = 0; pass < obj.passes; pass++) {
+ mode = shape.pmode[pass];
+ if (typeof shape.indices[mode] === "undefined") {
+ f = [];
+ switch (mode) {
+ case "culled": break;
+ case "points":
+ f.length = shape.vertexCount;
+ for (i=0; i < f.length; i++)
+ f[i] = i;
+ break;
+ case "lines":
+ if (typeof shape.it !== "undefined") {
+ f.length = 2* shape.it.length;
+ for (i=0; i < shape.it.length/3; i++) {
+ f[6*i] = shape.it[3*i];
+ f[6*i + 1] = shape.it[3*i + 1];
+ f[6*i + 2] = shape.it[3*i + 1];
+ f[6*i + 3] = shape.it[3*i + 2];
+ f[6*i + 4] = shape.it[3*i + 2];
+ f[6*i + 5] = shape.it[3*i];
+ }
+ } else {
+ f.length = 2*shape.ib.length;
+ for (i=0; i < shape.ib.length/4; i++) {
+ f[8*i] = shape.ib[4*i];
+ f[8*i + 1] = shape.ib[4*i + 1];
+ f[8*i + 2] = shape.ib[4*i + 1];
+ f[8*i + 3] = shape.ib[4*i + 2];
+ f[8*i + 4] = shape.ib[4*i + 2];
+ f[8*i + 5] = shape.ib[4*i + 3];
+ f[8*i + 6] = shape.ib[4*i + 3];
+ f[8*i + 7] = shape.ib[4*i];
+ }
+ }
+ break;
+ case "filled":
+ if (typeof shape.it !== "undefined")
+ f = shape.it;
+ else if (typeof shape.ib !== "undefined") {
+ f.length = 1.5*shape.ib.length;
+ for (i=0; i < shape.ib.length/4; i++) {
+ f[6*i] = shape.ib[4*i];
+ f[6*i+1] = shape.ib[4*i + 1];
+ f[6*i+2] = shape.ib[4*i + 2];
+ f[6*i+3] = shape.ib[4*i];
+ f[6*i+4] = shape.ib[4*i + 2];
+ f[6*i+5] = shape.ib[4*i + 3];
+ }
+ }
+ break;
+ }
+ shape.indices[mode] = new Uint16Array(f);
+ }
+ }
+ }
+ for (pass = 0; pass < obj.passes; pass++) {
+ mode = shape.pmode[pass];
+ shape.f[pass] = shape.indices[mode];
+ if (typeof obj.draw_front !== "undefined" &&
+ !obj.draw_front) {
+ shape.f[pass] = shape.f[pass].filter(is_back);
+ }
+ }
+ // console.log("Names in shapes not in shape:"+JSON.stringify(rglwidgetClass.keydiff(obj, shape)));
+ shape.initialized = true;
+ };
+
+ /**
+ * Initialize a subscene
+ * @param { number } id - id of subscene.
+ */
+ rglwidgetClass.prototype.initSubscene = function(id) {
+ var sub = this.getObj(id),
+ i, obj;
+
+ if (sub.type !== "subscene")
+ return;
+
+ sub.par3d.userMatrix = this.toCanvasMatrix4(sub.par3d.userMatrix);
+ sub.par3d.userProjection = this.toCanvasMatrix4(sub.par3d.userProjection);
+ sub.par3d.userProjection.transpose();
+ sub.par3d.listeners = [].concat(sub.par3d.listeners);
+ sub.backgroundId = undefined;
+ sub.subscenes = [];
+ sub.clipplanes = [];
+ sub.transparent = [];
+ sub.opaque = [];
+ sub.lights = [];
+ sub.needsBegin = true;
+ if (typeof sub.objects !== "undefined")
+ sub.objects = [].concat(sub.objects); /* make sure it's an array */
+ for (i=0; i < sub.objects.length; i++) {
+ obj = this.getObj(sub.objects[i]);
+ if (typeof obj === "undefined") {
+ sub.objects.splice(i, 1);
+ i--;
+ } else if (obj.type === "background")
+ sub.backgroundId = obj.id;
+ else
+ sub[this.whichList(obj.id)].push(obj.id);
+ }
+ };
+
+ rglwidgetClass.prototype.initBBox = function(obj) {
+ if (!this.cube)
+ this.initCube();
+ obj.cube = {id: obj.id + 0.1,
+ type: "quads",
+ flags: obj.flags,
+ material: obj.material,
+ colors: [obj.colors[0]],
+ vertices: this.cube.vertices,
+ normals: this.cube.normals,
+ draw_front: obj.draw_front,
+ initialized: false
+ };
+ if (this.getMaterial(obj.cube, "front") !==
+ this.getMaterial(obj.cube, "back"))
+ /* jshint bitwise: false */
+ obj.cube.flags |= rglwidgetClass.f_is_twosided;
+ /* jshint bitwise: true */
+ this.scene.objects[obj.cube.id] = obj.cube;
+ obj.ticks = {id: obj.id + 0.2,
+ type: "lines",
+ flags: rglwidgetClass.f_has_fog,
+ material: obj.material,
+ colors: (obj.colors.length > 1 ? [obj.colors[1]] : [obj.colors[0]]),
+ axes: obj.axes,
+ initialized: false
+ };
+ this.scene.objects[obj.ticks.id] = obj.ticks;
+ obj.labels = {id: obj.id + 0.3,
+ type: "text",
+ flags: rglwidgetClass.f_has_fog +
+ rglwidgetClass.f_fixed_size +
+ rglwidgetClass.f_fixed_quads,
+ material: {lit: false},
+ colors: (obj.colors.length > 1 ? [obj.colors[1]] : [obj.colors[0]]),
+ cex: [[1]],
+ family: [["sans"]],
+ font: [[1]],
+ adj: [[0.5, 0.5, 0.5]],
+ ignoreExtent: true,
+ initialized: false
+ };
+ this.scene.objects[obj.labels.id] = obj.labels;
+ obj.initialized = true;
+ };
+
+ rglwidgetClass.prototype.initBackground = function(obj) {
+ var material, fl = obj.defFlags;
+ if (typeof obj.ids !== "undefined")
+ obj.quad = rglwidgetClass.flatten([].concat(obj.ids));
+ else if (obj.sphere) {
+ fl.has_normals = true;
+ fl.needs_vnormal = true;
+ obj.defFlags = fl;
+ material = obj.material;
+ material.front = "culled";
+ obj.vertices = [[0,0,0]];
+ obj.texcoords = [[0,0]];
+ }
+ };
+
+ /**
+ * Initialize object for display
+ * @param { number } id - id of object to initialize
+ */
+ rglwidgetClass.prototype.initObjId = function(id) {
+ if (typeof id !== "number") {
+ this.alertOnce("initObj id is "+typeof id);
+ }
+ return this.initObj(this.getObj(id));
+ };
+
+ /**
+ * Initialize object for display
+ * @param { Object } obj - object to initialize
+ */
+ rglwidgetClass.prototype.initObj = function(obj) {
+ var type = obj.type,
+ flags = obj.flags,
+ normals = obj.normals,
+ round_points = (typeof obj.material === "undefined") ?
+ false : this.getMaterial(obj, "point_antialias"),
+ has_indices = typeof obj.indices !== "undefined",
+ has_spheres = type === "spheres" ||
+ (type === "background" && obj.sphere),
+ sprites_3d = rglwidgetClass.isSet(flags, rglwidgetClass.f_sprites_3d),
+ depth_sort = rglwidgetClass.isSet(flags, rglwidgetClass.f_depth_sort),
+ gl = this.gl || this.initGL(),
+ fl, polygon_offset,
+ texinfo, drawtype, nclipplanes, f, nrows, oldrows,
+ i,j,v,v1,v2, mat, uri, matobj, pass, pmode,
+ dim, nx, nz, nrow, shaders;
+
+ obj.initialized = true;
+
+ obj.someHidden = false; // used in selection
+
+ this.expandBufferedFields(obj);
+
+ if (type === "subscene")
+ return;
+
+ obj.defFlags = fl = rglwidgetClass.getDefFlags(flags, type, normals, round_points);
+
+ obj.is_transparent = fl.is_transparent;
+
+ if (type === "bboxdeco")
+ return this.initBBox(obj);
+
+ if (has_spheres && typeof this.sphere === "undefined")
+ this.initSphere(16, 16);
+
+ if (type === "light") {
+ obj.ambient = new Float32Array(obj.colors[0].slice(0,3));
+ obj.diffuse = new Float32Array(obj.colors[1].slice(0,3));
+ obj.specular = new Float32Array(obj.colors[2].slice(0,3));
+ obj.lightDir = new Float32Array(obj.vertices[0]);
+ return;
+ }
+
+ if (type === "clipplanes") {
+ obj.vClipplane = rglwidgetClass.flatten(rglwidgetClass.cbind(obj.normals, obj.offsets));
+ return;
+ }
+
+ if (type === "background") {
+ this.initBackground(obj);
+ if (!obj.sphere)
+ return;
+ }
+
+ polygon_offset = this.getMaterial(obj, "polygon_offset");
+ if (polygon_offset[0] !== 0 || polygon_offset[1] !== 0)
+ obj.polygon_offset = polygon_offset;
+
+ if (fl.is_transparent) {
+ depth_sort = ["triangles", "quads", "surface",
+ "spheres", "sprites", "text",
+ "planes"].indexOf(type) >= 0;
+ }
+
+ if (fl.is_brush)
+ this.initSelection(obj.id);
+
+ if (typeof obj.vertices === "undefined")
+ obj.vertices = [];
+
+ v = obj.vertices;
+ if (has_indices)
+ obj.vertexCount = obj.indices.length;
+ else
+ obj.vertexCount = v.length;
+
+ if (!obj.vertexCount) return;
+
+ if (fl.is_twosided && !fl.has_normals && type !== "background") {
+ if (typeof obj.userAttributes === "undefined")
+ obj.userAttributes = {};
+ v1 = Array(v.length);
+ v2 = Array(v.length);
+ if (obj.type === "triangles" || obj.type === "quads") {
+ if (obj.type === "triangles")
+ nrow = 3;
+ else
+ nrow = 4;
+ for (i=0; i= 0) {
+ key = this.scene.crosstalk.key[j];
+ options = this.scene.crosstalk.options[j];
+ colors = colors.slice(0);
+ for (i = 0; i < v.length; i++)
+ colors[i] = obj.colors[i % obj.colors.length].slice(0);
+ if ( (selection = this.scene.crosstalk.selection) &&
+ (selection.length || !options.selectedIgnoreNone) )
+ for (i = 0; i < v.length; i++) {
+ if (!selection.includes(key[i])) {
+ if (options.deselectedColor)
+ colors[i] = options.deselectedColor.slice(0);
+ colors[i][3] = colors[i][3]*options.deselectedFade; /* default: mostly transparent if not selected */
+ } else if (options.selectedColor)
+ colors[i] = options.selectedColor.slice(0);
+ }
+ if ( (filter = this.scene.crosstalk.filter) )
+ for (i = 0; i < v.length; i++)
+ if (!filter.includes(key[i])) {
+ if (options.filteredColor)
+ colors[i] = options.filteredColor.slice(0);
+ colors[i][3] = colors[i][3]*options.filteredFade; /* default: completely hidden if filtered */
+ }
+ }
+
+ nc = obj.colorCount = colors.length;
+ if (nc > 1) {
+ cofs = stride;
+ stride = stride + 4;
+ v = rglwidgetClass.cbind(v, colors);
+ } else {
+ cofs = -1;
+ obj.onecolor = rglwidgetClass.flatten(colors);
+ }
+
+ if (fl.has_normals && !has_spheres) {
+ nofs = stride;
+ stride = stride + 3;
+ v = rglwidgetClass.cbind(v, typeof obj.pnormals !== "undefined" ? obj.pnormals : obj.normals);
+ } else
+ nofs = -1;
+
+ if (typeof obj.radii !== "undefined") {
+ radofs = stride;
+ stride = stride + 1;
+ // FIXME: always concat the radii?
+ if (obj.radii.length === v.length) {
+ v = rglwidgetClass.cbind(v, obj.radii);
+ } else if (obj.radii.length === 1) {
+ v = v.map(function(row) { return row.concat(obj.radii[0]);});
+ }
+ } else
+ radofs = -1;
+
+ // Add default indices
+ if (has_indices) {
+ f = Array(obj.indices.length);
+ for (i = 0; i < f.length; i++)
+ f[i] = obj.indices[i] - 1;
+ } else {
+ f = Array(v.length);
+ for (i = 0; i < v.length; i++)
+ f[i] = i;
+ }
+ obj.f = [f,f];
+
+ if (type === "sprites" && !sprites_3d) {
+ tofs = stride;
+ stride += 2;
+ oofs = stride;
+ stride += 3;
+ vnew = new Array(4*v.length);
+ fnew = new Array(4*v.length);
+ alias = new Array(v.length);
+ var rescale = fl.fixed_size ? 72 : 1,
+ size = obj.radii, s = rescale*size[0]/2;
+ last = v.length;
+ f = obj.f[0];
+ obj.adj = rglwidgetClass.flatten(obj.adj);
+ if (typeof obj.pos !== "undefined") {
+ obj.pos = rglwidgetClass.flatten(obj.pos);
+ offset = obj.adj[0];
+ } else
+ offset = 0;
+ for (i=0; i < v.length; i++) {
+ adj = this.getAdj(obj, i, offset);
+ if (size.length > 1)
+ s = rescale*size[i]/2;
+ adj[0] = 2*s*(adj[0] - 0.5);
+ adj[1] = 2*s*(adj[1] - 0.5);
+ adj[2] = 2*s*(adj[2] - 0.5);
+ vnew[i] = v[i].concat([0,0]).concat([-s-adj[0],
+ -s-adj[1],
+ -adj[2]]);
+ fnew[4*i] = f[i];
+ vnew[last]= v[i].concat([1,0]).concat([s-adj[0],
+ -s-adj[1],
+ -adj[2]]);
+ fnew[4*i+1] = last++;
+ vnew[last]= v[i].concat([1,1]).concat([s-adj[0],
+ s-adj[1],
+ -adj[2]]);
+ fnew[4*i+2] = last++;
+ vnew[last]= v[i].concat([0,1]).concat([-s-adj[0],
+ s-adj[1],
+ -adj[2]]);
+ fnew[4*i+3] = last++;
+ alias[i] = [last-3, last-2, last-1];
+ }
+ v = vnew;
+ obj.vertexCount = v.length;
+ obj.f = [fnew, fnew];
+ } else if (type === "text") {
+ tofs = stride;
+ stride += 2;
+ oofs = stride;
+ stride += 3;
+ vnew = new Array(4*v.length);
+ f = obj.f[0];
+ fnew = new Array(4*f.length);
+ alias = new Array(v.length);
+ last = v.length;
+ adj = rglwidgetClass.flatten(obj.adj);
+ if (typeof obj.pos !== "undefined") {
+ obj.pos = rglwidgetClass.flatten(obj.pos);
+ offset = adj[0];
+ } else
+ offset = 0;
+ for (i=0; i < v.length; i++) {
+ adj = this.getAdj(obj, i, offset, obj.texts[i]);
+ vnew[i] = v[i].concat([0,-0.5]).concat(adj);
+ fnew[4*i] = f[i];
+ vnew[last] = v[i].concat([1,-0.5]).concat(adj);
+ fnew[4*i+1] = last++;
+ vnew[last] = v[i].concat([1, 1.5]).concat(adj);
+ fnew[4*i+2] = last++;
+ vnew[last] = v[i].concat([0, 1.5]).concat(adj);
+ fnew[4*i+3] = last++;
+ alias[i] = [last-3, last-2, last-1];
+ for (j=0; j < 4; j++) {
+ v1 = vnew[fnew[4*i+j]];
+ v1[oofs] = 2*(v1[tofs]-v1[oofs])*texinfo.widths[i];
+ v1[oofs+1] = 2*(v1[tofs+1]-v1[oofs+1])*texinfo.textHeights[i];
+ v1[oofs+2] = 2*(0.5-v1[oofs+2])*texinfo.textHeights[i]/1000.0;
+ v1[tofs] = (texinfo.offsetsx[i] + v1[tofs]*texinfo.widths[i])/texinfo.canvasX;
+ v1[tofs+1] = 1.0-(texinfo.offsetsy[i] -
+ v1[tofs+1]*texinfo.textHeights[i])/texinfo.canvasY;
+ vnew[fnew[4*i+j]] = v1;
+ }
+ }
+ v = vnew;
+ obj.vertexCount = v.length;
+ obj.f = [fnew, fnew];
+ } else if (typeof obj.texcoords !== "undefined") {
+ tofs = stride;
+ stride += 2;
+ oofs = -1;
+ v = rglwidgetClass.cbind(v, obj.texcoords);
+ } else {
+ tofs = -1;
+ oofs = -1;
+ }
+
+ obj.alias = alias;
+
+ if (typeof obj.userAttributes !== "undefined") {
+ obj.userAttribOffsets = {};
+ obj.userAttribLocations = {};
+ obj.userAttribSizes = {};
+ for (attr in obj.userAttributes) {
+ obj.userAttribLocations[attr] = gl.getAttribLocation(obj.prog, attr);
+ if (obj.userAttribLocations[attr] >= 0) { // Attribute may not have been used
+ obj.userAttribOffsets[attr] = stride;
+ v = rglwidgetClass.cbind(v, obj.userAttributes[attr]);
+ stride = v[0].length;
+ obj.userAttribSizes[attr] = stride - obj.userAttribOffsets[attr];
+ } else
+ console.warn("attribute '"+attr+"' not found in object "+obj.id+".");
+ }
+ }
+
+ if (typeof obj.userUniforms !== "undefined" ||
+ typeof obj.userTextures !== "undefined") {
+ obj.userUniformLocations = {};
+ for (attr in obj.userUniforms) {
+ obj.userUniformLocations[attr] = gl.getUniformLocation(obj.prog, attr);
+ if (obj.userUniformLocations[attr] === null)
+ console.warn("uniform '"+attr+"' not found in object "+obj.id+".");
+ }
+ for (attr in obj.userTextures) {
+ var texture = obj.userTextures[attr];
+ texture.texture = gl.createTexture();
+ // This is a trick from https://stackoverflow.com/a/19748905/2554330 to avoid warnings
+ gl.bindTexture(gl.TEXTURE_2D, texture.texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
+ new Uint8Array([255,255,255, 255])); // white
+ texture.sampler = gl.getUniformLocation(obj.prog, attr);
+ if (texture.sampler === null)
+ console.warn("sampler '"+attr+"' not found in object "+obj.id+".");
+ uri = texture.uri;
+ this.loadImageToTexture(uri, texture.texture);
+ }
+ }
+
+ if (sprites_3d) {
+ obj.userMatrix = new CanvasMatrix4();
+ obj.userMatrix.load(rglwidgetClass.flatten(obj.usermatrix));
+ obj.objects = rglwidgetClass.flatten([].concat(obj.ids));
+ fl.is_lit = false;
+ obj.adj = rglwidgetClass.flatten(obj.adj);
+
+ if (typeof obj.pos !== "undefined") {
+ obj.pos = rglwidgetClass.flatten(obj.pos);
+ obj.offset = obj.adj[0];
+ } else
+ obj.offset = 0;
+
+ var shapenum = rglwidgetClass.flatten(obj.shapenum);
+ obj.shapelens = [];
+ obj.shapefirst = [];
+ obj.shapefirst.push(0);
+ len = 0;
+ current = 0;
+ for (i = 0; i < shapenum.length; i++) {
+ if (shapenum[i] === shapenum[current]) {
+ len++;
+ } else {
+ obj.shapelens.push(len);
+ len = 1;
+ current = i;
+ obj.shapefirst.push(i);
+ }
+ }
+ obj.shapelens.push(len);
+
+ for (i=0; i < obj.objects.length; i++)
+ this.initObjId(obj.objects[i]);
+ }
+
+ nclipplanes = this.countClipplanes();
+ if (nclipplanes && !sprites_3d) {
+ obj.clipLoc = gl.getUniformLocation(obj.prog,"vClipplane");
+ }
+
+ if (fl.is_lit) {
+ obj.emissionLoc = gl.getUniformLocation(obj.prog, "emission");
+ obj.emission = new Float32Array(this.stringToRgb(this.getMaterial(obj, "emission")));
+ obj.shininessLoc = gl.getUniformLocation(obj.prog, "shininess");
+ obj.shininess = this.getMaterial(obj, "shininess");
+ obj.nlights = this.countLights();
+ if (obj.nlights > 0) {
+ obj.ambient = new Float32Array(this.stringToRgb(this.getMaterial(obj, "ambient")));
+ obj.specular = new Float32Array(this.stringToRgb(this.getMaterial(obj, "specular")));
+ obj.ambientLoc = gl.getUniformLocation(obj.prog, "ambient");
+ obj.specularLoc = gl.getUniformLocation(obj.prog, "specular");
+ obj.diffuseLoc = gl.getUniformLocation(obj.prog, "diffuse" );
+ obj.lightDirLoc = gl.getUniformLocation(obj.prog, "lightDir");
+ obj.viewpointLoc = gl.getUniformLocation(obj.prog, "viewpoint");
+ obj.finiteLoc = gl.getUniformLocation(obj.prog, "finite" );
+ }
+ }
+
+ obj.passes = fl.is_twosided + 1;
+ obj.pmode = new Array(obj.passes);
+ for (pass = 0; pass < obj.passes; pass++) {
+ if (type === "triangles" || type === "quads" || type === "surface" || has_spheres)
+ pmode = this.getMaterial(obj, (pass === 0) ? "front" : "back");
+ else pmode = "filled";
+ obj.pmode[pass] = pmode;
+ }
+ if (!has_spheres) {
+ obj.f.length = obj.passes;
+ for (pass = 0; pass < obj.passes; pass++) {
+ f = fnew = obj.f[pass];
+ pmode = obj.pmode[pass];
+ if (pmode === "culled")
+ fnew = [];
+ else if (pmode === "points") {
+ // stay with default
+ } else if ((type === "quads" || type === "text" ||
+ type === "sprites") && !sprites_3d) {
+ nrows = Math.floor(obj.vertexCount/4);
+ if (pmode === "filled") {
+ fnew = Array(6*nrows);
+ for (i=0; i < nrows; i++) {
+ fnew[6*i] = f[4*i];
+ fnew[6*i+1] = f[4*i + 1];
+ fnew[6*i+2] = f[4*i + 2];
+ fnew[6*i+3] = f[4*i];
+ fnew[6*i+4] = f[4*i + 2];
+ fnew[6*i+5] = f[4*i + 3];
+ }
+ } else {
+ fnew = Array(8*nrows);
+ for (i=0; i < nrows; i++) {
+ fnew[8*i] = f[4*i];
+ fnew[8*i+1] = f[4*i + 1];
+ fnew[8*i+2] = f[4*i + 1];
+ fnew[8*i+3] = f[4*i + 2];
+ fnew[8*i+4] = f[4*i + 2];
+ fnew[8*i+5] = f[4*i + 3];
+ fnew[8*i+6] = f[4*i + 3];
+ fnew[8*i+7] = f[4*i];
+ }
+ }
+ } else if (type === "triangles") {
+ nrows = Math.floor(obj.vertexCount/3);
+ if (pmode === "filled") {
+ fnew = Array(3*nrows);
+ for (i=0; i < fnew.length; i++) {
+ fnew[i] = f[i];
+ }
+ } else if (pmode === "lines") {
+ fnew = Array(6*nrows);
+ for (i=0; i < nrows; i++) {
+ fnew[6*i] = f[3*i];
+ fnew[6*i + 1] = f[3*i + 1];
+ fnew[6*i + 2] = f[3*i + 1];
+ fnew[6*i + 3] = f[3*i + 2];
+ fnew[6*i + 4] = f[3*i + 2];
+ fnew[6*i + 5] = f[3*i];
+ }
+ }
+ } else if (has_spheres) {
+ // default
+ } else if (type === "surface") {
+ dim = obj.dim[0];
+ nx = dim[0];
+ nz = dim[1];
+ if (pmode === "filled") {
+ fnew = [];
+ for (j=0; j 65535) {
+ if (this.index_uint) {
+ obj.f[pass] = new Uint32Array(obj.f[pass]);
+ obj.index_uint = true;
+ } else
+ this.alertOnce("Object has "+obj.vertexCount+" vertices, not supported in this browser.");
+ } else {
+ obj.f[pass] = new Uint16Array(obj.f[pass]);
+ obj.index_uint = false;
+ }
+ }
+
+ if (stride !== v[0].length) {
+ this.alertOnce("problem in stride calculation");
+ }
+
+ obj.vOffsets = {vofs:0, cofs:cofs, nofs:nofs, radofs:radofs, oofs:oofs, tofs:tofs,
+ nextofs:nextofs, pointofs:pointofs, stride:stride};
+
+ obj.values = new Float32Array(rglwidgetClass.flatten(v));
+
+ if (!has_spheres && !sprites_3d) {
+ obj.buf = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, obj.buf);
+ gl.bufferData(gl.ARRAY_BUFFER, obj.values, gl.STATIC_DRAW); //
+ obj.ibuf = Array(obj.passes);
+ obj.ibuf[0] = gl.createBuffer();
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.ibuf[0]);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, obj.f[0], gl[drawtype]);
+ if (fl.is_twosided) {
+ obj.ibuf[1] = gl.createBuffer();
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.ibuf[1]);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, obj.f[1], gl[drawtype]);
+ }
+ }
+
+ if (!sprites_3d) {
+ obj.mvMatLoc = gl.getUniformLocation(obj.prog, "mvMatrix");
+ obj.prMatLoc = gl.getUniformLocation(obj.prog, "prMatrix");
+
+ if (fl.fixed_size) {
+ obj.textScaleLoc = gl.getUniformLocation(obj.prog, "textScale");
+ }
+ }
+
+ if (fl.needs_vnormal) {
+ obj.normLoc = gl.getAttribLocation(obj.prog, "aNorm");
+ obj.normMatLoc = gl.getUniformLocation(obj.prog, "normMatrix");
+ }
+
+ if (fl.is_twosided) {
+ obj.frontLoc = gl.getUniformLocation(obj.prog, "front");
+ if (fl.has_normals)
+ obj.invPrMatLoc = gl.getUniformLocation(obj.prog, "invPrMatrix");
+ }
+ };
+
+ /**
+ * Initialize the DOM object
+ * @param { Object } el - the DOM object
+ * @param { Object } x - the scene data sent by JSON from R
+ */
+ rglwidgetClass.prototype.initialize = function(el, x) {
+ this.textureCanvas = document.createElement("canvas");
+ this.textureCanvas.style.display = "block";
+ this.scene = x;
+ this.normMatrix = new CanvasMatrix4();
+ this.invPrMatrix = new CanvasMatrix4();
+ this.saveMat = {};
+ this.distance = null;
+ this.posLoc = 0;
+ this.colLoc = 1;
+ if (el) {
+ el.rglinstance = this;
+ this.el = el;
+ this.webGLoptions = el.rglinstance.scene.webGLoptions;
+ this.initCanvas();
+ }
+ if (typeof Shiny !== "undefined") {
+ var self = this;
+ Shiny.addCustomMessageHandler("shinyGetPar3d",
+ function(message) {
+ var i, param,
+ subscene = self.getObj(message.subscene),
+ parameters = [].concat(message.parameters),
+ result = {tag: message.tag, subscene: message.subscene};
+ if (typeof subscene !== "undefined") {
+ for (i = 0; i < parameters.length; i++) {
+ param = parameters[i];
+ result[param] = subscene.par3d[param];
+ }
+ } else {
+ console.log("subscene "+message.subscene+" undefined.");
+ }
+ Shiny.setInputValue("par3d:shinyPar3d", result, {priority: "event"});
+ });
+
+ Shiny.addCustomMessageHandler("shinySetPar3d",
+ function(message) {
+ var param = message.parameter,
+ subscene = self.getObj(message.subscene);
+ if (typeof subscene !== "undefined") {
+ subscene.par3d[param] = message.value;
+ subscene.initialized = false;
+ self.drawScene();
+ } else {
+ console.log("subscene "+message.subscene+" undefined.");
+ }
+ });
+
+ Shiny.addCustomMessageHandler("resetBrush",
+ function(message) {
+ if (message === self.scene.selectionInput) {
+ self.clearBrush(null);
+ self.recordSelection(0);
+ }
+ });
+ }
+ };
+
+ /**
+ * Restart the WebGL canvas
+ */
+ rglwidgetClass.prototype.restartCanvas = function() {
+ var newcanvas = document.createElement("canvas"),
+ self = this,
+ labelid = this.el.getAttribute("aria-labelledby");
+ newcanvas.width = this.el.width;
+ newcanvas.height = this.el.height;
+ newcanvas.setAttribute("aria-labelledby",
+ labelid);
+
+ if (typeof this.scene.altText !== "undefined") {
+ // We're in Shiny, so alter the label
+ var label = document.getElementById(labelid);
+ if (label)
+ label.innerHTML = this.scene.altText;
+ }
+ newcanvas.addEventListener("webglcontextrestored",
+ this.onContextRestored, false);
+ newcanvas.addEventListener("webglcontextlost",
+ this.onContextLost, false);
+ while (this.el.firstChild) {
+ this.el.removeChild(this.el.firstChild);
+ }
+ this.el.appendChild(newcanvas);
+ this.canvas = newcanvas;
+ if (this.scene.javascript) {
+ /* jshint evil:true */
+ Function('"use strict";' + this.scene.javascript)();
+ /* jshint evil:false */
+ }
+ this.setMouseHandlers();
+ if (this.gl)
+ Object.keys(this.scene.objects).forEach(function(key){
+ self.getObj(parseInt(key, 10)).texture = undefined;
+ });
+ this.gl = null;
+ };
+
+ /**
+ * Initialize the WebGL canvas
+ */
+ rglwidgetClass.prototype.initCanvas = function() {
+ this.restartCanvas();
+ var objs = this.scene.objects,
+ self = this;
+
+ /* These hold context specific data. In Shiny, they
+ need to be deleted. Elsewhere, they don't exist
+ and these are no-ops. */
+
+ delete this.cube;
+ delete this.sphere;
+
+ Object.keys(objs).forEach(function(key){
+ self.initSubscene(parseInt(key, 10));
+ });
+
+ this.onContextRestored = function() {
+ self.initGL();
+ self.drawScene();
+ };
+
+ this.onContextLost = function(event) {
+ if (!self.drawing)
+ this.gl = null;
+ event.preventDefault();
+ };
+
+ this.initGL0();
+ this.lazyLoadScene = function() {
+ if (typeof self.slide === "undefined")
+ self.slide = self.getSlide();
+ if (self.isInBrowserViewport()) {
+ if (!self.gl || self.gl.isContextLost())
+ self.initGL();
+ self.drawScene();
+ }
+ };
+ window.addEventListener("DOMContentLoaded", this.lazyLoadScene, false);
+ window.addEventListener("load", this.lazyLoadScene, false);
+ window.addEventListener("resize", this.lazyLoadScene, false);
+ window.addEventListener("scroll", this.lazyLoadScene, false);
+ this.slide = this.getSlide();
+ if (this.slide) {
+ if (typeof this.slide.rgl === "undefined")
+ this.slide.rgl = [this];
+ else
+ this.slide.rgl.push(this);
+ if (this.scene.context.rmarkdown)
+ if (this.scene.context.rmarkdown === "ioslides_presentation") {
+ this.slide.setAttribute("slideenter", "this.rgl.forEach(function(scene) { scene.lazyLoadScene.call(window);})");
+ } else if (this.scene.context.rmarkdown === "slidy_presentation") {
+ // This method would also work in ioslides, but it gets triggered
+ // something like 5 times per slide for every slide change, so
+ // you'd need a quicker function than lazyLoadScene.
+ var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
+ observer = new MutationObserver(function(mutations) {
+ mutations.forEach(function() {
+ self.slide.rgl.forEach(function(scene) { scene.lazyLoadScene.call(window); });});});
+ observer.observe(this.slide, { attributes: true, attributeFilter:["class"] });
+ }
+ }
+ };
diff --git a/reference/libs/rglwidgetClass-1.3.1/mouse.src.js b/reference/libs/rglwidgetClass-1.3.1/mouse.src.js
new file mode 100644
index 0000000..ca992f8
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/mouse.src.js
@@ -0,0 +1,569 @@
+ /**
+ * Methods related to mouse handling
+ * @name ___METHODS_FOR_MOUSE_HANDLING___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+ */
+
+ rglwidgetClass.prototype.getCursor = function(mode) {
+ switch(mode) {
+ case "none":
+ return "none";
+ case "trackball":
+ case "xAxis":
+ case "yAxis":
+ case "zAxis":
+ case "polar":
+ return "grab";
+ case "selecting":
+ return "crosshair";
+ case "fov":
+ case "zoom":
+ return "zoom-in";
+ case "user":
+ return "default";
+ }
+ return "dragging";
+ };
+
+ /**
+ * Set mouse mode for a subscene
+ * @param { string } mode - name of mode
+ * @param { number } button - button number (0 to 4)
+ * @param { number } subscene - subscene id number
+ * @param { number } stayActive - if truthy, don't clear brush
+ */
+ rglwidgetClass.prototype.setMouseMode = function(mode, button, subscene, stayActive) {
+ var sub = this.getObj(subscene),
+ which = ["none", "left", "right", "middle", "wheel"][button];
+ if (!stayActive && sub.par3d.mouseMode[which] === "selecting")
+ this.clearBrush(null);
+ sub.par3d.mouseMode[which] = mode;
+ if (button === 1 || (button === 0 && mode !== "none"))
+ this.canvas.style.cursor = this.getCursor(mode);
+ if (button === 0 && mode !== "none")
+ sub.needsBegin = mode;
+ };
+
+ /**
+ * Compute mouse coordinates relative to current canvas
+ * @returns { Object }
+ * @param { Object } event - event object from mouse click
+ */
+ rglwidgetClass.prototype.relMouseCoords = function(event) {
+ var rect = this.canvas.getBoundingClientRect();
+ return {x:event.clientX-rect.left, y:event.clientY-rect.top};
+ };
+
+ /**
+ * Send mouse selection to Shiny
+ */
+ rglwidgetClass.prototype.recordSelection = function(subid) {
+ var result = {};
+ if (typeof this.select !== "undefined" &&
+ typeof this.select.state !== "undefined" &&
+ this.select.state !== "inactive") {
+ result = { subscene: subid,
+ state: this.select.state,
+ region: this.select.region
+ };
+ this.setmvMatrix(subid);
+ result.model = this.mvMatrix;
+ this.setprMatrix(subid);
+ result.proj = this.prMatrix;
+ this.getViewport(subid);
+ result.view = this.vp;
+ } else
+ result.state = "inactive";
+ Shiny.setInputValue(this.scene.selectionInput + ":shinyMouse3d", result);
+ };
+
+ /**
+ * Set mouse handlers for the scene
+ */
+ rglwidgetClass.prototype.setMouseHandlers = function() {
+ var self = this, activeSubscene, handler,
+ handlers = {}, drag = 0;
+
+ handlers.rotBase = 0;
+
+ self.screenToVector = function(x, y) {
+ var viewport = self.getObj(activeSubscene).par3d.viewport,
+ width = viewport.width*self.canvas.width,
+ height = viewport.height*self.canvas.height,
+ radius = Math.max(width, height)/2.0,
+ cx = width/2.0,
+ cy = height/2.0,
+ px = (x-cx)/radius,
+ py = (y-cy)/radius,
+ plen = Math.sqrt(px*px+py*py);
+ if (plen > 1.e-6) {
+ px = px/plen;
+ py = py/plen;
+ }
+ var angle = (Math.SQRT2 - plen)/Math.SQRT2*Math.PI/2,
+ z = Math.sin(angle),
+ zlen = Math.sqrt(1.0 - z*z);
+ px = px * zlen;
+ py = py * zlen;
+ return [px, py, z];
+ };
+
+ handlers.trackballdown = function(x,y) {
+ var activeSub = self.getObj(activeSubscene),
+ activeModel = self.getObj(self.useid(activeSub.id, "model")),
+ i, l = activeModel.par3d.listeners;
+ handlers.rotBase = self.screenToVector(x, y);
+ self.saveMat = [];
+ for (i = 0; i < l.length; i++) {
+ activeSub = self.getObj(l[i]);
+ activeSub.saveMat = new CanvasMatrix4(activeSub.par3d.userMatrix);
+ }
+ self.canvas.style.cursor = "grabbing";
+ };
+
+ handlers.trackballmove = function(x,y) {
+ var rotCurrent = self.screenToVector(x,y),
+ rotBase = handlers.rotBase,
+ dot = rotBase[0]*rotCurrent[0] +
+ rotBase[1]*rotCurrent[1] +
+ rotBase[2]*rotCurrent[2],
+ angle = Math.acos( dot/rglwidgetClass.vlen(rotBase)/rglwidgetClass.vlen(rotCurrent) )*180.0/Math.PI,
+ axis = rglwidgetClass.xprod(rotBase, rotCurrent),
+ objects = self.scene.objects,
+ activeSub = self.getObj(activeSubscene),
+ activeModel = self.getObj(self.useid(activeSub.id, "model")),
+ l = activeModel.par3d.listeners,
+ i;
+ if (angle === 0.0)
+ return;
+ for (i = 0; i < l.length; i++) {
+ activeSub = self.getObj(l[i]);
+ activeSub.par3d.userMatrix.load(objects[l[i]].saveMat);
+ activeSub.par3d.userMatrix.rotate(angle, axis[0], axis[1], axis[2]);
+ }
+ self.drawScene();
+ };
+ handlers.trackballend = 0;
+
+ self.clamp = function(x, lo, hi) {
+ return Math.max(lo, Math.min(x, hi));
+ };
+
+ self.screenToPolar = function(x,y) {
+ var viewport = self.getObj(activeSubscene).par3d.viewport,
+ width = viewport.width*self.canvas.width,
+ height = viewport.height*self.canvas.height,
+ r = Math.min(width, height)/2,
+ dx = self.clamp(x - width/2, -r, r),
+ dy = self.clamp(y - height/2, -r, r);
+ return [Math.asin(dx/r), Math.asin(-dy/r)];
+ };
+
+ handlers.polardown = function(x,y) {
+ var activeSub = self.getObj(activeSubscene),
+ activeModel = self.getObj(self.useid(activeSub.id, "model")),
+ i, l = activeModel.par3d.listeners;
+ handlers.dragBase = self.screenToPolar(x, y);
+ self.saveMat = [];
+ for (i = 0; i < l.length; i++) {
+ activeSub = self.getObj(l[i]);
+ activeSub.saveMat = new CanvasMatrix4(activeSub.par3d.userMatrix);
+ activeSub.camBase = [-Math.atan2(activeSub.saveMat.m13, activeSub.saveMat.m11),
+ Math.atan2(activeSub.saveMat.m32, activeSub.saveMat.m22)];
+ }
+ self.canvas.style.cursor = "grabbing";
+ };
+
+ handlers.polarmove = function(x,y) {
+ var dragCurrent = self.screenToPolar(x,y),
+ activeSub = self.getObj(activeSubscene),
+ activeModel = self.getObj(self.useid(activeSub.id, "model")),
+ objects = self.scene.objects,
+ l = activeModel.par3d.listeners,
+ i, j, changepos = [];
+ for (i = 0; i < l.length; i++) {
+ activeSub = self.getObj(l[i]);
+ for (j=0; j<2; j++)
+ changepos[j] = -(dragCurrent[j] - handlers.dragBase[j]);
+ activeSub.par3d.userMatrix.makeIdentity();
+ activeSub.par3d.userMatrix.rotate(changepos[0]*180/Math.PI, 0,-1,0);
+ activeSub.par3d.userMatrix.multRight(objects[l[i]].saveMat);
+ activeSub.par3d.userMatrix.rotate(changepos[1]*180/Math.PI, -1,0,0);
+ }
+ self.drawScene();
+ };
+ handlers.polarend = 0;
+
+ handlers.axisdown = function(x) {
+ handlers.rotBase = self.screenToVector(x, self.canvas.height/2);
+ var activeSub = self.getObj(activeSubscene),
+ activeModel = self.getObj(self.useid(activeSub.id, "model")),
+ i, l = activeModel.par3d.listeners;
+ for (i = 0; i < l.length; i++) {
+ activeSub = self.getObj(l[i]);
+ activeSub.saveMat = new CanvasMatrix4(activeSub.par3d.userMatrix);
+ }
+ self.canvas.style.cursor = "grabbing";
+ };
+
+ handlers.axismove = function(x) {
+ var rotCurrent = self.screenToVector(x, self.canvas.height/2),
+ rotBase = handlers.rotBase,
+ angle = (rotCurrent[0] - rotBase[0])*180/Math.PI,
+ rotMat = new CanvasMatrix4();
+ rotMat.rotate(angle, handlers.axis[0], handlers.axis[1], handlers.axis[2]);
+ var activeSub = self.getObj(activeSubscene),
+ activeModel = self.getObj(self.useid(activeSub.id, "model")),
+ i, l = activeModel.par3d.listeners;
+ for (i = 0; i < l.length; i++) {
+ activeSub = self.getObj(l[i]);
+ activeSub.par3d.userMatrix.load(activeSub.saveMat);
+ activeSub.par3d.userMatrix.multLeft(rotMat);
+ }
+ self.drawScene();
+ };
+ handlers.axisend = 0;
+
+ handlers.y0zoom = 0;
+ handlers.zoomdown = function(x, y) {
+ var activeSub = self.getObj(activeSubscene),
+ activeProjection = self.getObj(self.useid(activeSub.id, "projection")),
+ i, l = activeProjection.par3d.listeners;
+ handlers.y0zoom = y;
+ for (i = 0; i < l.length; i++) {
+ activeSub = self.getObj(l[i]);
+ activeSub.zoom0 = Math.log(activeSub.par3d.zoom);
+ }
+ self.canvas.style.cursor = "zoom-in";
+ };
+ handlers.zoommove = function(x, y) {
+ var activeSub = self.getObj(activeSubscene),
+ activeProjection = self.getObj(self.useid(activeSub.id, "projection")),
+ i, l = activeProjection.par3d.listeners;
+ for (i = 0; i < l.length; i++) {
+ activeSub = self.getObj(l[i]);
+ activeSub.par3d.zoom = Math.exp(activeSub.zoom0 + (y-handlers.y0zoom)/self.canvas.height);
+ }
+ self.drawScene();
+ };
+ handlers.zoomend = 0;
+
+ handlers.y0fov = 0;
+ handlers.fovdown = function(x, y) {
+ handlers.y0fov = y;
+ var activeSub = self.getObj(activeSubscene),
+ activeProjection = self.getObj(self.useid(activeSub.id, "projection")),
+ i, l = activeProjection.par3d.listeners;
+ for (i = 0; i < l.length; i++) {
+ activeSub = self.getObj(l[i]);
+ activeSub.fov0 = activeSub.par3d.FOV;
+ }
+ self.canvas.style.cursor = "zoom-in";
+ };
+ handlers.fovmove = function(x, y) {
+ var activeSub = self.getObj(activeSubscene),
+ activeProjection = self.getObj(self.useid(activeSub.id, "projection")),
+ i, l = activeProjection.par3d.listeners;
+ for (i = 0; i < l.length; i++) {
+ activeSub = self.getObj(l[i]);
+ activeSub.par3d.FOV = Math.max(1, Math.min(179, activeSub.fov0 +
+ 180*(y-handlers.y0fov)/self.canvas.height));
+ }
+ self.drawScene();
+ };
+ handlers.fovend = 0;
+
+ handlers.selectingdown = function(x, y) {
+ var viewport = self.getObj(activeSubscene).par3d.viewport,
+ width = viewport.width*self.canvas.width,
+ height = viewport.height*self.canvas.height,
+ p = {x: 2.0*x/width - 1.0, y: 2.0*y/height - 1.0};
+ self.select.region = {p1: p, p2: p};
+ if (self.select.subscene && self.select.subscene !== activeSubscene)
+ self.delFromSubscene(self.scene.brushId, self.select.subscene);
+ self.select.subscene = activeSubscene;
+ self.addToSubscene(self.scene.brushId, activeSubscene);
+ self.select.state = "changing";
+ if (typeof self.scene.brushId !== "undefined")
+ self.getObj(self.scene.brushId).initialized = false;
+ if (typeof self.scene.selectionInput !== "undefined")
+ self.recordSelection(activeSubscene);
+ self.drawScene();
+ self.canvas.style.cursor = "crosshair";
+ };
+
+ handlers.selectingmove = function(x, y) {
+ var viewport = self.getObj(activeSubscene).par3d.viewport,
+ width = viewport.width*self.canvas.width,
+ height = viewport.height*self.canvas.height;
+ if (self.select.state === "inactive")
+ return;
+ self.select.region.p2 = {x: 2.0*x/width - 1.0, y: 2.0*y/height - 1.0};
+ if (typeof self.scene.brushId !== "undefined")
+ self.getObj(self.scene.brushId).initialized = false;
+ if (typeof self.scene.selectionInput !== "undefined")
+ self.recordSelection(activeSubscene);
+ self.drawScene();
+ };
+
+ handlers.selectingend = 0;
+ /* jshint evil:true */
+ handlers.userdown = function(x, y) {
+ var sub = self.getObj(activeSubscene),
+ code = sub.callbacks[drag].begin;
+ if (code) {
+ var fn = Function('"use strict";return (' + code + ')')();
+ fn.call(self, x, y);
+ }
+ };
+
+ handlers.usermove = function(x, y) {
+ var sub = self.getObj(activeSubscene),
+ code = sub.callbacks[drag].update;
+ if (code) {
+ var fn = Function('"use strict";return (' + code + ')')();
+ fn.call(self, x, y);
+ }
+ };
+
+ handlers.userend = function() {
+ var sub = self.getObj(activeSubscene),
+ code = sub.callbacks[drag].end;
+ if (code) {
+ var fn = Function('"use strict";return (' + code + ')')();
+ fn.call(self);
+ }
+ };
+
+ self.canvas.onpointerdown = function ( ev ){
+ // pointers and mice differ in capture rules;
+ // act like a mouse.
+ if (ev.target.hasPointerCapture(ev.pointerId))
+ ev.target.releasePointerCapture(ev.pointerId);
+
+ if (!ev.which) // Use w3c defns in preference to MS
+ switch (ev.button) {
+ case 0: ev.which = 1; break;
+ case 1:
+ case 4: ev.which = 2; break;
+ case 2: ev.which = 3;
+ }
+ drag = ["none", "left", "middle", "right", "wheel"][ev.which];
+ var coords = self.relMouseCoords(ev);
+ coords.y = self.canvas.height-coords.y;
+ activeSubscene = self.whichSubscene(coords);
+ var sub = self.getObj(activeSubscene), f;
+ handler = sub.par3d.mouseMode[drag];
+ switch (handler) {
+ case "xAxis":
+ handler = "axis";
+ handlers.axis = [1.0, 0.0, 0.0];
+ break;
+ case "yAxis":
+ handler = "axis";
+ handlers.axis = [0.0, 1.0, 0.0];
+ break;
+ case "zAxis":
+ handler = "axis";
+ handlers.axis = [0.0, 0.0, 1.0];
+ break;
+ }
+ f = handlers[handler + "down"];
+ if (f) {
+ coords = self.translateCoords(activeSubscene, coords);
+ f.call(self, coords.x, coords.y);
+ ev.preventDefault();
+ } else
+ console.warn("Mouse handler '" + handler + "' is not implemented.");
+
+ };
+
+ self.canvas.onpointerup = function ( ev ){
+ if ( !drag ) return;
+ var f = handlers[handler + "end"];
+ if (f) {
+ f.call(self);
+ ev.preventDefault();
+ }
+ drag = 0;
+ handlers.onpointermove( ev );
+ };
+
+ self.canvas.onpointerout = self.canvas.onpointerup;
+
+ handlers.onpointermove = function ( ev ) {
+ var coords = self.relMouseCoords(ev), sub, f;
+ coords.y = self.canvas.height - coords.y;
+ if (ev.buttons === 0) {
+ activeSubscene = self.whichSubscene(coords);
+ drag = "none";
+ sub = self.getObj(activeSubscene);
+ handler = sub.par3d.mouseMode.none;
+ if (handler !== "none") {
+ if (sub.needsBegin) {
+ f = handlers[handler + "down"];
+ if (f) {
+ coords = self.translateCoords(activeSubscene, coords);
+ f.call(self, coords.x, coords.y);
+ }
+ sub.needsBegin = 0;
+ }
+ self.canvas.style.cursor = self.getCursor(sub.par3d.mouseMode.none);
+ } else {
+ self.canvas.style.cursor = self.getCursor(sub.par3d.mouseMode.left);
+ return;
+ }
+ }
+ f = handlers[handler + "move"];
+ if (f) {
+ coords = self.translateCoords(activeSubscene, coords);
+ f.call(self, coords.x, coords.y);
+ }
+ };
+
+
+ self.canvas.onpointerenter = function() {
+ self.canvas.addEventListener("pointermove", handlers.onpointermove);
+ };
+
+ self.canvas.onpointerleave = function() {
+ self.canvas.removeEventListener("pointermove",
+ handlers.onpointermove);
+ };
+
+ handlers.setZoom = function(ds) {
+ var i;
+ if (typeof activeSubscene === "undefined")
+ activeSubscene = self.scene.rootSubscene;
+ var activeSub = self.getObj(activeSubscene),
+ activeProjection = self.getObj(self.useid(activeSub.id, "projection")),
+ l = activeProjection.par3d.listeners;
+
+ for (i = 0; i < l.length; i++) {
+ activeSub = self.getObj(l[i]);
+ activeSub.par3d.zoom *= ds;
+ }
+ self.drawScene();
+ };
+
+ handlers.pushwheel = function(ev) {
+ ev.deltaY = -ev.deltaY;
+ handlers.pullwheel(ev);
+ };
+
+ handlers.pullwheel = function(ev) {
+ var del = 1.05;
+ if (ev.shiftKey) del = 1.005;
+ var ds = ev.deltaY < 0 ? del : (1 / del);
+ handlers.setZoom(ds);
+ };
+
+ handlers.user2wheel = function(ev) {
+ var sub = self.getObj(activeSubscene),
+ code = sub.callbacks.wheel.rotate;
+ if (code) {
+ var fn = Function('"use strict";return (' + code + ')')();
+ /* jshint evil:false */
+ fn.call(self, ev.deltaY < 0 ? 1 : 2);
+ }
+ };
+
+ handlers.wheelHandler = function(ev) {
+ var coords = self.relMouseCoords(ev);
+ coords.y = self.canvas.height - coords.y;
+ activeSubscene = self.whichSubscene(coords);
+ var sub = self.getObj(activeSubscene), f,
+ handler = sub.par3d.mouseMode.wheel,
+ evlocal;
+
+ ev.deltaY = ev.deltaY || ev.detail || ev.deltaX || ev.wheelDelta;
+
+ switch(handler) {
+ case "none": break;
+ case "push":
+ case "pull":
+ case "user2":
+ f = handlers[handler + "wheel"];
+ if (f) {
+ evlocal = {};
+ evlocal.deltaY = ev.deltaY;
+ evlocal.shiftKey = ev.shiftKey;
+ evlocal.preventDefault = function() { ev.preventDefault(); };
+ f.call(self, evlocal);
+ }
+ break;
+ default:
+ evlocal = {};
+ evlocal.preventDefault = function() { ev.preventDefault(); };
+ evlocal.which = 4;
+ evlocal.clientX = self.canvas.width/2;
+ evlocal.clientY = self.canvas.height/2;
+ self.canvas.onpointerdown(evlocal);
+ evlocal.clientX += ev.deltaX;
+ evlocal.clientY += ev.deltaY;
+ handlers.onpointermove(evlocal);
+ self.canvas.onpointerup(evlocal);
+ }
+ ev.preventDefault();
+ };
+
+ handlers.get_finger_dist = function(ev) {
+ var diffX = ev.touches[0].clientX - ev.touches[1].clientX,
+ diffY = ev.touches[0].clientY - ev.touches[1].clientY;
+ return Math.sqrt(diffX * diffX + diffY * diffY);
+ };
+
+ handlers.touchstart = function(ev) {
+ var touch = ev.touches[0],
+ mouseEvent = new MouseEvent("pointerdown",
+ {
+ clientX: touch.clientX,
+ clientY: touch.clientY
+ });
+ ev.preventDefault();
+ if (ev.touches.length === 2) {
+ var coords = self.relMouseCoords(touch);
+ coords.y = self.canvas.height-coords.y;
+ activeSubscene = self.whichSubscene(coords);
+ handlers.finger_dist0 = handlers.get_finger_dist(ev);
+ handlers.zoomdown(coords.x, coords.y);
+ }
+ self.dispatchEvent(mouseEvent);
+ };
+
+ handlers.touchend = function(ev) {
+ var mouseEvent;
+ ev.preventDefault();
+ if (ev.touches.length === 1) {
+ mouseEvent = new MouseEvent("pointerup", {});
+ self.dispatchEvent(mouseEvent);
+ }
+ };
+
+ handlers.touchmove = function(ev) {
+ var touch = ev.touches[0],
+ mouseEvent;
+ ev.preventDefault();
+ if (ev.touches.length > 1) {
+ var coords = self.relMouseCoords(touch),
+ new_dist = handlers.get_finger_dist(ev);
+ coords.y = self.canvas.height*Math.log(handlers.finger_dist0/new_dist) + handlers.y0zoom;
+ handlers.zoommove(coords.x, coords.y);
+ } else {
+ mouseEvent = new MouseEvent("pointermove",
+ {
+ clientX: touch.clientX,
+ clientY: touch.clientY
+ });
+ self.dispatchEvent(mouseEvent);
+ }
+ };
+
+ self.canvas.addEventListener("DOMMouseScroll", handlers.wheelHandler, false);
+ self.canvas.addEventListener("mousewheel", handlers.wheelHandler, false);
+ self.canvas.addEventListener("touchstart", handlers.touchstart, {passive: false});
+ self.canvas.addEventListener("touchend", handlers.touchend, {passive: false});
+ self.canvas.addEventListener("touchmove", handlers.touchmove, {passive: false});
+ };
diff --git a/reference/libs/rglwidgetClass-1.3.1/pieces.src.js b/reference/libs/rglwidgetClass-1.3.1/pieces.src.js
new file mode 100644
index 0000000..2ac8c91
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/pieces.src.js
@@ -0,0 +1,135 @@
+/**
+ * Methods related to drawing transparent objects
+ * @name ___METHODS_FOR_TRANSPARENCY___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+
+ * These functions order the centers of displayed objects so they
+ * can be drawn using the painters algorithm, necessary to support
+ * transparency.
+
+ * Note that objid is not obj.id when drawing spheres.
+ */
+
+/**
+ * Break objects into pieces
+ * @returns { array } Array of pieces
+ */
+ rglwidgetClass.prototype.getPieces = function(context, objid, subid, obj) {
+ var n = obj.centers.length,
+ depth,
+ result = new Array(n),
+ z, w, i;
+ context = context.slice();
+
+ for(i=0; i 0) {
+ var i,
+ thiscontext = pieces[0].context,
+ thisobjid = pieces[0].objid,
+ thissubid = pieces[0].subid,
+ indices = [];
+ for (i= 0; i < pieces.length; i++) {
+ if (pieces[i].context !== thiscontext ||
+ pieces[i].objid !== thisobjid ||
+ pieces[i].subid !== thissubid) {
+ result.push({context: thiscontext, objid: thisobjid,
+ subid: thissubid, indices: indices});
+ thiscontext = pieces[i].context;
+ thisobjid = pieces[i].objid;
+ thissubid = pieces[i].subid;
+ indices = [];
+ }
+ indices.push(pieces[i].index);
+ }
+ result.push({context: thiscontext, objid: thisobjid,
+ subid: thissubid,
+ indices: indices});
+ }
+ return result;
+ };
+
+ /**
+ * Sort pieces by depth
+ * @returns { array }
+ * @param { array } pieces - array of pieces
+ */
+ rglwidgetClass.prototype.sortPieces = function(pieces) {
+ var compare = function(i,j) {
+ var diff = j.depth - i.depth;
+ // We want to avoid context or obj changes,
+ // so sort on those next.
+ if (diff === 0) {
+ var c1 = j.context.slice(),
+ c2 = i.context.slice();
+ diff = c1.length - c2.length;
+ while (diff === 0 && c1.length > 0) {
+ diff = c1.pop() - c2.pop();
+ }
+ if (diff === 0)
+ diff = j.objid - i.objid;
+ if (diff === 0)
+ diff = j.subid - i.subid;
+ }
+ return diff;
+ }, result = [];
+ if (pieces.length)
+ result = pieces.sort(compare);
+ return result;
+ };
diff --git a/reference/libs/rglwidgetClass-1.3.1/pretty.src.js b/reference/libs/rglwidgetClass-1.3.1/pretty.src.js
new file mode 100644
index 0000000..5f9145a
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/pretty.src.js
@@ -0,0 +1,163 @@
+/**
+ * Pretty function from R
+ * @name ___PRETTY_FROM_R___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+ */
+
+
+/* This file is translated from pretty.c, which was
+ taken from the R sources, r61744 of src/appl/pretty.c,
+ with minimal changes */
+
+/*
+ * R : A Computer Language for Statistical Data Analysis
+ * Copyright (C) 1995-2012 The R Core Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, a copy is available at
+ * http://www.r-project.org/Licenses/
+ */
+
+/**
+ * Construct pretty values to cover an interval
+ * @param { number } lo - lower end of interval
+ * @param { number } up - upper end of interval
+ * @param { number } ndiv - requested number of divisions
+ * @param { number } min_n - minimum divisions
+ * @param { number } shrink_sml - if too many cells, amount to shrink by
+ * @param { number } high_u_fact - bias in favour of larger units
+ * @param { number } eps_correction - correction to bounds
+ * @param { Boolean } return_bounds - whether to return bounds
+ * @description
+ * Pretty Intervals
+
+ * Constructs m "pretty" values which cover the given interval *lo <= *up
+ * m ~= *ndiv + 1 (i.e., ndiv := approximate number of INTERVALS)
+ *
+ * It is not quite clear what should happen for *lo = *up;
+ * S itself behaves quite funilly, then.
+ *
+ * In my opinion, a proper 'pretty' should always ensure
+ * *lo < *up, and hence *ndiv >=1 in the result.
+ * However, in S and here, we allow *lo == *up, and *ndiv = 0.
+ * Note however, that we are NOT COMPATIBLE to S. [Martin M.]
+ *
+ * NEW (0.63.2): ns, nu are double (==> no danger of integer overflow)
+ *
+ * We determine
+ * if the interval (up - lo) is ``small'' [<==> i_small == TRUE, below].
+ * For the ``i_small'' situation, there is a parameter shrink_sml,
+ * the factor by which the "scale" is shrunk. ~~~~~~~~~~
+ * It is advisable to set it to some (smaller) integer power of 2,
+ * since this enables exact floating point division.
+ */
+rglwidgetClass.prototype.R_pretty = function(
+ lo, up, ndiv, min_n, shrink_sml, high_u_fact,
+ eps_correction, return_bounds) {
+ /* From version 0.65 on, we had rounding_eps := 1e-5, before, r..eps = 0
+ * 1e-7 is consistent with seq.default() */
+ var rounding_eps = 1e-7, h = high_u_fact[0],
+ h5 = high_u_fact[1],
+ dx, cell, unit, base, U, ns, nu, k, i_small,
+ DBL_EPSILON = Number.EPSILON,
+ DBL_MIN = Number.MIN_VALUE,
+ DBL_MAX = Number.MAX_VALUE;
+
+ dx = up - lo;
+ /* cell := "scale" here */
+ if (dx === 0 && up === 0) { /* up == lo == 0 */
+ cell = 1;
+ i_small = true;
+ } else {
+ cell = Math.max(Math.abs(lo), Math.abs(up));
+ /* U = upper bound on cell/unit */
+ U = (1 + (h5 >= 1.5*h+0.5)) ? 1/(1+h) : 1.5/(1+h5);
+ /* added times 3, as several calculations here */
+ i_small = dx < cell * U * Math.max(1,ndiv) * DBL_EPSILON *3;
+ }
+
+ /*OLD: cell = FLT_EPSILON+ dx / *ndiv; FLT_EPSILON = 1.192e-07 */
+ if(i_small) {
+ if(cell > 10)
+ cell = 9 + cell/10;
+ cell *= shrink_sml;
+ if(min_n > 1) cell /= min_n;
+ } else {
+ cell = dx;
+ if(ndiv > 1) cell /= ndiv;
+ }
+
+ if(cell < 20*DBL_MIN) {
+ /* warning(_("Internal(pretty()): very small range.. corrected")); */
+ cell = 20*DBL_MIN;
+ } else if(cell * 10 > DBL_MAX) {
+ /* warning(_("Internal(pretty()): very large range.. corrected")); */
+ cell = 0.1*DBL_MAX;
+ }
+ base = Math.pow(10, Math.floor(Math.log10(cell))); /* base <= cell < 10*base */
+
+ /* unit : from { 1,2,5,10 } * base
+ * such that |u - cell| is small,
+ * favoring larger (if h > 1, else smaller) u values;
+ * favor '5' more than '2' if h5 > h (default h5 = .5 + 1.5 h) */
+ unit = base;
+ if((U = 2*base)-cell < h*(cell-unit)) { unit = U;
+ if((U = 5*base)-cell < h5*(cell-unit)) { unit = U;
+ if((U =10*base)-cell < h*(cell-unit)) unit = U; }}
+ /* Result: c := cell, u := unit, b := base
+ * c in [ 1, (2+ h) /(1+h) ] b ==> u= b
+ * c in ( (2+ h)/(1+h), (5+2h5)/(1+h5)] b ==> u= 2b
+ * c in ( (5+2h)/(1+h), (10+5h) /(1+h) ] b ==> u= 5b
+ * c in ((10+5h)/(1+h), 10 ) b ==> u=10b
+ *
+ * ===> 2/5 *(2+h)/(1+h) <= c/u <= (2+h)/(1+h) */
+
+ ns = Math.floor(lo/unit+rounding_eps);
+ nu = Math.ceil (up/unit-rounding_eps);
+
+ if(eps_correction && (eps_correction > 1 || !i_small)) {
+ if(lo !== 0.0) lo *= (1- DBL_EPSILON); else lo = -DBL_MIN;
+ if(up !== 0.0) up *= (1+ DBL_EPSILON); else up = +DBL_MIN;
+ }
+
+ while(ns*unit > lo + rounding_eps*unit) ns--;
+
+ while(nu*unit < up - rounding_eps*unit) nu++;
+
+ k = Math.floor(0.5 + nu - ns);
+ if(k < min_n) {
+ /* ensure that nu - ns == min_n */
+
+ k = min_n - k;
+ if(ns >= 0) {
+ nu += k/2;
+ ns -= k/2 + k%2;/* ==> nu-ns = old(nu-ns) + min_n -k = min_n */
+ } else {
+ ns -= k/2;
+ nu += k/2 + k%2;
+ }
+ ndiv = min_n;
+ } else {
+ ndiv = k;
+ }
+ if(return_bounds) { /* if()'s to ensure that result covers original range */
+ if(ns * unit < lo) lo = ns * unit;
+ if(nu * unit > up) up = nu * unit;
+ } else {
+ lo = ns;
+ up = nu;
+ }
+ return {lo:lo, up:up, ndiv:ndiv, unit:unit};
+};
diff --git a/reference/libs/rglwidgetClass-1.3.1/projection.src.js b/reference/libs/rglwidgetClass-1.3.1/projection.src.js
new file mode 100644
index 0000000..4fc59c7
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/projection.src.js
@@ -0,0 +1,148 @@
+ /**
+ * Methods related to projections
+ * @name ___METHODS_FOR_PROJECTIONS___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+ */
+
+ /**
+ * Get the viewport
+ */
+
+ rglwidgetClass.prototype.getViewport = function(id) {
+ var vp = this.getObj(id).par3d.viewport,
+ x = vp.x*this.canvas.width,
+ y = vp.y*this.canvas.height,
+ width = vp.width*this.canvas.width,
+ height = vp.height*this.canvas.height;
+ this.vp = {x:x, y:y, width:width, height:height};
+ };
+
+ /**
+ * Set the gl viewport and scissor test
+ * @param { number } id - id of subscene
+ */
+ rglwidgetClass.prototype.setViewport = function(id) {
+ var gl = this.gl || this.initGL();
+ this.getViewport(id);
+ gl.viewport(this.vp.x, this.vp.y, this.vp.width, this.vp.height);
+ gl.scissor(this.vp.x, this.vp.y, this.vp.width, this.vp.height);
+ gl.enable(gl.SCISSOR_TEST);
+ };
+
+ /**
+ * Set the projection matrix for a subscene
+ * @param { number } id - id of subscene
+ */
+ rglwidgetClass.prototype.setprMatrix = function(id) {
+ var subscene = this.getObj(id),
+ embedding = subscene.embeddings.projection;
+ if (embedding === "replace")
+ this.prMatrix.makeIdentity();
+ else
+ this.setprMatrix(subscene.parent);
+ if (embedding === "inherit")
+ return;
+ // This is based on the Frustum::enclose code from geom.cpp
+ var bbox = subscene.par3d.bbox,
+ scale = subscene.par3d.scale,
+ ranges = [(bbox[1]-bbox[0])*scale[0]/2,
+ (bbox[3]-bbox[2])*scale[1]/2,
+ (bbox[5]-bbox[4])*scale[2]/2],
+ radius = Math.sqrt(this.sumsq(ranges))*1.1; // A bit bigger to handle labels
+ if (radius <= 0) radius = 1;
+ var observer = subscene.par3d.observer,
+ distance = observer[2],
+ FOV = subscene.par3d.FOV, ortho = FOV === 0,
+ t = ortho ? 1 : Math.tan(FOV*Math.PI/360),
+ near = distance - radius,
+ far = distance + radius,
+ hlen,
+ aspect = this.vp.width/this.vp.height,
+ z = subscene.par3d.zoom,
+ userProjection = subscene.par3d.userProjection;
+ if (far < 0.0)
+ far = 1.0;
+ if (near < far/100.0)
+ near = far/100.0;
+ this.frustum = {near:near, far:far};
+ hlen = t*near;
+ if (ortho) {
+ if (aspect > 1)
+ this.prMatrix.ortho(-hlen*aspect*z, hlen*aspect*z,
+ -hlen*z, hlen*z, near, far);
+ else
+ this.prMatrix.ortho(-hlen*z, hlen*z,
+ -hlen*z/aspect, hlen*z/aspect,
+ near, far);
+ } else {
+ if (aspect > 1)
+ this.prMatrix.frustum(-hlen*aspect*z, hlen*aspect*z,
+ -hlen*z, hlen*z, near, far);
+ else
+ this.prMatrix.frustum(-hlen*z, hlen*z,
+ -hlen*z/aspect, hlen*z/aspect,
+ near, far);
+ }
+ this.prMatrix.multRight(userProjection);
+ };
+
+ /**
+ * Set the model-view matrix for a subscene
+ * @param { number } id - id of the subscene
+ */
+ rglwidgetClass.prototype.setmvMatrix = function(id) {
+ var observer = this.getObj(id).par3d.observer;
+ this.mvMatrix.makeIdentity();
+ this.setmodelMatrix(id);
+ this.mvMatrix.translate(-observer[0], -observer[1], -observer[2]);
+
+ };
+
+ /**
+ * Set the model matrix for a subscene
+ * @param { number } id - id of the subscene
+ */
+ rglwidgetClass.prototype.setmodelMatrix = function(id) {
+ var subscene = this.getObj(id),
+ embedding = subscene.embeddings.model;
+ if (embedding === "replace") {
+ var bbox = subscene.par3d.bbox,
+ center = [(bbox[0]+bbox[1])/2,
+ (bbox[2]+bbox[3])/2,
+ (bbox[4]+bbox[5])/2];
+ this.mvMatrix.translate(-center[0], -center[1], -center[2]);
+ }
+ if (embedding !== "inherit") {
+ var scale = subscene.par3d.scale;
+ this.mvMatrix.scale(scale[0], scale[1], scale[2]);
+ this.mvMatrix.multRight( subscene.par3d.userMatrix );
+ }
+ if (embedding !== "replace")
+ this.setmodelMatrix(subscene.parent);
+ };
+
+ /**
+ * Set the normals matrix for a subscene
+ * @param { number } subsceneid - id of the subscene
+ */
+ rglwidgetClass.prototype.setnormMatrix2 = function() {
+ this.normMatrix = new CanvasMatrix4(this.mvMatrix);
+ this.normMatrix.invert();
+ this.normMatrix.transpose();
+ };
+
+ /**
+ * Set the combined projection-model-view matrix
+ */
+ rglwidgetClass.prototype.setprmvMatrix = function() {
+ this.prmvMatrix = new CanvasMatrix4( this.mvMatrix );
+ this.prmvMatrix.multRight( this.prMatrix );
+ };
+
+ rglwidgetClass.prototype.setInvPrMatrix = function() {
+ this.invPrMatrix = new CanvasMatrix4( this.prMatrix );
+ this.invPrMatrix.invert();
+ this.invPrMatrix.transpose();
+ };
diff --git a/reference/libs/rglwidgetClass-1.3.1/rgl.css b/reference/libs/rglwidgetClass-1.3.1/rgl.css
new file mode 100644
index 0000000..b22aaf6
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/rgl.css
@@ -0,0 +1,21 @@
+.rglPlayer {
+ width: auto;
+ height: auto;
+}
+
+.rglPlayer .rgl-button {
+ width: auto;
+ display: inline-block;
+ font-size: 75%;
+}
+
+.rglPlayer .rgl-slider {
+ display: inline-block;
+ width: 30%;
+}
+
+.rglPlayer .rgl-label {
+ display: inline;
+ padding-left: 6px;
+ padding-right: 6px;
+}
diff --git a/reference/libs/rglwidgetClass-1.3.1/rglClass.src.js b/reference/libs/rglwidgetClass-1.3.1/rglClass.src.js
new file mode 100644
index 0000000..475300e
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/rglClass.src.js
@@ -0,0 +1,71 @@
+//// To generate the help pages for this library, use
+
+// jsdoc --template /usr/local/lib/node_modules/foodoc/template *.src.js -R README.md -c JSDoc.json
+
+// To test, set environment variable RGL_DEBUGGING=true
+// before building.
+
+/* globals rglwidgetClass: true */
+
+/**
+ * The class of an rgl widget
+ * @class
+*/
+rglwidgetClass = function() {
+ this.canvas = null;
+ this.userMatrix = new CanvasMatrix4();
+ this.types = [];
+ this.prMatrix = new CanvasMatrix4();
+ this.mvMatrix = new CanvasMatrix4();
+ this.vp = null;
+ this.prmvMatrix = null;
+ this.origs = null;
+ this.gl = null;
+ this.scene = null;
+ this.select = {state: "inactive", subscene: null, region: {p1: {x:0, y:0}, p2: {x:0, y:0}}};
+ this.drawing = false;
+};
+
+ rglwidgetClass.f_is_lit = 1;
+ rglwidgetClass.f_is_smooth = 2;
+ rglwidgetClass.f_has_texture = 4;
+ rglwidgetClass.f_depth_sort = 8;
+ rglwidgetClass.f_fixed_quads = 16;
+ rglwidgetClass.f_is_transparent = 32;
+ rglwidgetClass.f_is_lines = 64;
+ rglwidgetClass.f_sprites_3d = 128;
+ rglwidgetClass.f_is_subscene = 256;
+ rglwidgetClass.f_is_clipplanes = 512;
+ rglwidgetClass.f_fixed_size = 1024;
+ rglwidgetClass.f_is_points = 2048;
+ rglwidgetClass.f_is_twosided = 4096;
+ rglwidgetClass.f_fat_lines = 8192;
+ rglwidgetClass.f_is_brush = 16384;
+ rglwidgetClass.f_has_fog = 32768;
+ rglwidgetClass.f_rotating = 65536;
+
+ rglwidgetClass.prototype.fogNone = 0;
+ rglwidgetClass.prototype.fogLinear = 1;
+ rglwidgetClass.prototype.fogExp = 2;
+ rglwidgetClass.prototype.fogExp2 = 3;
+
+ /**
+ * Methods related to obsolete approaches.
+ * @name ___OBSOLETE_METHODS___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+ */
+
+ /**
+ * Start the writeWebGL scene. This is only used by writeWebGL; rglwidget has
+ no debug element.
+ */
+ rglwidgetClass.prototype.start = function() {
+ if (typeof this.prefix !== "undefined") {
+ this.debugelement = document.getElementById(this.prefix + "debug");
+ this.debug("");
+ }
+ this.drag = 0;
+ this.drawScene();
+ };
diff --git a/reference/libs/rglwidgetClass-1.3.1/rglTimer.src.js b/reference/libs/rglwidgetClass-1.3.1/rglTimer.src.js
new file mode 100644
index 0000000..1091556
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/rglTimer.src.js
@@ -0,0 +1,155 @@
+
+/* globals rgltimerClass: true */
+
+/**
+ * The class of an rgl timer object
+ * @class
+*/
+
+/**
+ * Construct an rgltimerClass object
+ * @constructor
+ * @param { function } Tick - action when timer fires
+ * @param { number } startTime - nominal start time in seconds
+ * @param { number } interval - seconds between updates
+ * @param { number } stopTime - nominal stop time in seconds
+ * @param { number } stepSize - nominal step size
+ * @param { number } value - current nominal time
+ * @param { number } rate - nominal units per second
+ * @param { string } loop - "none", "cycle" or "oscillate"
+ * @param { Object } actions - list of actions
+ */
+rgltimerClass = function(Tick, startTime, interval, stopTime, stepSize, value, rate, loop, actions) {
+ this.enabled = false;
+ this.timerId = 0;
+ /** nominal start time in seconds */
+ this.startTime = startTime;
+ /** current nominal time */
+ this.value = value;
+ /** seconds between updates */
+ this.interval = interval;
+ /** nominal stop time */
+ this.stopTime = stopTime;
+ /** nominal step size */
+ this.stepSize = stepSize;
+ /** nominal units per second */
+ this.rate = rate;
+ /** "none", "cycle", or "oscillate" */
+ this.loop = loop;
+ /** real world start time */
+ this.realStart = undefined;
+ /** multiplier for fast-forward or reverse */
+ this.multiplier = 1;
+ this.actions = actions;
+ this.Tick = Tick;
+};
+
+
+ /**
+ * Methods related to players
+ * @name ___METHODS_FOR_PLAYERS___
+ * @memberof rgltimerClass
+ * @kind function
+ * @instance
+ */
+
+ /**
+ * Start playing
+ * @memberof rgltimerClass
+ */
+ rgltimerClass.prototype.play = function() {
+ if (this.enabled) {
+ this.enabled = false;
+ window.clearInterval(this.timerId);
+ this.timerId = 0;
+ return;
+ }
+ var tick = function(self) {
+ var now = new Date();
+ self.value = self.multiplier*self.rate*(now - self.realStart)/1000 + self.startTime;
+ self.forceToRange();
+ if (typeof self.Tick !== "undefined") {
+ self.Tick(self.value);
+ }
+
+ };
+ this.realStart = new Date() - 1000*(this.value - this.startTime)/this.rate/this.multiplier;
+ this.timerId = window.setInterval(tick, 1000*this.interval, this);
+ this.enabled = true;
+ };
+
+ /**
+ * Force value into legal range
+ */
+ rgltimerClass.prototype.forceToRange = function() {
+ if (this.value > this.stopTime + this.stepSize/2 || this.value < this.startTime - this.stepSize/2) {
+ if (!this.loop) {
+ this.reset();
+ } else {
+ var cycle = this.stopTime - this.startTime + this.stepSize,
+ newval = (this.value - this.startTime) % cycle + this.startTime;
+ if (newval < this.startTime) {
+ newval += cycle;
+ }
+ this.realStart += (this.value - newval)*1000/this.multiplier/this.rate;
+ this.value = newval;
+ }
+ }
+ };
+
+ /**
+ * Reset to start values
+ */
+ rgltimerClass.prototype.reset = function() {
+ this.value = this.startTime;
+ this.newmultiplier(1);
+ if (typeof this.Tick !== "undefined") {
+ this.Tick(this.value);
+ }
+ if (this.enabled)
+ this.play(); /* really pause... */
+ if (typeof this.PlayButton !== "undefined")
+ this.PlayButton.value = "Play";
+ };
+
+ /**
+ * Increase the multiplier to play faster
+ */
+ rgltimerClass.prototype.faster = function() {
+ this.newmultiplier(Math.SQRT2*this.multiplier);
+ };
+
+ /**
+ * Decrease the multiplier to play slower
+ */
+ rgltimerClass.prototype.slower = function() {
+ this.newmultiplier(this.multiplier/Math.SQRT2);
+ };
+
+ /**
+ * Change sign of multiplier to reverse direction
+ */
+ rgltimerClass.prototype.reverse = function() {
+ this.newmultiplier(-this.multiplier);
+ };
+
+ /**
+ * Set multiplier for play speed
+ * @param { number } newmult - new value
+ */
+ rgltimerClass.prototype.newmultiplier = function(newmult) {
+ if (newmult !== this.multiplier) {
+ this.realStart += 1000*(this.value - this.startTime)/this.rate*(1/this.multiplier - 1/newmult);
+ this.multiplier = newmult;
+ }
+ };
+
+ /**
+ * Take one step
+ */
+ rgltimerClass.prototype.step = function() {
+ this.value += this.rate*this.multiplier;
+ this.forceToRange();
+ if (typeof this.Tick !== "undefined")
+ this.Tick(this.value);
+ };
diff --git a/reference/libs/rglwidgetClass-1.3.1/selection.src.js b/reference/libs/rglwidgetClass-1.3.1/selection.src.js
new file mode 100644
index 0000000..a45e09e
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/selection.src.js
@@ -0,0 +1,129 @@
+ /**
+ * Methods related to selection
+ * @name ___METHODS_FOR_SELECTION___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+ */
+
+ /**
+ * Respond to brush change
+ */
+ rglwidgetClass.prototype.selectionChanged = function() {
+ var i, j, k, id, subid = this.select.subscene, subscene,
+ objids, obj,
+ p1 = this.select.region.p1, p2 = this.select.region.p2,
+ filter, selection = [], handle, keys, xmin, x, xmax, ymin, y, ymax, z, v,
+ someHidden;
+ if (!subid)
+ return;
+ subscene = this.getObj(subid);
+ objids = subscene.objects;
+ filter = this.scene.crosstalk.filter;
+ this.setmvMatrix(subid);
+ this.setprMatrix(subid);
+ this.setprmvMatrix();
+ xmin = Math.min(p1.x, p2.x);
+ xmax = Math.max(p1.x, p2.x);
+ ymin = Math.min(p1.y, p2.y);
+ ymax = Math.max(p1.y, p2.y);
+ for (i = 0; i < objids.length; i++) {
+ id = objids[i];
+ j = this.scene.crosstalk.id.indexOf(id);
+ if (j >= 0) {
+ keys = this.scene.crosstalk.key[j];
+ obj = this.getObj(id);
+ someHidden = false;
+ for (k = 0; k < keys.length; k++) {
+ if (filter && filter.indexOf(keys[k]) < 0) {
+ someHidden = true;
+ continue;
+ }
+ v = [].concat(obj.vertices[k]).concat(1.0);
+ v = rglwidgetClass.multVM(v, this.prmvMatrix);
+ x = v[0]/v[3];
+ y = v[1]/v[3];
+ z = v[2]/v[3];
+ if (xmin <= x && x <= xmax && ymin <= y && y <= ymax && -1.0 <= z && z <= 1.0) {
+ selection.push(keys[k]);
+ } else
+ someHidden = true;
+ }
+ obj.someHidden = someHidden && (filter || selection.length);
+ obj.initialized = false;
+ /* Who should we notify? Only shared data in the current subscene, or everyone? */
+ if (!this.equalArrays(selection, this.scene.crosstalk.selection)) {
+ handle = this.scene.crosstalk.sel_handle[j];
+ handle.set(selection, {rglSubsceneId: this.select.subscene});
+ }
+ }
+ }
+ };
+
+ /**
+ * Respond to selection or filter change from crosstalk
+ * @param { Object } event - crosstalk event
+ * @param { boolean } filter - filter or selection?
+ */
+ rglwidgetClass.prototype.selection = function(event, filter) {
+ var i, j, ids, obj, keys, crosstalk = this.scene.crosstalk,
+ selection, someHidden;
+
+ // Record the message and find out if this event makes some objects have mixed values:
+
+ crosstalk = this.scene.crosstalk;
+
+ if (filter) {
+ filter = crosstalk.filter = event.value;
+ selection = crosstalk.selection;
+ } else {
+ selection = crosstalk.selection = event.value;
+ filter = crosstalk.filter;
+ }
+ ids = crosstalk.id;
+ for (i = 0; i < ids.length ; i++) {
+ obj = this.getObj(ids[i]);
+ obj.initialized = false;
+ keys = crosstalk.key[i];
+ someHidden = false;
+ for (j = 0; j < keys.length && !someHidden; j++) {
+ if ((filter && filter.indexOf(keys[j]) < 0) ||
+ (selection.length && selection.indexOf(keys[j]) < 0))
+ someHidden = true;
+ }
+ obj.someHidden = someHidden;
+ }
+ this.drawScene();
+ };
+
+ /**
+ * Clear the selection brush
+ * @param { number } except - Subscene that should ignore this request
+ */
+ rglwidgetClass.prototype.clearBrush = function(except) {
+ if (this.select.subscene !== except) {
+ this.select.region = {p1: {x:Infinity, y:Infinity},
+ p2: {x:Infinity, y:Infinity}};
+ this.selectionChanged();
+ this.select.state = "inactive";
+ this.delFromSubscene(this.scene.brushId, this.select.subscene);
+ }
+ this.drawScene();
+ };
+
+ /**
+ * Set the vertices in the selection box object
+ */
+ rglwidgetClass.prototype.initSelection = function(id) {
+ if (typeof this.select.region === "undefined")
+ return;
+ var obj = this.getObj(id),
+ p1 = this.select.region.p1,
+ p2 = this.select.region.p2;
+
+ obj.vertices = [[p1.x, p1.y, 0.0],
+ [p2.x, p1.y, 0.0],
+ [p2.x, p2.y, 0.0],
+ [p1.x, p2.y, 0.0],
+ [p1.x, p1.y, 0.0]];
+ };
diff --git a/reference/libs/rglwidgetClass-1.3.1/shaders.src.js b/reference/libs/rglwidgetClass-1.3.1/shaders.src.js
new file mode 100644
index 0000000..e646678
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/shaders.src.js
@@ -0,0 +1,183 @@
+ /**
+ * Methods related to shaders
+ * @name ___METHODS_FOR_SHADERS___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+ */
+
+ /**
+ * Get flags that will end up as shader defines.
+ * Static method so it can be called from R
+ */
+ rglwidgetClass.getDefFlags = function(flags, type, normals, round_points) {
+ var f = {};
+ f.fat_lines = rglwidgetClass.isSet(flags, rglwidgetClass.f_fat_lines);
+ f.fixed_quads = rglwidgetClass.isSet(flags, rglwidgetClass.f_fixed_quads);
+ f.fixed_size = rglwidgetClass.isSet(flags, rglwidgetClass.f_fixed_size);
+ f.has_fog = rglwidgetClass.isSet(flags, rglwidgetClass.f_has_fog);
+ f.has_normals = (typeof normals !== "undefined") ||
+ type === "spheres";
+ f.has_texture = rglwidgetClass.isSet(flags, rglwidgetClass.f_has_texture);
+ f.is_brush = rglwidgetClass.isSet(flags, rglwidgetClass.f_is_brush);
+ f.is_lines = rglwidgetClass.isSet(flags, rglwidgetClass.f_is_lines);
+ f.is_lit = rglwidgetClass.isSet(flags, rglwidgetClass.f_is_lit);
+ f.is_points = rglwidgetClass.isSet(flags, rglwidgetClass.f_is_points);
+ f.is_transparent = rglwidgetClass.isSet(flags, rglwidgetClass.f_is_transparent);
+ f.is_twosided = rglwidgetClass.isSet(flags, rglwidgetClass.f_is_twosided);
+ f.needs_vnormal = !rglwidgetClass.isSet(flags, rglwidgetClass.f_sprites_3d) &&
+ (f.is_lit && !f.fixed_quads && !f.is_brush) || (f.is_twosided && f.has_normals);
+ f.rotating = rglwidgetClass.isSet(flags, rglwidgetClass.f_rotating);
+ f.round_points = round_points;
+ return f;
+ };
+
+
+ /**
+ * Generate the defines for the shader code for an object.
+ *
+ * This is a static method so it can be called from R.
+ *
+ * @returns {string}
+ * @param id - id of object
+ * @param type - type of object
+ * @param flags - object flags
+ * @param nclipplanes - number of clipping planes in scene
+ * (may not all be active)
+ * @param nlights - number of lights in scene (ditto)
+ * @param normals - normals for object
+ * @param pointSize - point size for object
+ * @param textype - texture type for object
+ * @param antialias - use antialiasing?
+ */
+ rglwidgetClass.getDefines = function(id, type, flags,
+ nclipplanes, nlights, normals, pointSize, textype,
+ texmode, texenvmap, antialias, fl) {
+ var
+ title, defines;
+
+ if (typeof fl === "undefined")
+ fl = rglwidgetClass.getDefFlags(flags, type, normals, antialias);
+
+ title = " /* ****** "+type+" object "+id+" shader ****** */\n";
+
+ defines = "#define NCLIPPLANES " + nclipplanes + "\n"+
+ "#define NLIGHTS " + nlights + "\n";
+
+ if (fl.fat_lines)
+ defines = defines + "#define FAT_LINES 1\n";
+
+ if (fl.fixed_quads)
+ defines = defines + "#define FIXED_QUADS 1\n";
+
+ if (fl.fixed_size)
+ defines = defines + "#define FIXED_SIZE 1\n";
+
+ if (fl.has_fog)
+ defines = defines + "#define HAS_FOG 1\n";
+
+ if (fl.has_normals)
+ defines = defines + "#define HAS_NORMALS 1\n";
+
+ if (fl.has_texture) {
+ defines = defines + "#define HAS_TEXTURE 1\n";
+ defines = defines + "#define TEXTURE_" + textype + "\n";
+ defines = defines + "#define TEXMODE_" + texmode + "\n";
+ if (texenvmap)
+ defines = defines + "#define USE_ENVMAP 1\n";
+ }
+
+ if (fl.is_brush)
+ defines = defines + "#define IS_BRUSH 1\n";
+
+ if (type === "linestrip")
+ defines = defines + "#define IS_LINESTRIP 1\n";
+
+ if (fl.is_lit)
+ defines = defines + "#define IS_LIT 1\n";
+
+ if (fl.is_points) {
+ defines = defines + "#define IS_POINTS 1\n";
+ defines = defines + "#define POINTSIZE " + Number.parseFloat(pointSize).toFixed(1) + "\n";
+ }
+
+ if (type === "sprites")
+ defines = defines + "#define IS_SPRITES 1\n";
+
+ if (type === "text")
+ defines = defines + "#define IS_TEXT 1\n";
+
+ if (fl.is_transparent)
+ defines = defines + "#define IS_TRANSPARENT 1\n";
+
+ if (fl.is_twosided)
+ defines = defines + "#define IS_TWOSIDED 1\n";
+
+ if (fl.needs_vnormal)
+ defines = defines + "#define NEEDS_VNORMAL 1\n";
+
+ if (fl.rotating)
+ defines = defines + "#define ROTATING 1\n";
+
+ if (fl.round_points)
+ defines = defines + "#define ROUND_POINTS 1\n";
+
+ // console.log(result);
+ return title + defines;
+ };
+
+ /**
+ * Create code for vertex and fragment shaders
+ * @returns {Object}
+ * @param { number } shaderType - gl code for shader type
+ * @param { string } code - code for the shader
+ */
+ rglwidgetClass.prototype.getShaders = function(obj) {
+ var header,
+ vertex = obj.userVertexShader,
+ fragment = obj.userFragmentShader;
+
+ header = rglwidgetClass.getDefines(
+ obj.id, obj.type, obj.flags,
+ this.countClipplanes(), this.countLights(),
+ obj.normals,
+ this.getMaterial(obj, "size"),
+ this.getMaterial(obj, "textype"),
+ this.getMaterial(obj, "texmode"),
+ this.getMaterial(obj, "texenvmap"),
+ this.getMaterial(obj, "point_antialias"),
+ obj.defFlags
+ );
+
+ if (typeof vertex === "undefined")
+ vertex = rglwidgetClass.rgl_vertex_shader();
+
+ if (typeof fragment === "undefined")
+ fragment = rglwidgetClass.rgl_fragment_shader();
+
+// console.log("vertex:");
+// console.log(header + vertex);
+// console.log("fragment:");
+// console.log(header + fragment);
+
+ return {vertex: header + vertex,
+ fragment: header + fragment};
+ };
+
+
+ /**
+ * Call gl functions to create and compile shader from code
+ * @returns {Object}
+ * @param { number } shaderType - gl code for shader type
+ * @param { string } code - code for the shader
+ */
+ rglwidgetClass.prototype.getShader = function(shaderType, code) {
+ var gl = this.gl, shader;
+ shader = gl.createShader(shaderType);
+ gl.shaderSource(shader, code);
+ gl.compileShader(shader);
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS) && !gl.isContextLost())
+ alert(gl.getShaderInfoLog(shader));
+ return shader;
+ };
+
diff --git a/reference/libs/rglwidgetClass-1.3.1/shadersrc.src.js b/reference/libs/rglwidgetClass-1.3.1/shadersrc.src.js
new file mode 100644
index 0000000..ccf93d4
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/shadersrc.src.js
@@ -0,0 +1,455 @@
+rglwidgetClass.rgl_vertex_shader = function() {
+return "#line 2 1\n"+
+"// File 1 is the vertex shader\n"+
+"#ifdef GL_ES\n"+
+"#ifdef GL_FRAGMENT_PRECISION_HIGH\n"+
+"precision highp float;\n"+
+"#else\n"+
+"precision mediump float;\n"+
+"#endif\n"+
+"#endif\n"+
+"\n"+
+"attribute vec3 aPos;\n"+
+"attribute vec4 aCol;\n"+
+"uniform mat4 mvMatrix;\n"+
+"uniform mat4 prMatrix;\n"+
+"varying vec4 vCol;\n"+
+"varying vec4 vPosition;\n"+
+"\n"+
+"#ifdef NEEDS_VNORMAL\n"+
+"attribute vec3 aNorm;\n"+
+"uniform mat4 normMatrix;\n"+
+"varying vec4 vNormal;\n"+
+"#endif\n"+
+"\n"+
+"#if defined(HAS_TEXTURE) || defined (IS_TEXT)\n"+
+"attribute vec2 aTexcoord;\n"+
+"varying vec2 vTexcoord;\n"+
+"#endif\n"+
+"\n"+
+"#ifdef FIXED_SIZE\n"+
+"uniform vec3 textScale;\n"+
+"#endif\n"+
+"\n"+
+"#ifdef FIXED_QUADS\n"+
+"attribute vec3 aOfs;\n"+
+"#endif\n"+
+"\n"+
+"#ifdef IS_TWOSIDED\n"+
+"#ifdef HAS_NORMALS\n"+
+"varying float normz;\n"+
+"uniform mat4 invPrMatrix;\n"+
+"#else\n"+
+"attribute vec3 aPos1;\n"+
+"attribute vec3 aPos2;\n"+
+"varying float normz;\n"+
+"#endif\n"+
+"#endif // IS_TWOSIDED\n"+
+"\n"+
+"#ifdef FAT_LINES\n"+
+"attribute vec3 aNext;\n"+
+"attribute vec2 aPoint;\n"+
+"varying vec2 vPoint;\n"+
+"varying float vLength;\n"+
+"uniform float uAspect;\n"+
+"uniform float uLwd;\n"+
+"#endif\n"+
+"\n"+
+"#ifdef USE_ENVMAP\n"+
+"varying vec3 vReflection;\n"+
+"#endif\n"+
+"\n"+
+"void main(void) {\n"+
+" \n"+
+"#ifndef IS_BRUSH\n"+
+"#if defined(NCLIPPLANES) || !defined(FIXED_QUADS) || defined(HAS_FOG) || defined(USE_ENVMAP)\n"+
+" vPosition = mvMatrix * vec4(aPos, 1.);\n"+
+"#endif\n"+
+" \n"+
+"#ifndef FIXED_QUADS\n"+
+" gl_Position = prMatrix * vPosition;\n"+
+"#endif\n"+
+"#endif // !IS_BRUSH\n"+
+" \n"+
+"#ifdef IS_POINTS\n"+
+" gl_PointSize = POINTSIZE;\n"+
+"#endif\n"+
+" \n"+
+" vCol = aCol;\n"+
+" \n"+
+"// USE_ENVMAP implies NEEDS_VNORMAL\n"+
+"\n"+
+"#ifdef NEEDS_VNORMAL\n"+
+" vNormal = normMatrix * vec4(-aNorm, dot(aNorm, aPos));\n"+
+"#endif\n"+
+"\n"+
+"#ifdef USE_ENVMAP\n"+
+" vReflection = normalize(reflect(vPosition.xyz/vPosition.w, \n"+
+" normalize(vNormal.xyz/vNormal.w)));\n"+
+"#endif\n"+
+" \n"+
+"#ifdef IS_TWOSIDED\n"+
+"#ifdef HAS_NORMALS\n"+
+" /* normz should be calculated *after* projection */\n"+
+" normz = (invPrMatrix*vNormal).z;\n"+
+"#else\n"+
+" vec4 pos1 = prMatrix*(mvMatrix*vec4(aPos1, 1.));\n"+
+" pos1 = pos1/pos1.w - gl_Position/gl_Position.w;\n"+
+" vec4 pos2 = prMatrix*(mvMatrix*vec4(aPos2, 1.));\n"+
+" pos2 = pos2/pos2.w - gl_Position/gl_Position.w;\n"+
+" normz = pos1.x*pos2.y - pos1.y*pos2.x;\n"+
+"#endif\n"+
+"#endif // IS_TWOSIDED\n"+
+" \n"+
+"#ifdef NEEDS_VNORMAL\n"+
+" vNormal = vec4(normalize(vNormal.xyz), 1);\n"+
+"#endif\n"+
+" \n"+
+"#if defined(HAS_TEXTURE) || defined(IS_TEXT)\n"+
+" vTexcoord = aTexcoord;\n"+
+"#endif\n"+
+" \n"+
+"#if defined(FIXED_SIZE) && !defined(ROTATING)\n"+
+" vec4 pos = prMatrix * mvMatrix * vec4(aPos, 1.);\n"+
+" pos = pos/pos.w;\n"+
+" gl_Position = pos + vec4(aOfs*textScale, 0.);\n"+
+"#endif\n"+
+" \n"+
+"#if defined(IS_SPRITES) && !defined(FIXED_SIZE)\n"+
+" vec4 pos = mvMatrix * vec4(aPos, 1.);\n"+
+" pos = pos/pos.w + vec4(aOfs, 0.);\n"+
+" gl_Position = prMatrix*pos;\n"+
+"#endif\n"+
+" \n"+
+"#ifdef FAT_LINES\n"+
+" /* This code was inspired by Matt Deslauriers' code in \n"+
+" https://mattdesl.svbtle.com/drawing-lines-is-hard */\n"+
+" vec2 aspectVec = vec2(uAspect, 1.0);\n"+
+" mat4 projViewModel = prMatrix * mvMatrix;\n"+
+" vec4 currentProjected = projViewModel * vec4(aPos, 1.0);\n"+
+" currentProjected = currentProjected/currentProjected.w;\n"+
+" vec4 nextProjected = projViewModel * vec4(aNext, 1.0);\n"+
+" vec2 currentScreen = currentProjected.xy * aspectVec;\n"+
+" vec2 nextScreen = (nextProjected.xy / nextProjected.w) * aspectVec;\n"+
+" float len = uLwd;\n"+
+" vec2 dir = vec2(1.0, 0.0);\n"+
+" vPoint = aPoint;\n"+
+" vLength = length(nextScreen - currentScreen)/2.0;\n"+
+" vLength = vLength/(vLength + len);\n"+
+" if (vLength > 0.0) {\n"+
+" dir = normalize(nextScreen - currentScreen);\n"+
+" }\n"+
+" vec2 normal = vec2(-dir.y, dir.x);\n"+
+" dir.x /= uAspect;\n"+
+" normal.x /= uAspect;\n"+
+" vec4 offset = vec4(len*(normal*aPoint.x*aPoint.y - dir), 0.0, 0.0);\n"+
+" gl_Position = currentProjected + offset;\n"+
+"#endif\n"+
+" \n"+
+"#ifdef IS_BRUSH\n"+
+" gl_Position = vec4(aPos, 1.);\n"+
+"#endif\n"+
+"}\n" ;};
+rglwidgetClass.rgl_fragment_shader = function() {
+return "#line 2 2\n"+
+"// File 2 is the fragment shader\n"+
+"#ifdef GL_ES\n"+
+"#ifdef GL_FRAGMENT_PRECISION_HIGH\n"+
+"precision highp float;\n"+
+"#else\n"+
+"precision mediump float;\n"+
+"#endif\n"+
+"#endif\n"+
+"varying vec4 vCol; // carries alpha\n"+
+"varying vec4 vPosition;\n"+
+"#if defined(HAS_TEXTURE) || defined (IS_TEXT)\n"+
+"varying vec2 vTexcoord;\n"+
+"uniform sampler2D uSampler;\n"+
+"#endif\n"+
+"\n"+
+"#ifdef HAS_FOG\n"+
+"uniform int uFogMode;\n"+
+"uniform vec3 uFogColor;\n"+
+"uniform vec4 uFogParms;\n"+
+"#endif\n"+
+"\n"+
+"#if defined(IS_LIT) && !defined(FIXED_QUADS)\n"+
+"varying vec4 vNormal;\n"+
+"#endif\n"+
+"\n"+
+"#if NCLIPPLANES > 0\n"+
+"uniform vec4 vClipplane[NCLIPPLANES];\n"+
+"#endif\n"+
+"\n"+
+"#if NLIGHTS > 0\n"+
+"uniform mat4 mvMatrix;\n"+
+"#endif\n"+
+"\n"+
+"#ifdef IS_LIT\n"+
+"uniform vec3 emission;\n"+
+"uniform float shininess;\n"+
+"#if NLIGHTS > 0\n"+
+"uniform vec3 ambient[NLIGHTS];\n"+
+"uniform vec3 specular[NLIGHTS]; // light*material\n"+
+"uniform vec3 diffuse[NLIGHTS];\n"+
+"uniform vec3 lightDir[NLIGHTS];\n"+
+"uniform bool viewpoint[NLIGHTS];\n"+
+"uniform bool finite[NLIGHTS];\n"+
+"#endif\n"+
+"#endif // IS_LIT\n"+
+"\n"+
+"#ifdef IS_TWOSIDED\n"+
+"uniform bool front;\n"+
+"varying float normz;\n"+
+"#endif\n"+
+"\n"+
+"#ifdef FAT_LINES\n"+
+"varying vec2 vPoint;\n"+
+"varying float vLength;\n"+
+"#endif\n"+
+"\n"+
+"#ifdef USE_ENVMAP\n"+
+"varying vec3 vReflection;\n"+
+"#endif\n"+
+"\n"+
+"void main(void) {\n"+
+" vec4 fragColor;\n"+
+"#ifdef FAT_LINES\n"+
+" vec2 point = vPoint;\n"+
+" bool neg = point.y < 0.0;\n"+
+" point.y = neg ? (point.y + vLength)/(1.0 - vLength) :\n"+
+" -(point.y - vLength)/(1.0 - vLength);\n"+
+"#if defined(IS_TRANSPARENT) && defined(IS_LINESTRIP)\n"+
+" if (neg && length(point) <= 1.0) discard;\n"+
+"#endif\n"+
+" point.y = min(point.y, 0.0);\n"+
+" if (length(point) > 1.0) discard;\n"+
+"#endif // FAT_LINES\n"+
+" \n"+
+"#ifdef ROUND_POINTS\n"+
+" vec2 coord = gl_PointCoord - vec2(0.5);\n"+
+" if (length(coord) > 0.5) discard;\n"+
+"#endif\n"+
+" \n"+
+"#if NCLIPPLANES > 0\n"+
+" for (int i = 0; i < NCLIPPLANES; i++)\n"+
+" if (dot(vPosition, vClipplane[i]) < 0.0) discard;\n"+
+"#endif\n"+
+" \n"+
+"#ifdef FIXED_QUADS\n"+
+" vec3 n = vec3(0., 0., 1.);\n"+
+"#elif defined(IS_LIT)\n"+
+" vec3 n = normalize(vNormal.xyz);\n"+
+"#endif\n"+
+" \n"+
+"#ifdef IS_TWOSIDED\n"+
+" if ((normz <= 0.) != front) discard;\n"+
+"#endif\n"+
+"\n"+
+"#ifdef IS_LIT\n"+
+" vec3 eye = normalize(-vPosition.xyz/vPosition.w);\n"+
+" vec3 lightdir;\n"+
+" vec4 colDiff;\n"+
+" vec3 halfVec;\n"+
+" vec4 lighteffect = vec4(emission, 0.);\n"+
+" vec3 col;\n"+
+" float nDotL;\n"+
+"#ifdef FIXED_QUADS\n"+
+" n = -faceforward(n, n, eye);\n"+
+"#endif\n"+
+" \n"+
+"#if NLIGHTS > 0\n"+
+" // Simulate two-sided lighting\n"+
+" if (n.z < 0.0)\n"+
+" n = -n;\n"+
+" for (int i=0;i 0) {\n"+
+" fogF = (uFogParms.y - vPosition.z/vPosition.w)/(uFogParms.y - uFogParms.x);\n"+
+" if (uFogMode > 1)\n"+
+" fogF = mix(uFogParms.w, 1.0, fogF);\n"+
+" fogF = fogF*uFogParms.z;\n"+
+" if (uFogMode == 2)\n"+
+" fogF = 1.0 - exp(-fogF);\n"+
+" // Docs are wrong: use (density*c)^2, not density*c^2\n"+
+" // https://gitlab.freedesktop.org/mesa/mesa/-/blob/master/src/mesa/swrast/s_fog.c#L58\n"+
+" else if (uFogMode == 3)\n"+
+" fogF = 1.0 - exp(-fogF*fogF);\n"+
+" fogF = clamp(fogF, 0.0, 1.0);\n"+
+" gl_FragColor = vec4(mix(fragColor.rgb, uFogColor, fogF), fragColor.a);\n"+
+" } else gl_FragColor = fragColor;\n"+
+"#else\n"+
+" gl_FragColor = fragColor;\n"+
+"#endif // HAS_FOG\n"+
+" \n"+
+"}\n" ;};
diff --git a/reference/libs/rglwidgetClass-1.3.1/subscenes.src.js b/reference/libs/rglwidgetClass-1.3.1/subscenes.src.js
new file mode 100644
index 0000000..fc6e05d
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/subscenes.src.js
@@ -0,0 +1,179 @@
+ /**
+ * Methods related to subscenes
+ * @name ___METHODS_FOR_SUBSCENES___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+ */
+
+ /**
+ * Is a particular id in a subscene?
+ * @returns { boolean }
+ * @param {number} id Which id?
+ * @param {number} subscene Which subscene id?
+ */
+ rglwidgetClass.prototype.inSubscene = function(id, subscene) {
+ return this.getObj(subscene).objects.indexOf(id) > -1;
+ };
+
+ /**
+ * Translate from window coordinates to viewport coordinates
+ * @returns { Object } translated coordinates
+ * @param { number } subsceneid - which subscene to use?
+ * @param { Object } coords - point to translate
+ */
+ rglwidgetClass.prototype.translateCoords = function(subsceneid, coords) {
+ var viewport = this.getObj(subsceneid).par3d.viewport;
+ return {x: coords.x - viewport.x*this.canvas.width,
+ y: coords.y - viewport.y*this.canvas.height};
+ };
+
+ /**
+ * Check whether point is in viewport of subscene
+ * @returns {boolean}
+ * @param { Object } coords - screen coordinates of point
+ * @param { number } subsceneid - subscene to check
+ */
+ rglwidgetClass.prototype.inViewport = function(coords, subsceneid) {
+ var viewport = this.getObj(subsceneid).par3d.viewport,
+ x0 = coords.x - viewport.x*this.canvas.width,
+ y0 = coords.y - viewport.y*this.canvas.height;
+ return 0 <= x0 && x0 <= viewport.width*this.canvas.width &&
+ 0 <= y0 && y0 <= viewport.height*this.canvas.height;
+ };
+
+ /**
+ * Find which subscene contains a point
+ * @returns { number } subscene id
+ * @param { Object } coords - coordinates of point
+ */
+ rglwidgetClass.prototype.whichSubscene = function(coords) {
+ var self = this,
+ recurse = function(subsceneid) {
+ var subscenes = self.getChildSubscenes(subsceneid), i, id;
+ for (i=0; i < subscenes.length; i++) {
+ id = recurse(subscenes[i]);
+ if (typeof(id) !== "undefined")
+ return(id);
+ }
+ if (self.inViewport(coords, subsceneid))
+ return(subsceneid);
+ else
+ return undefined;
+ },
+ rootid = this.scene.rootSubscene,
+ result = recurse(rootid);
+ if (typeof(result) === "undefined")
+ result = rootid;
+ return result;
+ };
+
+ /**
+ * Add an id to a subscene.
+ * @param {number} id Which id?
+ * @param {number} subscene Which subscene id?
+ */
+ rglwidgetClass.prototype.addToSubscene = function(id, subscene) {
+ var thelist,
+ thesub = this.getObj(subscene),
+ ids = [id],
+ obj = this.getObj(id), i;
+ if (typeof obj !== "undefined" && typeof (obj.newIds) !== "undefined") {
+ ids = ids.concat(obj.newIds);
+ }
+ thesub.objects = [].concat(thesub.objects);
+ for (i = 0; i < ids.length; i++) {
+ id = ids[i];
+ if (thesub.objects.indexOf(id) === -1) {
+ thelist = this.whichList(id);
+ thesub.objects.push(id);
+ thesub[thelist].push(id);
+ }
+ }
+ };
+
+ /**
+ * Delete an id from a subscene
+ * @param { number } id - the id to add
+ * @param { number } subscene - the id of the subscene
+ */
+ rglwidgetClass.prototype.delFromSubscene = function(id, subscene) {
+ var thelist,
+ thesub = this.getObj(subscene),
+ obj = this.getObj(id),
+ ids = [id], i, j;
+ if (typeof obj !== "undefined" && typeof (obj.newIds) !== "undefined")
+ ids = ids.concat(obj.newIds);
+ thesub.objects = [].concat(thesub.objects); // It might be a scalar
+ for (j=0; j -1) {
+ thesub.objects.splice(i, 1);
+ thelist = this.whichList(id);
+ i = thesub[thelist].indexOf(id);
+ thesub[thelist].splice(i, 1);
+ }
+ }
+ };
+
+ /**
+ * Set the ids in a subscene
+ * @param { number[] } ids - the ids to set
+ * @param { number } subsceneid - the id of the subscene
+ */
+ rglwidgetClass.prototype.setSubsceneEntries = function(ids, subsceneid) {
+ var sub = this.getObj(subsceneid);
+ sub.objects = ids;
+ this.initSubscene(subsceneid);
+ };
+
+ /**
+ * Get the ids in a subscene
+ * @returns {number[]}
+ * @param { number } subscene - the id of the subscene
+ */
+ rglwidgetClass.prototype.getSubsceneEntries = function(subscene) {
+ return this.getObj(subscene).objects;
+ };
+
+ /**
+ * Get the ids of the subscenes within a subscene
+ * @returns { number[] }
+ * @param { number } subscene - the id of the subscene
+ */
+ rglwidgetClass.prototype.getChildSubscenes = function(subscene) {
+ return this.getObj(subscene).subscenes;
+ };
+
+ /**
+ * Find a particular subscene by inheritance
+ * @returns { number } id of subscene to use
+ * @param { number } subsceneid - child subscene
+ * @param { string } type - type of inheritance: "projection" or "model"
+ */
+ rglwidgetClass.prototype.useid = function(subsceneid, type) {
+ var sub = this.getObj(subsceneid);
+ if (sub.embeddings[type] === "inherit")
+ return(this.useid(sub.parent, type));
+ else
+ return subsceneid;
+ };
+
+ /**
+ * Find bboxdeco for a subscene
+ * @returns { number } id of bboxdeco, or undefined if none
+ * @param { number } sub- subscene
+ */
+ rglwidgetClass.prototype.getBBoxDeco = function(sub) {
+ var objects = sub.objects, i, obj;
+ for (i = 0; i < objects.length; i++) {
+ obj = this.getObj(objects[i]);
+ if (obj.type === "bboxdeco")
+ return obj;
+ }
+ if (sub.parent)
+ return this.getBBoxDeco(this.getObj(sub.parent));
+ else
+ return undefined;
+ };
diff --git a/reference/libs/rglwidgetClass-1.3.1/textures.src.js b/reference/libs/rglwidgetClass-1.3.1/textures.src.js
new file mode 100644
index 0000000..3cf32d3
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/textures.src.js
@@ -0,0 +1,173 @@
+ /**
+ * Methods related to textures
+ * @name ___METHODS_FOR_TEXTURES___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+ */
+
+ rglwidgetClass.prototype.getTexFilter = function(filter) {
+ var gl = this.gl || this.initGL();
+ switch(filter) {
+ case "nearest": return gl.NEAREST;
+ case "linear": return gl.LINEAR;
+ case "nearest.mipmap.nearest": return gl.NEAREST_MIPMAP_NEAREST;
+ case "linear.mipmap.nearest": return gl.LINEAR_MIPMAP_NEAREST;
+ case "nearest.mipmap.linear": return gl.NEAREST_MIPMAP_LINEAR;
+ case "linear.mipmap.linear": return gl.LINEAR_MIPMAP_LINEAR;
+ default: console.error("Unknown filter: "+filter);
+ }
+ };
+
+ /**
+ * Handle a texture after its image has been loaded
+ * @param { Object } texture - the gl texture object
+ * @param { Object } textureCanvas - the canvas holding the image
+ */
+ rglwidgetClass.prototype.handleLoadedTexture = function(texture, textureCanvas) {
+ var gl = this.gl || this.initGL();
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
+
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureCanvas);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
+ gl.generateMipmap(gl.TEXTURE_2D);
+
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ };
+
+ /**
+ * Get maximum dimension of texture in current browser.
+ * @returns {number}
+ */
+ rglwidgetClass.prototype.getMaxTexSize = function() {
+ var gl = this.gl || this.initGL();
+ return Math.min(4096, gl.getParameter(gl.MAX_TEXTURE_SIZE));
+ };
+
+ /**
+ * Load an image to a texture
+ * @param { string } uri - The image location
+ * @param { Object } texture - the gl texture object
+ */
+ rglwidgetClass.prototype.loadImageToTexture = function(uri, texture) {
+ var canvas = this.textureCanvas,
+ ctx = canvas.getContext("2d"),
+ image = new Image(),
+ self = this;
+
+ image.onload = function() {
+
+ var w = image.width,
+ h = image.height,
+ canvasX = self.getPowerOfTwo(w),
+ canvasY = self.getPowerOfTwo(h),
+ maxTexSize = self.getMaxTexSize();
+ while (canvasX > 1 && canvasY > 1 && (canvasX > maxTexSize || canvasY > maxTexSize)) {
+ canvasX /= 2;
+ canvasY /= 2;
+ }
+ canvas.width = canvasX;
+ canvas.height = canvasY;
+ ctx.imageSmoothingEnabled = true;
+ ctx.drawImage(image, 0, 0, canvasX, canvasY);
+ self.handleLoadedTexture(texture, canvas);
+ self.texturesLoading -= 1;
+ if (!self.texturesLoading)
+ self.drawScene();
+ };
+ if (!self.texturesLoading)
+ self.texturesLoading = 0; // may have been undefined
+ self.texturesLoading += 1;
+ image.src = uri;
+ };
+
+ /**
+ * Draw text to the texture canvas
+ * @returns { Object } object with text measurements
+ * @param { string } text - the text
+ * @param { number } cex - expansion
+ * @param { string } family - font family
+ * @param { number } font - font number
+ */
+ rglwidgetClass.prototype.drawTextToCanvas = function(text, cex, family, font) {
+ var canvasX, canvasY,
+ scaling = 20,
+ textColour = "white",
+
+ backgroundColour = "rgba(0,0,0,0)",
+ canvas = this.textureCanvas,
+ ctx = canvas.getContext("2d"),
+ i, textHeight = 0, textHeights = [], width, widths = [],
+ offsetx, offsety = 0, line, lines = [], offsetsx = [],
+ offsetsy = [], lineoffsetsy = [], fontStrings = [],
+ maxTexSize = this.getMaxTexSize(),
+ getFontString = function(i) {
+ textHeights[i] = scaling*cex[i];
+ var fontString = textHeights[i] + "px",
+ family0 = family[i],
+ font0 = font[i];
+ if (family0 === "sans")
+ family0 = "sans-serif";
+ else if (family0 === "mono")
+ family0 = "monospace";
+ fontString = fontString + " " + family0;
+ if (font0 === 2 || font0 === 4)
+ fontString = "bold " + fontString;
+ if (font0 === 3 || font0 === 4)
+ fontString = "italic " + fontString;
+ return fontString;
+ };
+ cex = rglwidgetClass.repeatToLen(cex, text.length);
+ family = rglwidgetClass.repeatToLen(family, text.length);
+ font = rglwidgetClass.repeatToLen(font, text.length);
+
+ canvasX = 1;
+ line = -1;
+ offsetx = maxTexSize;
+ for (i = 0; i < text.length; i++) {
+ ctx.font = fontStrings[i] = getFontString(i);
+ width = widths[i] = ctx.measureText(text[i]).width;
+ if (offsetx + width > maxTexSize) {
+ offsety = offsety + 2*textHeight;
+ if (line >= 0)
+ lineoffsetsy[line] = offsety;
+ line += 1;
+ if (offsety > maxTexSize)
+ console.error("Too many strings for texture.");
+ textHeight = 0;
+ offsetx = 0;
+ }
+ textHeight = Math.max(textHeight, textHeights[i]);
+ offsetsx[i] = offsetx;
+ offsetx += width;
+ canvasX = Math.max(canvasX, offsetx);
+ lines[i] = line;
+ }
+ offsety = lineoffsetsy[line] = offsety + 2*textHeight;
+ for (i = 0; i < text.length; i++) {
+ offsetsy[i] = lineoffsetsy[lines[i]];
+ }
+
+ canvasX = this.getPowerOfTwo(canvasX);
+ canvasY = this.getPowerOfTwo(offsety);
+
+ canvas.width = canvasX;
+ canvas.height = canvasY;
+
+ ctx.fillStyle = backgroundColour;
+ ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+
+ ctx.textBaseline = "alphabetic";
+ for(i = 0; i < text.length; i++) {
+ ctx.font = fontStrings[i];
+ ctx.fillStyle = textColour;
+ ctx.textAlign = "left";
+ ctx.fillText(text[i], offsetsx[i], offsetsy[i]);
+ }
+ return {canvasX:canvasX, canvasY:canvasY,
+ widths:widths, textHeights:textHeights,
+ offsetsx:offsetsx, offsetsy:offsetsy};
+ };
+
diff --git a/reference/libs/rglwidgetClass-1.3.1/utils.src.js b/reference/libs/rglwidgetClass-1.3.1/utils.src.js
new file mode 100644
index 0000000..00bc5fc
--- /dev/null
+++ b/reference/libs/rglwidgetClass-1.3.1/utils.src.js
@@ -0,0 +1,654 @@
+ /**
+ * Utility methods
+ * @name ___UTILITY_METHODS___
+ * @memberof rglwidgetClass
+ * @kind function
+ * @instance
+ */
+
+ /**
+ * Multiply matrix by vector
+ * @returns {number[]}
+ * @param M {number[][]} Left operand
+ * @param v {number[]} Right operand
+ */
+ rglwidgetClass.multMV = function(M, v) {
+ return [ M.m11 * v[0] + M.m12 * v[1] + M.m13 * v[2] + M.m14 * v[3],
+ M.m21 * v[0] + M.m22 * v[1] + M.m23 * v[2] + M.m24 * v[3],
+ M.m31 * v[0] + M.m32 * v[1] + M.m33 * v[2] + M.m34 * v[3],
+ M.m41 * v[0] + M.m42 * v[1] + M.m43 * v[2] + M.m44 * v[3]
+ ];
+ };
+
+ /**
+ * Multiply row vector by Matrix
+ * @returns {number[]}
+ * @param v {number[]} left operand
+ * @param M {number[][]} right operand
+ */
+ rglwidgetClass.multVM = function(v, M) {
+ return [ M.m11 * v[0] + M.m21 * v[1] + M.m31 * v[2] + M.m41 * v[3],
+ M.m12 * v[0] + M.m22 * v[1] + M.m32 * v[2] + M.m42 * v[3],
+ M.m13 * v[0] + M.m23 * v[1] + M.m33 * v[2] + M.m43 * v[3],
+ M.m14 * v[0] + M.m24 * v[1] + M.m34 * v[2] + M.m44 * v[3]
+ ];
+ };
+
+ /**
+ * Euclidean length of a vector
+ * @returns {number}
+ * @param v {number[]}
+ */
+ rglwidgetClass.vlen = function(v) {
+ return Math.sqrt(rglwidgetClass.dotprod(v, v));
+ };
+
+ /**
+ * Dot product of two vectors
+ * @instance rglwidgetClass
+ * @returns {number}
+ * @param a {number[]}
+ * @param b {number[]}
+ */
+ rglwidgetClass.dotprod = function(a, b) {
+ return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
+ };
+
+ /**
+ * Cross product of two vectors
+ * @returns {number[]}
+ * @param a {number[]}
+ * @param b {number[]}
+ */
+ rglwidgetClass.xprod = function(a, b) {
+ return [a[1]*b[2] - a[2]*b[1],
+ a[2]*b[0] - a[0]*b[2],
+ a[0]*b[1] - a[1]*b[0]];
+ };
+
+ /**
+ * Bind vectors or matrices by columns
+ * @returns {number[][]}
+ * @param a {number[][]}
+ * @param b {number[]|number[][]}
+ */
+ rglwidgetClass.cbind = function(a, b) {
+ if (b.length < a.length)
+ b = rglwidgetClass.repeatToLen(b, a.length);
+ else if (a.length < b.length)
+ a = rglwidgetClass.repeatToLen(a, b.length);
+ return a.map(function(currentValue, index) {
+ return [].concat(currentValue).concat(b[index]);
+ });
+ };
+
+ /**
+ * Swap elements
+ * @returns {any[]}
+ * @param a {any[]}
+ * @param i {number} Element to swap
+ * @param j {number} Other element to swap
+ */
+ rglwidgetClass.swap = function(a, i, j) {
+ var temp = a[i];
+ a[i] = a[j];
+ a[j] = temp;
+ };
+
+ /**
+ * Flatten a matrix into a vector
+ * @returns {any[]}
+ * @param a {any[][]}
+ */
+ rglwidgetClass.flatten = function(arr, result) {
+ var value;
+ if (typeof result === "undefined") result = [];
+ for (var i = 0, length = arr.length; i < length; i++) {
+ value = arr[i];
+ if (Array.isArray(value)) {
+ rglwidgetClass.flatten(value, result);
+ } else {
+ result.push(value);
+ }
+ }
+ return result;
+ };
+
+ /**
+ * set element of 1d or 2d array as if it was flattened.
+ * Column major, zero based!
+ * @returns {any[]|any[][]}
+ * @param {any[]|any[][]} a - array
+ * @param {number} i - element
+ * @param {any} value
+ */
+ rglwidgetClass.prototype.setElement = function(a, i, value) {
+ if (Array.isArray(a[0])) {
+ var dim = a.length,
+ col = Math.floor(i/dim),
+ row = i % dim;
+ a[row][col] = value;
+ } else {
+ a[i] = value;
+ }
+ };
+
+ /**
+ * Transpose an array
+ * @returns {any[][]}
+ * @param {any[][]} a
+ */
+ rglwidgetClass.prototype.transpose = function(a) {
+ var newArray = [],
+ n = a.length,
+ m = a[0].length,
+ i;
+ for(i = 0; i < m; i++){
+ newArray.push([]);
+ }
+
+ for(i = 0; i < n; i++){
+ for(var j = 0; j < m; j++){
+ newArray[j].push(a[i][j]);
+ }
+ }
+ return newArray;
+ };
+
+ /**
+ * Calculate sum of squares of a numeric vector
+ * @returns {number}
+ * @param {number[]} x
+ */
+ rglwidgetClass.prototype.sumsq = function(x) {
+ var result = 0, i;
+ for (i=0; i < x.length; i++)
+ result += x[i]*x[i];
+ return result;
+ };
+
+ /**
+ * Convert a matrix to a CanvasMatrix4
+ * @returns {CanvasMatrix4}
+ * @param {number[][]|number[]} mat
+ */
+ rglwidgetClass.prototype.toCanvasMatrix4 = function(mat) {
+ if (mat instanceof CanvasMatrix4)
+ return mat;
+ var result = new CanvasMatrix4();
+ mat = rglwidgetClass.flatten(this.transpose(mat));
+ result.load(mat);
+ return result;
+ };
+
+ /**
+ * Convert an R-style numeric colour string to an rgb vector
+ * @returns {number[]}
+ * @param {string} s
+ */
+ /* jshint bitwise:false */
+ rglwidgetClass.prototype.stringToRgb = function(s) {
+ s = s.replace("#", "");
+ var bigint = parseInt(s, 16);
+ return [((bigint >> 16) & 255)/255,
+ ((bigint >> 8) & 255)/255,
+ (bigint & 255)/255];
+ };
+ /* jshint bitwise:true */
+ /**
+ * Which list does a particular id come from?
+ * @returns { string }
+ * @param {number} id The id to look up.
+ */
+ rglwidgetClass.prototype.whichList = function(id) {
+ var obj = this.getObj(id),
+ flags = obj.flags;
+ if (obj.type === "light")
+ return "lights";
+ if (rglwidgetClass.isSet(flags, rglwidgetClass.f_is_subscene))
+ return "subscenes";
+ if (rglwidgetClass.isSet(flags, rglwidgetClass.f_is_clipplanes))
+ return "clipplanes";
+ if (rglwidgetClass.isSet(flags, rglwidgetClass.f_is_transparent))
+ return "transparent";
+ return "opaque";
+ };
+
+ /**
+ * Take a component-by-component product of two 3 vectors
+ * @returns {number[]}
+ * @param {number[]} x
+ * @param {number[]} y
+ */
+ rglwidgetClass.prototype.componentProduct = function(x, y) {
+ if (typeof y === "undefined") {
+ this.alertOnce("Bad arg to componentProduct");
+ }
+ var result = new Float32Array(3), i;
+ for (i = 0; i<3; i++)
+ result[i] = x[i]*y[i];
+ return result;
+ };
+
+ /**
+ * Get next higher power of two
+ * @returns { number }
+ * @param { number } value - input value
+ */
+ rglwidgetClass.prototype.getPowerOfTwo = function(value) {
+ var pow = 1;
+ while(pow= -windHeight &&
+ rect.left >= -windWidth &&
+ rect.bottom <= 2*windHeight &&
+ rect.right <= 2*windWidth);
+ };
+
+ rglwidgetClass.keydiff = function(obj1, obj2) {
+ var keys = Object.keys(obj1), i, result = [];
+ for (i=0;i= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], points[i]) <= 0) {
+ lower.pop();
+ }
+ lower.push(points[i]);
+ }
+
+ for (i = points.length - 1; i >= 0; i--) {
+ while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], points[i]) <= 0) {
+ upper.pop();
+ }
+ upper.push(points[i]);
+ }
+
+ upper.pop();
+ lower.pop();
+ return lower.concat(upper);
+ };
+
+ /**
+ * Round number to given precision
+ * @param { number } x
+ * @param { number } digits
+ * @returns { number }
+ */
+ rglwidgetClass.signif = function(x, digits) {
+ return parseFloat(x.toPrecision(digits));
+ };
+
+ /**
+ * Check for NA, NaN, undefined, or null
+ * @param x
+ * @returns { bool }
+ */
+ rglwidgetClass.missing = function(x) {
+ return x !== "-Inf" && x !== "Inf" &&
+ (isNaN(x) || x === null || typeof(x) === "undefined");
+ };
+
+ /**
+ * Write matrix to log
+ * @param M
+ */
+ rglwidgetClass.logMatrix = function(M) {
+ console.log("matrix(c("+M.m11+","+M.m12+","+M.m13+","+M.m14+",\n"+
+ M.m21+","+M.m22+","+M.m23+","+M.m24+",\n"+
+ M.m31+","+M.m32+","+M.m33+","+M.m34+",\n"+
+ M.m41+","+M.m42+","+M.m43+","+M.m44+"), byrow=TRUE, ncol=4)");
+ };
+
+ /**
+ * Write vector to log
+ * @param {vector} v
+ */
+
+ rglwidgetClass.logVec3 = function(v) {
+ console.log("c("+v[0]+","+v[1]+","+v[2]+")");
+ };
+
+ /**
+ * Sum two vectors
+ * @param {vector} x
+ * @param {vector} y
+ */
+ rglwidgetClass.vsum = function(x, y) {
+ var i, result = [].concat(x);
+ for (i = 0; i < y.length; i++)
+ result[i] += y[i];
+ return result;
+ };
+
+ /**
+ * difference of two vectors
+ * @param {vector} x
+ * @param {vector} y
+ */
+ rglwidgetClass.vdiff = function(x, y) {
+ return rglwidgetClass.vsum(x, rglwidgetClass.vscale(y, -1));
+ };
+
+ /**
+ * Scale a vector
+ * @param {number} s
+ * @param {vector} x
+ */
+ rglwidgetClass.vscale = function(x, s) {
+ var i, result = [].concat(x);
+ for (i = 0; i < x.length; i++)
+ result[i] *= s;
+ return result;
+ };
+
+ /**
+ * Normalize a vector
+ * @param {vector} v
+ */
+ rglwidgetClass.normalize = function(v) {
+ return rglwidgetClass.vscale(v, 1/rglwidgetClass.vlen(v));
+ };
+
+ /**
+ * Compute the dimensions of a regular array
+ * without checking that it is regular
+ */
+ rglwidgetClass.arrayDim = function(arr) {
+ var result = [];
+ while (typeof arr.length !== "undefined") {
+ result = result.concat(arr.length);
+ arr = arr[0];
+ }
+ return result;
+ };
diff --git a/reference/nat.nblast-package.html b/reference/nat.nblast-package.html
new file mode 100644
index 0000000..f7bdf67
--- /dev/null
+++ b/reference/nat.nblast-package.html
@@ -0,0 +1,176 @@
+
+Neuron similarity, search and clustering tools — nat.nblast-package • nat.nblast
+ Skip to contents
+
+
+
+
+
+
+
+
+
nat.nblast provides tools to compare neuronal morphology using the
+NBLAST algorithm (Costa et al. 2016).
+
+
+
+
+
Similarity and search
+
+
+
+
The main entry point for similarity and search functions is
+ nblast
. Traced neurons will normally be converted to the
+ dotprops
format for search. When multiple neurons are
+ compared they should be in a neuronlist
object.
+
The current NBLAST version (2) depends on a scoring matrix. Default
+ matrices trained using Drosophila neurons in the FCWB template brain
+ space are distributed with this package (see smat.fcwb
); see
+ Scoring Matrices section below for creating new scoring matrices.
+
nblast
makes use of a more flexible but more complicated function
+ NeuriteBlast
which includes several additional options. The function
+ WeightedNNBasedLinesetMatching
provides the primitive functionality
+ of finding the nearest neighbour distances and absolute dot products for
+ two sets of segments. Neither of these functions are intended for end use.
+
Calculating all by all similarity scores is facilitated by the
+ nblast_allbyall
function which can take either a
+ neuronlist
as input or a character vector naming (a subset)
+ of neurons in a (large) neuronlist
. The
+ neuronlist
containing the input neurons should be resident in
+ memory i.e. not the neuronlistfh
.
+
+
+
Clustering
+
+
+
+
Once an all by all similarity score matrix is available it can be used as
+ the input to a variety of clustering algorithms. nhclust
+ provides a convenient wrapper for R's hierarchical clustering function
+ hclust
. If you wish to use another clustering function, then
+ you can use the sub_dist_mat
to convert a raw similarity
+ score matrix into a normalised distance matrix (or R dist
+ object) suitable for clustering. If you need a similarity matrix or want to
+ modify the normalisation then you can use sub_score_mat
.
+
Note that raw NBLAST scores are not symmetric (i.e. S(A,B) is not equal to
+ S(B,A)) so before clustering we construct a symmetric similarity/distance
+ matrix 1/2 * ( S(A,B)/S(A,A) + S(B,A)/S(B,B) )
. See
+ sub_score_mat
's documentation for details.
+
+
+
Cached scores
+
+
+
+
Although NBLAST is fast and can be parallelised, it makes sense to cache to
+ disk all by all similarity scores for a group of neurons that will be
+ subject to repeated clustering or other analysis. The matrix can simply be
+ saved to disk and then reloaded using base R functions like
+ save
and load
. sub_score_mat
and
+ sub_dist_mat
can be used to extract a subset of scores from
+ this raw score matrix. For large matrices, the bigmemory
or
+ ff
packages allow matrices to be stored on disk and portions loaded
+ into memory on demand. sub_score_mat
and
+ sub_dist_mat
work equally well for regular in-memory matrices
+ and these disk-backed matrices.
+
To give an example, for 16,129 neurons from the flycircuit.tw dataset, the
+ 260,144,641 comparisons took about 250 hours of compute time (half a day on
+ ~20 cores). When saved to disk as single precision (i.e. 4 bytes per score)
+ ff
matrix they occupy just over 1Gb.
+
+
+
Calculating scoring matrices
+
+
+
+
The NBLAST algorithm depends on appropriately calibrated scoring matrices.
+ These encapsulate the log odds ratio that a pair of segments come from two
+ structurally related neurons rather than two unrelated neurons, given the
+ observed distance and absolute dot product of the two segments. Scoring
+ matrices can be constructed using the create_scoringmatrix
+ function, supplying a set of matching neurons and a set of non-matching
+ neurons. See the create_scoringmatrix
documentation for links to
+ lower-level functions that provide finer control over construction of the
+ scoring matrix.
+
+
+
Package Options
+
+
+
+
There is one package option nat.nblast.defaultsmat
which is
+ NULL
by default, but could for example be set to one of the scoring
+ matrices included with the package such as "smat.fcwb"
or to a new
+ user-constructed matrix.
+
+
+
References
+
Costa, M., Ostrovsky, A.D., Manton, J.D., Prohaska, S., and
+ Jefferis, G.S.X.E. (2014). NBLAST: Rapid, sensitive comparison of neuronal
+ structure and construction of neuron family databases. bioRxiv preprint.
+ doi:10.1101/006346
+.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reference/nat.nblast.html b/reference/nat.nblast.html
new file mode 100644
index 0000000..12e9092
--- /dev/null
+++ b/reference/nat.nblast.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/reference/nblast-1.png b/reference/nblast-1.png
new file mode 100644
index 0000000..e9980d2
Binary files /dev/null and b/reference/nblast-1.png differ
diff --git a/reference/nblast-2.png b/reference/nblast-2.png
new file mode 100644
index 0000000..263ac53
Binary files /dev/null and b/reference/nblast-2.png differ
diff --git a/reference/nblast-3.png b/reference/nblast-3.png
new file mode 100644
index 0000000..d64dff3
Binary files /dev/null and b/reference/nblast-3.png differ
diff --git a/reference/nblast.html b/reference/nblast.html
new file mode 100644
index 0000000..4a9928c
--- /dev/null
+++ b/reference/nblast.html
@@ -0,0 +1,291 @@
+
+Calculate similarity score for neuron morphologies — nblast • nat.nblast
+ Skip to contents
+
+
+
+
+
+
+
+
+
Uses the NBLAST algorithm that compares the morphology of two neurons. For
+more control over the parameters of the algorithm, see the arguments of
+NeuriteBlast
.
+
+
+
+
Usage
+
nblast (
+ query ,
+ target = getOption ( "nat.default.neuronlist" ) ,
+ smat = NULL ,
+ sd = 3 ,
+ version = c ( 2 , 1 ) ,
+ normalised = FALSE ,
+ UseAlpha = FALSE ,
+ OmitFailures = NA ,
+ ...
+)
+
+
+
+
Arguments
+
+
+
query
+the query neuron.
+
+
+target
+a neuronlist
to compare neuron against.
+Defaults to options("nat.default.neuronlist")
. See
+nat-package
.
+
+
+smat
+the scoring matrix to use (see details)
+
+
+sd
+Standard deviation to use in distance dependence of NBLAST v1
+algorithm. Ignored when version=2
.
+
+
+version
+the version of the algorithm to use (the default, 2, is the
+latest).
+
+
+normalised
+whether to divide scores by the self-match score of the
+query
+
+
+UseAlpha
+whether to weight the similarity score for each matched
+segment to emphasise long range neurites rather then arbours (default:
+FALSE, see UseAlpha
section for details).
+
+
+OmitFailures
+Whether to omit neurons for which FUN
gives an
+error. The default value (NA
) will result in nblast
stopping
+with an error message the moment there is an error. For other values, see
+details.
+
+
+...
+Additional arguments passed to NeuriteBlast
or the
+function used to compute scores from distances/dot products. (expert use
+only).
+
+
+
+
Value
+
Named list of similarity scores.
+
+
+
Details
+
when smat=NULL
options("nat.nblast.defaultsmat")
will
+ be checked and if NULL
, then smat.fcwb
or
+ smat_alpha.fcwb
will be used depending on the value of
+ UseAlpha
.
+
When OmitFailures
is not NA
, individual nblast calls will be
+ wrapped in try
to ensure that failure for any single neuron does not
+ abort the whole nblast
call. When OmitFailures=FALSE
, missing
+ values will be left as NA
. OmitFailures=TRUE
is not (yet)
+ implemented. If you want to drop scores for neurons that failed you will
+ need to set OmitFailures=FALSE
and then use na.omit
or
+ similar to post-process the scores.
+
Note that when OmitFailures=FALSE
error messages will not be printed
+ because the call is wrapped as try(expr, silent=TRUE)
.
+
Internally, the plyr
package is used to provide options for
+ parallelising NBLAST and displaying progress. To display a progress bar as
+ the scores are computed, add .progress="natprogress"
to the
+ arguments (non-text progress bars are available – see
+ create_progress_bar
). To parallelise, add
+ .parallel=TRUE
to the arguments. In order to make use of parallel
+ calculation, you must register a parallel backend that will distribute the
+ computations. There are several possible backends, the simplest of which is
+ the multicore option made available by doMC
, which spreads the load
+ across cores of the same machine. Before using this, the backend must be
+ registered using registerDoMC
(see example below).
+
+
+
NBLAST Versions
+
The nblast
version argument presently
+ exposes two versions of the algorithm; both use the same core procedure of
+ aligning two vector clouds, segment by segment, and then computing the
+ distance and absolute dot product between the nearest segment in the target
+ neuron for every segment in the query neuron. However they differ
+ significantly in the procedure used to calculate a score using this set of
+ distances and absolute dot products.
+
Version 1 of the algorithm uses a standard deviation (argument
+ sd
) as a user-supplied parameter for a negative exponential
+ weighting function that determines the relationship between score and the
+ distance between segments. This corresponds to the parameter \(\sigma\)
+ in the weighting function:
+
\(f=\sqrt{|\vec{u_{i}}\cdot\vec{v_{i}}|\exp\left(-d_{i}^{2}/2\sigma^{2}\right)}\)
+
This is the same approach described in Kohl et al 2013 and the similarity
+ scores in the interval (0,1) described in that paper can exactly
+ recapitulated by setting version=1
and normalised=TRUE
.
+
Version 2 of the algorithm is described in Costa et al 2014. This
+ uses a more sophisticated and principled scoring approach based on a
+ log-odds ratio defined by the distribution of matches and non-matches in
+ sample data. This information is passed to the nblast
function in
+ the form of a scoring matrix (which can be computed by
+ create_scoringmatrix
); a default scoring matrix
+ smat.fcwb
has been constructed for Drosophila neurons.
+
Which version should I use? You should use version 2 if you are
+ working with Drosophila neurons or you have sufficient training data
+ (in the form of validated matching and random neuron pairs to construct a
+ scoring matrix). If this is not the case, you can always fall back to
+ version 1, setting the free parameter (sd or \(\sigma\)) to a value that
+ encapsulates your understanding of the location precision of neurons in
+ your species/brain region of interest. In the fly brain we have used
+ \(\sigma=3\) microns, since previous estimates of the localisation of
+ identifiable features of neurons (Jefferis, Potter et al 2007) are of this
+ order.
+
+
+
UseAlpha
+
In NBLAST v2, the alpha factor for a segment
+ indicates whether neighbouring segments are aligned in a similar direction
+ (as typical for e.g. a long range axonal projection) or randomly aligned
+ (as typical for dendritic arbours). See Costa et al. for details. Setting
+ UseAlpha=TRUE
will emphasise the axon, primary neurite etc. of a
+ neuron. This can be a particularly useful option e.g. when you are
+ searching by a traced fragment that you know or suspect to follow an axon
+ tract.
+
+
+
References
+
Kohl, J. Ostrovsky, A.D., Frechter, S., and Jefferis, G.S.X.E
+ (2013). A bidirectional circuit switch reroutes pheromone signals in male
+ and female brains. Cell 155 (7), 1610–23
+ doi:10.1016/j.cell.2013.11.025
+.
+
Costa, M., Ostrovsky, A.D., Manton, J.D., Prohaska, S., and Jefferis,
+ G.S.X.E. (2014). NBLAST: Rapid, sensitive comparison of neuronal structure
+ and construction of neuron family databases. bioRxiv preprint.
+ doi:10.1101/006346
+.
+
Jefferis G.S.X.E., Potter C.J., Chan A.M., Marin E.C., Rohlfing T., Maurer
+ C.R.J., and Luo L. (2007). Comprehensive maps of Drosophila higher
+ olfactory centers: spatially segregated fruit and pheromone representation.
+ Cell 128 (6), 1187–1203.
+ doi:10.1016/j.cell.2007.01.040
+
+
+
+
+
Examples
+
# load sample Kenyon cell data from nat package
+data ( kcs20 , package= 'nat' )
+# search one neuron against all neurons
+scores = nblast ( kcs20 [[ 'GadMARCM-F000142_seg002' ] ] , kcs20 )
+# scores from best to worst, top hit is of course same neuron
+sort ( scores , decreasing = TRUE )
+#> GadMARCM-F000142_seg002 ChaMARCM-F000586_seg002 GadMARCM-F000423_seg001
+#> 4043.1766 1914.8224 1772.8506
+#> GadMARCM-F000442_seg002 FruMARCM-M000115_seg001 FruMARCM-F000085_seg001
+#> 1007.4565 853.8108 713.2106
+#> FruMARCM-F001929_seg001 FruMARCM-F001494_seg002 GadMARCM-F000050_seg001
+#> 604.4071 481.4737 480.4146
+#> GadMARCM-F000071_seg001 FruMARCM-M001339_seg001 FruMARCM-F000188_seg001
+#> 448.1516 433.9340 404.0470
+#> GadMARCM-F000476_seg001 FruMARCM-F000270_seg001 FruMARCM-F000706_seg001
+#> 313.2021 254.7516 204.2116
+#> FruMARCM-M000842_seg002 FruMARCM-M001051_seg002 FruMARCM-M001205_seg002
+#> 196.4543 140.8164 -251.4999
+#> GadMARCM-F000122_seg001 FruMARCM-F001115_seg002
+#> -262.2352 -520.2581
+hist ( scores , breaks= 25 , col= 'grey' )
+abline ( v= 1500 , col= 'red' )
+
+
+# plot query neuron
+open3d ( )
+# plot top 3 hits (including self match with thicker lines)
+plot3d ( kcs20 [ which ( sort ( scores , decreasing = TRUE ) > 1500 ) ] , lwd= c ( 3 ,1 ,1 ) )
+rest = names ( which ( scores < 1500 ) )
+plot3d ( rest , db= kcs20 , col= 'grey' , lwd= 0.5 )
+
+# normalised scores (i.e. self match = 1) of all neurons vs each other
+# note use of progress bar
+scores.norm = nblast ( kcs20 , kcs20 , normalised = TRUE , .progress= "natprogress" )
+#> Warning: Cannot find progress bar progress_natprogress
+hist ( scores.norm , breaks= 25 , col= 'grey' )
+
+# produce a heatmap from normalised scores
+jet.colors <- colorRampPalette ( c ( "blue" , "green" , "yellow" , "red" ) )
+heatmap ( scores.norm , labCol = with ( kcs20 ,type ) , col= jet.colors ( 20 ) , symm = TRUE )
+
+
+if ( FALSE ) { # \dontrun{
+# Parallelise NBLASTing across 4 cores using doMC package
+library ( doMC )
+registerDoMC ( 4 )
+scores.norm2 = nblast ( kcs20 , kcs20 , normalised= TRUE , .parallel= TRUE )
+stopifnot ( all.equal ( scores.norm2 , scores.norm ) )
+} # }
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reference/nblast_allbyall-1.png b/reference/nblast_allbyall-1.png
new file mode 100644
index 0000000..5bb3520
Binary files /dev/null and b/reference/nblast_allbyall-1.png differ
diff --git a/reference/nblast_allbyall.character.html b/reference/nblast_allbyall.character.html
new file mode 100644
index 0000000..c1e88c9
--- /dev/null
+++ b/reference/nblast_allbyall.character.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/reference/nblast_allbyall.html b/reference/nblast_allbyall.html
new file mode 100644
index 0000000..13b4a98
--- /dev/null
+++ b/reference/nblast_allbyall.html
@@ -0,0 +1,155 @@
+
+Wrapper function to compute all by all NBLAST scores for a set of neurons — nblast_allbyall • nat.nblast
+ Skip to contents
+
+
+
+
+
+
+
+
+
Calls nblast
to compute the actual scores. Can accept
+ either a neuronlist
or neuron names as a character vector. This is a thin
+ wrapper around nblast
and its main advantage is the option of "mean"
+ normalisation for forward and reverse scores, which is the most sensible
+ input to give to a clustering algorithm as well as the choice of returning
+ a distance matrix.
+
+
+
+
Usage
+
nblast_allbyall ( x , ... )
+
+# S3 method for class 'character'
+nblast_allbyall ( x , smat = NULL , db = getOption ( "nat.default.neuronlist" ) , ... )
+
+# S3 method for class 'neuronlist'
+nblast_allbyall (
+ x ,
+ smat = NULL ,
+ distance = FALSE ,
+ normalisation = c ( "raw" , "normalised" , "mean" ) ,
+ ...
+)
+
+
+
+
Arguments
+
+
+
x
+Input neurons (neuronlist
or character vector)
+
+
+...
+Additional arguments for methods or nblast
+
+
+smat
+the scoring matrix to use (see details of nblast
+for meaning of default NULL
value)
+
+
+db
+A neuronlist
or a character vector naming one.
+Defaults to value of options("nat.default.neuronlist")
+
+
+distance
+logical indicating whether to return distances or scores.
+
+
+normalisation
+the type of normalisation procedure that should be
+carried out, selected from 'raw'
, 'normalised'
or
+'mean'
(i.e. the average of normalised scores in both directions).
+If distance=TRUE
then this cannot be raw.
+
+
+
+
Details
+
Note that nat
already provides a function
+ nhclust
for clustering, which is a wrapper for R's
+ hclust
function. nhclust
actually expects raw scores
+ as input.
+
+
+
TODO
+
It would be a good idea in the future to implement a parallel
+ version of this function.
+
+
+
+
+
Examples
+
library ( nat )
+kcs20.scoremat = nblast_allbyall ( kcs20 )
+kcs20.hclust = nhclust ( scoremat= kcs20.scoremat )
+#> The "ward" method has been renamed to "ward.D"; note new "ward.D2"
+plot ( kcs20.hclust )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reference/nblast_allbyall.neuronlist.html b/reference/nblast_allbyall.neuronlist.html
new file mode 100644
index 0000000..c1e88c9
--- /dev/null
+++ b/reference/nblast_allbyall.neuronlist.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/reference/neuron_pairs.html b/reference/neuron_pairs.html
new file mode 100644
index 0000000..34d322a
--- /dev/null
+++ b/reference/neuron_pairs.html
@@ -0,0 +1,122 @@
+
+Utility function to generate all or random pairs of neurons — neuron_pairs • nat.nblast
+ Skip to contents
+
+
+
+
+
+
+
+
+
Utility function to generate all or random pairs of neurons
+
+
+
+
Usage
+
neuron_pairs ( query , target , n = NA , ignoreSelf = TRUE )
+
+
+
+
Arguments
+
+
+
query, target
+either neuronlist
s or character vectors of
+names. If target is missing, query will be used as both query and target.
+
+
+n
+number of random pairs to draw. When NA, the default, uses
+expand.grid
to draw all pairs.
+
+
+ignoreSelf
+Logical indicating whether to omit pairs consisting of the
+same neuron (default TRUE
).
+
+
+
+
Value
+
a data.frame with two character vector columns, query and target.
+
+
+
+
+
Examples
+
neuron_pairs ( nat :: kcs20 , n= 20 )
+#> query target
+#> 1 FruMARCM-F000706_seg001 GadMARCM-F000142_seg002
+#> 2 FruMARCM-F000085_seg001 GadMARCM-F000071_seg001
+#> 3 FruMARCM-F000270_seg001 GadMARCM-F000122_seg001
+#> 4 FruMARCM-F001115_seg002 GadMARCM-F000050_seg001
+#> 5 FruMARCM-F001494_seg002 GadMARCM-F000476_seg001
+#> 6 FruMARCM-F001494_seg002 FruMARCM-M001051_seg002
+#> 7 GadMARCM-F000142_seg002 FruMARCM-M001051_seg002
+#> 8 FruMARCM-F001115_seg002 FruMARCM-M001339_seg001
+#> 9 ChaMARCM-F000586_seg002 GadMARCM-F000476_seg001
+#> 10 FruMARCM-F000270_seg001 GadMARCM-F000122_seg001
+#> 11 FruMARCM-F000270_seg001 FruMARCM-M000115_seg001
+#> 12 FruMARCM-F001494_seg002 FruMARCM-M001205_seg002
+#> 13 FruMARCM-F000706_seg001 FruMARCM-F001115_seg002
+#> 14 GadMARCM-F000122_seg001 GadMARCM-F000050_seg001
+#> 15 GadMARCM-F000476_seg001 FruMARCM-F001929_seg001
+#> 16 GadMARCM-F000476_seg001 FruMARCM-F001494_seg002
+#> 17 FruMARCM-F000270_seg001 FruMARCM-M001205_seg002
+#> 18 FruMARCM-F000085_seg001 FruMARCM-F000188_seg001
+#> 19 GadMARCM-F000122_seg001 GadMARCM-F000442_seg002
+#> 20 FruMARCM-F001115_seg002 FruMARCM-F000270_seg001
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reference/nhclust-1.png b/reference/nhclust-1.png
new file mode 100644
index 0000000..dfa92f9
Binary files /dev/null and b/reference/nhclust-1.png differ
diff --git a/reference/nhclust.html b/reference/nhclust.html
new file mode 100644
index 0000000..c756a6c
--- /dev/null
+++ b/reference/nhclust.html
@@ -0,0 +1,159 @@
+
+Cluster a set of neurons — nhclust • nat.nblast
+ Skip to contents
+
+
+
+
+
+
+
+
+
Given an NBLAST all by all score matrix (which may be specified by a package
+default) and/or a vector of neuron identifiers use hclust
to
+carry out a hierarchical clustering. The default value of the distfun
+argument will handle square distance matrices and R dist
objects.
+
+
+
+
Usage
+
nhclust (
+ neuron_names ,
+ method = "ward" ,
+ scoremat = NULL ,
+ distfun = as.dist ,
+ ... ,
+ maxneurons = 4000
+)
+
+
+
+
Arguments
+
+
+
neuron_names
+character vector of neuron identifiers.
+
+
+method
+clustering method (default Ward's).
+
+
+scoremat
+score matrix to use (see sub_score_mat
for details of
+default).
+
+
+distfun
+function to convert distance matrix returned by
+sub_dist_mat
into R dist
object (default=
+as.dist
).
+
+
+...
+additional parameters passed to hclust
.
+
+
+maxneurons
+set this to a sensible value to avoid loading huge (order
+N^2) distances directly into memory.
+
+
+
+
Value
+
An object of class hclust
which describes the tree
+ produced by the clustering process.
+
+
+
+
+
Examples
+
library ( nat )
+kcscores = nblast_allbyall ( kcs20 )
+hckcs = nhclust ( scoremat= kcscores )
+#> The "ward" method has been renamed to "ward.D"; note new "ward.D2"
+# divide hclust object into 3 groups
+library ( dendroextras )
+dkcs = colour_clusters ( hckcs , k= 3 )
+# change dendrogram labels to neuron type, extracting this information
+# from type column in the metadata data.frame attached to kcs20 neuronlist
+labels ( dkcs ) = with ( kcs20 [ labels ( dkcs ) ] , type )
+plot ( dkcs )
+
+# 3d plot of neurons in those clusters (with matching colours)
+open3d ( )
+plot3d ( hckcs , k= 3 , db= kcs20 )
+# names of neurons in 3 groups
+subset ( hckcs , k= 3 )
+#> [1] "FruMARCM-F000706_seg001" "FruMARCM-M000842_seg002"
+#> [3] "GadMARCM-F000050_seg001" "FruMARCM-M001339_seg001"
+#> [5] "FruMARCM-F001115_seg002" "FruMARCM-M001051_seg002"
+#> [7] "FruMARCM-F001494_seg002" "FruMARCM-F000188_seg001"
+#> [9] "FruMARCM-F000270_seg001" "FruMARCM-F001929_seg001"
+#> [11] "GadMARCM-F000423_seg001" "GadMARCM-F000142_seg002"
+#> [13] "ChaMARCM-F000586_seg002" "FruMARCM-M001205_seg002"
+#> [15] "GadMARCM-F000122_seg001" "GadMARCM-F000476_seg001"
+#> [17] "FruMARCM-F000085_seg001" "GadMARCM-F000071_seg001"
+#> [19] "FruMARCM-M000115_seg001" "GadMARCM-F000442_seg002"
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reference/plot3d.hclust-1.png b/reference/plot3d.hclust-1.png
new file mode 100644
index 0000000..5bb3520
Binary files /dev/null and b/reference/plot3d.hclust-1.png differ
diff --git a/reference/plot3d.hclust.html b/reference/plot3d.hclust.html
new file mode 100644
index 0000000..a247664
--- /dev/null
+++ b/reference/plot3d.hclust.html
@@ -0,0 +1,154 @@
+
+Methods to identify and plot groups of neurons cut from an hclust object — plot3d.hclust • nat.nblast
+ Skip to contents
+
+
+
+
+
+
+
+
+
plot3d.hclust
uses plot3d
to plot neurons from
+ each group, cut from the hclust
object, by colour.
+
+
+
+
Usage
+
# S3 method for class 'hclust'
+plot3d (
+ x ,
+ k = NULL ,
+ h = NULL ,
+ groups = NULL ,
+ col = rainbow ,
+ colour.selected = FALSE ,
+ ...
+)
+
+
+
+
Arguments
+
+
+
x
+an hclust
object generated by nhclust
.
+
+
+k
+number of clusters to cut from hclust
object.
+
+
+h
+height to cut hclust
object.
+
+
+groups
+numeric vector of groups to plot.
+
+
+col
+colours for groups (directly specified or a function).
+
+
+colour.selected
+When set to TRUE
the colour palette only
+applies to the displayed cluster groups (default FALSE
).
+
+
+...
+additional arguments for plot3d
+
+
+
+
Value
+
A list of rgl
IDs for plotted objects (see
+ plot3d
).
+
+
+
Details
+
Note that the colours are in the order of the dendrogram as assigned
+ by colour_clusters
.
+
+
+
+
+
Examples
+
# 20 Kenyon cells
+data ( kcs20 , package= 'nat' )
+# calculate mean, normalised NBLAST scores
+kcs20.aba = nblast_allbyall ( kcs20 )
+kcs20.hc = nhclust ( scoremat = kcs20.aba )
+#> The "ward" method has been renamed to "ward.D"; note new "ward.D2"
+# plot the resultant dendrogram
+plot ( kcs20.hc )
+
+
+# now plot the neurons in 3D coloured by cluster group
+# note that specifying db explicitly could be avoided by use of the
+# \code{nat.default.neuronlist} option.
+plot3d ( kcs20.hc , k= 3 , db= kcs20 )
+
+# only plot first two groups
+# (will plot in same colours as when all groups are plotted)
+plot3d ( kcs20.hc , k= 3 , db= kcs20 , groups= 1 : 2 )
+# only plot first two groups
+# (will be coloured with a two-tone palette)
+plot3d ( kcs20.hc , k= 3 , db= kcs20 , groups= 1 : 2 , colour.selected= TRUE )
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reference/show_similarity.html b/reference/show_similarity.html
new file mode 100644
index 0000000..7c2e0b7
--- /dev/null
+++ b/reference/show_similarity.html
@@ -0,0 +1,155 @@
+
+Display two neurons with segments in the query coloured by similarity — show_similarity • nat.nblast
+ Skip to contents
+
+
+
+
+
+
+
+
+
By default, the query neuron will be drawn with its segments shaded from red
+to blue, with red indicating a poor match to the target segments, and blue a
+good match.
+
+
+
+
Usage
+
show_similarity (
+ query ,
+ target ,
+ smat = NULL ,
+ cols = colorRampPalette ( c ( "red" , "yellow" , "cyan" , "navy" ) ) ,
+ col = "black" ,
+ AbsoluteScale = FALSE ,
+ PlotVectors = TRUE ,
+ ...
+)
+
+
+
+
Arguments
+
+
+
query
+a neuron to compare and colour.
+
+
+target
+the neuron to compare against.
+
+
+smat
+a score matrix (if NULL
, defaults to smat.fcwb
).
+
+
+cols
+the function to use to colour the segments (e.g.
+heat.colors
).
+
+
+col
+the colour with which to draw the target neuron.
+
+
+AbsoluteScale
+logical indicating whether the colours should be
+calculated based on the minimum and maximum similarities for the neuron
+(AbsoluteScale = FALSE
) or on the minimum and maximum possible for
+all neurons.
+
+
+PlotVectors
+logical indicating whether the vectors of the
+dotprops
representation should be plotted. If FALSE
, only the
+points are plotted.
+
+
+...
+extra arguments to pass to plot3d
.
+
+
+
+
Value
+
show_similarity
is called for the side effect of drawing the
+ plot; a vector of object IDs is returned.
+
+
+
+
+
Examples
+
if ( FALSE ) { # \dontrun{
+library ( nat )
+
+# Pull out gamma and alpha-beta neurons
+gamma_neurons <- subset ( kcs20 , type == 'gamma' )
+ab_neurons <- subset ( kcs20 , type == 'ab' )
+
+# Compare two alpha-beta neurons with similar branching, but dissimilar arborisation
+clear3d ( )
+show_similarity ( ab_neurons [[ 1 ] ] , ab_neurons [[ 2 ] ] )
+
+# Compare an alpha-beta and a gamma neuron with some similarities and differences
+clear3d ( )
+show_similarity ( ab_neurons [[ 1 ] ] , gamma_neurons [[ 3 ] ] )
+} # }
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reference/smat.fcwb.html b/reference/smat.fcwb.html
new file mode 100644
index 0000000..e65c5d5
--- /dev/null
+++ b/reference/smat.fcwb.html
@@ -0,0 +1,86 @@
+
+Scoring matrices for neuron similarities in FCWB template brain — smat.fcwb • nat.nblast
+ Skip to contents
+
+
+
+
+
+
+
+
+
Scoring matrices quantify the log2 odds ratio that a segment pair with a
+given distance and absolute dot product come from a pair of neurons of the
+same type, rather than unrelated neurons.
+
+
+
+
+
Details
+
These scoring matrices were generated using all by all pairs from 150 DL2
+antennal lobe projection neurons from the FlyCircuit dataset and 5000 random
+pairs from the same dataset.
+
smat.fcwb
was trained using nearest-neighbour distance and the
+tangent vector defined by the first eigen vector of the k=5 nearest
+neighbours.
+smat_alpha.fcwb
was defined as for smat.fcwb
but weighted
+by the factor alpha
defined as (l1-l2)/(l1+l2+l3) where l1,l2,l3 are
+the three eigen values.
+Most work on the flycircuit dataset has been carried out using the
+smat.fcwb
scoring matrix although the smat_alpha.fcwb
matrix
+which emphasises the significance of matches between linear regions of the
+neuron (such as axons) may have some advantages.
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reference/smat_alpha.fcwb.html b/reference/smat_alpha.fcwb.html
new file mode 100644
index 0000000..11bc9c2
--- /dev/null
+++ b/reference/smat_alpha.fcwb.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/reference/sparse_score_mat.html b/reference/sparse_score_mat.html
new file mode 100644
index 0000000..738a82a
--- /dev/null
+++ b/reference/sparse_score_mat.html
@@ -0,0 +1,129 @@
+
+Convert a subset of a square score matrix to a sparse representation — sparse_score_mat • nat.nblast
+ Skip to contents
+
+
+
+
+
+
+
+
+
This can be useful for storing raw forwards and reverse NBLAST scores for a
+set of neurons without having to store all the uncomputed elements in the
+full score matrix.
+
+
+
+
Usage
+
sparse_score_mat ( neuron_names , dense_matrix )
+
+
+
+
Arguments
+
+
+
neuron_names
+a character vector of neuron names to save scores for.
+
+
+dense_matrix
+the original, dense version of the full score matrix.
+
+
+
+
+
+
+
Examples
+
data ( kcs20 , package = "nat" )
+scores = nblast_allbyall ( kcs20 )
+scores.3.sparse = sparse_score_mat ( names ( kcs20 ) [ 3 ] , scores )
+scores.3.sparse
+#> [1] 3234.54125 -935.33772 3382.60124 -515.53376 125.68989 244.92594
+#> [7] 4259.57193 480.41458 2048.01759 2068.51808 1278.68688 1359.49507
+#> [13] -15.28677 3403.75462 -313.24728 73.74186 3277.87783 3122.69555
+#> [19] 2656.70773 1487.45673 301.63902 360.64137 23.53454 2849.13647
+#> [25] 309.92651 4043.17656 1962.12743 4202.62578 91.73056 4567.08113
+#> [31] 1259.18024 4703.75189 1075.50543 4726.53035 62.48739 4760.69804
+#> [37] 2889.29188 4965.70417 -410.27503 5159.32108 -377.93391 5204.87800
+#> [43] 2751.04667 5227.65646 2847.10004 5204.87800 1635.40240 5478.21951
+#> [49] 1731.55066 5512.38720 -514.09460 5637.66873 -204.79399 5432.66259
+#> [55] -393.66139 5762.95025 1968.02090 5580.72258
+#> Class 'spam' (32-bit)
+# can also add additional submatrices
+fill_in_sparse_score_mat ( scores.3.sparse ,scores [ 3 : 6 ,3 : 4 ] )
+#> [1] 3234.54125 -935.33772 3382.60124 -515.53376 125.68989 244.92594
+#> [7] 4259.57193 480.41458 2048.01759 2068.51808 1278.68688 1359.49507
+#> [13] -15.28677 3403.75462 -313.24728 73.74186 3277.87783 3122.69555
+#> [19] 2656.70773 1487.45673 301.63902 360.64137 23.53454 2849.13647
+#> [25] 309.92651 4043.17656 1962.12743 254.75164 4202.62578 91.73056
+#> [31] -520.25810 4567.08113 1259.18024 4703.75189 1075.50543 4726.53035
+#> [37] 62.48739 4760.69804 2889.29188 4965.70417 -410.27503 5159.32108
+#> [43] -377.93391 5204.87800 2751.04667 5227.65646 2847.10004 5204.87800
+#> [49] 1635.40240 5478.21951 1731.55066 5512.38720 -514.09460 5637.66873
+#> [55] -204.79399 5432.66259 -393.66139 5762.95025 1968.02090 5580.72258
+#> Class 'spam' (32-bit)
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reference/sub_dist_mat.html b/reference/sub_dist_mat.html
new file mode 100644
index 0000000..93e5be6
--- /dev/null
+++ b/reference/sub_dist_mat.html
@@ -0,0 +1,121 @@
+
+Convert (a subset of) a raw score matrix to a distance matrix — sub_dist_mat • nat.nblast
+ Skip to contents
+
+
+
+
+
+
+
+
+
This function can convert a raw score matrix returned by
+ nblast
into a square distance matrix or dist
object. It can
+ be used with file-backed matrices as well as regular R matrices resident in
+ memory.
+
+
+
+
Usage
+
sub_dist_mat (
+ neuron_names ,
+ scoremat = NULL ,
+ form = c ( "matrix" , "dist" ) ,
+ maxneurons = NA
+)
+
+
+
+
Arguments
+
+
+
neuron_names
+character vector of neuron identifiers.
+
+
+scoremat
+score matrix to use (see sub_score_mat
for details of
+default).
+
+
+form
+the type of object to return.
+
+
+maxneurons
+set this to a sensible value to avoid loading huge (order
+N^2) distances directly into memory.
+
+
+
+
Value
+
return An object of class matrix or dist (as determined by the form
+ argument), corresponding to a subset of the distance matrix
+
+
+
Details
+
Note that if neuron_names
is missing then the rownames of
+ scoremat
will be used i.e. every neuron in scoremat
will be
+ used.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reference/sub_score_mat.html b/reference/sub_score_mat.html
new file mode 100644
index 0000000..54ad61e
--- /dev/null
+++ b/reference/sub_score_mat.html
@@ -0,0 +1,119 @@
+
+Return scores (or distances) for given query and target neurons — sub_score_mat • nat.nblast
+ Skip to contents
+
+
+
+
+
+
+
+
+
Scores can either be returned as raw numbers, normalised such that a self-hit
+has score 1, or as the average of the normalised scores in both the forwards
+& reverse directions (i.e. |query->target| + |target->query| / 2
).
+Distances are returned as either 1 - normscore
in the forwards
+direction, or as 1 - normscorebar
, where normscorebar
is
+normscore
averaged across both directions.
+
+
+
+
Usage
+
sub_score_mat (
+ query ,
+ target ,
+ scoremat = NULL ,
+ distance = FALSE ,
+ normalisation = c ( "raw" , "normalised" , "mean" )
+)
+
+
+
+
Arguments
+
+
+
query, target
+character vectors of neuron identifiers.
+
+
+scoremat
+a matrix, ff matrix, bigmatrix
or a character vector
+specifying the name of an ff matrix containing the all by all score
+matrix.
+
+
+distance
+logical indicating whether to return distances or scores.
+
+
+normalisation
+the type of normalisation procedure that should be
+carried out, selected from 'raw'
, 'normalised'
or
+'mean'
(i.e. the average of normalised scores in both directions).
+If distance=TRUE
then this cannot be raw.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/reference/subset.hclust.html b/reference/subset.hclust.html
new file mode 100644
index 0000000..c936224
--- /dev/null
+++ b/reference/subset.hclust.html
@@ -0,0 +1,102 @@
+
+Return the labels of items in 1 or more groups cut from hclust object — subset.hclust • nat.nblast
+ Skip to contents
+
+
+
+
+
+
+
+
+
Return the labels of items in 1 or more groups cut from hclust object
+
+
+
+
Usage
+
# S3 method for class 'hclust'
+subset ( x , k = NULL , h = NULL , groups = NULL , ... )
+
+
+
+
Arguments
+
+
+
x
+tree like object
+
+
+k
+an integer scalar with the desired number of groups
+
+
+h
+numeric scalar with height where the tree should be cut
+
+
+groups
+a vector of which groups to inspect.
+
+
+...
+Additional parameters passed to methods
+
+
+
+
Value
+
A character vector of labels of selected items
+
+
+
Details
+
Only one of h
and k
should be supplied.
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/search.json b/search.json
new file mode 100644
index 0000000..60c4038
--- /dev/null
+++ b/search.json
@@ -0,0 +1 @@
+[{"path":"https://natverse.org/nat.nblast/SUPPORT.html","id":null,"dir":"","previous_headings":"","what":"Getting help with nat.nblast","title":"Getting help with nat.nblast","text":"Thanks using nat.nblast. ’re sorry ’re trouble, don’t worry, ’re help! get help quickly possible, please read following:","code":""},{"path":"https://natverse.org/nat.nblast/SUPPORT.html","id":"search","dir":"","previous_headings":"","what":"Search","title":"Getting help with nat.nblast","text":"first thing see anyone problem : Search nat-user google group. Search GitHub issues nat.nblast package. Search GitHub issues across whole natverse case problem related package. can add terms search search box top left page. solves problem, great!","code":""},{"path":"https://natverse.org/nat.nblast/SUPPORT.html","id":"known-problems","dir":"","previous_headings":"","what":"Known Problems","title":"Getting help with nat.nblast","text":"need help known problem: found relevant thread nat-user google group: please reply . found GitHub issue: please reply details just like (click emoji button add thumbs ) let us know ’re trouble . ’ll need GitHub login . ’re sure: ask new question nat-user google group linking found.","code":""},{"path":"https://natverse.org/nat.nblast/SUPPORT.html","id":"new-problems","dir":"","previous_headings":"","what":"New Problems","title":"Getting help with nat.nblast","text":"looks like new problem : just question: please ask nat-user google group. need report new bug: please file issue. ’re sure: please just ask nat-user google group! problem bug feature request, can easily return report - may able point existing report workaround.","code":""},{"path":"https://natverse.org/nat.nblast/SUPPORT.html","id":"good-questions-and-bug-reports","dir":"","previous_headings":"","what":"Good Questions and Bug Reports","title":"Getting help with nat.nblast","text":"specific Include code triggered problem possible, make minimal reproducible example (reprex) much easier us help can run reprex machine without data. might want use sample data nat package help, refer publicly available data online. can use reprex package help prepare reprex. Read Get help! section tidyverse site information. Thanks help!","code":""},{"path":"https://natverse.org/nat.nblast/articles/NBLAST-Clustering.html","id":"startup","dir":"Articles","previous_headings":"","what":"Startup","title":"NBLAST Clustering","text":"Install package required Load package set knitr / 3D snapshot figures","code":"install.packages('nat.nblast', dependencies = TRUE) library(nat.nblast) ## Warning in rgl.init(initValue, onlyNULL): RGL: unable to open X11 display ## Warning: 'rgl.init' failed, running with 'rgl.useNULL = TRUE'. rgl::setupKnitr()"},{"path":[]},{"path":"https://natverse.org/nat.nblast/articles/NBLAST-Clustering.html","id":"all-by-all-scores","dir":"Articles","previous_headings":"Clustering with NBLAST scores","what":"All by all scores","title":"NBLAST Clustering","text":"First create 20 x 20 NBLAST score matrix 20 Kenyon cells included nat package","code":"library(nat) kcscores <- nblast_allbyall(kcs20)"},{"path":"https://natverse.org/nat.nblast/articles/NBLAST-Clustering.html","id":"clustering","dir":"Articles","previous_headings":"Clustering with NBLAST scores","what":"Clustering","title":"NBLAST Clustering","text":"Hierarchically cluster Kenyon scores divide clustering 3 groups Plot dendrogram clustering, leaves labelled true neuron type","code":"hckcs <- nhclust(scoremat=kcscores) ## The \"ward\" method has been renamed to \"ward.D\"; note new \"ward.D2\" library(dendroextras) dkcs <- colour_clusters(hckcs, k=3) labels(dkcs) <- with(kcs20[labels(dkcs)], type) par(cex=.7) # so labels are legible plot(dkcs)"},{"path":"https://natverse.org/nat.nblast/articles/NBLAST-Clustering.html","id":"d-plot","dir":"Articles","previous_headings":"Clustering with NBLAST scores","what":"3D plot","title":"NBLAST Clustering","text":"can create interactive 3D plots using rgl package different subgroups neurons coloured according calculated clustering.","code":"plot3d(hckcs, k=3, db=kcs20, soma=T) par3d(userMatrix=diag(c(1,-1,-1,1), 4)) plot3d(MBL.surf, alpha=.1)"},{"path":"https://natverse.org/nat.nblast/authors.html","id":null,"dir":"","previous_headings":"","what":"Authors","title":"Authors and Citation","text":"Gregory Jefferis. Author, maintainer. James Manton. Author.","code":""},{"path":"https://natverse.org/nat.nblast/authors.html","id":"citation","dir":"","previous_headings":"","what":"Citation","title":"Authors and Citation","text":"Jefferis G, Manton J (2024). nat.nblast: NeuroAnatomy Toolbox ('nat') Extension Assessing Neuron Similarity Clustering. R package version 1.6.7, http://natverse.org/nat.nblast/, https://natverse.org/nat.nblast/.","code":"@Manual{, title = {nat.nblast: NeuroAnatomy Toolbox ('nat') Extension for Assessing Neuron Similarity and Clustering}, author = {Gregory Jefferis and James Manton}, year = {2024}, note = {R package version 1.6.7, http://natverse.org/nat.nblast/}, url = {https://natverse.org/nat.nblast/}, }"},{"path":"https://natverse.org/nat.nblast/index.html","id":"natnblast-","dir":"","previous_headings":"","what":"NeuroAnatomy Toolbox (nat) Extension for Assessing Neuron Similarity and Clustering","title":"NeuroAnatomy Toolbox (nat) Extension for Assessing Neuron Similarity and Clustering","text":"nat.nblast part NeuroAnatomy Toolbox suite R packages.","code":""},{"path":"https://natverse.org/nat.nblast/index.html","id":"quick-start","dir":"","previous_headings":"","what":"Quick Start","title":"NeuroAnatomy Toolbox (nat) Extension for Assessing Neuron Similarity and Clustering","text":"impatient …","code":"# install if (!require(\"devtools\")) install.packages(\"devtools\") devtools::install_github(c(\"natverse/nat\", \"natverse/nat.nblast\")) # use library(nat.nblast) # run examples for search example(\"nblast\") # run examples for clustering example(\"nhclust\") # get overview help for package ?nat.nblast # help for functions ?nblast ?nhclust # run tests library(testthat) test_package(\"nat.nblast\")"},{"path":"https://natverse.org/nat.nblast/index.html","id":"introduction","dir":"","previous_headings":"","what":"Introduction","title":"NeuroAnatomy Toolbox (nat) Extension for Assessing Neuron Similarity and Clustering","text":"R package implements NBLAST neuron similarity algorithm described Costa M, Manton JD, Ostrovsky AD, Prohaska S, Jefferis GS. NBLAST: Rapid, Sensitive Comparison Neuronal Structure Construction Neuron Family Databases.Neuron. 2016 Jul 20;91(2):293-311. doi: 10.1016/j.neuron.2016.06.012. addition basic pairwise comparison, package implements search databases neurons. also support x comparison group neurons. can produce distance matrix suitable hierarchical clustering, also implemented package. tools designed addon NeuroAnatomy Toolbox (nat) R package, installed dependency. probably find following online documentation helpful: https://natverse.org/nat.nblast/ - Online documentation R package https://jefferislab.org/si/nblast/ - Overview NBLAST algorithm online tools","code":""},{"path":"https://natverse.org/nat.nblast/index.html","id":"installation","dir":"","previous_headings":"","what":"Installation","title":"NeuroAnatomy Toolbox (nat) Extension for Assessing Neuron Similarity and Clustering","text":"package released CRAN (since v1.5), generally recommend installing development version GitHub, especially notice bug.","code":""},{"path":"https://natverse.org/nat.nblast/index.html","id":"cran-release","dir":"","previous_headings":"Installation","what":"CRAN release","title":"NeuroAnatomy Toolbox (nat) Extension for Assessing Neuron Similarity and Clustering","text":"","code":"install.packages(\"nat.nblast\")"},{"path":"https://natverse.org/nat.nblast/index.html","id":"development-version","dir":"","previous_headings":"Installation","what":"Development version","title":"NeuroAnatomy Toolbox (nat) Extension for Assessing Neuron Similarity and Clustering","text":"Use devtools install development version: Note also update nat package latest development version github. Windows users need Rtools install way.","code":"# install devtools if required if (!require(\"devtools\")) install.packages(\"devtools\") # then nat.nblast devtools::install_github(\"natverse/nat.nblast\")"},{"path":"https://natverse.org/nat.nblast/reference/NeuriteBlast.html","id":null,"dir":"Reference","previous_headings":"","what":"Produce similarity score for neuron morphologies — NeuriteBlast","title":"Produce similarity score for neuron morphologies — NeuriteBlast","text":"low-level entry point NBLAST algorithm compares morphology neuron list neurons. use cases, one probably wish use nblast instead.","code":""},{"path":"https://natverse.org/nat.nblast/reference/NeuriteBlast.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Produce similarity score for neuron morphologies — NeuriteBlast","text":"","code":"NeuriteBlast( query, target, targetBinds = NULL, normalised = FALSE, OmitFailures = NA, simplify = TRUE, ... )"},{"path":"https://natverse.org/nat.nblast/reference/NeuriteBlast.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Produce similarity score for neuron morphologies — NeuriteBlast","text":"query either single query neuron neuronlist target neuronlist compare neuron . targetBinds numeric indices names subset target. normalised whether divide scores self-match score query OmitFailures Whether omit neurons FUN gives error. default value (NA) result nblast stopping error message moment error. values, see details. simplify whether simplify scores list vector. TRUE default. time might want set false collecting something simple scores search function. See simplify2array details. ... extra arguments pass distance function.","code":""},{"path":"https://natverse.org/nat.nblast/reference/NeuriteBlast.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Produce similarity score for neuron morphologies — NeuriteBlast","text":"Named list similarity scores.","code":""},{"path":"https://natverse.org/nat.nblast/reference/NeuriteBlast.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Produce similarity score for neuron morphologies — NeuriteBlast","text":"detailed description OmitFailures argument, see details section nblast.","code":""},{"path":[]},{"path":"https://natverse.org/nat.nblast/reference/WeightedNNBasedLinesetMatching.html","id":null,"dir":"Reference","previous_headings":"","what":"Compute point & tangent vector similarity score between two linesets — WeightedNNBasedLinesetMatching","title":"Compute point & tangent vector similarity score between two linesets — WeightedNNBasedLinesetMatching","text":"WeightedNNBasedLinesetMatching low level function called nblast. end users usually need call directly. allow results NBLAST comparison inspected detail (see examples).","code":""},{"path":"https://natverse.org/nat.nblast/reference/WeightedNNBasedLinesetMatching.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Compute point & tangent vector similarity score between two linesets — WeightedNNBasedLinesetMatching","text":"","code":"WeightedNNBasedLinesetMatching(target, query, ...) # S3 method for class 'dotprops' WeightedNNBasedLinesetMatching(target, query, UseAlpha = FALSE, ...) # S3 method for class 'neuron' WeightedNNBasedLinesetMatching( target, query, UseAlpha = FALSE, OnlyClosestPoints = FALSE, ... )"},{"path":"https://natverse.org/nat.nblast/reference/WeightedNNBasedLinesetMatching.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Compute point & tangent vector similarity score between two linesets — WeightedNNBasedLinesetMatching","text":"target, query dotprops neuron objects compare (must class) ... extra arguments pass distance function. UseAlpha Whether scale dot product tangent vectors (default=F) OnlyClosestPoints Whether restrict searches closest points target (default FALSE, implemented dotprops).","code":""},{"path":"https://natverse.org/nat.nblast/reference/WeightedNNBasedLinesetMatching.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Compute point & tangent vector similarity score between two linesets — WeightedNNBasedLinesetMatching","text":"Value NNDistFun passed WeightedNNBasedLinesetMatching","code":""},{"path":"https://natverse.org/nat.nblast/reference/WeightedNNBasedLinesetMatching.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Compute point & tangent vector similarity score between two linesets — WeightedNNBasedLinesetMatching","text":"WeightedNNBasedLinesetMatching work 2 objects class dotprops neuron. code calculate scores directly neuron objects gives broadly comparable scores dotprops objects, lightly tested. Furthermore objects dotprops form used construction scoring matrices distributed package. therefore recommended convert neuron objects dotprops objects using dotprops function. UseAlpha determines whether alpha values (eig1-eig2)/sum(eig1:3) passed WeightedNNBasedLinesetMatching. used scale dot products direction vectors nearest neighbour pairs.","code":""},{"path":[]},{"path":"https://natverse.org/nat.nblast/reference/WeightedNNBasedLinesetMatching.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Compute point & tangent vector similarity score between two linesets — WeightedNNBasedLinesetMatching","text":"","code":"# Retrieve per segment distances / absolute dot products segvals=WeightedNNBasedLinesetMatching(kcs20[[1]], kcs20[[2]], NNDistFun=list) names(segvals)=c(\"dist\", \"adotprod\") pairs(segvals)"},{"path":"https://natverse.org/nat.nblast/reference/calc_dists_dotprods.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate distances and dot products between two sets of neurons — calc_dists_dotprods","title":"Calculate distances and dot products between two sets of neurons — calc_dists_dotprods","text":"Calculate distances dot products two sets neurons","code":""},{"path":"https://natverse.org/nat.nblast/reference/calc_dists_dotprods.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate distances and dot products between two sets of neurons — calc_dists_dotprods","text":"","code":"calc_dists_dotprods( query_neurons, target_neurons, subset = NULL, ignoreSelf = TRUE, ... )"},{"path":"https://natverse.org/nat.nblast/reference/calc_dists_dotprods.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate distances and dot products between two sets of neurons — calc_dists_dotprods","text":"query_neurons neuronlist use calculating distances dot products. target_neurons neuronlist use calculating distances dot products. subset data.frame specifying neurons query_neurons target_neurons compared, columns specifying query target neurons name, one row pair. unspecified, defaults --comparison. ignoreSelf Boolean indicating whether ignore comparisons neuron (default TRUE). ... extra arguments pass NeuriteBlast.","code":""},{"path":"https://natverse.org/nat.nblast/reference/calc_dists_dotprods.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate distances and dot products between two sets of neurons — calc_dists_dotprods","text":"list, one element pair neurons 2 column data.frame containing one column distances another absolute dot products.","code":""},{"path":"https://natverse.org/nat.nblast/reference/calc_dists_dotprods.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Calculate distances and dot products between two sets of neurons — calc_dists_dotprods","text":"Distances dot products raw inputs constructing scoring matrices NBLAST search algorithm.","code":""},{"path":"https://natverse.org/nat.nblast/reference/calc_prob_mat.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate probability matrix from distances and dot products between neuron segments — calc_prob_mat","title":"Calculate probability matrix from distances and dot products between neuron segments — calc_prob_mat","text":"Calculate probability matrix distances dot products neuron segments","code":""},{"path":"https://natverse.org/nat.nblast/reference/calc_prob_mat.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate probability matrix from distances and dot products between neuron segments — calc_prob_mat","text":"","code":"calc_prob_mat( nndists, dotprods, distbreaks, dotprodbreaks = seq(0, 1, by = 0.1), ReturnCounts = FALSE )"},{"path":"https://natverse.org/nat.nblast/reference/calc_prob_mat.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate probability matrix from distances and dot products between neuron segments — calc_prob_mat","text":"nndists list nearest-neighbour distances list nearest-neighbour distances dot products. dotprods list dot products. distbreaks vector specifying breaks distances probability matrix. dotprodbreaks vector specifying breaks dot products probability matrix. ReturnCounts Boolean indicating counts returned instead default probabilities.","code":""},{"path":"https://natverse.org/nat.nblast/reference/calc_prob_mat.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate probability matrix from distances and dot products between neuron segments — calc_prob_mat","text":"matrix columns specified dotprodbreaks rows specified distbreaks, containing probabilities (default value ReturnCounts=TRUE) counts (ReturnCounts=TRUE) finding neuron segments given distance dot product.","code":""},{"path":"https://natverse.org/nat.nblast/reference/calc_score_matrix.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate scoring matrix from probability matrices for matching and non-matching sets of neurons — calc_score_matrix","title":"Calculate scoring matrix from probability matrices for matching and non-matching sets of neurons — calc_score_matrix","text":"Calculate scoring matrix probability matrices matching non-matching sets neurons","code":""},{"path":"https://natverse.org/nat.nblast/reference/calc_score_matrix.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate scoring matrix from probability matrices for matching and non-matching sets of neurons — calc_score_matrix","text":"","code":"calc_score_matrix(matchmat, randmat, logbase = 2, epsilon = 1e-06)"},{"path":"https://natverse.org/nat.nblast/reference/calc_score_matrix.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate scoring matrix from probability matrices for matching and non-matching sets of neurons — calc_score_matrix","text":"matchmat probability matrix given considering 'matching' neurons. randmat probability matrix given considering 'non-matching' 'random' neurons. logbase base logarithm taken produce final scores. epsilon pseudocount prevent division zero constructing log odds ratio probability matrix.","code":""},{"path":"https://natverse.org/nat.nblast/reference/calc_score_matrix.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate scoring matrix from probability matrices for matching and non-matching sets of neurons — calc_score_matrix","text":"matrix class=c(\"scoringmatrix\", \"table\"), columns specified dotprodbreaks rows specified distbreaks, containing scores neuron segments given distance dot product.","code":""},{"path":"https://natverse.org/nat.nblast/reference/create_scoringmatrix.html","id":null,"dir":"Reference","previous_headings":"","what":"Create a scoring matrix given matching and non-matching sets of neurons — create_scoringmatrix","title":"Create a scoring matrix given matching and non-matching sets of neurons — create_scoringmatrix","text":"Calculate scoring matrix embodying logarithm odds matching pair neurite segments come structurally related rather random pair neurons. function embodies sensible default behaviours recommended end users. control available using individual functions listed See Also.","code":""},{"path":"https://natverse.org/nat.nblast/reference/create_scoringmatrix.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create a scoring matrix given matching and non-matching sets of neurons — create_scoringmatrix","text":"","code":"create_scoringmatrix( matching_neurons, nonmatching_neurons, matching_subset = NULL, non_matching_subset = NULL, ignoreSelf = TRUE, distbreaks, dotprodbreaks = seq(0, 1, by = 0.1), logbase = 2, epsilon = 1e-06, ... )"},{"path":"https://natverse.org/nat.nblast/reference/create_scoringmatrix.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create a scoring matrix given matching and non-matching sets of neurons — create_scoringmatrix","text":"matching_neurons neuronlist matching neurons. nonmatching_neurons neuronlist non-matching neurons. matching_subset, non_matching_subset data.frames indicating pairs neurons two input neuron lists used generate matching null distributions. See details default behaviour NULL. ignoreSelf Boolean indicating whether ignore comparisons neuron (default TRUE). distbreaks vector specifying breaks distances probability matrix. dotprodbreaks vector specifying breaks dot products probability matrix. logbase base logarithm taken produce final scores. epsilon pseudocount prevent division zero constructing log odds ratio probability matrix. ... extra arguments pass NeuriteBlast options call mlply call actually iterates neuron pairs.","code":""},{"path":"https://natverse.org/nat.nblast/reference/create_scoringmatrix.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create a scoring matrix given matching and non-matching sets of neurons — create_scoringmatrix","text":"matrix columns specified dotprodbreaks rows specified distbreaks, containing log odd scores neuron segments given distance dot product.","code":""},{"path":"https://natverse.org/nat.nblast/reference/create_scoringmatrix.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Create a scoring matrix given matching and non-matching sets of neurons — create_scoringmatrix","text":"default create_scoringmatrix use neurons matching_neurons create matching distribution. appropriate neurons single type. wish use multiple types neurons need specify matching_subset indicate pairs neurons type. default create_scoringmatrix use random set pairs non_matching_neurons create null distribution. number random pairs equal number matching pairs defined matching_neurons appropriate non_matching_neurons contains large collection neurons different types. may wish set random seed using set.seed want ensure exactly (pseudo-)random pairs neurons used subsequent calls.","code":""},{"path":[]},{"path":"https://natverse.org/nat.nblast/reference/create_scoringmatrix.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Create a scoring matrix given matching and non-matching sets of neurons — create_scoringmatrix","text":"3D plot","code":"# \\donttest{ # calculate scoring matrix # bring in some mushroom body neurons library(nat) data(kcs20) # convert the (connected) tracings into dotprops (point and vector) # representation, resampling at 1 micron intervals along neuron fctraces20.dps=dotprops(fctraces20, resample=1) # we will use both all kcs vs all fctraces20 and fctraces20 vs fctraces20 # as random_pairs to make the null distribution random_pairs=rbind(neuron_pairs(fctraces20), neuron_pairs(nat::kcs20, fctraces20)) # you can add .progress='natprogress' if this looks like taking a while smat=create_scoringmatrix(kcs20, c(kcs20, fctraces20.dps), non_matching_subset=random_pairs) # now plot the scoring matrix distbreaks=attr(smat,'distbreaks') distbreaks=distbreaks[-length(distbreaks)] dotprodbreaks=attr(smat,'dotprodbreaks')[-1] # Create a function interpolating colors in the range of specified colors jet.colors <- colorRampPalette( c(\"blue\", \"green\", \"yellow\", \"red\") ) # 2d filled contour plot of scoring matrix. Notice that the there is a region # at small distances and large abs dot product with the highest log odds ratio # i.e. most indicative of a match rather than non-match filled.contour(x=distbreaks, y=dotprodbreaks, z=smat, col=jet.colors(20), main='smat: log odds ratio', xlab='distance /um', ylab='abs dot product') # 3d perspective plot of the scoring matrix persp3d(x=distbreaks, y=dotprodbreaks, z=smat, col=jet.colors(20)[cut(smat,20)], xlab='distance /um', ylab='abs dot product', zlab='log odds ratio') 3D plot {\"x\":{\"material\":{\"color\":\"#000000\",\"alpha\":1,\"lit\":true,\"ambient\":\"#000000\",\"specular\":\"#FFFFFF\",\"emission\":\"#000000\",\"shininess\":50,\"smooth\":true,\"front\":\"filled\",\"back\":\"filled\",\"size\":3,\"lwd\":1,\"fog\":true,\"point_antialias\":false,\"line_antialias\":false,\"texture\":null,\"textype\":\"rgb\",\"texmode\":\"modulate\",\"texmipmap\":false,\"texminfilter\":\"linear\",\"texmagfilter\":\"linear\",\"texenvmap\":false,\"depth_mask\":true,\"depth_test\":\"less\",\"isTransparent\":false,\"polygon_offset\":[0,0],\"margin\":\"\",\"floating\":false,\"tag\":\"\",\"blend\":[\"src_alpha\",\"one_minus_src_alpha\"]},\"rootSubscene\":1,\"objects\":{\"7\":{\"id\":7,\"type\":\"surface\",\"material\":{},\"vertices\":\"0\",\"colors\":\"2\",\"dim\":[[21,10]],\"centers\":\"3\",\"normals\":\"1\",\"ignoreExtent\":false,\"flipped\":true,\"flags\":32771},\"9\":{\"id\":9,\"type\":\"text\",\"material\":{\"lit\":false,\"margin\":0,\"floating\":true,\"edge\":[0,1,1]},\"vertices\":\"4\",\"colors\":\"5\",\"texts\":[[\"distance /um\"]],\"cex\":[[1]],\"adj\":[[0.5,0.5,0.5]],\"centers\":\"6\",\"family\":[[\"sans\"]],\"font\":[[1]],\"ignoreExtent\":true,\"flags\":33808},\"10\":{\"id\":10,\"type\":\"text\",\"material\":{\"lit\":false,\"margin\":1,\"floating\":true,\"edge\":[1,1,1]},\"vertices\":\"7\",\"colors\":\"8\",\"texts\":[[\"abs dot product\"]],\"cex\":[[1]],\"adj\":[[0.5,0.5,0.5]],\"centers\":\"9\",\"family\":[[\"sans\"]],\"font\":[[1]],\"ignoreExtent\":true,\"flags\":33808},\"11\":{\"id\":11,\"type\":\"text\",\"material\":{\"lit\":false,\"margin\":2,\"floating\":true,\"edge\":[1,1,1]},\"vertices\":\"10\",\"colors\":\"11\",\"texts\":[[\"log odds ratio\"]],\"cex\":[[1]],\"adj\":[[0.5,0.5,0.5]],\"centers\":\"12\",\"family\":[[\"sans\"]],\"font\":[[1]],\"ignoreExtent\":true,\"flags\":33808},\"5\":{\"id\":5,\"type\":\"light\",\"vertices\":[[0,0,1]],\"colors\":[[1,1,1,1],[1,1,1,1],[1,1,1,1]],\"viewpoint\":true,\"finite\":false},\"4\":{\"id\":4,\"type\":\"background\",\"material\":{},\"colors\":\"13\",\"centers\":\"14\",\"sphere\":false,\"fogtype\":\"none\",\"fogscale\":1,\"flags\":32768},\"6\":{\"id\":6,\"type\":\"background\",\"material\":{\"lit\":false,\"back\":\"lines\"},\"colors\":\"15\",\"centers\":\"16\",\"sphere\":false,\"fogtype\":\"none\",\"fogscale\":1,\"flags\":32768},\"8\":{\"id\":8,\"type\":\"bboxdeco\",\"material\":{\"front\":\"lines\",\"back\":\"lines\"},\"vertices\":\"17\",\"colors\":\"18\",\"axes\":{\"mode\":[\"pretty\",\"pretty\",\"pretty\"],\"step\":[10,0.2000000029802322,2],\"nticks\":[5,5,5],\"marklen\":[15,15,15],\"expand\":[1.029999971389771,1.029999971389771,1.029999971389771]},\"draw_front\":true,\"flags\":32769},\"1\":{\"id\":1,\"type\":\"subscene\",\"par3d\":{\"antialias\":8,\"FOV\":30,\"ignoreExtent\":false,\"listeners\":1,\"mouseMode\":{\"none\":\"none\",\"left\":\"trackball\",\"right\":\"zoom\",\"middle\":\"fov\",\"wheel\":\"pull\"},\"observer\":[0,0,100.7774124145508],\"modelMatrix\":[[0.594437837600708,0,0,-11.88875675201416],[0,9.03598690032959,2.288785457611084,-5.083317279815674],[0,-24.82616996765137,0.8330498337745667,-87.16433715820312],[0,0,0,1]],\"projMatrix\":[[3.732050895690918,0,0,0],[0,3.732050895690918,0,0],[0,0,-3.863703489303589,-363.2909240722656],[0,0,-1,0]],\"skipRedraw\":false,\"userMatrix\":[[1,0,0,0],[0,0.3420201433256682,0.9396926207859085,0],[0,-0.9396926207859085,0.3420201433256682,0],[0,0,0,1]],\"userProjection\":[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],\"scale\":[0.594437837600708,26.41945838928223,2.435674667358398],\"viewport\":{\"x\":0,\"y\":0,\"width\":1,\"height\":1},\"zoom\":1,\"bbox\":[0,40,0.1000000014901161,1,-4.831493377685547,4.930694103240967],\"windowRect\":[0,0,256,256],\"family\":\"sans\",\"font\":1,\"cex\":1,\"useFreeType\":true,\"fontname\":\"NULL\",\"maxClipPlanes\":2147483647,\"glVersion\":\"NA\",\"activeSubscene\":0},\"embeddings\":{\"viewport\":\"replace\",\"projection\":\"replace\",\"model\":\"replace\",\"mouse\":\"replace\"},\"objects\":[6,8,7,9,10,11,5],\"subscenes\":[],\"flags\":34067}},\"crosstalk\":{\"key\":[],\"group\":[],\"id\":[],\"options\":[]},\"width\":700,\"height\":432.6328800988875,\"buffer\":{\"accessors\":[{\"bufferView\":0,\"componentType\":5126,\"count\":210,\"type\":\"VEC3\"},{\"bufferView\":1,\"componentType\":5126,\"count\":210,\"type\":\"VEC3\"},{\"bufferView\":2,\"componentType\":5121,\"count\":210,\"type\":\"VEC4\",\"normalized\":true},{\"bufferView\":3,\"componentType\":5126,\"count\":180,\"type\":\"VEC3\"},{\"bufferView\":4,\"componentType\":5126,\"count\":1,\"type\":\"VEC3\"},{\"bufferView\":5,\"componentType\":5121,\"count\":1,\"type\":\"VEC4\"},{\"bufferView\":6,\"componentType\":5126,\"count\":1,\"type\":\"VEC3\"},{\"bufferView\":7,\"componentType\":5126,\"count\":1,\"type\":\"VEC3\"},{\"bufferView\":8,\"componentType\":5121,\"count\":1,\"type\":\"VEC4\"},{\"bufferView\":9,\"componentType\":5126,\"count\":1,\"type\":\"VEC3\"},{\"bufferView\":10,\"componentType\":5126,\"count\":1,\"type\":\"VEC3\"},{\"bufferView\":11,\"componentType\":5121,\"count\":1,\"type\":\"VEC4\"},{\"bufferView\":12,\"componentType\":5126,\"count\":1,\"type\":\"VEC3\"},{\"bufferView\":13,\"componentType\":5126,\"count\":1,\"type\":\"VEC4\"},{\"bufferView\":14,\"componentType\":5121,\"count\":1,\"type\":\"VEC3\"},{\"bufferView\":15,\"componentType\":5121,\"count\":1,\"type\":\"VEC4\"},{\"bufferView\":16,\"componentType\":5121,\"count\":1,\"type\":\"VEC3\"},{\"bufferView\":17,\"componentType\":5126,\"count\":15,\"type\":\"VEC3\"},{\"bufferView\":18,\"componentType\":5121,\"count\":1,\"type\":\"VEC4\"}],\"bufferViews\":[{\"buffer\":0,\"byteLength\":2520,\"byteOffset\":0},{\"buffer\":0,\"byteLength\":2520,\"byteOffset\":2520},{\"buffer\":0,\"byteLength\":840,\"byteOffset\":5040},{\"buffer\":0,\"byteLength\":2160,\"byteOffset\":5880},{\"buffer\":0,\"byteLength\":12,\"byteOffset\":8040},{\"buffer\":0,\"byteLength\":4,\"byteOffset\":8052},{\"buffer\":0,\"byteLength\":12,\"byteOffset\":8056},{\"buffer\":0,\"byteLength\":12,\"byteOffset\":8068},{\"buffer\":0,\"byteLength\":4,\"byteOffset\":8080},{\"buffer\":0,\"byteLength\":12,\"byteOffset\":8084},{\"buffer\":0,\"byteLength\":12,\"byteOffset\":8096},{\"buffer\":0,\"byteLength\":4,\"byteOffset\":8108},{\"buffer\":0,\"byteLength\":12,\"byteOffset\":8112},{\"buffer\":0,\"byteLength\":16,\"byteOffset\":8124},{\"buffer\":0,\"byteLength\":3,\"byteOffset\":8140},{\"buffer\":0,\"byteLength\":4,\"byteOffset\":8143},{\"buffer\":0,\"byteLength\":3,\"byteOffset\":8147},{\"buffer\":0,\"byteLength\":180,\"byteOffset\":8152},{\"buffer\":0,\"byteLength\":4,\"byteOffset\":8332}],\"buffers\":[{\"byteLength\":8336,\"bytes\":\"AAAAAM3MzD16mTNAAABAP83MzD2kPxxAAADAP83MzD1TMBtAAAAAQM3MzD23SAlAAAAgQM3M\\nzD1cFQ5AAABAQM3MzD14XPQ/AABgQM3MzD0oE+U/AACAQM3MzD0Vb/M/AACgQM3MzD3s1+A/\\nAADAQM3MzD0Hjt8/AADgQM3MzD2G7OY/AAAAQc3MzD186uA/AAAQQc3MzD0xm9s/AAAgQc3M\\nzD2TNec/AABAQc3MzD05zs0/AABgQc3MzD0D/pA/AACAQc3MzD3xhjY/AACgQc3MzD0jGoy9\\nAADIQc3MzD1GI8O+AADwQc3MzD0UUre/AAAgQs3MzD3GQInAAAAAAM3MTD6QBDdAAABAP83M\\nTD7egjRAAADAP83MTD5LRjFAAAAAQM3MTD4HzQxAAAAgQM3MTD52xwFAAABAQM3MTD4X/w5A\\nAABgQM3MTD5sUg1AAACAQM3MTD4RlAFAAACgQM3MTD5U6QVAAADAQM3MTD7M9/w/AADgQM3M\\nTD6mbOE/AAAAQc3MTD79ffs/AAAQQc3MTD6NTOc/AAAgQc3MTD7xZeE/AABAQc3MTD7MF7o/\\nAABgQc3MTD7tx50/AACAQc3MTD4LfkU/AACgQc3MTD6wNY89AADIQc3MTD5tFB+/AADwQc3M\\nTD4uv9m/AAAgQs3MTD7kbYnAAAAAAJqZmT4bJTFAAABAP5qZmT416S5AAADAP5qZmT5orzJA\\nAAAAQJqZmT50gg5AAAAgQJqZmT5beBFAAABAQJqZmT70SvI/AABgQJqZmT5N7wZAAACAQJqZ\\nmT44ygtAAACgQJqZmT57IAJAAADAQJqZmT4FHfk/AADgQJqZmT6PJ/8/AAAAQZqZmT4t8fw/\\nAAAQQZqZmT75ZPs/AAAgQZqZmT4QleI/AABAQZqZmT6icMs/AABgQZqZmT5aQa8/AACAQZqZ\\nmT4CBE4/AACgQZqZmT6orD0+AADIQZqZmT6+xAq/AADwQZqZmT7tesK/AAAgQpqZmT68rYvA\\nAAAAAM3MzD5tJi1AAABAP83MzD7nrEFAAADAP83MzD4VPSBAAAAAQM3MzD49QCpAAAAgQM3M\\nzD7LxDpAAABAQM3MzD5QRB9AAABgQM3MzD66jhNAAACAQM3MzD5tZRpAAACgQM3MzD7cERFA\\nAADAQM3MzD7uBhJAAADgQM3MzD7j+gNAAAAAQc3MzD75vQpAAAAQQc3MzD7ebf0/AAAgQc3M\\nzD5La/Y/AABAQc3MzD5ImeI/AABgQc3MzD6TzrM/AACAQc3MzD6p4Fk/AACgQc3MzD43c1M+\\nAADIQc3MzD4Q3x2/AADwQc3MzD4SGcO/AAAgQs3MzD74hIHAAAAAAAAAAD/EajJAAABAPwAA\\nAD9MkUFAAADAPwAAAD/dVDBAAAAAQAAAAD8SeiBAAAAgQAAAAD+rWDpAAABAQAAAAD8aYRxA\\nAABgQAAAAD9GuipAAACAQAAAAD8mVyNAAACgQAAAAD9k8A1AAADAQAAAAD++kQxAAADgQAAA\\nAD+WCyBAAAAAQQAAAD/RCQ1AAAAQQQAAAD9laQFAAAAgQQAAAD8yufk/AABAQQAAAD957d4/\\nAABgQQAAAD/+dLI/AACAQQAAAD98tGs/AACgQQAAAD8QXVo+AADIQQAAAD81yCu/AADwQQAA\\nAD8Aws6/AAAgQgAAAD8e7IrAAAAAAJqZGT/H4W5AAABAP5qZGT+nTnJAAADAP5qZGT9BOkhA\\nAAAAQJqZGT/JYDpAAAAgQJqZGT9dkS1AAABAQJqZGT8xpjJAAABgQJqZGT8DiyVAAACAQJqZ\\nGT9YAC1AAACgQJqZGT/bNSFAAADAQJqZGT8CRBlAAADgQJqZGT+cTxtAAAAAQZqZGT8KSwlA\\nAAAQQZqZGT+kxAVAAAAgQZqZGT//L/A/AABAQZqZGT8DAOA/AABgQZqZGT+BOcg/AACAQZqZ\\nGT8fwHs/AACgQZqZGT+utHI+AADIQZqZGT86VTC/AADwQZqZGT9O+s6/AAAgQpqZGT98zYfA\\nAAAAADMzMz/6FINAAABAPzMzMz9qP2JAAADAPzMzMz/u/V1AAAAAQDMzMz9ZOU5AAAAgQDMz\\nMz8YZUlAAABAQDMzMz8NPUFAAABgQDMzMz/hkC5AAACAQDMzMz/4NSlAAACgQDMzMz8lgBtA\\nAADAQDMzMz8eORxAAADgQDMzMz/2hx1AAAAAQTMzMz8JyhNAAAAQQTMzMz+lMw5AAAAgQTMz\\nMz/S7AhAAABAQTMzMz+tQf4/AABgQTMzMz+uiM4/AACAQTMzMz/6LnI/AACgQTMzMz/adnM+\\nAADIQTMzMz9UtUa/AADwQTMzMz+bRti/AAAgQjMzMz/hlYXAAAAAAM3MTD+eTJBAAABAP83M\\nTD/B84pAAADAP83MTD/lF2pAAAAAQM3MTD8+zGdAAAAgQM3MTD8WgU5AAABAQM3MTD+z0EFA\\nAABgQM3MTD+wyktAAACAQM3MTD9sTC5AAACgQM3MTD998CpAAADAQM3MTD/kPSBAAADgQM3M\\nTD/+yBhAAAAAQc3MTD875BpAAAAQQc3MTD8+RRhAAAAgQc3MTD///w5AAABAQc3MTD+vkwZA\\nAABgQc3MTD8jpN4/AACAQc3MTD/caoY/AACgQc3MTD+YcOs9AADIQc3MTD+AkT2/AADwQc3M\\nTD9Le9W/AAAgQs3MTD/QS47AAAAAAGZmZj8/yJ1AAABAP2ZmZj8Qc49AAADAP2ZmZj8OZYFA\\nAAAAQGZmZj+bEXBAAAAgQGZmZj+lW2FAAABAQGZmZj/111VAAABgQGZmZj+nmllAAACAQGZm\\nZj8UkFBAAACgQGZmZj8jMkFAAADAQGZmZj8MVjNAAADgQGZmZj/c+DFAAAAAQWZmZj8isChA\\nAAAQQWZmZj9nQiJAAAAgQWZmZj9v/CNAAABAQWZmZj8z1whAAABgQWZmZj9BxtI/AACAQWZm\\nZj9k1Xw/AACgQWZmZj9cJAA+AADIQWZmZj+glV6/AADwQWZmZj9Zi8W/AAAgQmZmZj+Ym5rA\\nAAAAAAAAgD8aA5FAAABAPwAAgD9gqY5AAADAPwAAgD9hqn1AAAAAQAAAgD+uCX9AAAAgQAAA\\ngD/1N3FAAABAQAAAgD/01GBAAABgQAAAgD+NSlRAAACAQAAAgD/ULVNAAACgQAAAgD80g0lA\\nAADAQAAAgD80bzxAAADgQAAAgD+pdj1AAAAAQQAAgD/CwTJAAAAQQQAAgD/+Sy1AAAAgQQAA\\ngD8ciSVAAABAQQAAgD9IxRFAAABgQQAAgD/qFs8/AACAQQAAgD8bFok/AACgQQAAgD/OSiG9\\nAADIQQAAgD+ASpO/AADwQQAAgD/8X9a/AAAgQgAAgD+86pnAOvi8PfK4R78gZR4/HYLtPeAt\\nZb89Sdw+uThTPuZ2Yb/5S9o+RjfVPrTVqr5ngVg/k3IWPH9hIT44ynw/XW7BPTpFWL/g0wY/\\naT/hO3KCb79EwbQ+Uac1PdPbY7+GReg+vJHiPB/xab+7bs8+iPTWPP5rRL9nCiQ/h6grO9Mr\\nvL42FW4/YlmHPIa2Ir+Ml0U/d3HQPFG4C78caVY/0DNTPTl7mz6cjXM/QdkFPmao6D4Uk2E/\\nQdI3PgDBs74AQms/bX0dPnBxIr/y5kE/0EANPjbn4L6XQGM/N3/APRXYQD9LoyY/WpoWPirE\\nTT9zkxM/6E1VPkhRFj8GPkg/3og9PrUm0LwTfns/SR1aPY2Jnb7mMnM/l7jKPpf0Bb+6LUE/\\nhGXCPpREz76u81Q/qpVKPio4Qb4+P3Y/YQ2CPEFF2739fn4/6dZMPBlWBL9NHls/BlERPTHT\\nK78ijT0/VkFoPTd9DL+Ag1U/SI6cPd2Qy75iFGo/PZ9gu18x5b6x6mQ/Pi4HOiaTGb+h0Uw/\\ncqZiPejcCL8b4Vc/xr+pPcxeCb4MzHw/JdwFPkvWTb4iiHg/g60jPgokD7+dQlA/H14mPqnh\\nGb/3UEg/RaT/PWImDb/IKlM/eXoiPoU9SL78v3c/kJtrPtD/Xj5T0HI/nHqEPqndej5PMm8/\\nLfa6vcTUoz5taXE/tzriPIRsiD7PpHY/t73LPpURqTx/zGo/AXu3PmeLKb9rcig/9NMyPVEh\\nYr9i9+4+h/o4O8uOJL9BGkQ/Ef+wO7djAr8iTVw/c7TDPO+6Mb/0JTg/oN50PYn6Eb8jvlE/\\nThaSPdVFDL9SXlU/2/ATPIWVK7/5+D0/gi2aO9xyH7/HRkg/WAC7PbxkC78XclU/0EK5PQ6s\\nH7/swUY/9defPZ0COL9m2jA/fAEoPm1/HL9TMUY/mAs+Pi6W6L6nDl8/+SISPuCmvL7yKms/\\nos8mPriDjr4VUnI/BtVVPjSurb4P0Go/Jth0Ph/21b4bYGA/ZFxwvhb6pr6sbGo/9f74vAL/\\nwb7MyGw/5hanPpCzlb7aHWY/ce1DvqRdt75V8mk/9TD1PURUHL/qZEg/vgx+Pv2xO7+kFiI/\\nPHGwvFUkXr+WO/4+W4hRPWHtUr+iehA/s4bEPUAiDL+K01Q/JObwOk4ABL/fV1s/LxcHPOTS\\nNr8uLzM/2ynDPTllHr8IoUc/HW3SPTtB5r5JImM/IX+ZPSgn7L7IVGI/dEn4PYr/vL4W42s/\\nDiVPPh6YjL5ypnA/XJlMPgD8oL6Rkm0/Cq4kPljQf71VKXw/dpglPvFtrj46GW0/ReRLPsyN\\nyD4m9mU/YKR2PsARkD66ym0/lxJPvibXSL9cEBY/ZO8VPdqLV7//ywk/qpJWPveIU79R1QU/\\nK0h5vuP7sL6H/mc/NCsMPgPWeT43x3U/TtKzPjkiGb5SnWw/kv5xvATqD79Mr1M/yWW9PcVr\\nLb810To/22rgPfYmBL9rc1k/kg2PvDJLlL57/HQ/2bCYPcbhLr4QhHs/HCIaPir0H75553k/\\nV+P8PRdBQ76WTnk//sO+PUU/Ibzj334/gKLwPY24gruFOX4/KtxEPkPerb41tGs/ZBJDPk6n\\n2b7ghmI/x08vPoU2DL7OxXk/bfExPsUCYT7Kv3U/DmpPPmhTqj7qyGs/1zZ7PqmMoT73qWo/\\nJoFrPaWaar+LyMo+onUpPk1OZL8Gktc+iyQpPtCWbb9R36o+2ebKPVQTbb8Wa7o+M81TPeG+\\nWb//9wU/6A0tPj69Qb9UpSE/w0okPb4O9b6ziWA/F8TFPeIEV76hEXk/3psCPlGr0b6nQWc/\\n7sRUvJ6h3L7m/GY/QYaUPYmEPb415Ho/72AyPr33lL6u1HA/4urJPdMuCL9BTFc/umLJPWjR\\nAb/DM1s/l6u3PftPEL91MlI/ml46PqLRB7+H8FM/hxFePuNghb5c2HA/otg5Pl1VvjyjrXs/\\n7LQ0PjldiD4glXI/sjpdPjTj4j0LV3g/KDR+Pv4OQr7QMXM/kKEvPjAcY7/DWts+sJo/Po/9\\nWr8qQ/c+4ik2PrDaZb/OMc4+ZAn/PYqGbL/ZNLk+C885PmUDVb9iLQY/q13+PXgwQ7+tjyI/\\ntqVAPmk7Nb8bRi4/LqMwPu+IyL7lXmc/7tnePZtMir6g53Q/Bi8oPSsAkb5LS3U/R5CFPfMz\\nmL4u23M/31ufPU9WJ7/8tUA/Zi9qPW5cUb/9lxI/cpxcPfg3Vb91Ag0/rveZPf7sS7+zjhk/\\n+kBBPjSRHL+nsUQ/PcR5PuZuNL7DInQ/Wbk7Pj6GXj6Ib3U/yxI2Pk+ggD69knM/9XhWPvQ4\\nXj5JFHQ/1Ul2PvGreD4rlXA/UOTyPY+0cr9cG5c+YadBPtW5bb/PcaM+vlkdPqY2ar8aIb8+\\nylNTPiVjYL+9qt4+THdqPi7oSb+CDxI/LK0aPssFSr/pZxg/ZompPZkwYb+zze8+H1gDPoiH\\nZr8KvNQ+qeNuPbRZZL+Wg+U+fhcuPc1VSr9tdBw//1wsPW5WKr8RzT4/ZGYiPeZpR7+0NSA/\\nw7gmPWyTV7/brAk/U/xkPfIBSr8Lmhw/UtcHPjz+Er+t0k4/ci6APoZ/eb7S3m8/qjOHPtrh\\n6TwTznY/kQBEPi17eT4YZ3M/v+M0PrVPvz001no/LupYPom/lT08fXk/kF5+PnzXwz7N0mM/\\nFsTXPpPghr6iJV4/IzfUPgXel76wQFw/GGu6PlP3Bb/TO0U/SQxjPkjVUb8IOAc/y8Q7Pm1j\\nZr8zhco+o3OwPToQWr+RRwQ/uN8JPvqYJr8eSz8/ULkNPuxmPL+RqSk/JODDPW7AXb9oGfs+\\nhJ5RPRq6Zr9zRNw+HPQBPUZuaL/H+NU+6LUzPaeiYr+jCO0+u7INPTDeU7+/aw8/AnOtPR9C\\nN7++bjE/Na04PmzqxL5UwWc/H1CMPpQewT1PA3U/bjCGPi/dXj7Gr3A/nAE8PqdF9z67Lls/\\nG0gAPsqKKz9yTDs/TX5KPo4J8z5Oj1s/VPKTPsYNmD4v/Wg/5xsOPpGTMj/R9DM/UE3TPoS5\\nCj+gbTs/l+/nPrGLDz4/Y2E/aT87PlBvOb8ULCo/NMs4PhJ+Zb8NOM8+jn9NPsVtLb8UKTU/\\nzPvwPYcXPjxJNH4/suwzPnR4h750vnI/tLMLPtZPNb/MUTE/lVOBPfw8U783tg8/8RAlPb83\\nWr/NdAU/MsKCPUHtWr9frgM/wRBZPee9Pr9PNCo/j1G0PSHjE79Rvk8/tpRRPg+i577ANV4/\\nsWaKPgRMhb6MSW0/B+uGPgxLOr1mrXY/pKsrPtzWLT999TY/aqeVPWaqYz9pH+c+fjc0PvNq\\nLD/eyTc/neKTPqyBrj4ICGU//6EA///JAP//yQD//8kA///JAP//8QD///EA///xAP//8QD/\\n//EA///xAP//8QD///EA///xAP//8QD/5P8A/7v/AP9r/wD/a/8A/wDxDf8AKNb//6EA//+h\\nAP//oQD//8kA///JAP//yQD//8kA///JAP//yQD///EA///xAP//8QD///EA///xAP/k/wD/\\n5P8A/7v/AP+T/wD/Q/8A/wDxDf8AKNb//6EA//+hAP//oQD//8kA///JAP//8QD//8kA///J\\nAP//yQD///EA///xAP//8QD///EA///xAP//8QD/5P8A/7v/AP+T/wD/Q/8A/wDxDf8AAP//\\n/6EA//94AP//oQD//6EA//+hAP//yQD//8kA///JAP//yQD//8kA///JAP//yQD///EA///x\\nAP//8QD/5P8A/7v/AP+T/wD/Q/8A/wDxDf8AKNb//6EA//94AP//oQD//6EA//+hAP//yQD/\\n/6EA//+hAP//yQD//8kA//+hAP//yQD//8kA///xAP//8QD/5P8A/7v/AP+T/wD/Q/8A/wDx\\nDf8AKNb//1AA//9QAP//eAD//6EA//+hAP//oQD//6EA//+hAP//oQD//8kA///JAP//yQD/\\n/8kA///xAP//8QD///EA/7v/AP+T/wD/Q/8A/wDxDf8AKNb//ygA//9QAP//UAD//3gA//94\\nAP//eAD//6EA//+hAP//yQD//8kA///JAP//yQD//8kA///JAP//8QD///EA/7v/AP+T/wD/\\nQ/8A/wDxDf8AKNb//wAA//8oAP//UAD//1AA//94AP//eAD//3gA//+hAP//oQD//6EA///J\\nAP//yQD//8kA///JAP//yQD///EA/+T/AP+T/wD/Q/8A/wDxDf8AAP///wAA//8AAP//KAD/\\n/1AA//9QAP//eAD//3gA//94AP//eAD//6EA//+hAP//oQD//6EA//+hAP//yQD///EA/7v/\\nAP+T/wD/Q/8A/wDxDf8AAP///wAA//8AAP//KAD//ygA//9QAP//UAD//3gA//94AP//eAD/\\n/6EA//+hAP//oQD//6EA//+hAP//yQD///EA/+T/AP9r/wD/Gv8A/wDxDf8AAP//AADAPpqZ\\nGT4k2C5AAACQP5qZGT5ITidAAADgP5qZGT4XoxhAAAAQQJqZGT6kfAlAAAAwQJqZGT6JQgZA\\nAABQQJqZGT5VQgJAAABwQJqZGT7Ok/0/AACQQJqZGT5y0Pg/AACwQJqZGT4aTPI/AADQQJqZ\\nGT7AN+k/AADwQJqZGT5pMOk/AAAIQZqZGT4N1Oc/AAAYQZqZGT7Q4OI/AAAwQZqZGT5iINQ/\\nAABQQZqZGT79qq0/AABwQZqZGT43ZHY/AACQQZqZGT43NL4+AAC0QZqZGT5PIYC+AADcQZqZ\\nGT4SWYS/AAAMQpqZGT59eTvAAADAPgAAgD5v5TJAAACQPwAAgD5x2DFAAADgPwAAgD5M0R9A\\nAAAQQAAAgD7TowtAAAAwQAAAgD4Y2QZAAABQQAAAgD6TGQdAAABwQAAAgD4AaAhAAACQQAAA\\ngD4GWgVAAACwQAAAgD4OxQBAAADQQAAAgD5CqvU/AADwQAAAgD7YQPY/AAAIQQAAgD4syPY/\\nAAAYQQAAgD4iq+k/AAAwQQAAgD7cYNI/AABQQQAAgD5spLQ/AABwQQAAgD6TsoU/AACQQQAA\\ngD73aeo+AAC0QQAAgD5Lh2i+AADcQQAAgD6sSYy/AAAMQgAAgD4UFT7AAADAPjMzsz5puDNA\\nAACQPzMzsz6m4DBAAADgPzMzsz7L6yJAAAAQQDMzsz72PyFAAAAwQDMzsz68KRlAAABQQDMz\\nsz70uQxAAABwQDMzsz5rKxBAAACQQDMzsz5/WA5AAACwQDMzsz7ycQhAAADQQDMzsz4HiQRA\\nAADwQDMzsz5OMQNAAAAIQTMzsz7/ZwFAAAAYQTMzsz7NdPQ/AAAwQTMzsz6RwuE/AABQQTMz\\nsz52RsQ/AABwQTMzsz6RwI0/AACQQTMzsz4oCwM/AAC0QTMzsz7WW0S+AADcQTMzsz56eYa/\\nAAAMQjMzsz7aSzfAAADAPmZm5j7ZszhAAACQP2Zm5j4J9DRAAADgP2Zm5j4Q0yZAAAAQQGZm\\n5j7x9S9AAAAwQGZm5j64MCxAAABQQGZm5j6aex5AAABwQGZm5j5lAR9AAACQQGZm5j60LxdA\\nAACwQGZm5j67Zg9AAADQQGZm5j7JpxBAAADwQGZm5j6R8w5AAAAIQWZm5j4H+gVAAAAYQWZm\\n5j5IGfw/AAAwQWZm5j7Qauw/AABQQWZm5j6U8sk/AABwQWZm5j6JQ5I/AACQQWZm5j5OQgw/\\nAAC0QWZm5j4zM16+AADcQWZm5j6tq42/AAAMQmZm5j7tczjAAADAPs3MDD8gS1VAAACQP83M\\nDD/EG0tAAADgP83MDD9+2jRAAAAQQM3MDD84sTBAAAAwQM3MDD9UvC1AAABQQM3MDD8l0ydA\\nAABwQM3MDD8yJyhAAACQQM3MDD9v3x9AAACwQM3MDD8APxVAAADQQM3MDD88TBhAAADwQM3M\\nDD8EbBRAAAAIQc3MDD+5YAdAAAAYQc3MDD9REf4/AAAwQc3MDD+sNeo/AABQQc3MDD//Zs4/\\nAABwQc3MDD8zmps/AACQQc3MDD9DrhY/AAC0Qc3MDD8A2Wi+AADcQc3MDD/C8pK/AAAMQs3M\\nDD9XFD3AAADAPmdmJj9yZnJAAACQP2dmJj+QsV5AAADgP2dmJj+UtEtAAAAQQGdmJj8m5D9A\\nAAAwQGdmJj9ttjpAAABQQGdmJj/I/zFAAABwQGdmJj+NlCpAAACQQGdmJj8UuyRAAACwQGdm\\nJj/IjBxAAADQQGdmJj8slRtAAADwQGdmJj8pexVAAAAIQWdmJj9XQwxAAAAYQWdmJj9GPwVA\\nAAAwQWdmJj/VEvg/AABQQWdmJj/4QN0/AABwQWdmJj9vbqM/AACQQWdmJj9/3hk/AAC0QWdm\\nJj+sf32+AADcQWdmJj+MsZi/AAAMQmdmJj/MmTvAAADAPgAAQD9D3YNAAACQPwAAQD8vD3BA\\nAADgPwAAQD/ahl9AAAAQQAAAQD/yelNAAAAwQAAAQD/7vEZAAABQQAAAQD9UWj9AAABwQAAA\\nQD99dzRAAACQQAAAQD/BfCdAAACwQAAAQD/puSBAAADQQAAAQD/+sRxAAADwQAAAQD/OPxlA\\nAAAIQQAAQD/KSRVAAAAYQQAAQD9tmQ9AAAAwQQAAQD9WaAdAAABQQQAAQD93Je4/AABwQQAA\\nQD/LK6s/AACQQQAAQD8fVBY/AAC0QQAAQD+G/ZS+AADcQQAAQD9U+Zu/AAAMQgAAQD8WqT/A\\nAADAPpqZWT/sHpJAAACQP5qZWT/0NYRAAADgP5qZWT/3L3FAAAAQQJqZWT+l7mFAAAAwQJqZ\\nWT9Y4VFAAABQQJqZWT+AQ09AAABwQJqZWT92EElAAACQQJqZWT/IvzpAAACwQJqZWT+k7S9A\\nAADQQJqZWT9ylSdAAADwQJqZWT+OlSNAAAAIQZqZWT8Ahx9AAAAYQZqZWT8FYRtAAAAwQZqZ\\nWT/UmRBAAABQQZqZWT8KEPQ/AABwQZqZWT/8j60/AACQQZqZWT+SyBE/AAC0QZqZWT/7V6++\\nAADcQZqZWT+NRpq/AAAMQpqZWT+I1EfAAADAPjMzcz/yOZNAAACQPzMzcz+slYdAAADgPzMz\\ncz/y43tAAAAQQDMzcz+5a3BAAAAwQDMzcz8hUGJAAABQQDMzcz+HJFlAAABwQDMzcz/HaFRA\\nAACQQDMzcz/QnEtAAACwQDMzcz+mnj5AAADQQDMzcz8xzTdAAADwQDMzcz9auDJAAAAIQTMz\\ncz8SwCpAAAAYQTMzcz/8RCZAAAAwQTMzcz+CCBlAAABQQTMzcz+IxfU/AABwQTMzcz9/V6o/\\nAACQQTMzcz+BPQk/AAC0QTMzcz8bm/e+AADcQTMzcz8poKe/AAAMQjMzcz+UwE3AAADAfwAA\\ngEAAAIA/AAAAAQAAwH8AAIBAAACAPwAAwH8AAIBAAACAPwAAAAEAAMB/AACAQAAAgD8AAMB/\\nAACAQAAAgD8AAAABAADAfwAAgEAAAIA/mZiYPpmYmD6ZmJg+AACAPwAAAAEBAQEAAAAAAAAA\\nAAAAAMB/AADAfwAAIEEAAMB/AADAfwAAoEEAAMB/AADAfwAA8EEAAMB/AADAfwAAIEIAAMB/\\nAADAfwAAwH/NzEw+AADAfwAAwH/NzMw+AADAfwAAwH+amRk/AADAfwAAwH/NzEw/AADAfwAA\\nwH8AAIA/AADAfwAAwH8AAMB/AACAwAAAwH8AAMB/AAAAwAAAwH8AAMB/AAAAAAAAwH8AAMB/\\nAAAAQAAAwH8AAMB/AACAQAAAAAE=\"}]},\"context\":{\"shiny\":false,\"rmarkdown\":null},\"vertexShader\":\"#line 2 1\\n// File 1 is the vertex shader\\n#ifdef GL_ES\\n#ifdef GL_FRAGMENT_PRECISION_HIGH\\nprecision highp float;\\n#else\\nprecision mediump float;\\n#endif\\n#endif\\n\\nattribute vec3 aPos;\\nattribute vec4 aCol;\\nuniform mat4 mvMatrix;\\nuniform mat4 prMatrix;\\nvarying vec4 vCol;\\nvarying vec4 vPosition;\\n\\n#ifdef NEEDS_VNORMAL\\nattribute vec3 aNorm;\\nuniform mat4 normMatrix;\\nvarying vec4 vNormal;\\n#endif\\n\\n#if defined(HAS_TEXTURE) || defined (IS_TEXT)\\nattribute vec2 aTexcoord;\\nvarying vec2 vTexcoord;\\n#endif\\n\\n#ifdef FIXED_SIZE\\nuniform vec3 textScale;\\n#endif\\n\\n#ifdef FIXED_QUADS\\nattribute vec3 aOfs;\\n#endif\\n\\n#ifdef IS_TWOSIDED\\n#ifdef HAS_NORMALS\\nvarying float normz;\\nuniform mat4 invPrMatrix;\\n#else\\nattribute vec3 aPos1;\\nattribute vec3 aPos2;\\nvarying float normz;\\n#endif\\n#endif // IS_TWOSIDED\\n\\n#ifdef FAT_LINES\\nattribute vec3 aNext;\\nattribute vec2 aPoint;\\nvarying vec2 vPoint;\\nvarying float vLength;\\nuniform float uAspect;\\nuniform float uLwd;\\n#endif\\n\\n#ifdef USE_ENVMAP\\nvarying vec3 vReflection;\\n#endif\\n\\nvoid main(void) {\\n \\n#ifndef IS_BRUSH\\n#if defined(NCLIPPLANES) || !defined(FIXED_QUADS) || defined(HAS_FOG) || defined(USE_ENVMAP)\\n vPosition = mvMatrix * vec4(aPos, 1.);\\n#endif\\n \\n#ifndef FIXED_QUADS\\n gl_Position = prMatrix * vPosition;\\n#endif\\n#endif // !IS_BRUSH\\n \\n#ifdef IS_POINTS\\n gl_PointSize = POINTSIZE;\\n#endif\\n \\n vCol = aCol;\\n \\n// USE_ENVMAP implies NEEDS_VNORMAL\\n\\n#ifdef NEEDS_VNORMAL\\n vNormal = normMatrix * vec4(-aNorm, dot(aNorm, aPos));\\n#endif\\n\\n#ifdef USE_ENVMAP\\n vReflection = normalize(reflect(vPosition.xyz/vPosition.w, \\n normalize(vNormal.xyz/vNormal.w)));\\n#endif\\n \\n#ifdef IS_TWOSIDED\\n#ifdef HAS_NORMALS\\n /* normz should be calculated *after* projection */\\n normz = (invPrMatrix*vNormal).z;\\n#else\\n vec4 pos1 = prMatrix*(mvMatrix*vec4(aPos1, 1.));\\n pos1 = pos1/pos1.w - gl_Position/gl_Position.w;\\n vec4 pos2 = prMatrix*(mvMatrix*vec4(aPos2, 1.));\\n pos2 = pos2/pos2.w - gl_Position/gl_Position.w;\\n normz = pos1.x*pos2.y - pos1.y*pos2.x;\\n#endif\\n#endif // IS_TWOSIDED\\n \\n#ifdef NEEDS_VNORMAL\\n vNormal = vec4(normalize(vNormal.xyz), 1);\\n#endif\\n \\n#if defined(HAS_TEXTURE) || defined(IS_TEXT)\\n vTexcoord = aTexcoord;\\n#endif\\n \\n#if defined(FIXED_SIZE) && !defined(ROTATING)\\n vec4 pos = prMatrix * mvMatrix * vec4(aPos, 1.);\\n pos = pos/pos.w;\\n gl_Position = pos + vec4(aOfs*textScale, 0.);\\n#endif\\n \\n#if defined(IS_SPRITES) && !defined(FIXED_SIZE)\\n vec4 pos = mvMatrix * vec4(aPos, 1.);\\n pos = pos/pos.w + vec4(aOfs, 0.);\\n gl_Position = prMatrix*pos;\\n#endif\\n \\n#ifdef FAT_LINES\\n /* This code was inspired by Matt Deslauriers' code in \\n https://mattdesl.svbtle.com/drawing-lines-is-hard */\\n vec2 aspectVec = vec2(uAspect, 1.0);\\n mat4 projViewModel = prMatrix * mvMatrix;\\n vec4 currentProjected = projViewModel * vec4(aPos, 1.0);\\n currentProjected = currentProjected/currentProjected.w;\\n vec4 nextProjected = projViewModel * vec4(aNext, 1.0);\\n vec2 currentScreen = currentProjected.xy * aspectVec;\\n vec2 nextScreen = (nextProjected.xy / nextProjected.w) * aspectVec;\\n float len = uLwd;\\n vec2 dir = vec2(1.0, 0.0);\\n vPoint = aPoint;\\n vLength = length(nextScreen - currentScreen)/2.0;\\n vLength = vLength/(vLength + len);\\n if (vLength > 0.0) {\\n dir = normalize(nextScreen - currentScreen);\\n }\\n vec2 normal = vec2(-dir.y, dir.x);\\n dir.x /= uAspect;\\n normal.x /= uAspect;\\n vec4 offset = vec4(len*(normal*aPoint.x*aPoint.y - dir), 0.0, 0.0);\\n gl_Position = currentProjected + offset;\\n#endif\\n \\n#ifdef IS_BRUSH\\n gl_Position = vec4(aPos, 1.);\\n#endif\\n}\",\"fragmentShader\":\"#line 2 2\\n// File 2 is the fragment shader\\n#ifdef GL_ES\\n#ifdef GL_FRAGMENT_PRECISION_HIGH\\nprecision highp float;\\n#else\\nprecision mediump float;\\n#endif\\n#endif\\nvarying vec4 vCol; // carries alpha\\nvarying vec4 vPosition;\\n#if defined(HAS_TEXTURE) || defined (IS_TEXT)\\nvarying vec2 vTexcoord;\\nuniform sampler2D uSampler;\\n#endif\\n\\n#ifdef HAS_FOG\\nuniform int uFogMode;\\nuniform vec3 uFogColor;\\nuniform vec4 uFogParms;\\n#endif\\n\\n#if defined(IS_LIT) && !defined(FIXED_QUADS)\\nvarying vec4 vNormal;\\n#endif\\n\\n#if NCLIPPLANES > 0\\nuniform vec4 vClipplane[NCLIPPLANES];\\n#endif\\n\\n#if NLIGHTS > 0\\nuniform mat4 mvMatrix;\\n#endif\\n\\n#ifdef IS_LIT\\nuniform vec3 emission;\\nuniform float shininess;\\n#if NLIGHTS > 0\\nuniform vec3 ambient[NLIGHTS];\\nuniform vec3 specular[NLIGHTS]; // light*material\\nuniform vec3 diffuse[NLIGHTS];\\nuniform vec3 lightDir[NLIGHTS];\\nuniform bool viewpoint[NLIGHTS];\\nuniform bool finite[NLIGHTS];\\n#endif\\n#endif // IS_LIT\\n\\n#ifdef IS_TWOSIDED\\nuniform bool front;\\nvarying float normz;\\n#endif\\n\\n#ifdef FAT_LINES\\nvarying vec2 vPoint;\\nvarying float vLength;\\n#endif\\n\\n#ifdef USE_ENVMAP\\nvarying vec3 vReflection;\\n#endif\\n\\nvoid main(void) {\\n vec4 fragColor;\\n#ifdef FAT_LINES\\n vec2 point = vPoint;\\n bool neg = point.y < 0.0;\\n point.y = neg ? (point.y + vLength)/(1.0 - vLength) :\\n -(point.y - vLength)/(1.0 - vLength);\\n#if defined(IS_TRANSPARENT) && defined(IS_LINESTRIP)\\n if (neg && length(point) <= 1.0) discard;\\n#endif\\n point.y = min(point.y, 0.0);\\n if (length(point) > 1.0) discard;\\n#endif // FAT_LINES\\n \\n#ifdef ROUND_POINTS\\n vec2 coord = gl_PointCoord - vec2(0.5);\\n if (length(coord) > 0.5) discard;\\n#endif\\n \\n#if NCLIPPLANES > 0\\n for (int i = 0; i < NCLIPPLANES; i++)\\n if (dot(vPosition, vClipplane[i]) < 0.0) discard;\\n#endif\\n \\n#ifdef FIXED_QUADS\\n vec3 n = vec3(0., 0., 1.);\\n#elif defined(IS_LIT)\\n vec3 n = normalize(vNormal.xyz);\\n#endif\\n \\n#ifdef IS_TWOSIDED\\n if ((normz <= 0.) != front) discard;\\n#endif\\n\\n#ifdef IS_LIT\\n vec3 eye = normalize(-vPosition.xyz/vPosition.w);\\n vec3 lightdir;\\n vec4 colDiff;\\n vec3 halfVec;\\n vec4 lighteffect = vec4(emission, 0.);\\n vec3 col;\\n float nDotL;\\n#ifdef FIXED_QUADS\\n n = -faceforward(n, n, eye);\\n#endif\\n \\n#if NLIGHTS > 0\\n // Simulate two-sided lighting\\n if (n.z < 0.0)\\n n = -n;\\n for (int i=0;i 0) {\\n fogF = (uFogParms.y - vPosition.z/vPosition.w)/(uFogParms.y - uFogParms.x);\\n if (uFogMode > 1)\\n fogF = mix(uFogParms.w, 1.0, fogF);\\n fogF = fogF*uFogParms.z;\\n if (uFogMode == 2)\\n fogF = 1.0 - exp(-fogF);\\n // Docs are wrong: use (density*c)^2, not density*c^2\\n // https://gitlab.freedesktop.org/mesa/mesa/-/blob/master/src/mesa/swrast/s_fog.c#L58\\n else if (uFogMode == 3)\\n fogF = 1.0 - exp(-fogF*fogF);\\n fogF = clamp(fogF, 0.0, 1.0);\\n gl_FragColor = vec4(mix(fragColor.rgb, uFogColor, fogF), fragColor.a);\\n } else gl_FragColor = fragColor;\\n#else\\n gl_FragColor = fragColor;\\n#endif // HAS_FOG\\n \\n}\",\"players\":[],\"webGLoptions\":{\"preserveDrawingBuffer\":true}},\"evals\":[],\"jsHooks\":[]}# }"},{"path":"https://natverse.org/nat.nblast/reference/diagonal.html","id":null,"dir":"Reference","previous_headings":"","what":"Extract diagonal terms from a variety of matrix types — diagonal","title":"Extract diagonal terms from a variety of matrix types — diagonal","text":"Extract diagonal terms variety matrix types","code":""},{"path":"https://natverse.org/nat.nblast/reference/diagonal.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Extract diagonal terms from a variety of matrix types — diagonal","text":"","code":"diagonal(x, indices = NULL) # Default S3 method diagonal(x, indices = NULL)"},{"path":"https://natverse.org/nat.nblast/reference/diagonal.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Extract diagonal terms from a variety of matrix types — diagonal","text":"x square matrix indices specifies subset diagonal using character vector names, logical vector integer indices. default (NULL) implies elements.","code":""},{"path":"https://natverse.org/nat.nblast/reference/diagonal.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Extract diagonal terms from a variety of matrix types — diagonal","text":"named vector containing diagonal elements.","code":""},{"path":"https://natverse.org/nat.nblast/reference/diagonal.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Extract diagonal terms from a variety of matrix types — diagonal","text":"Insists input matrix square. Uses 'diagonal' attribute available specialised handling ff, big.matrix, dgCMatrix matrices. check row column names identical matrix classes (unlike base diag function, always uses rownames.","code":""},{"path":"https://natverse.org/nat.nblast/reference/diagonal.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Extract diagonal terms from a variety of matrix types — diagonal","text":"","code":"m=fill_in_sparse_score_mat(letters[1:5]) diagonal(m) #> a b c d e #> 0 0 0 0 0"},{"path":"https://natverse.org/nat.nblast/reference/extract-methods.html","id":null,"dir":"Reference","previous_headings":"","what":"Extract parts of a sparse spam matrix — [","title":"Extract parts of a sparse spam matrix — [","text":"Extract parts sparse spam matrix","code":""},{"path":"https://natverse.org/nat.nblast/reference/extract-methods.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Extract parts of a sparse spam matrix — [","text":"","code":"# S4 method for class 'spam,character,character,logical' x[i, j, ..., drop = TRUE] # S4 method for class 'spam,character,character,missing' x[i, j, ..., drop = TRUE] # S4 method for class 'spam,character,missing,logical' x[i, j, ..., drop = TRUE] # S4 method for class 'spam,character,missing,missing' x[i, j, ..., drop = TRUE] # S4 method for class 'spam,missing,character,logical' x[i, j, ..., drop = TRUE] # S4 method for class 'spam,missing,character,missing' x[i, j, ..., drop = TRUE]"},{"path":"https://natverse.org/nat.nblast/reference/extract-methods.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Extract parts of a sparse spam matrix — [","text":"x object extract . row identifiers. j column identifiers. ... additional arguments. drop logical indicating dimensions dropped.","code":""},{"path":"https://natverse.org/nat.nblast/reference/fctraces20.html","id":null,"dir":"Reference","previous_headings":"","what":"20 traced Drosophila neurons from Chiang et al 2011 — fctraces20","title":"20 traced Drosophila neurons from Chiang et al 2011 — fctraces20","text":"R list (additional class neuronlist) contains 15 skeletonized Drosophila neurons dotprops objects. Original data due Chiang et al. [1], generously shared raw data. Automated tracing neuron skeletons carried Lee et al [2]. Image registration processing carried Greg Jefferis, Marta Costa James Manton[3].","code":""},{"path":"https://natverse.org/nat.nblast/reference/fctraces20.html","id":"references","dir":"Reference","previous_headings":"","what":"References","title":"20 traced Drosophila neurons from Chiang et al 2011 — fctraces20","text":"[1] Chiang .S., Lin C.Y., Chuang C.C., Chang H.M., Hsieh C.H., Yeh C.W., Shih C.T., Wu J.J., Wang G.T., Chen Y.C., Wu C.C., Chen G.Y., Ching Y.T., Lee P.C., Lin C.Y., Lin H.H., Wu C.C., Hsu H.W., Huang Y.., Chen J.Y., et al. (2011). Three-dimensional reconstruction brain-wide wiring networks Drosophila single-cell resolution. Curr Biol 21 (1), 1–11. doi: doi:10.1016/j.cub.2010.11.056 [2] P.-C. Lee, C.-C. Chuang, .-S. Chiang, Y.-T. Ching. (2012). High-throughput computer method 3d neuronal structure reconstruction image stack Drosophila brain applications. PLoS Comput Biol, 8(9):e1002658, Sep 2012. doi: doi:10.1371/journal.pcbi.1002658 . [3] NBLAST: Rapid, sensitive comparison neuronal structure construction neuron family databases. Marta Costa, Aaron D. Ostrovsky, James D. Manton, Steffen Prohaska, Gregory S.X.E. Jefferis. bioRxiv doi: doi:10.1101/006346 .","code":""},{"path":"https://natverse.org/nat.nblast/reference/fill_in_sparse_score_mat.html","id":null,"dir":"Reference","previous_headings":"","what":"Add one or more submatrices to a sparse score matrix — fill_in_sparse_score_mat","title":"Add one or more submatrices to a sparse score matrix — fill_in_sparse_score_mat","text":"Add one submatrices sparse score matrix","code":""},{"path":"https://natverse.org/nat.nblast/reference/fill_in_sparse_score_mat.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Add one or more submatrices to a sparse score matrix — fill_in_sparse_score_mat","text":"","code":"fill_in_sparse_score_mat(sparse_matrix, ..., diag = NULL)"},{"path":"https://natverse.org/nat.nblast/reference/fill_in_sparse_score_mat.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Add one or more submatrices to a sparse score matrix — fill_in_sparse_score_mat","text":"sparse_matrix either existing (square) sparse matrix character vector names used define empty sparse matrix. ... Additional matrices insert sparse_matrix. Row column names must matches sparse_matrix. diag optional full diagonal sparse matrix .e. self-match scores.","code":""},{"path":[]},{"path":"https://natverse.org/nat.nblast/reference/fill_pairs_sparse_score_mat.html","id":null,"dir":"Reference","previous_headings":"","what":"Add forwards, reverse and self scores for a pair of neurons to a sparse score matrix — fill_pairs_sparse_score_mat","title":"Add forwards, reverse and self scores for a pair of neurons to a sparse score matrix — fill_pairs_sparse_score_mat","text":"Add forwards, reverse self scores pair neurons sparse score matrix","code":""},{"path":"https://natverse.org/nat.nblast/reference/fill_pairs_sparse_score_mat.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Add forwards, reverse and self scores for a pair of neurons to a sparse score matrix — fill_pairs_sparse_score_mat","text":"","code":"fill_pairs_sparse_score_mat( sparse_matrix, n1, n2, dense_matrix, reverse = TRUE, self = TRUE, reverse_self = (reverse && self) )"},{"path":"https://natverse.org/nat.nblast/reference/fill_pairs_sparse_score_mat.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Add forwards, reverse and self scores for a pair of neurons to a sparse score matrix — fill_pairs_sparse_score_mat","text":"sparse_matrix sparse matrix fill . n1 name query neuron. n2 name target neuron. dense_matrix score matrix extract scores. reverse logical indicating reverse score also filled (default TRUE). self logical indicating self-score query also filled (used normalised scores; default TRUE). reverse_self logical indicating self-score target also filled (used mean scores; default TRUE).","code":""},{"path":"https://natverse.org/nat.nblast/reference/fill_pairs_sparse_score_mat.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Add forwards, reverse and self scores for a pair of neurons to a sparse score matrix — fill_pairs_sparse_score_mat","text":"sparse matrix (class spam) specified score entries filled.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nat.nblast-package.html","id":null,"dir":"Reference","previous_headings":"","what":"Neuron similarity, search and clustering tools — nat.nblast-package","title":"Neuron similarity, search and clustering tools — nat.nblast-package","text":"nat.nblast provides tools compare neuronal morphology using NBLAST algorithm (Costa et al. 2016).","code":""},{"path":"https://natverse.org/nat.nblast/reference/nat.nblast-package.html","id":"similarity-and-search","dir":"Reference","previous_headings":"","what":"Similarity and search","title":"Neuron similarity, search and clustering tools — nat.nblast-package","text":"main entry point similarity search functions nblast. Traced neurons normally converted dotprops format search. multiple neurons compared neuronlist object. current NBLAST version (2) depends scoring matrix. Default matrices trained using Drosophila neurons FCWB template brain space distributed package (see smat.fcwb); see Scoring Matrices section creating new scoring matrices. nblast makes use flexible complicated function NeuriteBlast includes several additional options. function WeightedNNBasedLinesetMatching provides primitive functionality finding nearest neighbour distances absolute dot products two sets segments. Neither functions intended end use. Calculating similarity scores facilitated nblast_allbyall function can take either neuronlist input character vector naming (subset) neurons (large) neuronlist. neuronlist containing input neurons resident memory .e. neuronlistfh.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nat.nblast-package.html","id":"clustering","dir":"Reference","previous_headings":"","what":"Clustering","title":"Neuron similarity, search and clustering tools — nat.nblast-package","text":"similarity score matrix available can used input variety clustering algorithms. nhclust provides convenient wrapper R's hierarchical clustering function hclust. wish use another clustering function, can use sub_dist_mat convert raw similarity score matrix normalised distance matrix (R dist object) suitable clustering. need similarity matrix want modify normalisation can use sub_score_mat. Note raw NBLAST scores symmetric (.e. S(,B) equal S(B,)) clustering construct symmetric similarity/distance matrix 1/2 * ( S(,B)/S(,) + S(B,)/S(B,B) ). See sub_score_mat's documentation details.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nat.nblast-package.html","id":"cached-scores","dir":"Reference","previous_headings":"","what":"Cached scores","title":"Neuron similarity, search and clustering tools — nat.nblast-package","text":"Although NBLAST fast can parallelised, makes sense cache disk similarity scores group neurons subject repeated clustering analysis. matrix can simply saved disk reloaded using base R functions like save load. sub_score_mat sub_dist_mat can used extract subset scores raw score matrix. large matrices, bigmemory ff packages allow matrices stored disk portions loaded memory demand. sub_score_mat sub_dist_mat work equally well regular -memory matrices disk-backed matrices. give example, 16,129 neurons flycircuit.tw dataset, 260,144,641 comparisons took 250 hours compute time (half day ~20 cores). saved disk single precision (.e. 4 bytes per score) ff matrix occupy just 1Gb.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nat.nblast-package.html","id":"calculating-scoring-matrices","dir":"Reference","previous_headings":"","what":"Calculating scoring matrices","title":"Neuron similarity, search and clustering tools — nat.nblast-package","text":"NBLAST algorithm depends appropriately calibrated scoring matrices. encapsulate log odds ratio pair segments come two structurally related neurons rather two unrelated neurons, given observed distance absolute dot product two segments. Scoring matrices can constructed using create_scoringmatrix function, supplying set matching neurons set non-matching neurons. See create_scoringmatrix documentation links lower-level functions provide finer control construction scoring matrix.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nat.nblast-package.html","id":"package-options","dir":"Reference","previous_headings":"","what":"Package Options","title":"Neuron similarity, search and clustering tools — nat.nblast-package","text":"one package option nat.nblast.defaultsmat NULL default, example set one scoring matrices included package \"smat.fcwb\" new user-constructed matrix.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nat.nblast-package.html","id":"references","dir":"Reference","previous_headings":"","what":"References","title":"Neuron similarity, search and clustering tools — nat.nblast-package","text":"Costa, M., Ostrovsky, .D., Manton, J.D., Prohaska, S., Jefferis, G.S.X.E. (2014). NBLAST: Rapid, sensitive comparison neuronal structure construction neuron family databases. bioRxiv preprint. doi:10.1101/006346 .","code":""},{"path":[]},{"path":"https://natverse.org/nat.nblast/reference/nat.nblast-package.html","id":"author","dir":"Reference","previous_headings":"","what":"Author","title":"Neuron similarity, search and clustering tools — nat.nblast-package","text":"Maintainer: Gregory Jefferis jefferis@gmail.com (ORCID) Authors: James Manton ajd.manton@googlemail.com (ORCID)","code":""},{"path":"https://natverse.org/nat.nblast/reference/nblast.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate similarity score for neuron morphologies — nblast","title":"Calculate similarity score for neuron morphologies — nblast","text":"Uses NBLAST algorithm compares morphology two neurons. control parameters algorithm, see arguments NeuriteBlast.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nblast.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate similarity score for neuron morphologies — nblast","text":"","code":"nblast( query, target = getOption(\"nat.default.neuronlist\"), smat = NULL, sd = 3, version = c(2, 1), normalised = FALSE, UseAlpha = FALSE, OmitFailures = NA, ... )"},{"path":"https://natverse.org/nat.nblast/reference/nblast.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate similarity score for neuron morphologies — nblast","text":"query query neuron. target neuronlist compare neuron . Defaults options(\"nat.default.neuronlist\"). See nat-package. smat scoring matrix use (see details) sd Standard deviation use distance dependence NBLAST v1 algorithm. Ignored version=2. version version algorithm use (default, 2, latest). normalised whether divide scores self-match score query UseAlpha whether weight similarity score matched segment emphasise long range neurites rather arbours (default: FALSE, see UseAlpha section details). OmitFailures Whether omit neurons FUN gives error. default value (NA) result nblast stopping error message moment error. values, see details. ... Additional arguments passed NeuriteBlast function used compute scores distances/dot products. (expert use ).","code":""},{"path":"https://natverse.org/nat.nblast/reference/nblast.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate similarity score for neuron morphologies — nblast","text":"Named list similarity scores.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nblast.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Calculate similarity score for neuron morphologies — nblast","text":"smat=NULL options(\"nat.nblast.defaultsmat\") checked NULL, smat.fcwb smat_alpha.fcwb used depending value UseAlpha. OmitFailures NA, individual nblast calls wrapped try ensure failure single neuron abort whole nblast call. OmitFailures=FALSE, missing values left NA. OmitFailures=TRUE (yet) implemented. want drop scores neurons failed need set OmitFailures=FALSE use na.omit similar post-process scores. Note OmitFailures=FALSE error messages printed call wrapped try(expr, silent=TRUE). Internally, plyr package used provide options parallelising NBLAST displaying progress. display progress bar scores computed, add .progress=\"natprogress\" arguments (non-text progress bars available – see create_progress_bar). parallelise, add .parallel=TRUE arguments. order make use parallel calculation, must register parallel backend distribute computations. several possible backends, simplest multicore option made available doMC, spreads load across cores machine. using , backend must registered using registerDoMC (see example ).","code":""},{"path":"https://natverse.org/nat.nblast/reference/nblast.html","id":"nblast-versions","dir":"Reference","previous_headings":"","what":"NBLAST Versions","title":"Calculate similarity score for neuron morphologies — nblast","text":"nblast version argument presently exposes two versions algorithm; use core procedure aligning two vector clouds, segment segment, computing distance absolute dot product nearest segment target neuron every segment query neuron. However differ significantly procedure used calculate score using set distances absolute dot products. Version 1 algorithm uses standard deviation (argument sd) user-supplied parameter negative exponential weighting function determines relationship score distance segments. corresponds parameter \\(\\sigma\\) weighting function: \\(f=\\sqrt{|\\vec{u_{}}\\cdot\\vec{v_{}}|\\exp\\left(-d_{}^{2}/2\\sigma^{2}\\right)}\\) approach described Kohl et al 2013 similarity scores interval (0,1) described paper can exactly recapitulated setting version=1 normalised=TRUE. Version 2 algorithm described Costa et al 2014. uses sophisticated principled scoring approach based log-odds ratio defined distribution matches non-matches sample data. information passed nblast function form scoring matrix (can computed create_scoringmatrix); default scoring matrix smat.fcwb constructed Drosophila neurons. version use? use version 2 working Drosophila neurons sufficient training data (form validated matching random neuron pairs construct scoring matrix). case, can always fall back version 1, setting free parameter (sd \\(\\sigma\\)) value encapsulates understanding location precision neurons species/brain region interest. fly brain used \\(\\sigma=3\\) microns, since previous estimates localisation identifiable features neurons (Jefferis, Potter et al 2007) order.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nblast.html","id":"usealpha","dir":"Reference","previous_headings":"","what":"UseAlpha","title":"Calculate similarity score for neuron morphologies — nblast","text":"NBLAST v2, alpha factor segment indicates whether neighbouring segments aligned similar direction (typical e.g. long range axonal projection) randomly aligned (typical dendritic arbours). See Costa et al. details. Setting UseAlpha=TRUE emphasise axon, primary neurite etc. neuron. can particularly useful option e.g. searching traced fragment know suspect follow axon tract.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nblast.html","id":"references","dir":"Reference","previous_headings":"","what":"References","title":"Calculate similarity score for neuron morphologies — nblast","text":"Kohl, J. Ostrovsky, .D., Frechter, S., Jefferis, G.S.X.E (2013). bidirectional circuit switch reroutes pheromone signals male female brains. Cell 155 (7), 1610–23 doi:10.1016/j.cell.2013.11.025 . Costa, M., Ostrovsky, .D., Manton, J.D., Prohaska, S., Jefferis, G.S.X.E. (2014). NBLAST: Rapid, sensitive comparison neuronal structure construction neuron family databases. bioRxiv preprint. doi:10.1101/006346 . Jefferis G.S.X.E., Potter C.J., Chan .M., Marin E.C., Rohlfing T., Maurer C.R.J., Luo L. (2007). Comprehensive maps Drosophila higher olfactory centers: spatially segregated fruit pheromone representation. Cell 128 (6), 1187–1203. doi:10.1016/j.cell.2007.01.040","code":""},{"path":[]},{"path":"https://natverse.org/nat.nblast/reference/nblast.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Calculate similarity score for neuron morphologies — nblast","text":"","code":"# load sample Kenyon cell data from nat package data(kcs20, package='nat') # search one neuron against all neurons scores=nblast(kcs20[['GadMARCM-F000142_seg002']], kcs20) # scores from best to worst, top hit is of course same neuron sort(scores, decreasing = TRUE) #> GadMARCM-F000142_seg002 ChaMARCM-F000586_seg002 GadMARCM-F000423_seg001 #> 4043.1766 1914.8224 1772.8506 #> GadMARCM-F000442_seg002 FruMARCM-M000115_seg001 FruMARCM-F000085_seg001 #> 1007.4565 853.8108 713.2106 #> FruMARCM-F001929_seg001 FruMARCM-F001494_seg002 GadMARCM-F000050_seg001 #> 604.4071 481.4737 480.4146 #> GadMARCM-F000071_seg001 FruMARCM-M001339_seg001 FruMARCM-F000188_seg001 #> 448.1516 433.9340 404.0470 #> GadMARCM-F000476_seg001 FruMARCM-F000270_seg001 FruMARCM-F000706_seg001 #> 313.2021 254.7516 204.2116 #> FruMARCM-M000842_seg002 FruMARCM-M001051_seg002 FruMARCM-M001205_seg002 #> 196.4543 140.8164 -251.4999 #> GadMARCM-F000122_seg001 FruMARCM-F001115_seg002 #> -262.2352 -520.2581 hist(scores, breaks=25, col='grey') abline(v=1500, col='red') # plot query neuron open3d() # plot top 3 hits (including self match with thicker lines) plot3d(kcs20[which(sort(scores, decreasing = TRUE)>1500)], lwd=c(3,1,1)) rest=names(which(scores<1500)) plot3d(rest, db=kcs20, col='grey', lwd=0.5) # normalised scores (i.e. self match = 1) of all neurons vs each other # note use of progress bar scores.norm=nblast(kcs20, kcs20, normalised = TRUE, .progress=\"natprogress\") #> Warning: Cannot find progress bar progress_natprogress hist(scores.norm, breaks=25, col='grey') # produce a heatmap from normalised scores jet.colors <- colorRampPalette( c(\"blue\", \"green\", \"yellow\", \"red\") ) heatmap(scores.norm, labCol = with(kcs20,type), col=jet.colors(20), symm = TRUE) if (FALSE) { # \\dontrun{ # Parallelise NBLASTing across 4 cores using doMC package library(doMC) registerDoMC(4) scores.norm2=nblast(kcs20, kcs20, normalised=TRUE, .parallel=TRUE) stopifnot(all.equal(scores.norm2, scores.norm)) } # }"},{"path":"https://natverse.org/nat.nblast/reference/nblast_allbyall.html","id":null,"dir":"Reference","previous_headings":"","what":"Wrapper function to compute all by all NBLAST scores for a set of neurons — nblast_allbyall","title":"Wrapper function to compute all by all NBLAST scores for a set of neurons — nblast_allbyall","text":"Calls nblast compute actual scores. Can accept either neuronlist neuron names character vector. thin wrapper around nblast main advantage option \"mean\" normalisation forward reverse scores, sensible input give clustering algorithm well choice returning distance matrix.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nblast_allbyall.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Wrapper function to compute all by all NBLAST scores for a set of neurons — nblast_allbyall","text":"","code":"nblast_allbyall(x, ...) # S3 method for class 'character' nblast_allbyall(x, smat = NULL, db = getOption(\"nat.default.neuronlist\"), ...) # S3 method for class 'neuronlist' nblast_allbyall( x, smat = NULL, distance = FALSE, normalisation = c(\"raw\", \"normalised\", \"mean\"), ... )"},{"path":"https://natverse.org/nat.nblast/reference/nblast_allbyall.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Wrapper function to compute all by all NBLAST scores for a set of neurons — nblast_allbyall","text":"x Input neurons (neuronlist character vector) ... Additional arguments methods nblast smat scoring matrix use (see details nblast meaning default NULL value) db neuronlist character vector naming one. Defaults value options(\"nat.default.neuronlist\") distance logical indicating whether return distances scores. normalisation type normalisation procedure carried , selected 'raw', 'normalised' 'mean' (.e. average normalised scores directions). distance=TRUE raw.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nblast_allbyall.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Wrapper function to compute all by all NBLAST scores for a set of neurons — nblast_allbyall","text":"Note nat already provides function nhclust clustering, wrapper R's hclust function. nhclust actually expects raw scores input.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nblast_allbyall.html","id":"todo","dir":"Reference","previous_headings":"","what":"TODO","title":"Wrapper function to compute all by all NBLAST scores for a set of neurons — nblast_allbyall","text":"good idea future implement parallel version function.","code":""},{"path":[]},{"path":"https://natverse.org/nat.nblast/reference/nblast_allbyall.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Wrapper function to compute all by all NBLAST scores for a set of neurons — nblast_allbyall","text":"","code":"library(nat) kcs20.scoremat=nblast_allbyall(kcs20) kcs20.hclust=nhclust(scoremat=kcs20.scoremat) #> The \"ward\" method has been renamed to \"ward.D\"; note new \"ward.D2\" plot(kcs20.hclust)"},{"path":"https://natverse.org/nat.nblast/reference/neuron_pairs.html","id":null,"dir":"Reference","previous_headings":"","what":"Utility function to generate all or random pairs of neurons — neuron_pairs","title":"Utility function to generate all or random pairs of neurons — neuron_pairs","text":"Utility function generate random pairs neurons","code":""},{"path":"https://natverse.org/nat.nblast/reference/neuron_pairs.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Utility function to generate all or random pairs of neurons — neuron_pairs","text":"","code":"neuron_pairs(query, target, n = NA, ignoreSelf = TRUE)"},{"path":"https://natverse.org/nat.nblast/reference/neuron_pairs.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Utility function to generate all or random pairs of neurons — neuron_pairs","text":"query, target either neuronlists character vectors names. target missing, query used query target. n number random pairs draw. NA, default, uses expand.grid draw pairs. ignoreSelf Logical indicating whether omit pairs consisting neuron (default TRUE).","code":""},{"path":"https://natverse.org/nat.nblast/reference/neuron_pairs.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Utility function to generate all or random pairs of neurons — neuron_pairs","text":"data.frame two character vector columns, query target.","code":""},{"path":[]},{"path":"https://natverse.org/nat.nblast/reference/neuron_pairs.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Utility function to generate all or random pairs of neurons — neuron_pairs","text":"","code":"neuron_pairs(nat::kcs20, n=20) #> query target #> 1 FruMARCM-F000706_seg001 GadMARCM-F000142_seg002 #> 2 FruMARCM-F000085_seg001 GadMARCM-F000071_seg001 #> 3 FruMARCM-F000270_seg001 GadMARCM-F000122_seg001 #> 4 FruMARCM-F001115_seg002 GadMARCM-F000050_seg001 #> 5 FruMARCM-F001494_seg002 GadMARCM-F000476_seg001 #> 6 FruMARCM-F001494_seg002 FruMARCM-M001051_seg002 #> 7 GadMARCM-F000142_seg002 FruMARCM-M001051_seg002 #> 8 FruMARCM-F001115_seg002 FruMARCM-M001339_seg001 #> 9 ChaMARCM-F000586_seg002 GadMARCM-F000476_seg001 #> 10 FruMARCM-F000270_seg001 GadMARCM-F000122_seg001 #> 11 FruMARCM-F000270_seg001 FruMARCM-M000115_seg001 #> 12 FruMARCM-F001494_seg002 FruMARCM-M001205_seg002 #> 13 FruMARCM-F000706_seg001 FruMARCM-F001115_seg002 #> 14 GadMARCM-F000122_seg001 GadMARCM-F000050_seg001 #> 15 GadMARCM-F000476_seg001 FruMARCM-F001929_seg001 #> 16 GadMARCM-F000476_seg001 FruMARCM-F001494_seg002 #> 17 FruMARCM-F000270_seg001 FruMARCM-M001205_seg002 #> 18 FruMARCM-F000085_seg001 FruMARCM-F000188_seg001 #> 19 GadMARCM-F000122_seg001 GadMARCM-F000442_seg002 #> 20 FruMARCM-F001115_seg002 FruMARCM-F000270_seg001"},{"path":"https://natverse.org/nat.nblast/reference/nhclust.html","id":null,"dir":"Reference","previous_headings":"","what":"Cluster a set of neurons — nhclust","title":"Cluster a set of neurons — nhclust","text":"Given NBLAST score matrix (may specified package default) /vector neuron identifiers use hclust carry hierarchical clustering. default value distfun argument handle square distance matrices R dist objects.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nhclust.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Cluster a set of neurons — nhclust","text":"","code":"nhclust( neuron_names, method = \"ward\", scoremat = NULL, distfun = as.dist, ..., maxneurons = 4000 )"},{"path":"https://natverse.org/nat.nblast/reference/nhclust.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Cluster a set of neurons — nhclust","text":"neuron_names character vector neuron identifiers. method clustering method (default Ward's). scoremat score matrix use (see sub_score_mat details default). distfun function convert distance matrix returned sub_dist_mat R dist object (default= .dist). ... additional parameters passed hclust. maxneurons set sensible value avoid loading huge (order N^2) distances directly memory.","code":""},{"path":"https://natverse.org/nat.nblast/reference/nhclust.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Cluster a set of neurons — nhclust","text":"object class hclust describes tree produced clustering process.","code":""},{"path":[]},{"path":"https://natverse.org/nat.nblast/reference/nhclust.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Cluster a set of neurons — nhclust","text":"","code":"library(nat) kcscores=nblast_allbyall(kcs20) hckcs=nhclust(scoremat=kcscores) #> The \"ward\" method has been renamed to \"ward.D\"; note new \"ward.D2\" # divide hclust object into 3 groups library(dendroextras) dkcs=colour_clusters(hckcs, k=3) # change dendrogram labels to neuron type, extracting this information # from type column in the metadata data.frame attached to kcs20 neuronlist labels(dkcs)=with(kcs20[labels(dkcs)], type) plot(dkcs) # 3d plot of neurons in those clusters (with matching colours) open3d() plot3d(hckcs, k=3, db=kcs20) # names of neurons in 3 groups subset(hckcs, k=3) #> [1] \"FruMARCM-F000706_seg001\" \"FruMARCM-M000842_seg002\" #> [3] \"GadMARCM-F000050_seg001\" \"FruMARCM-M001339_seg001\" #> [5] \"FruMARCM-F001115_seg002\" \"FruMARCM-M001051_seg002\" #> [7] \"FruMARCM-F001494_seg002\" \"FruMARCM-F000188_seg001\" #> [9] \"FruMARCM-F000270_seg001\" \"FruMARCM-F001929_seg001\" #> [11] \"GadMARCM-F000423_seg001\" \"GadMARCM-F000142_seg002\" #> [13] \"ChaMARCM-F000586_seg002\" \"FruMARCM-M001205_seg002\" #> [15] \"GadMARCM-F000122_seg001\" \"GadMARCM-F000476_seg001\" #> [17] \"FruMARCM-F000085_seg001\" \"GadMARCM-F000071_seg001\" #> [19] \"FruMARCM-M000115_seg001\" \"GadMARCM-F000442_seg002\""},{"path":"https://natverse.org/nat.nblast/reference/plot3d.hclust.html","id":null,"dir":"Reference","previous_headings":"","what":"Methods to identify and plot groups of neurons cut from an hclust object — plot3d.hclust","title":"Methods to identify and plot groups of neurons cut from an hclust object — plot3d.hclust","text":"plot3d.hclust uses plot3d plot neurons group, cut hclust object, colour.","code":""},{"path":"https://natverse.org/nat.nblast/reference/plot3d.hclust.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Methods to identify and plot groups of neurons cut from an hclust object — plot3d.hclust","text":"","code":"# S3 method for class 'hclust' plot3d( x, k = NULL, h = NULL, groups = NULL, col = rainbow, colour.selected = FALSE, ... )"},{"path":"https://natverse.org/nat.nblast/reference/plot3d.hclust.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Methods to identify and plot groups of neurons cut from an hclust object — plot3d.hclust","text":"x hclust object generated nhclust. k number clusters cut hclust object. h height cut hclust object. groups numeric vector groups plot. col colours groups (directly specified function). colour.selected set TRUE colour palette applies displayed cluster groups (default FALSE). ... additional arguments plot3d","code":""},{"path":"https://natverse.org/nat.nblast/reference/plot3d.hclust.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Methods to identify and plot groups of neurons cut from an hclust object — plot3d.hclust","text":"list rgl IDs plotted objects (see plot3d).","code":""},{"path":"https://natverse.org/nat.nblast/reference/plot3d.hclust.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Methods to identify and plot groups of neurons cut from an hclust object — plot3d.hclust","text":"Note colours order dendrogram assigned colour_clusters.","code":""},{"path":[]},{"path":"https://natverse.org/nat.nblast/reference/plot3d.hclust.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Methods to identify and plot groups of neurons cut from an hclust object — plot3d.hclust","text":"","code":"# 20 Kenyon cells data(kcs20, package='nat') # calculate mean, normalised NBLAST scores kcs20.aba=nblast_allbyall(kcs20) kcs20.hc=nhclust(scoremat = kcs20.aba) #> The \"ward\" method has been renamed to \"ward.D\"; note new \"ward.D2\" # plot the resultant dendrogram plot(kcs20.hc) # now plot the neurons in 3D coloured by cluster group # note that specifying db explicitly could be avoided by use of the # \\code{nat.default.neuronlist} option. plot3d(kcs20.hc, k=3, db=kcs20) # only plot first two groups # (will plot in same colours as when all groups are plotted) plot3d(kcs20.hc, k=3, db=kcs20, groups=1:2) # only plot first two groups # (will be coloured with a two-tone palette) plot3d(kcs20.hc, k=3, db=kcs20, groups=1:2, colour.selected=TRUE)"},{"path":"https://natverse.org/nat.nblast/reference/show_similarity.html","id":null,"dir":"Reference","previous_headings":"","what":"Display two neurons with segments in the query coloured by similarity — show_similarity","title":"Display two neurons with segments in the query coloured by similarity — show_similarity","text":"default, query neuron drawn segments shaded red blue, red indicating poor match target segments, blue good match.","code":""},{"path":"https://natverse.org/nat.nblast/reference/show_similarity.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Display two neurons with segments in the query coloured by similarity — show_similarity","text":"","code":"show_similarity( query, target, smat = NULL, cols = colorRampPalette(c(\"red\", \"yellow\", \"cyan\", \"navy\")), col = \"black\", AbsoluteScale = FALSE, PlotVectors = TRUE, ... )"},{"path":"https://natverse.org/nat.nblast/reference/show_similarity.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Display two neurons with segments in the query coloured by similarity — show_similarity","text":"query neuron compare colour. target neuron compare . smat score matrix (NULL, defaults smat.fcwb). cols function use colour segments (e.g. heat.colors). col colour draw target neuron. AbsoluteScale logical indicating whether colours calculated based minimum maximum similarities neuron (AbsoluteScale = FALSE) minimum maximum possible neurons. PlotVectors logical indicating whether vectors dotprops representation plotted. FALSE, points plotted. ... extra arguments pass plot3d.","code":""},{"path":"https://natverse.org/nat.nblast/reference/show_similarity.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Display two neurons with segments in the query coloured by similarity — show_similarity","text":"show_similarity called side effect drawing plot; vector object IDs returned.","code":""},{"path":[]},{"path":"https://natverse.org/nat.nblast/reference/show_similarity.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Display two neurons with segments in the query coloured by similarity — show_similarity","text":"","code":"if (FALSE) { # \\dontrun{ library(nat) # Pull out gamma and alpha-beta neurons gamma_neurons <- subset(kcs20, type=='gamma') ab_neurons <- subset(kcs20, type=='ab') # Compare two alpha-beta neurons with similar branching, but dissimilar arborisation clear3d() show_similarity(ab_neurons[[1]], ab_neurons[[2]]) # Compare an alpha-beta and a gamma neuron with some similarities and differences clear3d() show_similarity(ab_neurons[[1]], gamma_neurons[[3]]) } # }"},{"path":"https://natverse.org/nat.nblast/reference/smat.fcwb.html","id":null,"dir":"Reference","previous_headings":"","what":"Scoring matrices for neuron similarities in FCWB template brain — smat.fcwb","title":"Scoring matrices for neuron similarities in FCWB template brain — smat.fcwb","text":"Scoring matrices quantify log2 odds ratio segment pair given distance absolute dot product come pair neurons type, rather unrelated neurons.","code":""},{"path":"https://natverse.org/nat.nblast/reference/smat.fcwb.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Scoring matrices for neuron similarities in FCWB template brain — smat.fcwb","text":"scoring matrices generated using pairs 150 DL2 antennal lobe projection neurons FlyCircuit dataset 5000 random pairs dataset. smat.fcwb trained using nearest-neighbour distance tangent vector defined first eigen vector k=5 nearest neighbours. smat_alpha.fcwb defined smat.fcwb weighted factor alpha defined (l1-l2)/(l1+l2+l3) l1,l2,l3 three eigen values. work flycircuit dataset carried using smat.fcwb scoring matrix although smat_alpha.fcwb matrix emphasises significance matches linear regions neuron (axons) may advantages.","code":""},{"path":"https://natverse.org/nat.nblast/reference/sparse_score_mat.html","id":null,"dir":"Reference","previous_headings":"","what":"Convert a subset of a square score matrix to a sparse representation — sparse_score_mat","title":"Convert a subset of a square score matrix to a sparse representation — sparse_score_mat","text":"can useful storing raw forwards reverse NBLAST scores set neurons without store uncomputed elements full score matrix.","code":""},{"path":"https://natverse.org/nat.nblast/reference/sparse_score_mat.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Convert a subset of a square score matrix to a sparse representation — sparse_score_mat","text":"","code":"sparse_score_mat(neuron_names, dense_matrix)"},{"path":"https://natverse.org/nat.nblast/reference/sparse_score_mat.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Convert a subset of a square score matrix to a sparse representation — sparse_score_mat","text":"neuron_names character vector neuron names save scores . dense_matrix original, dense version full score matrix.","code":""},{"path":"https://natverse.org/nat.nblast/reference/sparse_score_mat.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Convert a subset of a square score matrix to a sparse representation — sparse_score_mat","text":"spare matrix, compressed, column-oriented form, R object inheriting CsparseMatrix-class generalMatrix-class.","code":""},{"path":[]},{"path":"https://natverse.org/nat.nblast/reference/sparse_score_mat.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Convert a subset of a square score matrix to a sparse representation — sparse_score_mat","text":"","code":"data(kcs20, package = \"nat\") scores=nblast_allbyall(kcs20) scores.3.sparse=sparse_score_mat(names(kcs20)[3], scores) scores.3.sparse #> [1] 3234.54125 -935.33772 3382.60124 -515.53376 125.68989 244.92594 #> [7] 4259.57193 480.41458 2048.01759 2068.51808 1278.68688 1359.49507 #> [13] -15.28677 3403.75462 -313.24728 73.74186 3277.87783 3122.69555 #> [19] 2656.70773 1487.45673 301.63902 360.64137 23.53454 2849.13647 #> [25] 309.92651 4043.17656 1962.12743 4202.62578 91.73056 4567.08113 #> [31] 1259.18024 4703.75189 1075.50543 4726.53035 62.48739 4760.69804 #> [37] 2889.29188 4965.70417 -410.27503 5159.32108 -377.93391 5204.87800 #> [43] 2751.04667 5227.65646 2847.10004 5204.87800 1635.40240 5478.21951 #> [49] 1731.55066 5512.38720 -514.09460 5637.66873 -204.79399 5432.66259 #> [55] -393.66139 5762.95025 1968.02090 5580.72258 #> Class 'spam' (32-bit) # can also add additional submatrices fill_in_sparse_score_mat(scores.3.sparse,scores[3:6,3:4]) #> [1] 3234.54125 -935.33772 3382.60124 -515.53376 125.68989 244.92594 #> [7] 4259.57193 480.41458 2048.01759 2068.51808 1278.68688 1359.49507 #> [13] -15.28677 3403.75462 -313.24728 73.74186 3277.87783 3122.69555 #> [19] 2656.70773 1487.45673 301.63902 360.64137 23.53454 2849.13647 #> [25] 309.92651 4043.17656 1962.12743 254.75164 4202.62578 91.73056 #> [31] -520.25810 4567.08113 1259.18024 4703.75189 1075.50543 4726.53035 #> [37] 62.48739 4760.69804 2889.29188 4965.70417 -410.27503 5159.32108 #> [43] -377.93391 5204.87800 2751.04667 5227.65646 2847.10004 5204.87800 #> [49] 1635.40240 5478.21951 1731.55066 5512.38720 -514.09460 5637.66873 #> [55] -204.79399 5432.66259 -393.66139 5762.95025 1968.02090 5580.72258 #> Class 'spam' (32-bit)"},{"path":"https://natverse.org/nat.nblast/reference/sub_dist_mat.html","id":null,"dir":"Reference","previous_headings":"","what":"Convert (a subset of) a raw score matrix to a distance matrix — sub_dist_mat","title":"Convert (a subset of) a raw score matrix to a distance matrix — sub_dist_mat","text":"function can convert raw score matrix returned nblast square distance matrix dist object. can used file-backed matrices well regular R matrices resident memory.","code":""},{"path":"https://natverse.org/nat.nblast/reference/sub_dist_mat.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Convert (a subset of) a raw score matrix to a distance matrix — sub_dist_mat","text":"","code":"sub_dist_mat( neuron_names, scoremat = NULL, form = c(\"matrix\", \"dist\"), maxneurons = NA )"},{"path":"https://natverse.org/nat.nblast/reference/sub_dist_mat.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Convert (a subset of) a raw score matrix to a distance matrix — sub_dist_mat","text":"neuron_names character vector neuron identifiers. scoremat score matrix use (see sub_score_mat details default). form type object return. maxneurons set sensible value avoid loading huge (order N^2) distances directly memory.","code":""},{"path":"https://natverse.org/nat.nblast/reference/sub_dist_mat.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Convert (a subset of) a raw score matrix to a distance matrix — sub_dist_mat","text":"return object class matrix dist (determined form argument), corresponding subset distance matrix","code":""},{"path":"https://natverse.org/nat.nblast/reference/sub_dist_mat.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Convert (a subset of) a raw score matrix to a distance matrix — sub_dist_mat","text":"Note neuron_names missing rownames scoremat used .e. every neuron scoremat used.","code":""},{"path":[]},{"path":"https://natverse.org/nat.nblast/reference/sub_score_mat.html","id":null,"dir":"Reference","previous_headings":"","what":"Return scores (or distances) for given query and target neurons — sub_score_mat","title":"Return scores (or distances) for given query and target neurons — sub_score_mat","text":"Scores can either returned raw numbers, normalised self-hit score 1, average normalised scores forwards & reverse directions (.e. |query->target| + |target->query| / 2). Distances returned either 1 - normscore forwards direction, 1 - normscorebar, normscorebar normscore averaged across directions.","code":""},{"path":"https://natverse.org/nat.nblast/reference/sub_score_mat.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Return scores (or distances) for given query and target neurons — sub_score_mat","text":"","code":"sub_score_mat( query, target, scoremat = NULL, distance = FALSE, normalisation = c(\"raw\", \"normalised\", \"mean\") )"},{"path":"https://natverse.org/nat.nblast/reference/sub_score_mat.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Return scores (or distances) for given query and target neurons — sub_score_mat","text":"query, target character vectors neuron identifiers. scoremat matrix, ff matrix, bigmatrix character vector specifying name ff matrix containing score matrix. distance logical indicating whether return distances scores. normalisation type normalisation procedure carried , selected 'raw', 'normalised' 'mean' (.e. average normalised scores directions). distance=TRUE raw.","code":""},{"path":[]},{"path":"https://natverse.org/nat.nblast/reference/subset.hclust.html","id":null,"dir":"Reference","previous_headings":"","what":"Return the labels of items in 1 or more groups cut from hclust object — subset.hclust","title":"Return the labels of items in 1 or more groups cut from hclust object — subset.hclust","text":"Return labels items 1 groups cut hclust object","code":""},{"path":"https://natverse.org/nat.nblast/reference/subset.hclust.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Return the labels of items in 1 or more groups cut from hclust object — subset.hclust","text":"","code":"# S3 method for class 'hclust' subset(x, k = NULL, h = NULL, groups = NULL, ...)"},{"path":"https://natverse.org/nat.nblast/reference/subset.hclust.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Return the labels of items in 1 or more groups cut from hclust object — subset.hclust","text":"x tree like object k integer scalar desired number groups h numeric scalar height tree cut groups vector groups inspect. ... Additional parameters passed methods","code":""},{"path":"https://natverse.org/nat.nblast/reference/subset.hclust.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Return the labels of items in 1 or more groups cut from hclust object — subset.hclust","text":"character vector labels selected items","code":""},{"path":"https://natverse.org/nat.nblast/reference/subset.hclust.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Return the labels of items in 1 or more groups cut from hclust object — subset.hclust","text":"one h k supplied.","code":""},{"path":"https://natverse.org/nat.nblast/news/index.html","id":"natnblast-166","dir":"Changelog","previous_headings":"","what":"nat.nblast 1.6.6","title":"nat.nblast 1.6.6","text":"Keep scale factor normalising (#38) Doc fixes","code":""},{"path":"https://natverse.org/nat.nblast/news/index.html","id":"natnblast-164","dir":"Changelog","previous_headings":"","what":"nat.nblast 1.6.4","title":"nat.nblast 1.6.4","text":"pkgdown documentation use development version nat package install_github nat.nblast spelling","code":""},{"path":"https://natverse.org/nat.nblast/news/index.html","id":"natnblast-163","dir":"Changelog","previous_headings":"","what":"nat.nblast 1.6.3","title":"nat.nblast 1.6.3","text":"fixes/examples show_similarity allow score matrices passed first param nhclust (issue warning) change vignette engine rmarkdown","code":""},{"path":"https://natverse.org/nat.nblast/news/index.html","id":"natnblast-162","dir":"Changelog","previous_headings":"","what":"nat.nblast 1.6.2","title":"nat.nblast 1.6.2","text":"CRAN release: 2016-04-26 minor fixes 3.3 add vignette simplify travis setup","code":""},{"path":"https://natverse.org/nat.nblast/news/index.html","id":"natnblast-161","dir":"Changelog","previous_headings":"","what":"nat.nblast 1.6.1","title":"nat.nblast 1.6.1","text":"dev: fix test different normalisation types","code":""},{"path":"https://natverse.org/nat.nblast/news/index.html","id":"natnblast-16","dir":"Changelog","previous_headings":"","what":"nat.nblast 1.6","title":"nat.nblast 1.6","text":"fix handling non-square matrices sub_score_mat minor doc fixes","code":""},{"path":"https://natverse.org/nat.nblast/news/index.html","id":"natnblast-15","dir":"Changelog","previous_headings":"","what":"nat.nblast 1.5","title":"nat.nblast 1.5","text":"CRAN release: 2014-09-19 switch importing nat depending ","code":""},{"path":"https://natverse.org/nat.nblast/news/index.html","id":"natnblast-14","dir":"Changelog","previous_headings":"","what":"nat.nblast 1.4","title":"nat.nblast 1.4","text":"use nabor package, RANN, finding nearest neighbours add functions creating sparse score matrices, using spam package speed extraction diagonal elements -disk score matrices nblast now handles combinations neuron dotprops objects","code":""},{"path":"https://natverse.org/nat.nblast/news/index.html","id":"natnblast-13","dir":"Changelog","previous_headings":"","what":"nat.nblast 1.3","title":"nat.nblast 1.3","text":"functions create scoring matrices; see create_scoringmatrix details. fix: really use nhclust’s distfun argument new nblast_allbyall function add plyr support (gives parallelisation progress bar options)","code":""},{"path":"https://natverse.org/nat.nblast/news/index.html","id":"natnblast-12","dir":"Changelog","previous_headings":"","what":"nat.nblast 1.2","title":"nat.nblast 1.2","text":"add nhclust, plot3d.hclust, subset.hclust help cluster/plot based nblast scores (ported flycircuit package). rename showSimilarity -> show_similarity dev: make sure tests run check() test: update baselines given score matrix changes 1.1","code":""},{"path":"https://natverse.org/nat.nblast/news/index.html","id":"natnblast-11","dir":"Changelog","previous_headings":"","what":"nat.nblast 1.1","title":"nat.nblast 1.1","text":"fix: make smat.fcwb default scoring matrix (rather smat_alpha) nblast’s UseAlpha parameter FALSE (default) smat_alpha.fcwb default UseAlpha=TRUE. rename smat->smat.fcwb smat_alpha->smat_alpha.fcwb make clear defined FCWB template space (though work OK fly template brains absolute physical scale) don’t set options(“nat.nblast.defaultsmat”“), just query . doc: basic package documentations","code":""}]
diff --git a/sitemap.xml b/sitemap.xml
new file mode 100644
index 0000000..7d91217
--- /dev/null
+++ b/sitemap.xml
@@ -0,0 +1,34 @@
+
+https://natverse.org/nat.nblast/404.html
+https://natverse.org/nat.nblast/SUPPORT.html
+https://natverse.org/nat.nblast/articles/NBLAST-Clustering.html
+https://natverse.org/nat.nblast/articles/index.html
+https://natverse.org/nat.nblast/authors.html
+https://natverse.org/nat.nblast/index.html
+https://natverse.org/nat.nblast/news/index.html
+https://natverse.org/nat.nblast/reference/NeuriteBlast.html
+https://natverse.org/nat.nblast/reference/WeightedNNBasedLinesetMatching.html
+https://natverse.org/nat.nblast/reference/calc_dists_dotprods.html
+https://natverse.org/nat.nblast/reference/calc_prob_mat.html
+https://natverse.org/nat.nblast/reference/calc_score_matrix.html
+https://natverse.org/nat.nblast/reference/create_scoringmatrix.html
+https://natverse.org/nat.nblast/reference/diagonal.html
+https://natverse.org/nat.nblast/reference/extract-methods.html
+https://natverse.org/nat.nblast/reference/fctraces20.html
+https://natverse.org/nat.nblast/reference/fill_in_sparse_score_mat.html
+https://natverse.org/nat.nblast/reference/fill_pairs_sparse_score_mat.html
+https://natverse.org/nat.nblast/reference/index.html
+https://natverse.org/nat.nblast/reference/nat.nblast-package.html
+https://natverse.org/nat.nblast/reference/nblast.html
+https://natverse.org/nat.nblast/reference/nblast_allbyall.html
+https://natverse.org/nat.nblast/reference/neuron_pairs.html
+https://natverse.org/nat.nblast/reference/nhclust.html
+https://natverse.org/nat.nblast/reference/plot3d.hclust.html
+https://natverse.org/nat.nblast/reference/show_similarity.html
+https://natverse.org/nat.nblast/reference/smat.fcwb.html
+https://natverse.org/nat.nblast/reference/sparse_score_mat.html
+https://natverse.org/nat.nblast/reference/sub_dist_mat.html
+https://natverse.org/nat.nblast/reference/sub_score_mat.html
+https://natverse.org/nat.nblast/reference/subset.hclust.html
+
+