From e676af761229001101e66b0fcd325aeaa53fa521 Mon Sep 17 00:00:00 2001 From: soulwire Date: Mon, 2 Mar 2015 01:24:29 +0000 Subject: [PATCH] Added examples --- .gitignore | 3 + examples/async.js | 95 ++++++++++++++++++++++++++ examples/canvas.js | 41 ++++++++++++ examples/random.js | 87 ++++++++++++++++++++++++ examples/types.js | 164 +++++++++++++++++++++++++++++++++++++++++++++ examples/webgl.js | 114 +++++++++++++++++++++++++++++++ 6 files changed, 504 insertions(+) create mode 100644 .gitignore create mode 100644 examples/async.js create mode 100644 examples/canvas.js create mode 100644 examples/random.js create mode 100644 examples/types.js create mode 100644 examples/webgl.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..101522e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*~ +*# +.DS_STORE \ No newline at end of file diff --git a/examples/async.js b/examples/async.js new file mode 100644 index 0000000..7507d62 --- /dev/null +++ b/examples/async.js @@ -0,0 +1,95 @@ + +/* + + This example demonstrates one technique for creating asynchronous generators - ones that, for + various reasons including performance, might not be able to produce an image in a single frame. + + Here we render a single circle at a regular interval until the desired number of circles have + rendered before firing the `done` callback passed to the `generate` method. We also ensure that + all queued tasks from the last call to `generate` are cleared so as to ensure to memory or + visual leak occurs. + +*/ + +var Generator = (function() { + + var canvas = document.createElement( 'canvas' ) + var context = canvas.getContext( '2d' ) + var counter = 0 + var interval = -1 + + var settings = { + + numCircles: { + type: 'number', + range: [ 20, 250 ], + value: 100, + step: 1 + } + } + + function addCircle() { + + // remember to use `stuvio.random` to keep it deterministic! + + var width = context.canvas.width + var height = context.canvas.height + var radius = Math.min( width, height ) * stuvio.random.float( 0.01, 0.1 ) + var offset = Math.min( width, height ) * stuvio.random.float( 0.5 ) - radius + var angle = stuvio.random.float( 0, Math.PI * 2 ) + + var x = ( width / 2 ) + Math.cos( angle ) * offset + var y = ( height / 2 ) + Math.sin( angle ) * offset + + var hue = Math.round( stuvio.random.float( 360 ) ) + var sat = Math.round( stuvio.random.float( 30, 100 ) ) + var lum = Math.round( stuvio.random.float( 30, 90 ) ) + + context.beginPath() + context.arc( x, y, radius, 0, Math.PI * 2 ) + context.fillStyle = 'hsla(' + hue + ',' + sat + '%,' + lum + '%,0.8)' + context.fill() + } + + return { + + context: context, + + settings: settings, + + initialize: function( done ) { + + done() + }, + + generate: function( done ) { + + clearInterval( interval ) + + context.fillStyle = '#ddd' + context.fillRect( 0, 0, context.canvas.width, context.canvas.height ) + + counter = 0 + + interval = setInterval( function() { + + if ( counter++ < settings.numCircles.value ) { + + addCircle() + + } else { + + clearInterval( interval ) + done() + } + + }, 10 ) + }, + + destroy: function( done ) { + + done() + } + } + +})(); \ No newline at end of file diff --git a/examples/canvas.js b/examples/canvas.js new file mode 100644 index 0000000..374c61e --- /dev/null +++ b/examples/canvas.js @@ -0,0 +1,41 @@ + +var Generator = (function() { + + var canvas = document.createElement( 'canvas' ) + var context = canvas.getContext( '2d' ) + + var settings = { + + backgroundColor: { + type: 'color', + label: 'Background Color', + value: '#d9ebee' + } + } + + return { + + context: context, + + settings: settings, + + initialize: function( done ) { + + done() + }, + + generate: function( done ) { + + context.fillStyle = settings.backgroundColor.value + context.fillRect( 0, 0, canvas.width, canvas.height ) + + done() + }, + + destroy: function( done ) { + + done() + } + } + +})(); \ No newline at end of file diff --git a/examples/random.js b/examples/random.js new file mode 100644 index 0000000..3cf6574 --- /dev/null +++ b/examples/random.js @@ -0,0 +1,87 @@ + +/* + + This example demonstrates how to use randomness in your generators. Because we must ensure that + an image is reproducible, when using random numbers they must be generated by a _deterministic_ + pseudo-random number generator, rather than `Math.random`. For this, we provide you with + `studio.random`, which is documented in the [sandbox readme](https://github.com/stuvio/sandbox) + + When a generator is initialized in the Stuvio environment, it is assigned a `seed` value and + before each call to `generate` the pseudo-random number generator powering `stuvio.random` is + reset to this seed value. You are of course free to override this value by creating a setting + for your own seed value, as is show here. Be sure however to use this value to set the seed of + `studio.random`, inside your `generate` method and before you actually generate any numbers. + +*/ + +var Generator = (function() { + + var canvas = document.createElement( 'canvas' ) + var context = canvas.getContext( '2d' ) + + var settings = { + + seed: { + type: 'number', + range: [ 0, 1000 ], + value: 12, + step: 1 + }, + + lines: { + type: 'number', + range: [ 5, 20 ], + value: 10, + step: 1 + } + } + + return { + + context: context, + + settings: settings, + + initialize: function( done ) { + + done() + }, + + generate: function( done ) { + + // override the current stuvio seed with the settings seed + stuvio.random.seed = settings.seed.value + + context.globalAlpha = 1.0 + context.fillStyle = '#323f4c' + context.fillRect( 0, 0, canvas.width, canvas.height ) + + for ( var i = 0, n = settings.lines.value; i < n; i++ ) { + + context.beginPath() + + context.moveTo( + canvas.width * stuvio.random.float( 0.05, 0.95 ), + canvas.height * stuvio.random.float( 0.05, 0.95 ) + ) + + context.lineTo( + canvas.width * stuvio.random.float( 0.05, 0.95 ), + canvas.height * stuvio.random.float( 0.05, 0.95 ) + ) + + context.lineWidth = canvas.width * stuvio.random.float( 0.02, 0.1 ) + context.globalAlpha = stuvio.random.float( 0.1, 0.9 ) + context.strokeStyle = '#fff' + context.lineCap = 'round' + context.stroke() + } + }, + + destroy: function( done ) { + + done() + } + } + +})(); \ No newline at end of file diff --git a/examples/types.js b/examples/types.js new file mode 100644 index 0000000..7d899cf --- /dev/null +++ b/examples/types.js @@ -0,0 +1,164 @@ + +/* + + This is the kitchen sink example, showing all of the current input types + +*/ + +var Generator = (function() { + + var canvas = document.createElement( 'canvas' ) + var context = canvas.getContext( '2d' ) + + var settings = { + + exampleColor: { + type: 'color', + label: 'Color', + description: 'Example color input', + value: '#4DECB4' + }, + + exampleImage: { + type: 'image', + label: 'Image', + description: 'Example image input', + value: new Image() + }, + + exampleNumber: { + type: 'number', + label: 'Number', + description: 'Example number input', + range: [ 0.1, 1.0 ], + value: 0.5, + step: 0.01 + }, + + exampleRange: { + type: 'number', + label: 'Range', + description: 'Example range input', + range: [ 0.0, 1.0 ], + value: [ 0.1, 0.9 ], + step: 0.01 + }, + + exampleBoolean: { + type: 'boolean', + label: 'Boolean', + description: 'Example boolean input', + value: true + }, + + exampleAudio: { + type: 'audio', + label: 'Audio', + description: 'Example audio input', + interval: 1 / 20, // interval (in seconds) between samples (1 / samplesPerSecond) + duration: [ 1, 3 ], // min / max duration in seconds + bands: 64, // number of bands per sample + value: null + } + } + + return { + + context: context, + + settings: settings, + + initialize: function( done ) { + + // load assets before declaring the generator ready... + settings.exampleImage.value.src = 'assets/logo.png' + settings.exampleImage.value.onload = done + }, + + generate: function( done ) { + + // use color + + context.fillStyle = settings.exampleColor.value + context.fillRect( 0, 0, canvas.width, canvas.height ) + + // use audio and range + + var audioData = settings.exampleAudio.value + + if ( audioData && audioData.length ) { + + var slices = audioData.length + var bands = settings.exampleAudio.bands + + var xMin = canvas.width * settings.exampleRange.value[0] + var xMax = canvas.width * settings.exampleRange.value[1] + + var yStep = canvas.height / ( audioData.length + 1 ) + var xStep = ( xMax - xMin ) / bands + + var slice, band, data + + context.save() + context.translate( xMin, 0 ) + context.beginPath() + + for ( slice = 0; slice < slices; slice++ ) { + + data = audioData[ slice ] + + context.translate( 0, yStep ) + + for ( band = 0; band < bands; band++ ) { + + if ( band === 0 ) { + + context.moveTo( band * xStep, ( data[ band ] / 255 ) * yStep * -2.0 ) + + } else { + + context.lineTo( band * xStep, ( data[ band ] / 255 ) * yStep * -2.0 ) + } + + } + } + + context.strokeStyle = '#111' + context.lineWidth = 1 + context.lineJoin = 'round' + context.lineCap = 'round' + context.stroke() + context.restore() + } + + // use image, number and boolean + + if ( settings.exampleBoolean.value ) { + + var texture = settings.exampleImage.value + var aspect = texture.height / texture.width + var sideA = Math.min( canvas.width, canvas.height ) + var sideB = Math.max( texture.width, texture.height ) + var scale = settings.exampleNumber.value * ( sideA / sideB ) + + context.save() + context.translate( canvas.width / 2, canvas.height / 2 ) + context.scale( scale, scale ) + context.translate( texture.width / -2, texture.height / -2 ) + context.drawImage( texture, 0, 0 ) + context.restore() + } + + + // all present and correct + + done() + }, + + destroy: function( done ) { + + done() + } + } + +})(); \ No newline at end of file diff --git a/examples/webgl.js b/examples/webgl.js new file mode 100644 index 0000000..b5b4ad5 --- /dev/null +++ b/examples/webgl.js @@ -0,0 +1,114 @@ + +/* + + This is an example of a WebGL based generator, built on top of the Three.js library. It shows + how to use a rendering context created by a third party library, as well as how to utilize the + `studio.env` attribute in order to perform conditional actions based on whether the generator + is running on the web, or being used to generate high resolution output for print. + + When the generator is being used to create print ready images (this happens on our servers once + a customers has created and purchased a print), the `studio.env` attribute is set to ’print’ as + opposed to ‘web’. For WebGL based generators, the drawing buffer must be preserved when in this + mode, and so when constructing the WebGL context (in this case via the Three.js WebGLRenderer + Class) you must ensure that `preserveDrawingBuffer` is set to `true` + +*/ + +var Generator = (function() { + + var renderer, camera, scene, cube + + return { + + context: null, + + settings: { + + rotationX: { + type: 'number', + label: 'X Rotation', + description: 'Rotation of the cube along the x axis', + range: [ 0, Math.PI * 2 ], + value: Math.PI * 0.25, + step: Math.PI / 180 + }, + + rotationY: { + type: 'number', + label: 'Y Rotation', + description: 'Rotation of the cube along the y axis', + range: [ 0, Math.PI * 2 ], + value: Math.PI * 0.25, + step: Math.PI / 180 + }, + + rotationZ: { + type: 'number', + label: 'Z Rotation', + description: 'Rotation of the cube along the z axis', + range: [ 0, Math.PI * 2 ], + value: 0, + step: Math.PI / 180 + } + }, + + initialize: function( done ) { + + // normally, you should add and link dependancies from the dependancies directory + // we're injecting Three.js from a CDN here simply to avoid cluttering index.html + // for the sake of this example + var script = document.createElement( 'script' ) + script.src = 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js' + document.body.appendChild( script ) + + script.onload = function() { + + renderer = new THREE.WebGLRenderer({ + // we need to preserve the drawing buffer when this generator is being used to output for print + preserveDrawingBuffer: stuvio.env === 'print', + antialias: true + }) + + camera = new THREE.PerspectiveCamera() + camera.position.z = 1000 + + scene = new THREE.Scene() + + cube = new THREE.Mesh( new THREE.BoxGeometry( 200, 200, 200 ), new THREE.MeshNormalMaterial() ) + cube.rotation.x = Math.PI * 0.2 + cube.rotation.y = Math.PI * 0.2 + scene.add( cube ) + + // since the context is created by Three, we must add it to the Generator object + // here since once initialize completes a context is required + Generator.context = renderer.context + + done() + } + }, + + generate: function( done ) { + + var width = this.context.canvas.width + var height = this.context.canvas.height + + cube.rotation.x = this.settings.rotationX.value + cube.rotation.y = this.settings.rotationY.value + cube.rotation.z = this.settings.rotationZ.value + + camera.aspect = width / height + camera.updateProjectionMatrix() + + renderer.setSize( width, height ) + renderer.render( scene, camera ) + + done() + }, + + destroy: function( done ) { + + done() + } + } + +})() \ No newline at end of file