Steiner Chain(複数圏)
複数圏で、それぞれ別々の原点で反転
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/up6F
*/
// forked from Aquioux's Steiner Chain
package {
//import aquioux.display.colorUtil.RGBWheel;
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
[SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#ffffff")]
/**
* シュタイナー・チェーン(円の反転)
* 複数圏
*/
public class Main122 extends Sprite {
// 拡大率
static private const SCALE:int = 40;
// キャンバス
private var canvas_:Shape;
private var g_:Graphics;
// 連鎖を構成する円の数
static private var NUM_OF_CHAIN:int = 60;
// 各円の半径を求めるために必要な値
// 2個の接する連鎖円の中心座標間が占める角度(弧度法)
static private var RADIAN:Number = Math.PI * 2 / NUM_OF_CHAIN;
// RADIAN の半分の値のサイン値
static private var SIN:Number = Math.sin(RADIAN / 2);
// 円の情報を格納するリスト(1次元配列)
private var circles1_:Vector.<Number>;
private var circles2_:Vector.<Number>;
private var circles3_:Vector.<Number>;
// circles_ に格納される一つの円の情報がいくつの要素から構成されているか
static private var NUM_OF_ELEMENTS:int = 5; // 中心X座標、中心Y座標、半径、色、表示有無
// 円の情報を格納するリストを回す for 文の限界値
private var limit_:int;
// 反転原点座標用(極座標形式)
private var originAngle_:Number = 0.0;
private const ORIGIN_LENGTH:Number = 0.9; // 座標(0,0)から反転原点までの距離
// 回転に使うパラメータ
private const RADIAN120:Number = 120 * Math.PI / 180;
private const RADIAN240:Number = 240 * Math.PI / 180;
private const RADIAN_ADD:Number = -0.2 * Math.PI / 180;
/**
* コンストラクタ
*/
public function Main122():void {
setup();
addEventListener(Event.ENTER_FRAME, update);
}
/**
* セットアップ
*/
private function setup():void {
const COLOR1:uint = 0x0;
RGBWheel.s = 0.5;
const COLOR2:uint = RGBWheel.getDegreeColor(Math.random() * 360 >> 0);
// 円の情報格納リスト生成
circles1_ = new Vector.<Number>();
circles2_ = new Vector.<Number>();
circles3_ = new Vector.<Number>();
// 第1圏
// 各円の半径
var inRadius:Number = 1.0; // 内接円
var chainRadius:Number = inRadius * SIN / (1 - SIN); // 連鎖円
var outRadius:Number = inRadius + chainRadius * 2; // 外接円
// 円の情報格納
circles1_.push(0, 0, inRadius, COLOR1, true);
circles1_.push(0, 0, outRadius, COLOR2, false);
var length:Number = inRadius + chainRadius;
for (var i:int = 0; i < NUM_OF_CHAIN; i++) {
var myRadian:Number = RADIAN * i;
circles1_.push(Math.cos(myRadian) * length, Math.sin(myRadian) * length, chainRadius, COLOR2, true);
}
circles1_.fixed = true;
// 第2圏
// 各円の半径
inRadius = outRadius; // 内接円
chainRadius = inRadius * SIN / (1 - SIN); // 連鎖円
outRadius = inRadius + chainRadius * 2; // 外接円
// 円の情報格納
circles2_.push(0, 0, inRadius, COLOR2, true);
circles2_.push(0, 0, outRadius, COLOR1, false);
length = inRadius + chainRadius;
for (i = 0; i < NUM_OF_CHAIN; i++) {
myRadian = RADIAN * i;
circles2_.push(Math.cos(myRadian) * length, Math.sin(myRadian) * length, chainRadius, COLOR1, true);
}
circles2_.fixed = true;
// 第3圏
// 各円の半径
inRadius = outRadius; // 内接円
chainRadius = inRadius * SIN / (1 - SIN); // 連鎖円
outRadius = inRadius + chainRadius * 2; // 外接円
// 円の情報格納
circles3_.push(0, 0, inRadius, COLOR1, true);
circles3_.push(0, 0, outRadius, COLOR2, true);
length = inRadius + chainRadius;
for (i = 0; i < NUM_OF_CHAIN; i++) {
myRadian = RADIAN * i;
circles3_.push(Math.cos(myRadian) * length, Math.sin(myRadian) * length, chainRadius, COLOR2, true);
}
circles3_.fixed = true;
limit_ = circles1_.length / NUM_OF_ELEMENTS;
// キャンバス
canvas_ = new Shape();
canvas_.x = stage.stageWidth / 2;
canvas_.y = stage.stageHeight / 2;
addChild(canvas_);
g_ = canvas_.graphics;
}
/**
* アップデート
*/
private function update(event:Event):void {
// 反転原点座標取得(自動計算)
var radian:Number = originAngle_;
var originX:Number = Math.cos(radian) * ORIGIN_LENGTH;
var originY:Number = Math.sin(radian) * ORIGIN_LENGTH;
g_.clear();
// 反転計算と描画
// 第1圏
for (var i:int = 0; i < limit_; ++i) {
var idx:int = i * NUM_OF_ELEMENTS;
// 反転前の各円の中心座標と半径
var a:Number = circles1_[idx] + originX;
var b:Number = circles1_[idx + 1] + originY;
var r:Number = circles1_[idx + 2];
// 反転計算のための係数
var s:Number = 1 / (a * a + b * b - r * r);
// 反転後の円の中心座標と半径
var invertedX:Number = a * s;
var invertedY:Number = -b * s;
var invertedR:Number = r * s;
// 描画座標と半径
var drawX:Number = (invertedX + originX) * SCALE;
var drawY:Number = (invertedY + originY) * SCALE;
var drawR:Number = invertedR * SCALE;
// この圏の位置シフト
if (i == 0) {
var currentShiftX:Number = -drawX;
var currentShiftY:Number = -drawY;
}
// 次の圏の位置シフト
if (i == 1) {
var nextShiftX:Number = drawX + currentShiftX;
var nextShiftY:Number = drawY + currentShiftY;
}
// 描画
if (circles1_[idx + 4]) {
g_.beginFill(circles1_[idx + 3]);
g_.drawCircle(drawX + currentShiftX, drawY + currentShiftY, drawR);
g_.endFill();
}
}
// 第2圏
radian = originAngle_ + RADIAN120;
originX = Math.cos(radian) * ORIGIN_LENGTH;
originY = Math.sin(radian) * ORIGIN_LENGTH;
// 反転計算と描画
for (i = 0; i < limit_; ++i) {
idx = i * NUM_OF_ELEMENTS;
// 反転前の各円の中心座標と半径
a = circles2_[idx] + originX;
b = circles2_[idx + 1] + originY;
r = circles2_[idx + 2];
// 反転計算のための係数
s = 1 / (a * a + b * b - r * r);
// 反転後の円の中心座標と半径
invertedX = a * s;
invertedY = -b * s;
invertedR = r * s;
// 描画座標と半径
drawX = (invertedX + originX) * SCALE;
drawY = (invertedY + originY) * SCALE;
drawR = invertedR * SCALE;
// この圏の位置シフト
if (i == 0) {
currentShiftX = nextShiftX - drawX;
currentShiftY = nextShiftY - drawY;
}
// 次の圏の位置シフト
if (i == 1) {
nextShiftX = drawX + currentShiftX;
nextShiftY = drawY + currentShiftY;
}
// 描画
if (circles2_[idx + 4]) {
g_.beginFill(circles2_[idx + 3]);
g_.drawCircle(drawX + currentShiftX, drawY + currentShiftY, drawR);
g_.endFill();
}
}
// 第3圏
radian = originAngle_ + RADIAN240;
originX = Math.cos(radian) * ORIGIN_LENGTH;
originY = Math.sin(radian) * ORIGIN_LENGTH;
// 反転計算と描画
for (i = 0; i < limit_; ++i) {
idx = i * NUM_OF_ELEMENTS;
// 反転前の各円の中心座標と半径
a = circles3_[idx] + originX;
b = circles3_[idx + 1] + originY;
r = circles3_[idx + 2];
// 反転計算のための係数
s = 1 / (a * a + b * b - r * r);
// 反転後の円の中心座標と半径
invertedX = a * s;
invertedY = -b * s;
invertedR = r * s;
// 描画座標と半径
drawX = (invertedX + originX) * SCALE;
drawY = (invertedY + originY) * SCALE;
drawR = invertedR * SCALE;
// この圏の位置シフト
if (i == 0) {
currentShiftX = nextShiftX - drawX;
currentShiftY = nextShiftY - drawY;
}
// 描画
if (circles3_[idx + 4]) {
g_.beginFill(circles3_[idx + 3]);
g_.drawCircle(drawX + currentShiftX, drawY + currentShiftY, drawR);
g_.endFill();
}
}
originAngle_ += RADIAN_ADD;
}
}
}
//package aquioux.display.colorUtil {
/**
* コサインカーブで色相環的に RGB を計算
* @author YOSHIDA, Akio
*/
/*public*/ class RGBWheel {
/**
* 彩度(HSV の彩度 S と同じ扱い)
*/
static public function get s():Number { return _s; }
static public function set s(value:Number):void {
_s = adjust1(value);
}
static private var _s:Number = 1.0;
/**
* 明度(HSV の彩度 V と同じ扱い)
*/
static public function get v():Number { return _v; }
static public function set v(value:Number):void {
_v = adjust1(value);
}
static private var _v:Number = 1.0;
/**
* 角度に応じた RGB を得る(度数法指定)
* @param angle 角度(度数法)
* @return 色(0xRRGGBB)
*/
static private const TO_RADIAN:Number = Math.PI / 180; // 度数を弧度に
static public function getDegreeColor(angle:Number):uint {
var r:uint = (Math.cos( angle * TO_RADIAN) + 1) * 0xff >> 1;
var g:uint = (Math.cos((angle + 120) * TO_RADIAN) + 1) * 0xff >> 1;
var b:uint = (Math.cos((angle - 120) * TO_RADIAN) + 1) * 0xff >> 1;
if (_s != 1.0) {
r += calcShiftS(r);
g += calcShiftS(g);
b += calcShiftS(b);
}
if (_v != 1.0) {
r -= calcShiftV(r);
g -= calcShiftV(g);
b -= calcShiftV(b);
}
return r << 16 | g << 8 | b;
}
/**
* 角度に応じた RGB を得る(弧度法指定)
* @param radian 角度(弧度法)
* @return 色(0xRRGGBB)
*/
static private const RADIAN120:Number = Math.PI * 2 / 3; // 120度を弧度で
static public function getRadianColor(radian:Number):uint {
var r:uint = (Math.cos(radian) + 1) * 0xff >> 1;
var g:uint = (Math.cos(radian + RADIAN120) + 1) * 0xff >> 1;
var b:uint = (Math.cos(radian - RADIAN120) + 1) * 0xff >> 1;
if (_s != 1.0) {
r += calcShiftS(r);
g += calcShiftS(g);
b += calcShiftS(b);
}
if (_v != 1.0) {
r -= calcShiftV(r);
g -= calcShiftV(g);
b -= calcShiftV(b);
}
return r << 16 | g << 8 | b;
}
/**
* 彩度の反映
* @param gray 諧調(0~255)
* @return 諧調のシフト値
* @private
*/
static private function calcShiftS(gray:uint):uint {
return (0xff - gray) * (1 - _s) >> 0;
}
/**
* 明度の反映
* @param gray 諧調(0~255)
* @return 諧調のシフト値
* @private
*/
static private function calcShiftV(gray:uint):uint {
return gray * (1 - _v) >> 0;
}
/**
* 数値を 0.0 <= value <= 1.0 の範囲に収める
* @param value
* @return チェック後の値
* @private
*/
static public function adjust1(value:Number):Number {
if (value < 0.0) value = 0.0;
if (value > 1.0) value = 1.0;
return value;
}
}
//}