Example demonstrating simulation of OpenGL-rendered fireworks. Adapted from the Vispy gallery.
See the seamless examples directory:
The seamless YouTube playlist contains the following videos:
In [ ]:
# Download fireworks example context
import urllib.request
url = "https://raw.githubusercontent.com/sjdv1982/seamless/master/examples/fireworks/fireworks.seamless"
urllib.request.urlretrieve(url, filename = "fireworks.seamless")
url = "https://raw.githubusercontent.com/sjdv1982/seamless/master/examples/fireworks/orca.png"
urllib.request.urlretrieve(url, filename = "orca.png")
In [ ]:
# Boilerplate
import seamless
from seamless import cell, pythoncell, context, reactor, transformer
from seamless.lib.gui.basic_editor import edit
from seamless.lib.gui.basic_display import display
from seamless.lib import link
In [ ]:
ctx = seamless.fromfile("fireworks.seamless")
In [ ]:
# Piece of code to link a seamless cell and an ipywidget
import traitlets
from collections import namedtuple
import traceback
def widgetlink(c, w):
assert isinstance(c, seamless.core.Cell)
assert isinstance(w, traitlets.HasTraits)
assert w.has_trait("value")
handler = lambda d: c.set(d["new"])
value = c.value
if value is not None:
w.value = value
else:
c.set(w.value)
def set_traitlet(value):
try:
w.value = value
except:
traceback.print_exc()
w.observe(handler, names=["value"])
obs = seamless.observer(c, set_traitlet )
result = namedtuple('Widgetlink', ["unobserve"])
def unobserve():
nonlocal obs
t[0].unobserve(handler)
del obs
result.unobserve = unobserve
return result
In [ ]:
# Build ipywidgets and link them to seamless cells
# Clean up any old widgetlinks, created by repeated execution of this cell
try:
for w in widgetlinks:
widget.unobserve()
except NameError:
pass
from ipywidgets import Layout, Box, FloatSlider, IntSlider, Checkbox, Label
layout = Layout(
display='flex',
flex_flow='row',
justify_content='space-between',
)
from ipywidgets import FloatSlider, IntSlider, Checkbox, HBox
from collections import OrderedDict
# Build widgets
widgets = OrderedDict()
widgets["N"] = IntSlider(min=1, max=20000, description = "N (number of points)", layout=layout)
widgets["gravity"] = FloatSlider(min=0, max=5, description = "Gravity", layout=layout)
widgets["pointsize"] = IntSlider(min=1, max=100, description = "Pointsize", layout=layout)
widgets["period"] = FloatSlider(min=0.01, max=5, description = "Period (time between explosions)", layout=layout)
widgets["shrink_with_age"] = Checkbox(description = "Shrink points with age", layout=layout)
widgetlinks = [] # You need to hang on to the object returned by traitlink
form_items = []
for k,w in widgets.items():
c = getattr(ctx, k)
widgetlinks.append(widgetlink(c, w))
# Replace the description with a label-widget pair
row = Box([Label(value=w.description), w], layout=layout)
form_items.append(row)
w.description = ""
form = Box(form_items, layout=Layout(
display='flex',
flex_flow='column',
border='solid 2px',
align_items='stretch',
width='70%'
))
form
In [ ]:
# Replace the randomly generated texture with an image
ctx.tex_filename.set("orca.png")
ctx.tex_radius.set(100)
In [ ]:
# Define a mask, drawing a text onto it
import numpy as np
from PyQt5.QtGui import QImage, QFont, QColor
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter
imsize = 1000
img = QImage(imsize, imsize, QImage.Format_Grayscale8)
img.fill(Qt.white)
text = "Hello world!"
qp = QPainter()
try:
qp.begin(img)
qp.setPen(Qt.black)
font = QFont("Arial", 100)
qp.setFont(font)
mx = img.width()
my = img.height()
qp.drawText(0, 0, mx, my, Qt.AlignCenter,text)
finally:
qp.end()
mask = np.array(img.bits().asarray(img.byteCount())).reshape(img.width(),img.height())
ctx.mask = cell("array").set(mask)
In [ ]:
# Connect mask to pin
pin = ctx.display_texture.display_numpy.array
pin.cell().disconnect(pin)
ctx.mask.connect(pin)
In [ ]:
# Connect texture to pin (undoing the last cell)
pin = ctx.display_texture.display_numpy.array
pin.cell().disconnect(pin)
ctx.texture.connect(pin)
Change the fireworks into exploding letters (step 1)
In [ ]:
params = ctx.params_gen_vertexdata.value
params["mask"] = {"pin": "input", "dtype": "array"}
ctx.params_gen_vertexdata.set(params)
ctx.mask.connect(ctx.gen_vertexdata.mask)
Change the fireworks into exploding letters (step 2)
rotmask = np.rot90(mask, 3) #in (x,y) form
start_values0 = np.random.random((1000000, 3))
p = (start_values0*len(mask)).astype(np.int)[:,:2]
mask_values = rotmask[p[:,0], p[:,1]]
start_values0 = start_values0[mask_values==0]
start_values = 2*start_values0[:N]-1
To undo the exploding letters, change back cell-gen-vertexdata.py, and then:
In [ ]:
params = ctx.params_gen_vertexdata.value
params.pop("mask", None)
ctx.params_gen_vertexdata.set(params)