Skip to content

Commit

Permalink
Merge pull request #234 from parasol-framework/test/animation
Browse files Browse the repository at this point in the history
Animation improvements
  • Loading branch information
paul-manias authored Apr 23, 2024
2 parents c4ed45f + 7c775d7 commit 50e1a8d
Show file tree
Hide file tree
Showing 10 changed files with 362 additions and 278 deletions.
2 changes: 1 addition & 1 deletion include/parasol/modules/xml.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ typedef struct XMLAttrib {
std::string Value; // Value of the attribute
inline bool isContent() const { return Name.empty(); }
inline bool isTag() const { return !Name.empty(); }
XMLAttrib(std::string pName, std::string pValue) : Name(pName), Value(pValue) { };
XMLAttrib(std::string pName, std::string pValue = "") : Name(pName), Value(pValue) { };
XMLAttrib() = default;
} XMLATTRIB;

Expand Down
288 changes: 180 additions & 108 deletions src/svg/animation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,50 @@
// https://www.w3.org/TR/2001/REC-smil-animation-20010904

//********************************************************************************************************************
// Set common animation properties

void anim_base::activate(extSVG *SVG)
{
// Reset all the variables that control time management and the animation will start from scratch.
begin_offset = (double(PreciseTime()) / 1000000.0) - SVG->AnimEpoch;
repeat_index = 0;
start_time = SVG->AnimEpoch + begin_offset;
end_time = 0;

// Test: w3-animate-elem-21-t.svg

for (auto &other : start_on_begin) {
other->activate(SVG);
other->start_time = start_time; // Ensure that times match exactly
}
}

//********************************************************************************************************************

void anim_base::stop(extSVG *SVG, double Time)
{
if (!begin_series.empty()) {
// Check if there's a serialised begin offset following the one that's completed.
LONG i;
for (i=0; i < std::ssize(begin_series)-1; i++) {
if (begin_offset IS begin_series[i]) {
begin_offset = begin_series[i+1];
start_time = 0;
return;
}
}
}

end_time = Time;
seek = 1.0; // Necessary in case the seek range calculation has overflowed

// Start animations that are to be triggered from our ending.
for (auto &other : start_on_end) {
other->activate(SVG);
other->start_time = Time;
}
}

//********************************************************************************************************************

static ERR parse_spline(APTR Path, LONG Index, LONG Command, double X, double Y, anim_base::SPLINE_POINTS &Meta)
{
Expand All @@ -17,6 +60,9 @@ static ERR parse_spline(APTR Path, LONG Index, LONG Command, double X, double Y,
return ERR::Okay;
}

//********************************************************************************************************************
// Set common animation properties

static ERR set_anim_property(extSVG *Self, anim_base &Anim, XMLTag &Tag, ULONG Hash, const std::string_view Value)
{
switch (Hash) {
Expand Down Expand Up @@ -735,147 +781,173 @@ void anim_transform::perform(extSVG &SVG)
}

//********************************************************************************************************************
// <rect><animate attributeType="CSS" attributeName="opacity" from="1" to="0" dur="5s" repeatCount="indefinite"/></rect>
// <animate attributeName="font-size" attributeType="CSS" begin="0s" dur="6s" fill="freeze" from="40" to="80"/>
// <animate attributeName="fill" attributeType="CSS" begin="0s" dur="6s" fill="freeze" from="#00f" to="#070"/>

void anim_value::perform(extSVG &SVG)
{
if ((end_time) and (!freeze)) return;
pf::Log log;

if ((end_time) and (!freeze)) return;

pf::ScopedObjectLock<objVector> vector(target_vector, 1000);
if (vector.granted()) {
// Determine the type of the attribute that we're targeting, then interpolate the value and set it.
if (vector->Class->ClassID IS ID_VECTORGROUP) {
// Groups are a special case because they act as a placeholder and aren't guaranteed to propagate all
// attributes to their children.

switch(StrHash(target_attrib)) {
case SVF_FONT_SIZE: {
auto val = get_numeric_value(**vector, FID_FontSize);
vector->set(FID_FontSize, val);
break;
}
// Note that group attributes do not override values that are defined by the client.

case SVF_FILL: {
auto val = get_colour_value(**vector, FID_FillColour);
vector->setArray(FID_FillColour, (float *)&val, 4);
break;
}
for (auto &child : tag->Children) {
if (!child.isTag()) continue;
// Any tag producing a vector object can theoretically be subject to animation.
if (auto si = child.attrib("_id")) {
// We can't override attributes that were defined by the client.
if (child.attrib(target_attrib)) continue;

case SVF_FILL_OPACITY: {
auto val = get_numeric_value(**vector, FID_FillOpacity);
vector->set(FID_FillOpacity, val);
break;
pf::ScopedObjectLock<objVector> cv(std::stoi(*si), 1000);
if (cv.granted()) set_value(**cv);
}
}
}
else set_value(**vector);
}
}

case SVF_STROKE: {
auto val = get_colour_value(**vector, FID_StrokeColour);
vector->setArray(FID_StrokeColour, (float *)&val, 4);
break;
}
//********************************************************************************************************************

case SVF_STROKE_WIDTH:
vector->set(FID_StrokeWidth, get_numeric_value(**vector, FID_StrokeWidth));
break;
void anim_value::set_value(objVector &Vector)
{
// Determine the type of the attribute that we're targeting, then interpolate the value and set it.

case SVF_OPACITY:
vector->set(FID_Opacity, get_numeric_value(**vector, FID_Opacity));
break;
switch(StrHash(target_attrib)) {
case SVF_FONT_SIZE: {
auto val = get_numeric_value(Vector, FID_FontSize);
Vector.set(FID_FontSize, val);
break;
}

case SVF_DISPLAY: {
auto val = get_string();
if (StrMatch("none", val) IS ERR::Okay) vector->set(FID_Visibility, LONG(VIS::HIDDEN));
else if (StrMatch("inline", val) IS ERR::Okay) vector->set(FID_Visibility, LONG(VIS::VISIBLE));
else if (StrMatch("inherit", val) IS ERR::Okay) vector->set(FID_Visibility, LONG(VIS::INHERIT));
break;
}
case SVF_FILL: {
auto val = get_colour_value(Vector, FID_FillColour);
Vector.setArray(FID_FillColour, (float *)&val, 4);
break;
}

case SVF_R:
vector->set(FID_Radius, get_dimension(**vector, FID_Radius));
break;
case SVF_FILL_OPACITY: {
auto val = get_numeric_value(Vector, FID_FillOpacity);
Vector.set(FID_FillOpacity, val);
break;
}

case SVF_RX:
vector->set(FID_RadiusX, get_dimension(**vector, FID_RadiusX));
break;
case SVF_STROKE: {
auto val = get_colour_value(Vector, FID_StrokeColour);
Vector.setArray(FID_StrokeColour, (float *)&val, 4);
break;
}

case SVF_RY:
vector->set(FID_RadiusY, get_dimension(**vector, FID_RadiusY));
break;
case SVF_STROKE_WIDTH:
Vector.set(FID_StrokeWidth, get_numeric_value(Vector, FID_StrokeWidth));
break;

case SVF_CX:
vector->set(FID_CX, get_dimension(**vector, FID_CX));
break;
case SVF_OPACITY:
Vector.set(FID_Opacity, get_numeric_value(Vector, FID_Opacity));
break;

case SVF_CY:
vector->set(FID_CY, get_dimension(**vector, FID_CY));
break;
case SVF_DISPLAY: {
auto val = get_string();
if (StrMatch("none", val) IS ERR::Okay) Vector.set(FID_Visibility, LONG(VIS::HIDDEN));
else if (StrMatch("inline", val) IS ERR::Okay) Vector.set(FID_Visibility, LONG(VIS::VISIBLE));
else if (StrMatch("inherit", val) IS ERR::Okay) Vector.set(FID_Visibility, LONG(VIS::INHERIT));
break;
}

case SVF_VISIBILITY: {
auto val = get_string();
Vector.set(FID_Visibility, val);
break;
}

case SVF_R:
Vector.set(FID_Radius, get_dimension(Vector, FID_Radius));
break;

case SVF_RX:
Vector.set(FID_RadiusX, get_dimension(Vector, FID_RadiusX));
break;

case SVF_RY:
Vector.set(FID_RadiusY, get_dimension(Vector, FID_RadiusY));
break;

case SVF_CX:
Vector.set(FID_CX, get_dimension(Vector, FID_CX));
break;

case SVF_CY:
Vector.set(FID_CY, get_dimension(Vector, FID_CY));
break;

case SVF_X1:
vector->set(FID_X1, get_dimension(**vector, FID_X1));
break;
case SVF_X1:
Vector.set(FID_X1, get_dimension(Vector, FID_X1));
break;

case SVF_Y1:
vector->set(FID_Y1, get_dimension(**vector, FID_Y1));
break;
case SVF_Y1:
Vector.set(FID_Y1, get_dimension(Vector, FID_Y1));
break;

case SVF_X2:
vector->set(FID_X2, get_dimension(**vector, FID_X2));
break;
case SVF_X2:
Vector.set(FID_X2, get_dimension(Vector, FID_X2));
break;

case SVF_Y2:
vector->set(FID_Y2, get_dimension(**vector, FID_Y2));
break;
case SVF_Y2:
Vector.set(FID_Y2, get_dimension(Vector, FID_Y2));
break;

case SVF_X: {
if (vector->Class->ClassID IS ID_VECTORGROUP) {
// Special case: SVG groups don't have an (x,y) position, but can declare one in the form of a
// transform. Refer to xtag_use() for a working example as to why.
case SVF_X: {
if (Vector.Class->ClassID IS ID_VECTORGROUP) {
// Special case: SVG groups don't have an (x,y) position, but can declare one in the form of a
// transform. Refer to xtag_use() for a working example as to why.

VectorMatrix *m;
for (m=vector->Matrices; (m) and (m->Tag != MTAG_SVG_TRANSFORM); m=m->Next);
VectorMatrix *m;
for (m=Vector.Matrices; (m) and (m->Tag != MTAG_SVG_TRANSFORM); m=m->Next);

if (!m) {
vecNewMatrix(*vector, &m, false);
m->Tag = MTAG_SVG_TRANSFORM;
}
if (!m) {
vecNewMatrix(&Vector, &m, false);
m->Tag = MTAG_SVG_TRANSFORM;
}

if (m) {
m->TranslateX = get_dimension(**vector, FID_X);
vecFlushMatrix(m);
}
if (m) {
m->TranslateX = get_dimension(Vector, FID_X);
vecFlushMatrix(m);
}
else vector->set(FID_X, get_dimension(**vector, FID_X));
break;
}
else Vector.set(FID_X, get_dimension(Vector, FID_X));
break;
}

case SVF_Y: {
if (vector->Class->ClassID IS ID_VECTORGROUP) {
VectorMatrix *m;
for (m=vector->Matrices; (m) and (m->Tag != MTAG_SVG_TRANSFORM); m=m->Next);
case SVF_Y: {
if (Vector.Class->ClassID IS ID_VECTORGROUP) {
VectorMatrix *m;
for (m=Vector.Matrices; (m) and (m->Tag != MTAG_SVG_TRANSFORM); m=m->Next);

if (!m) {
vecNewMatrix(*vector, &m, false);
m->Tag = MTAG_SVG_TRANSFORM;
}
if (!m) {
vecNewMatrix(&Vector, &m, false);
m->Tag = MTAG_SVG_TRANSFORM;
}

if (m) {
m->TranslateY = get_dimension(**vector, FID_Y);
vecFlushMatrix(m);
}
if (m) {
m->TranslateY = get_dimension(Vector, FID_Y);
vecFlushMatrix(m);
}
else vector->set(FID_Y, get_dimension(**vector, FID_Y));
break;
}
else Vector.set(FID_Y, get_dimension(Vector, FID_Y));
break;
}

case SVF_WIDTH:
vector->set(FID_Width, get_dimension(**vector, FID_Width));
break;

case SVF_HEIGHT:
vector->set(FID_Height, get_dimension(**vector, FID_Height));
break;
case SVF_WIDTH:
Vector.set(FID_Width, get_dimension(Vector, FID_Width));
break;

case SVF_VISIBILITY:
vector->set(FID_Visibility, get_string());
break;
}
case SVF_HEIGHT:
Vector.set(FID_Height, get_dimension(Vector, FID_Height));
break;
}
}
}
Loading

0 comments on commit 50e1a8d

Please sign in to comment.