Skip to content

Commit

Permalink
new experimental render function
Browse files Browse the repository at this point in the history
  • Loading branch information
bwlewis committed Mar 3, 2018
1 parent 93be547 commit 829edc0
Show file tree
Hide file tree
Showing 11 changed files with 302 additions and 4 deletions.
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export(globejs)
export(graphjs)
export(lines3d)
export(points3d)
export(render)
export(renderGlobe)
export(renderScatterplotThree)
export(scatterplot3js)
Expand All @@ -23,6 +24,7 @@ importFrom(igraph,as_edgelist)
importFrom(igraph,layout_with_fr)
importFrom(igraph,norm_coords)
importFrom(igraph,vertices)
importFrom(methods,new)
importFrom(methods,setMethod)
importFrom(methods,setOldClass)
importFrom(stats,na.omit)
12 changes: 12 additions & 0 deletions R/data-definitions.R
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,15 @@ NULL
#' graph includes a force-directed layout with vertices colored by the \code{\link{cluster_fast_greedy}}
#' algorithm from the igraph package.
NULL

#' Pump rendering
#'
#' A keyframe animation example from the threejs.org website.
#'
#' @docType data
#' @name pump
#' @keywords threejs animation
#' @source The threejs.org web site \url{https://threejs.org/examples/#webgl_animation_keyframes_json}
#' @usage threejs:::render(gzfile(system.file("extdata/pump.json.gz", package="threejs")))
#' @format A compressed JSON file.
NULL
5 changes: 3 additions & 2 deletions R/globe.R
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,9 @@
#' ll <- unique(frequent_flights[,3:4])
#' # Plot frequent destinations as bars, and the flights to and from
#' # them as arcs. Adjust arc width and color by frequency.
#' globejs(lat=ll[,1], long=ll[,2], arcs=frequent_flights, bodycolor="#aaaaff",
#' arcsHeight=0.3, arcsLwd=2, arcsColor="#ffff00", arcsOpacity=0.15,
#' globejs(lat=ll[, 1], long=ll[, 2], arcs=frequent_flights,
#' bodycolor="#aaaaff", arcsHeight=0.3, arcsLwd=2,
#' arcsColor="#ffff00", arcsOpacity=0.15,
#' atmosphere=TRUE, color="#00aaff", pointsize=0.5)
#'
#' \dontrun{
Expand Down
52 changes: 52 additions & 0 deletions R/render.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
setClass("pointLight", representation(color="character", position="numeric"))

#' Directly render threejs scenes
#'
#' Directly render low-level JSON-encoded threejs scenes and objects.
#' Objects with an 'animations' field will be animated using the
#' threejs THREE.animationMixer function.
#'
#' @param scene A JSON-encoded threejs scene or object that can be added to a threejs scene (for instance, a group).
#' @param width The container div width.
#' @param height The container div height.
#' @param bg The container background color.
#' @param camera_position A three-element coordinate position vector of the threejs perspective camera.
#' @param camera_lookat A three-element coordinate position vector that the camera looks at.
#' @param ambient The ambient light color.
#' @param lights Either a single 'pointLight' object, or a list of them.
#' @param ... Optional additional parameters passed to the rendering code.
#'
#' @examples
#' render(gzfile(system.file("extdata/pump.json.gz", package="threejs")), bg="black")
#'
#' # An example from the threejs.org source code:
#' source <- "https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/json/scene-animation.json"
#' render(url(source), camera_position=c(-100, 0, 100))
#' @importFrom methods new
#' @export
render <- function(
scene,
height = NULL,
width = NULL,
bg="white",
camera_position=c(-5, 3, 10),
camera_lookat=c(-1, 2, 4),
ambient="#555555",
lights=new("pointLight", color="white", position=c(-5, 3, 10)),
...)
{
i <- grep("^#", bg)
if (length(i) > 0) bg <- substr(bg, 1, 7)
if (class(lights) != "list") lights <- list(lights)
x <- list(datauri=jsuri(scene), bg=bg, camera_position=camera_position, camera_lookat=camera_lookat, ambient=ambient, lights=lights)
additional_args <- list(...)
if (length(additional_args) > 0) x <- c(x, additional_args)

htmlwidgets::createWidget(
name = "render",
x = x,
width = width,
height = height,
htmlwidgets::sizingPolicy(padding = 0, browser.fill = TRUE),
package = "threejs")
}
8 changes: 8 additions & 0 deletions R/utilities.R
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,11 @@ indexline <- function(x) # zero index and make sure each element is an array in
if (length(a) == 1) a <- list(a)
a
}

# internal function to convert x to a JSON dataURI, where x is either character or raw
# JSON text or a connection or a non-compressed file.
jsuri <- function(x)
{
if(is.character(x) && file.exists(x)) return(dataURI(file=x, encoding=NULL, mime="application/javascript"))
dataURI(data=x, encoding=NULL, mime="application/javascript")
}
Binary file added inst/extdata/pump.json.gz
Binary file not shown.
147 changes: 147 additions & 0 deletions inst/htmlwidgets/render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
HTMLWidgets.widget(
{
name: "render",
type: "output",

initialize: function(el, width, height)
{
var w = parseInt(width);
var h = parseInt(height);
var g = new Widget.render(w, h);
if(w == 0) w = 1; // set minimum object size
if(h == 0) h = 1;
return {widget: g, width: parseInt(width), height: parseInt(height)};
},

resize: function(el, width, height, obj)
{
obj.width = width;
obj.height = height;
obj.widget.camera.aspect = width / height;
obj.widget.camera.updateProjectionMatrix();
obj.widget.renderer.setSize(obj.width, obj.height);
obj.widget.animate();
},

renderValue: function(el, x, obj)
{
obj.widget.init(el, obj.widget.init_width, obj.widget.init_height); // init here, see issue #52
obj.widget.create_plot(x); // see below
obj.widget.renderer.setSize(obj.width, obj.height);
obj.widget.animate();
}
})


var Widget = Widget || {};
Widget.render = function(w, h)
{

var _this = this;
var clock = new THREE.Clock();
var mixer;
var is_animated = false;

_this.init = function (el, width, height)
{
if(Detector.webgl)
{
_this.renderer = new THREE.WebGLRenderer({alpha: true, antialias: true});
_this.renderer.GL = true;
} else {
_this.renderer = new THREE.CanvasRenderer();
_this.renderer.GL = false;
}
_this.renderer.sortObjects = false;
_this.renderer.autoClearColor = false;
_this.renderer.setSize(width, height);
_this.el = el; // stash a reference to our container for posterity
_this.width = width;
_this.height = height;
_this.scene = new THREE.Scene();

if(height > 0) _this.camera = new THREE.PerspectiveCamera(40, width / height, 1e-5, 10000);
else _this.camera = new THREE.PerspectiveCamera(40, 1, 1e-5, 10000);

_this.controls = new THREE.StateOrbitControls(_this.camera, _this.el);
_this.controls.rotateSpeed = 0.6;
_this.controls.zoomSpeed = 1.5;
_this.controls.panSpeed = 1;
_this.controls.enableZoom = true;
_this.controls.enablePan = true;
_this.controls.enableDamping = true;
_this.controls.dampingFactor = 0.15;
_this.controls.addEventListener('change', render);


while (el.hasChildNodes()) {
el.removeChild(el.lastChild);
}
el.appendChild(_this.renderer.domElement);
}

/*
* Simplified low-level rendering API
* Required fields:
* x.datauri dataURI encoded scene or group compatible with ObjectLoader
* x.scene logical, if true then datauri represents a threejs scene
* x.bg background color
* x.camera_position 3-element perspective camera position vector
* x.camera_lookat 3-element perspective camera look at vector
* Optional fields:
* x.pointlight a vector of pointLight objects, each with 'color' (charachter) and 'position' (3-element numeric) fields
* x.ambient scene ambient light color
* x.fov perspective camera field of view (numeric)
*/
_this.create_plot = function(x)
{
new THREE.ObjectLoader().load(x.datauri, function (loadedScene)
{
// XXX what about geometries? (https://threejs.org/docs/index.html#api/loaders/ObjectLoader)
if(loadedScene.type && loadedScene.type == "scene") _this.scene = loadedScene;
else _this.scene.add(loadedScene);
_this.scene.background = new THREE.Color(x.bg);
_this.camera.position.set(x.camera_position[0], x.camera_position[1], x.camera_position[2]);
_this.camera.lookAt(new THREE.Vector3(x.camera_lookat[0], x.camera_lookat[1], x.camera_lookat[2]));
if(x.fov) _this.camera.fov = x.fov;
if(x.lights)
{
for(i=0; i < x.lights.length; i++)
{
var p = new THREE.PointLight(new THREE.Color(x.lights[i].color), 1);
p.position.x = x.lights[i].position[0];
p.position.y = x.lights[i].position[1];
p.position.z = x.lights[i].position[2];
_this.scene.add(p);
}
}
if(x.ambeint) _this.scene.add(new THREE.AmbientLight(new THREE.Color(x.ambient)));
if(loadedScene.animations)
{
is_animated = true;
mixer = new THREE.AnimationMixer(loadedScene);
mixer.clipAction(loadedScene.animations[0]).play();
}
animate();
}, null, function (err) { console.log(err); });
}

_this.animate = function ()
{
animate();
}

animate = function ()
{
requestAnimationFrame(animate); // (hogs CPU)
render();
};

function render()
{
if(is_animated) mixer.update(clock.getDelta());
_this.renderer.clear();
_this.renderer.render(_this.scene, _this.camera);
}

};
15 changes: 15 additions & 0 deletions inst/htmlwidgets/render.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
dependencies:
- name: jquery
version: 1.12.4
src: "htmlwidgets/lib/jquery"
script: jquery.min.js
- name: threejs
version: 85
src: "htmlwidgets/lib/threejs-85"
script:
- three.min.js
- Detector.js
- Projector.js
- CanvasRenderer.js
- TrackballControls.js
- StateOrbitControls.js
5 changes: 3 additions & 2 deletions man/globejs.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions man/pump.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions man/render.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 829edc0

Please sign in to comment.