forked from: Hirakana Fractal in AS3
Emotion Fractal in AS3
refer to http://levitated.net/daily/levEmotionFractal.html
// forked from ht_tregra's Hirakana Fractal in AS3
// Emotion Fractal in AS3
// refer to http://levitated.net/daily/levEmotionFractal.html
package {
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.IOErrorEvent;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.geom.Rectangle;
import flash.geom.ColorTransform;
import flash.text.*;
import caurina.transitions.Tweener;
import caurina.transitions.properties.TextShortcuts;
import flash.system.System;
import flash.system.Security;
import flash.system.ApplicationDomain;
import flash.system.SecurityDomain;
import flash.system.LoaderContext;
[SWF(backgroundColor="#000000", frameRate=30)]
public class StringFractal extends Sprite {
private var holder:Sprite;
private var queue:Array;
private var words:Array;
private var colors:Array;
private var font:Class;
private var fontCache:Array = [];
private var fontLoaded:Number = 0;
private var fontTotal:Number = 0;
//private var fontNameBase:String = 'Mplus_2c_Thin';
private var fontNameBase:String = 'KozMinPro_ExtraLight';
// for benchmark
private var fontSwfBytesTotal:Number = 0;
public function StringFractal() {
stage.align = "TL";
stage.scaleMode = "noScale";
words = [];
for (var i:int = 0x3041;i < 0x3095;i++) {
words.push( String.fromCharCode(i) );
}
holder = new Sprite();
addChild(holder);
TextShortcuts.init();
prepareFont();
}
private function onclick( e :MouseEvent ):void {
switch (stage.displayState) {
case "normal" :
stage.displayState = "fullScreen";
break;
case "fullScreen" :
default :
stage.displayState = "normal";
break;
}
init(null);
}
public function init(e:MouseEvent):void {
Tweener.removeAllTweens();
while (holder.numChildren) holder.removeChildAt(0);
queue = [new Rectangle(0, 0, stage.stageWidth, stage.stageHeight)];
colors = [0xffffff, Math.ceil(Math.random()*0xffffff)];
// フルスクリーン用ボタン
var oldBtn:Sprite = getChildByName('fullscreen_btn') as Sprite;
if (oldBtn) {
removeChild( oldBtn );
oldBtn = null;
}
var btn :Sprite = new Sprite;
addChild( btn );
btn.name = 'fullscreen_btn';
btn.alpha = 0.8;
btn.graphics.beginFill(colors[1]);
btn.graphics.drawRect(0,0,25,25);
btn.buttonMode = true;
var tf: TextField = new TextField();
tf.selectable = false;
tf.embedFonts = true;
tf.y = -3;
switch (stage.displayState) {
case "fullScreen":
tf.defaultTextFormat = new TextFormat(fontNameBase + '_5C00', 20, 0xffffff); // 小 5C0F
tf.text = '小';
break;
case "fullScreen":
default:
tf.defaultTextFormat = new TextFormat(fontNameBase + '_5900', 20, 0xffffff); // 大 5927
tf.text = '大';
break;
}
btn.addChild(tf);
btn.addEventListener( MouseEvent.CLICK, onclick );
addEventListener(Event.ENTER_FRAME, fill);
}
public function prepareFont():void {
loadFontForString();
}
public function loadFontForString():void {
var str:String = words.join();
str += '大小'; // フルスクリーンボタン用
var str_len:int = str.length;
for (var i:int = 0;i < str_len;i++) {
var charCode:String = NumberToString( str.charCodeAt(i) );
while (charCode.length < 4) {
charCode = '0' + charCode;
}
var fontName:String = fontNameBase + '_' + charCode.charAt(0) + charCode.charAt(1) + '00';
var insert:Boolean = true;
for (var j:int = 0;j < fontCache.length;j++) {
if (fontCache[j] == fontName) {
insert = false;
}
}
if (insert) {
loadFontSwf("http://www.tregra.jp/as3/font/"+fontName+".swf");
//loadFontSwf("../../as3/font/"+fontName+".swf");
fontCache.push(fontName);
fontTotal++;
}
}
}
private function loadFontSwf(url:String):void {
var ldr:Loader = new Loader();
var ldr_context:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain, SecurityDomain.currentDomain);
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, fontSwfLoaded);
ldr.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, fontSwfLoadIOError);
ldr.load(new URLRequest(url), ldr_context);
}
private function fontSwfLoadIOError(event:Event):void {
fontLoaded++;
}
private function fontSwfLoaded(event:Event):void {
var url:String = event.target.url;
var fontName:String = fontNameBase + url.match(/_[0-9a-fA-F]{2}00/);
fontSwfBytesTotal += event.target.bytesTotal;
//var FontLibrary:Class = ApplicationDomain.currentDomain.getDefinition(fontName) as Class;
var FontLibrary:Class = event.target.applicationDomain.getDefinition(fontName) as Class;
Font.registerFont(FontLibrary[fontName]);
fontLoaded++;
if (fontLoaded == fontTotal) { // フォント読み込み完了
init(null);
holder.addEventListener(MouseEvent.MOUSE_DOWN, init);
}
}
public function fill(e:Event):void {
var i:int = 0;
while (queue.length > 0 && i < 3) {
var rect:Rectangle = queue.pop();
if (rect.width > 2 && rect.height > 2) {
fillRegion(rect);
i++;
}
}
if (!queue.length) removeEventListener(Event.ENTER_FRAME, fill);
}
public function fillRegion(region:Rectangle):void {
var tf:TextField = new TextField();
var fmt:TextFormat = new TextFormat();
var str:String = choice(words);
// glyph check :p
var hasGlyphs:Boolean = false;
var fonts:Array = Font.enumerateFonts();
while (! hasGlyphs) {
for ( var str_i:int = 0;str_i < str.length;str_i++) {
var hasGlyph:Boolean = false;
for ( var font_i:String in fonts) {
if (fonts[font_i].hasGlyphs(str.charAt(str_i))) {
hasGlyph = true;
}
}
hasGlyphs = hasGlyph;
}
if (! hasGlyphs) str = choice(words);
}
fmt.size = 24;
fmt.letterSpacing = -0.4;
fmt.rightMargin = 0.4;
tf.text = str;
tf.autoSize = TextFormatAlign.LEFT;
tf.embedFonts = true;
tf.gridFitType = GridFitType.PIXEL;
tf.selectable = false;
for (var i:int = 0;i < str.length;i++) {
var charCode:String = NumberToString( str.charCodeAt(i) );
while ( charCode.length < 4 ) {
charCode = '0' + charCode;
}
var fontName:String = fontNameBase + '_' + charCode.charAt(0) + charCode.charAt(1) + '00';
fmt.font = fontName;
tf.setTextFormat(fmt, i);
}
var bitmap:BitmapData = new BitmapData(tf.width, tf.height, true);
bitmap.draw(tf);
var bound:Rectangle = bitmap.getColorBoundsRect(0xFFFFFFFF, 0xFFFFFFFF, false);
bitmap.dispose();
var s:Number = region.width / bound.width * (Math.random() * 0.4 + 0.1);
if (bound.height * s > region.height) s = region.height / bound.height;
tf.scaleX = s;
tf.scaleY = s;
bound.x *= s;
bound.y *= s;
bound.width *= s;
bound.height *= s;
switch (choice([1,2,3,4])) {
case 1:
tf.x = region.x - bound.x;
tf.y = region.y - bound.y;
queue.push(
new Rectangle(region.x + bound.width, region.y, region.width - bound.width, bound.height),
new Rectangle(region.x, region.y + bound.height, region.width, region.height - bound.height)
);
break;
case 2:
tf.x = region.x - bound.x;
tf.y = region.bottom - bound.bottom;
queue.push(
new Rectangle(region.x + bound.width, region.bottom - bound.height, region.width - bound.width, bound.height),
new Rectangle(region.x, region.y, region.width, region.height - bound.height)
);
break;
case 3:
tf.x = region.right - bound.right;
tf.y = region.y - bound.y;
queue.push(
new Rectangle(region.x, region.y, region.width - bound.width, bound.height),
new Rectangle(region.x, region.y + bound.height, region.width, region.height - bound.height)
);
break;
case 4:
tf.x = region.right - bound.right;
tf.y = region.bottom - bound.bottom;
queue.push(
new Rectangle(region.x, region.bottom - bound.height, region.width - bound.width, bound.height),
new Rectangle(region.x, region.y, region.width, region.height - bound.height)
);
break;
}
holder.addChild(tf);
Tweener.addTween(tf, {_text_color: choice(colors), time: 5, transition: "liner"});
}
private function choice(ary:Array):* {
return ary[Math.floor(ary.length * Math.random())];
}
// code from Copyright (c) 2008 Spark project (www.libspark.org)
// org.libspark.utils.BaseUtil
/**
* 数値を文字列に変換します。
* numberが127で、dictionaryが"0123456789ABCDEF"(デフォルト)の場合、"7F"を返します。
* @author Kenichi Ueno
* @param number: 数値に変換したい文字列
* @param dictionary: 辞書になる文字列("."は小数点として扱うので使えません)
* @param maxDisplayDigitNumber: 小数点以下の最大桁数
* @return numberをdictionaryを基底として表した文字列を返します。
*/
static public function NumberToString(number:Number, dictionary:String = "0123456789ABCDEF", maxDisplayUnderPoint:int = 10):String
{
var _base:Number = dictionary.length;
var _divideNum:Number;
var _dictionary:Array = new Array(_base);
var _i:int;
var _ret:String = "";
var _digitNumber:Number;
var _tempNum:Number;
var _digitCount:int = maxDisplayUnderPoint;
for ( _i = 0; _i < _base; _i++ )
{
_dictionary[_i] = dictionary.charCodeAt(_i);
}
if ( number == 0 )
{
return String.fromCharCode(_dictionary[0]);
} else {
_digitNumber = Math.floor( Math.log(number) / Math.log(_base) ); // 最大桁数
}
_divideNum = Math.pow( _base, _digitNumber );
if ( _digitNumber < 0 )
{
_ret += String.fromCharCode(_dictionary[0]);
}
while ( (number > 0) && (maxDisplayUnderPoint != (++_digitCount) ) )
{
if ( _digitNumber-- == -1 )
{
_ret += ".";
_digitCount = 0;
}
_tempNum = Math.floor(number / _divideNum);
_ret += String.fromCharCode(_dictionary[_tempNum]);
number %= _divideNum;
number *= _base;
}
while ( _digitNumber-- >= 0 )
{
_ret += String.fromCharCode(_dictionary[0]);
}
return _ret;
}
}
}