Skip to content

Instantly share code, notes, and snippets.

@anthonydouc
Last active August 23, 2017 14:54
Show Gist options
  • Save anthonydouc/7e5bb2a01972e29f82e4072304b539e9 to your computer and use it in GitHub Desktop.
Save anthonydouc/7e5bb2a01972e29f82e4072304b539e9 to your computer and use it in GitHub Desktop.
from __future__ import division
import numpy as np
from bokeh.core.properties import Instance, String, List, Either
from bokeh.models import ColumnDataSource, LayoutDOM
from bokeh.models.widgets import Button
from bokeh.layouts import column, row
from bokeh.io import curdoc
JS_CODE = """
# These "require" lines are similar to python "import" statements
import * as p from "core/properties"
import {LayoutDOM, LayoutDOMView} from "models/layouts/layout_dom"
# This defines the layout options for a Plotly 3d graph.
layout =
title: 'Title'
autosize: false
width: 400
height: 400
scene:
aspectratio:
x: 0.85
y: 0.85
z: 0.85
margin:
l: 0
r: 0
b: 0
t: 0
pad: 0
# To create custom model extensions that will render on to the HTML canvas
# or into the DOM, we must create a View subclass for the model. Currently
# Bokeh models and views are based on BackBone. More information about
# using Backbone can be found here:
#
# http://backbonejs.org/
#
# In this case we will subclass from the existing BokehJS ``LayoutDOMView``,
# corresponding to our
export class Surface3dView extends LayoutDOMView
initialize: (layout) ->
super(layout)
url = "https://cdn.plot.ly/plotly-latest.min.js"
script = document.createElement('script')
script.src = url
script.async = false
script.onreadystatechange = script.onload = () => @_init()
document.querySelector("head").appendChild(script)
_init: () ->
@_graph = new Plotly.newPlot(@el, @get_data(), layout)
# Set Backbone listener so that when the Bokeh data source has a change
# event, we can process the new data
# currently configured to bokeh 0.12.4
@listenTo(@model.data_source, 'change', () =>
Plotly.newPlot(@el, @get_data(), layout)
)
# This is the callback executed when the Bokeh data has an change.
# Plotly data is stored as a simple dict, so we can simply send this across.
get_data: () ->
source = @model.data_source
data= {
z: source.get_column(@model.z),
x: source.get_column(@model.x),
y: source.get_column(@model.y),
type: 'surface',
showscale: false
}
return [data]
# We must also create a corresponding JavaScript Backbone model sublcass to
# correspond to the python Bokeh model subclass. In this case, since we want
# an element that can position itself in the DOM according to a Bokeh layout,
# we subclass from ``LayoutDOM``
export class Surface3d extends LayoutDOM
# This is usually boilerplate. In some cases there may not be a view.
default_view: Surface3dView
# The ``type`` class attribute should generally match exactly the name
# of the corresponding Python class.
type: "Surface3d"
# The @define block adds corresponding "properties" to the JS model. These
# should basically line up 1-1 with the Python model class. Most property
# types have counterparts, e.g. ``bokeh.core.properties.String`` will be
# ``p.String`` in the JS implementatin. Where the JS type system is not yet
# as rich, you can use ``p.Any`` as a "wildcard" property type.
@define {
x: [ p.String ]
y: [ p.String ]
z: [ p.String ]
data_source: [ p.Instance ]
}
"""
# This custom extension model will have a DOM view that should layout-able in
# Bokeh layouts, so use ``LayoutDOM`` as the base class. If you wanted to create
# a custom tool, you could inherit from ``Tool``, or from ``Glyph`` if you
# wanted to create a custom glyph, etc.
class Surface3d(LayoutDOM):
# The special class attribute ``__implementation__`` should contain a string
# of JavaScript (or CoffeeScript) code that implements the JavaScript side
# of the custom extension model.
__implementation__ = JS_CODE
# Below are all the "properties" for this model. Bokeh properties are
# class attributes that define the fields (and their types) that can be
# communicated automatically between Python and the browser. Properties
# also support type validation. More information about properties in
# can be found here:
#
# http://bokeh.pydata.org/en/latest/docs/reference/core.html#bokeh-core-properties
# This is a Bokeh ColumnDataSource that can be updated in the Bokeh
# server by Python code
data_source = Instance(ColumnDataSource)
# The vis.js library that we are wrapping expects data for x, y, z, and
# color. The data will actually be stored in the ColumnDataSource, but
# these properties let us specify the *name* of the column that should
# be used for each field.
x = String
y = String
z = String
x = np.arange(0, 300, 10)
y = np.arange(0, 300, 10)
xx, yy = np.meshgrid(x, y)
z = np.sin(xx/50) * np.cos(yy/50) * 50 + 50
x1 = [list(x_i) for x_i in xx]
y1 = [list(x_i) for x_i in yy]
z1 = [list(x_i) for x_i in z]
source1 = ColumnDataSource(data=dict(x=x1, y=y1, z=z1))
surface1 = Surface3d(x="x", y="y", z="z", data_source=source1)
# Applying the parametric equation..
s = np.linspace(0, 2 * np.pi, 240)
t = np.linspace(0, np.pi, 240)
tGrid, sGrid = np.meshgrid(s, t)
r = 2 + np.sin(7 * sGrid + 5 * tGrid) # r = 2 + sin(7s+5t)
x = r * np.cos(sGrid) * np.sin(tGrid) # x = r*cos(s)*sin(t)
y = r * np.sin(sGrid) * np.sin(tGrid) # y = r*sin(s)*sin(t)
z = r * np.cos(tGrid) # z = r*cos(t)
x = [list(arr) for arr in x]
y = [list(arr) for arr in y]
z = [list(arr) for arr in z]
source2 = ColumnDataSource(data=dict(x=x, y=y, z=z))
surface2 = Surface3d(x="x", y="y", z="z", data_source=source2)
curdoc().add_root(column(surface1, surface2))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment