multiframe_buttons
forked from multiframe (diff: 257)
TO DO: make noise duration by real duration, not frame count TO DO: hello
ActionScript3 source code
/**
* Copyright wexler ( http://wonderfl.net/user/wexler )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/7X2q
*/
package {
import flash.ui.ContextMenuClipboardItems;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.*;
import flash.utils.getTimer;
import flash.text.*;
import net.hires.debug.Stats;
import com.bit101.components.*;
[SWF(width = "700", height = "500", frameRate = "60", backgroundColor = "#7F7F7F")]
public class main extends Sprite {
private const _size:uint = 500; // in [SWF... field, height should be _size
private const _frame_rate:Number = 60; // make sure that this is the same as in [SWF...]
private const _n_dots:uint = 500;
private const _r_min:Number = 0.475;
private const _r_max:Number = 0.95;
private const _radius:uint = 7;
private const _max_frames:uint = 53; // this should be a prime number
private const _fp_radius:uint = 4;
private const _control_field:uint = 200; // in [SWF... field, width should be _size + _control_field
private const _button_margin:uint = 50;
private const _button_spacing:uint = 20;
private const _expl_button_size:uint = 60;
private const _explanation:String = ( <![CDATA[Most observers perceive slow rotation, punctuated by brief bursts of much faster rotation in the opposite direction. <b>The bursts of very fast motion are illusory.</b> (The illusion may get stronger after one or two jumps, and is enhanced when paying close attention to the dots in the ring.)
The illusion is produced by completely re-randomizing the positions and contrasts of the dots on every frame; this is done for one or more frames, and then the new configuration is set to turning once again. In other words, although a match between, say, a black dot before and after the 'jump' may be found at many different positions, chances are none of the neighboring dots will match. Technically, there is no peak in motion energy at the particlar backwards jumps that are seen.
]]> ).toString();
private var _dots:Array = new Array(_max_frames);
private var _in_noise:Boolean = false;
private var _frame:uint = 0; // current frame, which just keeps counting up (the actual frame used is mod _max_frames)
private var _noise_stop_t:uint;
private var _noise_frames:uint = 3;
private var _stats:* = undefined;
private var _theta0:Number;
private var _dir:int = +1;
private var _speed:Number = 10.0; // in deg/sec
private var _last_velmod_t:uint;
private var _last_velmod_ang:Number;
private var _noise_period:uint = 5000; // in msec; if 0, means no automatic noise
private var _last_noise_t:uint;
private var _state:String;
private var _intro_display:*;
private var _begin_button:*;
private var _expl_but:* = undefined;
private var _expl_text:*;
private var _expl_prev_state:String;
public function main()
{
Style.LABEL_TEXT = 0x202020;
Style.BACKGROUND = 0xA0A0A0;
randomize();
_last_velmod_t = _last_noise_t = getTimer();
_theta0 = _last_velmod_ang = 0;
addEventListener(Event.ENTER_FRAME, update);
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouse_listener);
create_buttons();
begin_intro();
}
public function begin_intro():void
{
_state = "intro";
_intro_display = new TextField();
_intro_display.text = "Fixate the red point you will see in the center\nwhile paying attention to the rotating ring\nof gray circles\n";
var format:TextFormat = new TextFormat();
format.color = 0x000000;
format.size = 24;
format.align = "center";
_intro_display.setTextFormat(format);
_intro_display.x = 0;
_intro_display.y = 100;
_intro_display.width = _size;
_intro_display.height = 100;
addChild(_intro_display);
_begin_button = new PushButton(this, _size/2, _size/2, "BEGIN", begin);
_begin_button.x -= _begin_button.width/2;
_begin_button.y -= _begin_button.height/2;
}
public function end_intro():void
{
removeChild(_intro_display);
removeChild(_begin_button);
}
public function begin(e:MouseEvent):void
{
end_intro();
begin_display();
}
public function begin_display():void
{
_state = "display";
_last_velmod_t = _last_noise_t = getTimer();
_theta0 = _last_velmod_ang = 0;
}
public function end_display():void
{
graphics.clear();
}
public function create_buttons():void
{
var y:uint = 0;
var rb:RadioButton;
var rev_but:PushButton = new PushButton(this, _size + _button_margin, y, "REVERSE", reverse);
y = 45;
var freq_vbox:VBox = new VBox(this, _size + _button_margin, y);
new Label(freq_vbox, 0, 0, "NOISE FREQUENCY");
rb = new RadioButton(freq_vbox, 0, 0, "LOW", true , set_noise_freq); rb.tag = 0; rb.groupName = "freq";
rb = new RadioButton(freq_vbox, 0, 0, "HIGH", false, set_noise_freq); rb.tag = 1; rb.groupName = "freq";
rb = new RadioButton(freq_vbox, 0, 0, "MANUAL\n(CLICK ON STIMULUS)",
false, set_noise_freq); rb.tag = 2; rb.groupName = "freq";
y = 145;
var dur_vbox:VBox = new VBox(this, _size + _button_margin, y);
new Label(dur_vbox, 0, 0, "NOISE DURATION");
rb = new RadioButton(dur_vbox, 0, 0, "BRIEF", false, set_noise_dur); rb.tag = 0; rb.groupName = "dur";
rb = new RadioButton(dur_vbox, 0, 0, "MEDIUM", true , set_noise_dur); rb.tag = 1; rb.groupName = "dur";
rb = new RadioButton(dur_vbox, 0, 0, "LONG", false, set_noise_dur); rb.tag = 2; rb.groupName = "dur";
rb = new RadioButton(dur_vbox, 0, 0, "VERY LONG", false, set_noise_dur); rb.tag = 3; rb.groupName = "dur";
y = 247;
var speed_vbox:VBox = new VBox(this, _size + _button_margin, y);
new Label(speed_vbox, 0, 0, "INDUCER SPEED");
rb = new RadioButton(speed_vbox, 0, 0, "ZERO", false, change_speed); rb.tag = 0; rb.groupName = "speed";
rb = new RadioButton(speed_vbox, 0, 0, "VERY SLOW", false, change_speed); rb.tag = 1; rb.groupName = "speed";
rb = new RadioButton(speed_vbox, 0, 0, "SLOW", true , change_speed); rb.tag = 2; rb.groupName = "speed";
rb = new RadioButton(speed_vbox, 0, 0, "MEDIUM", false, change_speed); rb.tag = 3; rb.groupName = "speed";
rb = new RadioButton(speed_vbox, 0, 0, "FAST", false, change_speed); rb.tag = 4; rb.groupName = "speed";
_expl_but = new PushButton(this, _size + _button_margin, _size - _expl_button_size, "WHAT'S GOING ON?", toggle_explain);
_expl_but.height = _expl_button_size;
}
public function reverse(e:MouseEvent):void
{
_last_velmod_t = _last_noise_t = getTimer();
_last_velmod_ang = _theta0;
_dir *= -1;
_in_noise = false;
}
public function set_noise_freq(e:MouseEvent):void
{
switch(RadioButton(e.currentTarget).tag) {
case 0:
_noise_period = 5000;
break;
case 1:
_noise_period = 500;
break;
case 2:
_noise_period = 0;
break;
}
}
public function set_noise_dur(e:MouseEvent):void
{
switch(RadioButton(e.currentTarget).tag) {
case 0:
_noise_frames = 1;
break;
case 1:
_noise_frames = 3;
break;
case 2:
_noise_frames = 6;
break;
case 3:
_noise_frames = 30;
break;
}
}
public function change_speed(e:MouseEvent):void
{
_last_velmod_t = _last_noise_t = getTimer();
_last_velmod_ang = _theta0;
_in_noise = false;
switch(RadioButton(e.currentTarget).tag) {
case 0:
_speed = 0;
break;
case 1:
_speed = 5;
break;
case 2:
_speed = 10;
break;
case 3:
_speed = 20;
break;
case 4:
_speed = 40;
break;
}
}
public function toggle_explain(e:MouseEvent):void
{
if(_state != "explain") {
_expl_prev_state = _state;
_state = "explain";
if(_expl_prev_state == "intro")
end_intro();
else if(_expl_prev_state == "display")
end_display();
_expl_text = new TextField();
_expl_text.multiline = true;
_expl_text.wordWrap = true;
var style:StyleSheet = new StyleSheet();
var styleObj:Object = new Object();
styleObj.fontSize = 14;
style.setStyle("p", styleObj);
_expl_text.styleSheet = style;
_expl_text.htmlText = _explanation;
_expl_text.x = _intro_display.y = 0;
_expl_text.width = _expl_text.height = _size;
addChild(_expl_text);
_expl_but.label = "GO BACK";
}
else {
removeChild(_expl_text);
_state = _expl_prev_state;
if(_state == "intro")
begin_intro();
else if(_state == "display")
begin_display();
_expl_but.label = "WHAT'S GOING ON?";
}
}
public function update(e:Event):void
{
if(_state == "display") {
const t:uint = getTimer();
_theta0 = _last_velmod_ang + (t - _last_velmod_t)*_dir*_speed*(Math.PI/180)/1000.0;
if(_noise_period > 0) { // check if we need to start noise
if(t > _last_noise_t + _noise_period) {
start_noise();
_last_noise_t = t;
}
}
if(_in_noise) {
//if(_frame < _last_noise_frame) {
if(t < _noise_stop_t) {
_frame++;
}
else {
_in_noise = false;
}
}
graphics.clear();
for each(var p:dot in _dots[_frame%_max_frames]) {
const x:Number = (1 + p.r*Math.cos(p.theta + _theta0))*_size/2;
const y:Number = (1 - p.r*Math.sin(p.theta + _theta0))*_size/2;
graphics.beginFill(p.color);
graphics.drawCircle(x, y, _radius);
graphics.endFill();
}
graphics.beginFill(0xFF0000);
graphics.drawCircle(_size/2, _size/2, _fp_radius);
graphics.endFill();
}
}
public function mouse_listener(e:MouseEvent):void
{
if(!e.shiftKey) {
if(e.stageX < _size) {
start_noise();
}
}
else {
if(_stats) {
removeChild(_stats);
_stats = undefined;
}
else {
_stats = addChild(new Stats());
}
}
}
public function start_noise():void
{
if(!_in_noise) {
_in_noise = true;
_noise_stop_t = getTimer() + (0.5 + _noise_frames)*1000.0/_frame_rate;
}
}
private function randomize():void {
const z:Number = Math.pow(_r_min/_r_max, 2);
const u:Number = 1 - z;
for(var f:uint = 0; f < _max_frames; f++) {
_dots[f] = new Array(_n_dots);
for(var i:uint = 0; i < _n_dots; i++) {
var r:Number = Math.sqrt(z + u*Math.random())*_r_max;
var th:Number = Math.random()*2*Math.PI;
var gray:uint = Math.random()*256;
_dots[f][i] = new dot(r, th, gray + 256*gray + 65536*gray);
}
}
}
}
}
class dot
{
public var r:Number;
public var theta:Number;
public var color:uint;
public function dot(R:Number, Theta:Number, Color:uint)
{
r = R;
theta = Theta,
color = Color;
}
}
