174 lines
7.2 KiB
PHP
174 lines
7.2 KiB
PHP
<?php
|
|
|
|
namespace swf2ass;
|
|
|
|
class MorphShapeDefinition implements ObjectDefinition {
|
|
public int $id;
|
|
public Rectangle $startBounds;
|
|
public Rectangle $endBounds;
|
|
public DrawPathList $startShapeList;
|
|
public DrawPathList $endShapeList;
|
|
|
|
|
|
public function __construct(int $id, Rectangle $startBounds, Rectangle $endBounds, DrawPathList $startShapeList, DrawPathList $endShapeList) {
|
|
$this->id = $id;
|
|
$this->startBounds = $startBounds;
|
|
$this->endBounds = $endBounds;
|
|
$this->startShapeList = $startShapeList;
|
|
$this->endShapeList = $endShapeList;
|
|
if(count($this->startShapeList->commands) !== count($this->endShapeList->commands)){
|
|
throw new \Exception("Morph command count is different: start " . count($this->startShapeList->commands) . " != end " . count($this->endShapeList->commands));
|
|
}
|
|
}
|
|
|
|
public function getObjectId(): int {
|
|
return $this->id;
|
|
}
|
|
|
|
/**
|
|
* @param Shape $a
|
|
* @param Shape $b
|
|
* @return \Iterator<RecordPair>|RecordPair[]
|
|
* @throws \Exception
|
|
*/
|
|
private static function iterateShapes(Shape $a, Shape $b) : \Iterator {
|
|
$recordsA = $a->getRecords();
|
|
$recordsB = $b->getRecords();
|
|
reset($recordsA);
|
|
reset($recordsB);
|
|
|
|
|
|
/** @var ?Record $prevA */
|
|
$prevA = null;
|
|
/** @var ?Record $prevB */
|
|
$prevB = null;
|
|
do{
|
|
$a = current($recordsA);
|
|
$b = current($recordsB);
|
|
|
|
if($a === false or $b === false){
|
|
break;
|
|
}
|
|
|
|
$advanceA = true;
|
|
$advanceB = true;
|
|
|
|
if($prevA !== null and !$prevA->getEnd()->equals($a->getStart())){
|
|
$advanceA = false;
|
|
$a = new MoveRecord($a->getStart(), $prevA->getEnd());
|
|
}
|
|
if($prevB !== null and !$prevB->getEnd()->equals($b->getStart())){
|
|
$advanceB = false;
|
|
$b = new MoveRecord($b->getStart(), $prevB->getEnd());
|
|
}
|
|
|
|
if($a instanceof $b){
|
|
yield new RecordPair($a, $b);
|
|
} elseif ($a instanceof LineRecord and $b instanceof QuadraticCurveRecord){
|
|
yield new RecordPair($a = QuadraticCurveRecord::fromLineRecord($a), $b);
|
|
} elseif ($a instanceof QuadraticCurveRecord and $b instanceof LineRecord){
|
|
yield new RecordPair($a, $b = QuadraticCurveRecord::fromLineRecord($b));
|
|
} elseif ($a instanceof MoveRecord and !($b instanceof MoveRecord)){
|
|
yield new RecordPair($a, $b = new MoveRecord($b->getStart(), $b->getStart()));
|
|
$advanceB = false;
|
|
} elseif (!($a instanceof MoveRecord) and $b instanceof MoveRecord){
|
|
yield new RecordPair($a = new MoveRecord($a->getStart(), $a->getStart()), $b);
|
|
$advanceA = false;
|
|
}else{
|
|
throw new \Exception("Incompatible " . get_class($a) . " != " . get_class($b));
|
|
}
|
|
|
|
if($advanceA){
|
|
next($recordsA);
|
|
}
|
|
|
|
if($advanceB){
|
|
next($recordsB);
|
|
}
|
|
|
|
$prevA = $a;
|
|
$prevB = $b;
|
|
}while(true);
|
|
|
|
if(!($a === false and $b === false)){
|
|
//mismatched exit
|
|
var_dump($a);
|
|
var_dump($b);
|
|
throw new \Exception("Incompatible a !== b on exit, both should be false");
|
|
}
|
|
}
|
|
|
|
public function getShapeList(?float $ratio): DrawPathList {
|
|
//TODO: cache shapes by ratio
|
|
//TOD: refactor this to use color transforms (and if able) matrix transforms
|
|
if($ratio === null or abs($ratio) < Constants::EPSILON){
|
|
return $this->startShapeList;
|
|
}
|
|
if(abs($ratio - 1.0) < Constants::EPSILON){
|
|
return $this->endShapeList;
|
|
}
|
|
|
|
$drawPathList = new DrawPathList();
|
|
foreach ($this->startShapeList->commands as $i => $c1){
|
|
$c2 = $this->endShapeList->commands[$i];
|
|
|
|
$records1 = $c1->commands->getRecords();
|
|
$records2 = $c2->commands->getRecords();
|
|
|
|
$shape = new Shape();
|
|
|
|
foreach (self::iterateShapes($c1->commands, $c2->commands) as $recordPair){
|
|
$r1 = $recordPair->a;
|
|
$r2 = $recordPair->b;
|
|
|
|
//No need to convert types!
|
|
if($r1 instanceof LineRecord and $r2 instanceof LineRecord){
|
|
$shape->addRecord(new LineRecord(Utils::lerpVector2($r1->to, $r2->to, $ratio), Utils::lerpVector2($r1->start, $r2->start, $ratio)));
|
|
}else if($r1 instanceof QuadraticCurveRecord and $r2 instanceof QuadraticCurveRecord){
|
|
$shape->addRecord(new QuadraticCurveRecord(Utils::lerpVector2($r1->control, $r2->control, $ratio), Utils::lerpVector2($r1->anchor, $r2->anchor, $ratio), Utils::lerpVector2($r1->start, $r2->start, $ratio)));
|
|
}else if($r1 instanceof MoveRecord and $r2 instanceof MoveRecord){
|
|
$shape->addRecord(new MoveRecord(Utils::lerpVector2($r1->to, $r2->to, $ratio), Utils::lerpVector2($r1->start, $r2->start, $ratio)));
|
|
}else{
|
|
var_dump($records1);
|
|
var_dump($records2);
|
|
throw new \Exception();
|
|
}
|
|
}
|
|
|
|
//TODO: morph styles properly
|
|
if($c1->style instanceof FillStyleRecord and $c2->style instanceof FillStyleRecord){
|
|
if($c1->style->fill instanceof Color){
|
|
$drawPathList->commands[] = DrawPath::fill(new FillStyleRecord(Utils::lerpColor($c1->style->fill, $c2->style->fill, $ratio)), $shape);
|
|
}else if($c1->style->fill instanceof Gradient){
|
|
//TODO: proper gradients
|
|
$drawPathList->commands[] = DrawPath::fill(new FillStyleRecord(Utils::lerpColor($c1->style->fill->getItems()[0]->color, $c2->style->fill->getItems()[0]->color, $ratio)), $shape);
|
|
}else{
|
|
var_dump($c1->style);
|
|
var_dump($c2->style);
|
|
throw new \Exception();
|
|
}
|
|
}else if($c1->style instanceof LineStyleRecord and $c2->style instanceof LineStyleRecord){
|
|
$drawPathList->commands[] = DrawPath::stroke(new LineStyleRecord(Utils::lerpInteger($c1->style->width, $c2->style->width, $ratio), Utils::lerpColor($c1->style->color, $c2->style->color, $ratio)), $shape);
|
|
}else{
|
|
var_dump($c1->style);
|
|
var_dump($c2->style);
|
|
throw new \Exception();
|
|
}
|
|
}
|
|
|
|
return $drawPathList;
|
|
}
|
|
|
|
static function fromArray(array $element): MorphShapeDefinition {
|
|
$styles = MorphStyleList::fromArray($element);
|
|
|
|
$start = DrawPathList::fromArray($element["startEdges"], $styles->getStartStyleList(), $element["endEdges"], false);
|
|
$end = DrawPathList::fromArray($element["startEdges"], $styles->getStartStyleList(), $element["endEdges"], true);
|
|
|
|
return new MorphShapeDefinition($element["characterId"], Rectangle::fromArray($element["startBounds"]), Rectangle::fromArray($element["endBounds"]), $start, $end);
|
|
}
|
|
|
|
public function getSafeObject() : MorphShapeDefinition{
|
|
return $this;
|
|
}
|
|
} |