package
{
import flash.display.*;
import flash.geom.*;
import CharacterExport.Piece;
import CharacterExport.PieceFrameInfo;
import CharacterExport.PieceSequence;
public class AnimatedSkeletonGenerator implements IFramesSource
{
private var src:MovieClip;
private var def:GraphicExportDef;
private var callback;
private var pieces:Array;
private var pieceSequences:Array;
public function GetFrames():Array
{
return [];
}
public function GetData()
{
return {};
}
public function AnimatedSkeletonGenerator(src:MovieClip, def:GraphicExportDef)
{
this.src = src;
this.def = def;
}
private var library:PieceLibrary
private var currentSeq:int;
public function Go(callback)
{
library = new PieceLibrary();
pieces = new Array();
pieceSequences = new Array();
this.callback = callback;
for (var i = 0; i < src.totalFrames; i++)
{
currentSeq = i;
Utils.WeirdGotoFrame(src, i + 1);
Utils.RecursivelyStop(src);
for (var j = 0; j < src.numChildren; j++)
{
var c = src.getChildAt(j);
if (c is MovieClip)
{
GetAnimation(c, i);
break;
}
}
}
callback(true);
}
private var currentSeqLength:int;
private function GetAnimation(m:MovieClip, seq:int)
{
currentSeqLength = m.totalFrames;
for (var i = 0; i < m.totalFrames; i++)
{
Utils.WeirdGotoFrame(m, i + 1);
Utils.RecursivelyStop(m);
RecursePieces(m, m, i + 1, 0, new Matrix());
}
}
protected function RecursePieces(base:MovieClip, graphic:MovieClip, frame, depth, parentMatrix:Matrix)
{
//_strace(depth+" "+graphic.name+" frame:"+frame);
var mat:Matrix = graphic.transform.matrix.clone();
mat.concat(parentMatrix);
var currentPieces:Array = [];
var allChildren:Array = Utils.GetChildren(graphic);
for (var i = 0; i < allChildren.length; i++)
{
if (!allChildren[i].visible) continue;
if (allChildren[i] is MovieClip)
{
if (IsFullPiece(base, graphic, allChildren[i]))
{
currentPieces.push(allChildren[i]);
} else {
depth = GeneratePieceForClips(currentPieces, depth, frame, mat.clone());
currentPieces.splice(0);
depth = RecursePieces(base, allChildren[i], frame, depth, mat.clone());
}
} else {
currentPieces.push(allChildren[i]);
}
}
depth = GeneratePieceForClips(currentPieces, depth, frame, mat.clone());
return depth;
}
protected function GeneratePieceForClips(clips:Array, depth, frame, parentMatrix:Matrix)
{
if (clips.length > 0)
{
var l = clips.length;
var bounds:Rectangle = null;
var invertMatrix:Matrix = clips[0].transform.matrix.clone();
invertMatrix.invert();
var i;
var m:Matrix;
var useMatrix:Array = [];
for (i = 0; i < clips.length; i++)
{
if (i == 0)
{
m = new Matrix();
} else {
m = clips[i].transform.matrix.clone();
m.concat(invertMatrix);
}
var r:Rectangle = Utils.GetAccurateBounds(clips[i]);
Utils.TransformRect(r, m);
if (bounds == null)
{
bounds = r.clone();
} else {
if (r.left < bounds.left) bounds.left = r.left;
if (r.top < bounds.top) bounds.top = r.top;
if (r.right > bounds.right) bounds.right = r.right;
if (r.bottom > bounds.bottom) bounds.bottom = r.bottom;
}
useMatrix.push(m);
}
Utils.ScaleRect(bounds, def.scale);
Utils.RoundRect(bounds);
var bd:BitmapData = new BitmapData(Math.max(1, bounds.width), Math.max(1,bounds.height), true, 0x0);
for (i = 0; i < clips.length; i++)
{
m = useMatrix[i];
m.scale(def.scale, def.scale);
m.translate(-bounds.left, -bounds.top);
var ct:ColorTransform = clips[i] is Shape ? clips[i].parent.transform.colorTransform : clips[i].transform.colorTransform;
bd.draw(clips[i], m, ct, null, null, true);
}
var center:Point = new Point(-bounds.left, -bounds.top);
var piece:Piece = library.GetPieceFromBitmapData(bd, clips[0].parent.name, center);
if (piece)
{
var mat:Matrix = new Matrix(1/def.scale, 0, 0, 1/def.scale, center.x - piece.CenterPoint.x, center.y - piece.CenterPoint.y);
mat.concat(clips[0].transform.matrix);
mat.concat(parentMatrix);
//mat.a /= exporter.Upscale;
//mat.d /= exporter.Upscale;
var pieceSequence:PieceSequence = GetPieceSequence(piece, frame, clips[0], mat, depth);
var info:PieceFrameInfo = pieceSequence.GetFrame(frame);
if (info)
{
info.Present = true;
info.Transform = mat;
info.Depth = depth;
depth++;
}
}
}
return depth;
}
protected function GetPieceSequence(piece:Piece, frame, clip:DisplayObject, newTransform:Matrix, newDepth)
{
if (this.pieces.indexOf(piece) == -1) this.pieces.push(piece);
var options:Array = [];
var i;
for (i in pieceSequences)
{
if (pieceSequences[i].GetPiece() == piece)
{
var frameInfo:PieceFrameInfo = pieceSequences[i].GetFrame(frame);
if (frameInfo && !frameInfo.Present)
{
if (pieceSequences[i].CheckMatches(clip))
{
return pieceSequences[i];
} else {
options.push(pieceSequences[i]);
}
}
}
}
if (options.length == 0)
{
var s:PieceSequence = new PieceSequence(piece, this.currentSeqLength);
piece.AddUse(currentSeq);
pieceSequences.push(s);
return s;
}
var scores:Array = [];
for (i = 0; i < options.length; i++)
{
var score = 0;
if (frame > 1)
{
var prevInfo:PieceFrameInfo = options[i].GetFrame(frame - 1);
if (prevInfo && prevInfo.Present)
{
score = ComputeTransformScore(prevInfo.Transform, prevInfo.Depth, newTransform, newDepth);
}
}
scores.push(score);
}
var sorted:Array = scores.sort(Array.NUMERIC | Array.RETURNINDEXEDARRAY);
return options[sorted[0]];
}
protected function ComputeTransformScore(transformA:Matrix, depthA, transformB:Matrix, depthB, posVal = 1, scaleVal = 15, rotVal = 20, depthVal = 10)
{
var deltaPos = Point.distance(new Point(transformA.tx, transformA.ty), new Point(transformB.tx, transformB.ty));
deltaPos /= def.scale;
var partsA = Utils.Decompose(transformA);
var partsB = Utils.Decompose(transformB);
var rotA = partsA.rot;
var rotB = partsB.rot;
var deltaRot = Utils.GetAngleDiffAbs(rotA, rotB);
var deltaScaleX = partsA.sx / partsB.sx;
if (deltaScaleX < 1) deltaScaleX = 1 / deltaScaleX;
var deltaScaleY = partsA.sy / partsB.sy;
if (deltaScaleY < 1) deltaScaleY = 1 / deltaScaleY;
var deltaScale = Math.max(deltaScaleX, deltaScaleY);
var deltaDepth = Math.abs(depthA - depthB);
return deltaPos * posVal + (deltaScale - 1) * scaleVal + deltaRot * rotVal + deltaDepth * depthVal;
}
public static var ONLYCOMBINEADJACENT = true;
protected function CombinePieces()
{
var i, j, info:PieceFrameInfo, index;
var sequenceByPresent = {};
for (i in pieceSequences)
{
var str = "";
for (j = 0; j < length; j++)
{
info = pieceSequences[i].GetFrame(j + 1);
str += (info && info.Present) ? "1" : "0";
}
if (!(str in sequenceByPresent))
{
sequenceByPresent[str] = [];
}
sequenceByPresent[str].push(pieceSequences[i]);
}
for (var presentStr in sequenceByPresent)
{
var all:Array = sequenceByPresent[presentStr];
var firstFrame = ("" + presentStr).indexOf("1") + 1;
var depths:Array = [];
for (i = 0; i < all.length; i++)
{
depths.push(all[i].GetFrame(firstFrame).Depth);
}
var sorted:Array = depths.sort(Array.NUMERIC | Array.RETURNINDEXEDARRAY);
var groups:Array = [];
var lastGroup:Array = null;
for (i = 0; i < all.length; i++)
{
index = sorted[i];
if (ONLYCOMBINEADJACENT)
{
if (lastGroup != null && SequenceMatches(lastGroup[0], all[index]))
{
lastGroup.push(all[index]);
} else {
lastGroup = [];
groups.push(lastGroup);
lastGroup.push(all[index]);
}
} else {
var foundGroup = false;
for (j in groups)
{
if (SequenceMatches(groups[j][0], all[index]))
{
groups[j].push(all[index]);
foundGroup = true;
break;
}
}
if (!foundGroup)
{
var g:Array = [];
g.push(all[index]);
groups.push(g);
}
}
}
}
var p:Piece;
for (i in groups)
{
// recovering the original pieces...
// for now: render the bitmaps? (one will be transformed... but it would be transformed anyways, right?)
var group:Array = groups[i];
if (group.length > 1)
{
var origSeq:PieceSequence = group[0];
var invert:Matrix;
var firstPresent = null;
for (j = 0; j < length; j++)
{
info = origSeq.GetFrame(j + 1);
if (info && info.Present)
{
firstPresent = j + 1;
invert = info.Transform.clone();
invert.invert();
break;
}
}
var bounds:Rectangle = null;
var transforms:Array = [];
for (j = 0; j < group.length; j++)
{
p = group[j].GetPiece();
p.RemoveUse(currentSeq);
var m:Matrix = new Matrix();
if (j > 0)
{
//m.translate(-group[j].CenterPoint.x, -group[j].CenterPoint.y);
m.concat(invert);
m.concat(group[j].GetFrame(firstPresent).Transform);
//m.translate(origSeq.CenterPoint.x, origSeq.CenterPoint.y);
}
var r:Rectangle = p.FullData.rect.clone();
Utils.TransformRect(r, m);
if (bounds == null)
{
bounds = r;
} else {
if (r.left < bounds.left) bounds.left = r.left;
if (r.right > bounds.right) bounds.right = r.right;
if (r.top < bounds.top) bounds.top = r.top;
if (r.bottom > bounds.bottom) bounds.bottom = r.bottom;
}
transforms.push(m);
}
Utils.RoundRect(bounds);
var bd:BitmapData = new BitmapData(bounds.width, bounds.height, true, 0x0);
for (j = 0; j < group.length; j++)
{
transforms[j].translate(-bounds.left, -bounds.top);
bd.draw(group[j].GetPiece().FullData, transforms[j], null, null, null, true);
}
p = library.GetPieceFromBitmapData(bd, "CombinedPiece");
var seq:PieceSequence = new PieceSequence(p, currentSeqLength);
p.AddUse(currentSeq);
seq.CopyTransformsFrom(origSeq, group[group.length - 1].GetFrame(firstPresent).Depth);
this.pieceSequences.push(seq);
this.pieces.push(p);
}
}
for (i in groups)
{
if (groups[i].length > 1)
{
for (j in groups[i])
{
index = this.pieceSequences.indexOf(groups[i][j]);
if (index == -1)
{
throw new Error("huh?");
} else {
pieceSequences.splice(index, 1);
}
}
}
}
for (i = 0; i < pieces.length; i++)
{
if (pieces[i].GetUses(currentSeq) == 0)
{
pieces.splice(i, 1);
i--;
}
}
}
protected function SequenceMatches(a:PieceSequence, b:PieceSequence)
{
var relative:Matrix = null;
for (var i = 0; i < length; i++)
{
var infoA:PieceFrameInfo = a.GetFrame(i + 1);
var infoB:PieceFrameInfo = b.GetFrame(i + 1);
if (infoA && infoB && infoA.Present && infoB.Present)
{
var m:Matrix = Utils.GetTransform(infoA.Transform, infoB.Transform);
if (relative == null)
{
relative = m;
} else {
if (ComputeTransformScore(relative, 0, m, 0, 4, 15, 5, 0) > 4) return false;
}
}
}
return true;
}
public function ProduceFrame(frame, excludeNames:Array = null):MovieClip
{
var i;
var depths:Array = [];
for (i = 0; i < pieceSequences.length; i++)
{
depths.push(pieceSequences[i].GetFrame(frame).Depth);
}
var sorts:Array = depths.sort(Array.NUMERIC | Array.RETURNINDEXEDARRAY);
var m:MovieClip = new MovieClip();
for (i = 0; i < sorts.length; i++)
{
var ps:PieceSequence = pieceSequences[sorts[i]];
var info:PieceFrameInfo = ps.GetFrame(frame);
if (info.Present && (excludeNames == null || excludeNames.indexOf(ps.GetPiece().Name) == -1))
{
var p:Piece = ps.GetPiece();
var b:Bitmap = new Bitmap(p.FullData);
var mat:Matrix = new Matrix();
mat.translate(-p.CenterPoint.x, -p.CenterPoint.y);
mat.concat(info.Transform);
//mat.scale(1 / exporter.Upscale, 1 / exporter.Upscale);
//b.smoothing = true;
b.transform.matrix = mat;
m.addChild(b);
}
}
return m;
}
public function ProduceFrameBitmapData(frame, excludeNames:Array = null)
{
var ps:PieceSequence;
var r:Rectangle = null;
var i;
var mat:Matrix;
for (i in pieceSequences)
{
ps = pieceSequences[i];
var frameInfo:PieceFrameInfo = ps.GetFrame(frame);
if (frameInfo && frameInfo.Present && (excludeNames == null || excludeNames.indexOf(ps.GetPiece().Name) == -1))
{
var piece:Piece = ps.GetPiece();
var bounds:Rectangle = piece.FullData.rect;
bounds.x -= piece.CenterPoint.x;
bounds.y -= piece.CenterPoint.y;
mat = frameInfo.Transform.clone();
//mat.scale(1 / exporter.Upscale, 1 / exporter.Upscale);
Utils.TransformRect(bounds, mat);
if (r == null)
{
r = bounds;
} else {
if (bounds.left < r.left) r.left = bounds.left;
if (bounds.top < r.top) r.top = bounds.top;
if (bounds.right > r.right) r.right = bounds.right;
if (bounds.bottom > r.bottom) r.bottom = bounds.bottom;
}
}
}
if (r == null) {
return {data:new BitmapData(1, 1, true, 0x0),center_point:new Point(0, 0)};
}
Utils.RoundRect(r);
var bd:BitmapData;
try {
bd = new BitmapData(Math.min(512, r.width), Math.min(512, r.height), true, 0x0);
} catch (e){
//_strace("failed to create bitmap data");
}
var depths:Array = [];
for (i = 0; i < pieceSequences.length; i++)
{
depths.push(pieceSequences[i].GetFrame(frame).Depth);
}
var sorts:Array = depths.sort(Array.NUMERIC | Array.RETURNINDEXEDARRAY);
for (i = 0; i < sorts.length; i++)
{
ps = pieceSequences[sorts[i]];
var info:PieceFrameInfo = ps.GetFrame(frame);
if (info && info.Present && (excludeNames == null || excludeNames.indexOf(ps.GetPiece().Name) == -1))
{
var p:Piece = ps.GetPiece();
mat = new Matrix();
mat.translate(-ps.GetPiece().CenterPoint.x, -ps.GetPiece().CenterPoint.y);
mat.concat(info.Transform);
//mat.scale(1 / exporter.Upscale, 1 / exporter.Upscale);
mat.translate(-r.left, -r.top);
bd.draw(p.FullData, mat, null, null, null, true);
}
}
return {data:bd,center_point:new Point(-r.left, -r.top)};
}
public static function IsFullPiece(baseClip:DisplayObject, parentClip:MovieClip, clip:DisplayObject)
{
//if (HasAnyNamedChildren(clip)) return false;
//if (!clip.name.match("^instance\\d+$")) return true;
if (parentClip.totalFrames != 1) return false;
//if (clip == baseClip || (clip.parent && clip.parent == baseClip)) return false;
if (clip is MovieClip)
{
var m:MovieClip = clip as MovieClip;
if (m.totalFrames > 1) return false;
if (!m.name.match("^instance\\d+$")) return false;
for (var i = 0; i < m.numChildren; i++)
{
var c = m.getChildAt(i);
if (!IsFullPiece(baseClip, m, c)) return false;
}
}
return true;
}
}
}