Process SMIL directives in XML data: Difference between revisions
Content added Content deleted
m (wikipedia links) |
m (→{{header|Wren}}: Minor tidy) |
||
(57 intermediate revisions by 15 users not shown) | |||
Line 1: | Line 1: | ||
{{draft task}} |
|||
One very common task in OpenGL is to load data from a file that contains geometry. These datas most often also contain additional informations, for example animation informations. In this task we propose to load X3D geometric datas with additional Smil animation statments. X3D ([http://en.wikipedia.org/wiki/X3D WP]) is not particularly supposed to be mixed with Smil ([http://en.wikipedia.org/wiki/Synchronized_Multimedia_Integration_Language WP]), but Smil is supposed to be mixable with any XML format. |
|||
In order to represent evolutions of contents over time, the [http://www.w3.org/TR/SMIL/ SMIL] standard provides a solution to record the animation of data. Smil animations can be added to any kind of contents formated in XML. |
|||
Here is the pure X3D base of our file: |
|||
* [[wp:Synchronized_Multimedia_Integration_Language|SMIL on Wikipedia]] and [http://www.w3.org/TR/SMIL/smil-animation.html#q35 at W3] |
|||
<lang xml><?xml version="1.0" ?> |
|||
The task is to create an utility that given the first Smiled XML file, would return the following ones: |
|||
<syntaxhighlight lang="xml"><?xml version="1.0" ?> |
|||
<smil> |
|||
<X3D> |
<X3D> |
||
<Scene> |
<Scene> |
||
<Viewpoint |
<Viewpoint position="0 0 8" orientation="0 0 1 0"/> |
||
<PointLight color='1 1 1' location='0 2 0'/> |
<PointLight color='1 1 1' location='0 2 0'/> |
||
<Shape> |
|||
<Transform translation='0.0 0.0 -3.0' scale='1 1 1'> |
|||
< |
<Box size='2 1 2'> |
||
< |
<animate attributeName="size" from="2 1 2" |
||
to="1 2 1" begin="0s" dur="10s"/> |
|||
<Appearance> |
|||
</Box> |
|||
<Material diffuseColor='0.0 0.6 1.0'/> |
|||
<Appearance> |
|||
<Material diffuseColor='0.0 0.6 1.0'> |
|||
</Shape> |
|||
<animate attributeName="diffuseColor" from="0.0 0.6 1.0" |
|||
</Transform> |
|||
to="1.0 0.4 0.0" begin="0s" dur="10s"/> |
|||
</Material> |
|||
</Appearance> |
|||
</Shape> |
|||
</Scene> |
</Scene> |
||
</X3D |
</X3D> |
||
</smil></syntaxhighlight> |
|||
At t = 0 second here is the expected output: |
|||
And here is the same datas with additional Smil statments that you should load, display and animate with OpenGL: |
|||
< |
<syntaxhighlight lang="xml"><?xml version="1.0" ?> |
||
<X3D> |
|||
<Scene> |
|||
<Viewpoint position="0 0 8" orientation="0 0 1 0"/> |
|||
<PointLight color='1 1 1' location='0 2 0'/> |
|||
<Shape> |
|||
<Box size='2 1 2'/> |
|||
<Appearance> |
|||
<Material diffuseColor='0.0 0.6 1.0'/> |
|||
</Appearance> |
|||
</Shape> |
|||
</Scene> |
|||
</X3D></syntaxhighlight> |
|||
At t = 2 second here is the expected output: |
|||
<syntaxhighlight lang="xml"><?xml version="1.0" ?> |
|||
<X3D> |
|||
<Scene> |
|||
<Viewpoint position="0 0 8" orientation="0 0 1 0"/> |
|||
<PointLight color='1 1 1' location='0 2 0'/> |
|||
<Shape> |
|||
<Box size='1.8 1.2 1.8'/> |
|||
<Appearance> |
|||
<Material diffuseColor='0.2 0.56 0.8'/> |
|||
</Appearance> |
|||
</Shape> |
|||
</Scene> |
|||
</X3D></syntaxhighlight> |
|||
=={{header|Go}}== |
|||
{{libheader|etree}} |
|||
<syntaxhighlight lang="go">package main |
|||
import ( |
|||
"fmt" |
|||
"github.com/beevik/etree" |
|||
"log" |
|||
"os" |
|||
"strconv" |
|||
"strings" |
|||
) |
|||
type animData struct { |
|||
element *etree.Element |
|||
attrib string |
|||
from string |
|||
to string |
|||
begin float64 |
|||
dur float64 |
|||
} |
|||
func check(err error) { |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
} |
|||
func (ad *animData) AtTime(t float64) string { |
|||
beg := ad.begin |
|||
end := beg + ad.dur |
|||
if t < beg || t > end { |
|||
log.Fatalf("time must be in interval [%g, %g]", beg, end) |
|||
} |
|||
fromSplit := strings.Fields(ad.from) |
|||
toSplit := strings.Fields(ad.to) |
|||
le := len(fromSplit) |
|||
interSplit := make([]string, le) |
|||
for i := 0; i < le; i++ { |
|||
fromF, err := strconv.ParseFloat(fromSplit[i], 64) |
|||
check(err) |
|||
toF, err := strconv.ParseFloat(toSplit[i], 64) |
|||
check(err) |
|||
interF := (fromF*(end-t) + toF*(t-beg)) / ad.dur |
|||
interSplit[i] = fmt.Sprintf("%.2f", interF) |
|||
} |
|||
return strings.Join(interSplit, " ") |
|||
} |
|||
func main() { |
|||
doc := etree.NewDocument() |
|||
check(doc.ReadFromFile("smil.xml")) |
|||
smil := doc.SelectElement("smil") |
|||
if smil == nil { |
|||
log.Fatal("'smil' element not found") |
|||
} |
|||
x3d := smil.SelectElement("X3D") |
|||
if x3d == nil { |
|||
log.Fatal("'X3D' element not found") |
|||
} |
|||
doc.SetRoot(x3d) // remove 'smil' element |
|||
var ads []*animData |
|||
for _, a := range doc.FindElements("//animate") { |
|||
attrib := a.SelectAttrValue("attributeName", "?") |
|||
from := a.SelectAttrValue("from", "?") |
|||
to := a.SelectAttrValue("to", "?") |
|||
beginS := a.SelectAttrValue("begin", "?") |
|||
durS := a.SelectAttrValue("dur", "?") |
|||
if attrib == "?" || from == "?" || to == "?" || |
|||
beginS == "?" || durS == "?" { |
|||
log.Fatal("an animate element has missing attribute(s)") |
|||
} |
|||
begin, err := strconv.ParseFloat(beginS[:len(beginS)-1], 64) |
|||
check(err) |
|||
dur, err := strconv.ParseFloat(durS[:len(durS)-1], 64) |
|||
check(err) |
|||
p := a.Parent() |
|||
if p == nil { |
|||
log.Fatal("an animate element has no parent") |
|||
} |
|||
pattrib := p.SelectAttrValue(attrib, "?") |
|||
if pattrib == "?" { |
|||
log.Fatal("an animate element's parent has missing attribute") |
|||
} |
|||
ads = append(ads, &animData{p, attrib, from, to, begin, dur}) |
|||
p.RemoveChild(a) // remove 'animate' element |
|||
} |
|||
ts := []float64{0, 2} |
|||
for _, t := range ts { |
|||
for _, ad := range ads { |
|||
s := ad.AtTime(t) |
|||
ad.element.CreateAttr(ad.attrib, s) |
|||
} |
|||
doc.Indent(2) |
|||
fmt.Printf("At time = %g seconds:\n\n", t) |
|||
doc.WriteTo(os.Stdout) |
|||
fmt.Println() |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
At time = 0 seconds: |
|||
<?xml version="1.0" ?> |
|||
<X3D> |
|||
<Scene> |
|||
<Viewpoint position="0 0 8" orientation="0 0 1 0"/> |
|||
<PointLight color="1 1 1" location="0 2 0"/> |
|||
<Shape> |
|||
<Box size="2.00 1.00 2.00"/> |
|||
<Appearance> |
|||
<Material diffuseColor="0.00 0.60 1.00"/> |
|||
</Appearance> |
|||
</Shape> |
|||
</Scene> |
|||
</X3D> |
|||
At time = 2 seconds: |
|||
<?xml version="1.0" ?> |
|||
<X3D> |
|||
<Scene> |
|||
<Viewpoint position="0 0 8" orientation="0 0 1 0"/> |
|||
<PointLight color="1 1 1" location="0 2 0"/> |
|||
<Shape> |
|||
<Box size="1.80 1.20 1.80"/> |
|||
<Appearance> |
|||
<Material diffuseColor="0.20 0.56 0.80"/> |
|||
</Appearance> |
|||
</Shape> |
|||
</Scene> |
|||
</X3D> |
|||
</pre> |
|||
=={{header|Nim}}== |
|||
<syntaxhighlight lang="nim">import sequtils, strformat, strtabs, strutils, xmlparser, xmltree |
|||
type |
|||
Animation = object |
|||
attrName: string # Attribute to animate. |
|||
fromValues: seq[float] # List of "from" values. |
|||
toValues: seq[float] # List of "to" values. |
|||
begin: float # Animation starting time. |
|||
duration: float # Animation duration. |
|||
func getAnim(node: XmlNode): Animation = |
|||
## Build an animation object from an XML node. |
|||
result.attrName = node.attr("attributeName") |
|||
if result.attrName.len == 0: |
|||
raise newException(ValueError, "missing 'attributeName' attribute") |
|||
var str = node.attr("from") |
|||
if str.len == 0: |
|||
raise newException(ValueError, "missing 'from' attribute") |
|||
result.fromValues = str.splitWhitespace().map(parseFloat) |
|||
str = node.attr("to") |
|||
if str.len == 0: |
|||
raise newException(ValueError, "missing 'to' attribute") |
|||
result.toValues = str.splitWhitespace().map(parseFloat) |
|||
if result.fromValues.len != result.toValues.len: |
|||
raise newException(ValueError, "inconsistent number of values") |
|||
str = node.attr("begin") |
|||
if str.len == 0: |
|||
raise newException(ValueError, "missing 'begin' attribute") |
|||
if str[^1] != 's': |
|||
raise newException(ValueError, "missing unit") |
|||
result.begin = parseFloat(str[0..^2]) |
|||
str = node.attr("dur") |
|||
if str.len == 0: |
|||
raise newException(ValueError, "missing 'dur' attribute") |
|||
if str[^1] != 's': |
|||
raise newException(ValueError, "missing unit") |
|||
result.duration = parseFloat(str[0..^2]) |
|||
func buildXml(node: XmlNode; t: float): XmlNode = |
|||
## Build the XML tree of a SMIL animation at time "t". |
|||
if node.len > 0 and node[0].tag == "animate": |
|||
# Child is an animate node. |
|||
let anim = node[0].getAnim() |
|||
# Check attribute name. |
|||
if node.attr(anim.attrName).len == 0: |
|||
raise newException(ValueError, &"unable to find attribute '{anim.attrName}'") |
|||
# Check time. |
|||
if t notin anim.begin..(anim.begin + anim.duration): |
|||
raise newException(ValueError, "time value out of range") |
|||
# Build attribute value. |
|||
var aVal = newSeq[float](anim.fromValues.len) |
|||
for i in 0..aVal.high: |
|||
aVal[i] = (anim.fromValues[i] * (anim.begin + anim.duration - t) + |
|||
anim.toValues[i] * (t - anim.begin)) / anim.duration |
|||
# Set attributes. |
|||
let a = node.attrs |
|||
a[anim.attrName] = aVal.mapIt(it.formatFloat(ffDefault, 2)).join(" ") |
|||
result = newElement(node.tag) |
|||
result.attrs = a |
|||
else: |
|||
# Node child (if any) is not an animate node: copy the node and process children. |
|||
result = newElement(node.tag) |
|||
result.attrs = node.attrs |
|||
for child in node: |
|||
result.add child.buildXml(t) |
|||
when isMainModule: |
|||
let smil = loadXml("smil.xml") |
|||
if smil.tag != "smil": |
|||
raise newException(ValueError, "not a SMIL file") |
|||
let newRoot = smil[0] |
|||
echo "At time 0 second:\n" |
|||
echo xmlHeader, newRoot.buildXml(0) |
|||
echo "\nAt time 2 seconds:\n" |
|||
echo xmlHeader, newRoot.buildXml(2)</syntaxhighlight> |
|||
{{out}} |
|||
<pre>At time 0 second: |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<X3D> |
|||
<Scene> |
|||
<Viewpoint position="0 0 8" orientation="0 0 1 0" /> |
|||
<PointLight color="1 1 1" location="0 2 0" /> |
|||
<Shape> |
|||
<Box size="2.0 1.0 2.0" /> |
|||
<Appearance> |
|||
<Material diffuseColor="0.0 0.60 1.0" /> |
|||
</Appearance> |
|||
</Shape> |
|||
</Scene> |
|||
</X3D> |
|||
At time 2 seconds: |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<X3D> |
|||
<Scene> |
|||
<Viewpoint position="0 0 8" orientation="0 0 1 0" /> |
|||
<PointLight color="1 1 1" location="0 2 0" /> |
|||
<Shape> |
|||
<Box size="1.8 1.2 1.8" /> |
|||
<Appearance> |
|||
<Material diffuseColor="0.20 0.56 0.80" /> |
|||
</Appearance> |
|||
</Shape> |
|||
</Scene> |
|||
</X3D></pre> |
|||
=={{header|Perl}}== |
|||
{{trans|Raku}} |
|||
<syntaxhighlight lang="perl"># 20201101 added Perl programming solution |
|||
use 5.014; # for s///r; |
|||
use strict; |
|||
use warnings; |
|||
BEGIN { |
|||
package Animatee; |
|||
use Moo; |
|||
has [qw(todo begin dur from to)] => ( is => 'rw'); |
|||
$INC{"Animatee.pm"} = 1; |
|||
} |
|||
use Animatee; |
|||
use XML::Twig; |
|||
use List::AllUtils 'pairwise'; |
|||
my $smil = <<'DATA'; |
|||
<?xml version="1.0" ?> <smil><X3D><Scene><Viewpoint position="0 0 8" orientation="0 0 1 0" /><PointLight color="1 1 1" location="0 2 0" /><Shape><Box size="2 1 2"><animate attributeName="size" from="2 1 2" to="1 2 1" begin="0s" dur="10s" /></Box><Appearance><Material diffuseColor="0.0 0.6 1.0"><animate attributeName="diffuseColor" from="0.0 0.6 1.0" to="1.0 0.4 0.0" begin="0s" dur="10s" /></Material></Appearance></Shape></Scene></X3D></smil> |
|||
DATA |
|||
my %Parents; |
|||
my $x = XML::Twig->new->parse($smil); |
|||
for my $node ($x->findnodes("//animate")) { |
|||
my $y = $node->parent; |
|||
exists($Parents{$y}) ? (die) : ($Parents{my $k = $y->getName} = Animatee->new); |
|||
for my $animatee ($y->getChildNodes) { |
|||
my %h = %{$animatee->atts}; |
|||
$Parents{$k}->todo($h{attributeName}); |
|||
$Parents{$k}->from([ split(/\s+/,$h{from}) ]); |
|||
$Parents{$k}->to([ split(/\s+/,$h{to}) ]); |
|||
$Parents{$k}->begin( $h{begin} =~ m/\d+/g); |
|||
$Parents{$k}->dur ( $h{dur} =~ m/\d+/g); |
|||
} |
|||
} |
|||
my $z = XML::Twig->new->parse($smil =~ s/\<\/?smil\>//gr) or die; |
|||
for my $t ( 0, 2, 4 ) { |
|||
my $clone = $z; |
|||
while ( my( $k,$v ) = each %Parents) { |
|||
my @incre = pairwise { ($a-$b)/$v->dur } @{$v->to}, @{$v->from}; |
|||
for my $f ($clone->findnodes("//$k")) { |
|||
my $c = join (' ', pairwise { $a+$b*$t } @{$v->from}, @incre); |
|||
$f->set_att($v->todo,$c); |
|||
} |
|||
for my $f ($clone->findnodes("//animate")) { |
|||
$f->delete |
|||
} |
|||
} |
|||
print "when t = $t\n"; |
|||
print $clone->sprint,"\n"; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>when t = 0 |
|||
<?xml version="1.0"?> |
|||
<X3D><Scene><Viewpoint orientation="0 0 1 0" position="0 0 8"/><PointLight color="1 1 1" location="0 2 0"/><Shape><Box size="2 1 2"></Box><Appearance><Material diffuseColor="0 0.6 1"></Material></Appearance></Shape></Scene></X3D> |
|||
when t = 2 |
|||
<?xml version="1.0"?> |
|||
<X3D><Scene><Viewpoint orientation="0 0 1 0" position="0 0 8"/><PointLight color="1 1 1" location="0 2 0"/><Shape><Box size="1.8 1.2 1.8"></Box><Appearance><Material diffuseColor="0.2 0.56 0.8"></Material></Appearance></Shape></Scene></X3D> |
|||
when t = 4 |
|||
<?xml version="1.0"?> |
|||
<X3D><Scene><Viewpoint orientation="0 0 1 0" position="0 0 8"/><PointLight color="1 1 1" location="0 2 0"/><Shape><Box size="1.6 1.4 1.6"></Box><Appearance><Material diffuseColor="0.4 0.52 0.6"></Material></Appearance></Shape></Scene></X3D></pre> |
|||
=={{header|Phix}}== |
|||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
|||
<span style="color: #008080;">include</span> <span style="color: #000000;">builtins</span><span style="color: #0000FF;">\</span><span style="color: #000000;">xml</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span> |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">xml</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""" |
|||
<?xml version="1.0" ?> |
|||
<smil> |
|||
<X3D> |
|||
<Scene> |
|||
<Viewpoint position="0 0 8" orientation="0 0 1 0"/> |
|||
<PointLight color='1 1 1' location='0 2 0'/> |
|||
<Shape> |
|||
<Box size='2 1 2'> |
|||
<animate attributeName="size" from="2 1 2" |
|||
to="1 2 1" begin="0s" dur="10s"/> |
|||
</Box> |
|||
<Appearance> |
|||
<Material diffuseColor='0.0 0.6 1.0'> |
|||
<animate attributeName="diffuseColor" from="0.0 0.6 1.0" |
|||
to="1.0 0.4 0.0" begin="0s" dur="10s"/> |
|||
</Material> |
|||
</Appearance> |
|||
</Shape> |
|||
</Scene> |
|||
</X3D> |
|||
</smil> |
|||
"""</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">animate_contents</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">doc</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">atom</span> <span style="color: #000000;">t</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">doc</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">a</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">xml_get_nodes</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"animate"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">a</span><span style="color: #0000FF;">={}</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">])</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">animate_contents</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #000000;">t</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">else</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">])</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">XML_TAGNAME</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">"animate"</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">name</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">xml_get_attribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #008000;">"attributeName"</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #000000;">vfrm</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">xml_get_attribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #008000;">"from"</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #000000;">v_to</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">xml_get_attribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #008000;">"to"</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #000000;">sbeg</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">xml_get_attribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #008000;">"begin"</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #000000;">sdur</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">xml_get_attribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #008000;">"dur"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">from</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">apply</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">vfrm</span><span style="color: #0000FF;">),</span><span style="color: #7060A8;">to_number</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #000000;">to_s</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">apply</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">v_to</span><span style="color: #0000FF;">),</span><span style="color: #7060A8;">to_number</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">begin</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">to_number</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">trim_tail</span><span style="color: #0000FF;">(</span><span style="color: #000000;">sbeg</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"s"</span><span style="color: #0000FF;">)),</span> |
|||
<span style="color: #000000;">durat</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">to_number</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">trim_tail</span><span style="color: #0000FF;">(</span><span style="color: #000000;">sdur</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"s"</span><span style="color: #0000FF;">)),</span> |
|||
<span style="color: #000000;">fj</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">begin</span><span style="color: #0000FF;">+</span><span style="color: #000000;">durat</span><span style="color: #0000FF;">-</span><span style="color: #000000;">t</span><span style="color: #0000FF;">,</span> |
|||
<span style="color: #000000;">tj</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">t</span><span style="color: #0000FF;">-</span><span style="color: #000000;">begin</span> |
|||
<span style="color: #000080;font-style:italic;">-- plenty more error handling possible here...</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">tj</span><span style="color: #0000FF;"><</span><span style="color: #000000;">0</span> <span style="color: #008080;">or</span> <span style="color: #000000;">fj</span><span style="color: #0000FF;"><</span><span style="color: #000000;">0</span> <span style="color: #008080;">or</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">from</span><span style="color: #0000FF;">)!=</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">to_s</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #0000FF;">?</span><span style="color: #000000;">9</span><span style="color: #0000FF;">/</span><span style="color: #000000;">0</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">j</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">from</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #000000;">from</span><span style="color: #0000FF;">[</span><span style="color: #000000;">j</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"%.2f"</span><span style="color: #0000FF;">,(</span><span style="color: #000000;">from</span><span style="color: #0000FF;">[</span><span style="color: #000000;">j</span><span style="color: #0000FF;">]*</span><span style="color: #000000;">fj</span><span style="color: #0000FF;">+</span><span style="color: #000000;">to_s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">j</span><span style="color: #0000FF;">]*</span><span style="color: #000000;">tj</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">durat</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #000000;">doc</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">xml_set_attribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">,</span><span style="color: #000000;">name</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #000000;">from</span><span style="color: #0000FF;">,</span><span style="color: #008000;">" "</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">i</span><span style="color: #0000FF;">..</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span> <span style="color: #000080;font-style:italic;">-- remove 'animate'</span> |
|||
<span style="color: #008080;">exit</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #000000;">doc</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">animate</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">doc</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">atom</span> <span style="color: #000000;">t</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">doc</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #000080;font-style:italic;">-- remove smil</span> |
|||
<span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">animate_contents</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">],</span><span style="color: #000000;">t</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #000000;">doc</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">doc</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">xml_parse</span><span style="color: #0000FF;">(</span><span style="color: #000000;">xml</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_DOCUMENT</span><span style="color: #0000FF;">]!=</span><span style="color: #008000;">"document"</span> |
|||
<span style="color: #008080;">or</span> <span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">XML_TAGNAME</span><span style="color: #0000FF;">]!=</span><span style="color: #008000;">"smil"</span> |
|||
<span style="color: #008080;">or</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">])!=</span><span style="color: #000000;">1</span> |
|||
<span style="color: #008080;">or</span> <span style="color: #000000;">doc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">XML_CONTENTS</span><span style="color: #0000FF;">][</span><span style="color: #000000;">1</span><span style="color: #0000FF;">][</span><span style="color: #000000;">XML_TAGNAME</span><span style="color: #0000FF;">]!=</span><span style="color: #008000;">"X3D"</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #0000FF;">?</span><span style="color: #000000;">9</span><span style="color: #0000FF;">/</span><span style="color: #000000;">0</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"At time = 0:\n\n"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">xml_sprint</span><span style="color: #0000FF;">(</span><span style="color: #000000;">animate</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)))</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\nAt time = 2:\n\n"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">xml_sprint</span><span style="color: #0000FF;">(</span><span style="color: #000000;">animate</span><span style="color: #0000FF;">(</span><span style="color: #000000;">doc</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)))</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{out}} |
|||
<pre> |
|||
At time = 0: |
|||
<?xml version="1.0" ?> |
|||
<X3D> |
|||
<Scene> |
|||
<Viewpoint position="0 0 8" orientation="0 0 1 0" /> |
|||
<PointLight color="1 1 1" location="0 2 0" /> |
|||
<Shape> |
|||
<Box size="2.00 1.00 2.00" /> |
|||
<Appearance> |
|||
<Material diffuseColor="0.00 0.60 1.00" /> |
|||
</Appearance> |
|||
</Shape> |
|||
</Scene> |
|||
</X3D> |
|||
At time = 2: |
|||
<?xml version="1.0" ?> |
|||
<X3D> |
|||
<Scene> |
|||
<Viewpoint position="0 0 8" orientation="0 0 1 0" /> |
|||
<PointLight color="1 1 1" location="0 2 0" /> |
|||
<Shape> |
|||
<Box size="1.80 1.20 1.80" /> |
|||
<Appearance> |
|||
<Material diffuseColor="0.20 0.56 0.80" /> |
|||
</Appearance> |
|||
</Shape> |
|||
</Scene> |
|||
</X3D> |
|||
</pre> |
|||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
A crude attempt that only works with task data. |
|||
<syntaxhighlight lang="raku" line>use XML::XPath; |
|||
my $smil = q:to<DATA>; # cramped verison, modified from task data |
|||
<?xml version="1.0" ?> <smil><X3D><Scene><Viewpoint position="0 0 8" orientation="0 0 1 0" /><PointLight color="1 1 1" location="0 2 0" /><Shape><Box size="2 1 2"><animate attributeName="size" from="2 1 2" to="1 2 1" begin="0s" dur="10s" /></Box><Appearance><Material diffuseColor="0.0 0.6 1.0"><animate attributeName="diffuseColor" from="0.0 0.6 1.0" to="1.0 0.4 0.0" begin="0s" dur="10s" /></Material></Appearance></Shape></Scene></X3D></smil> |
|||
DATA |
|||
class Animatee { has ($.todo, @.from, @.to, $.begin, $.dur) is rw }; |
|||
my %Parents; # keys store the parent tags that got <animate> child |
|||
my $x = XML::XPath.new(xml => $smil) or die; |
|||
for @($x.find("//animate")) { # strangely need .List or @ coercion to work |
|||
my $y = .parent.name; |
|||
%Parents{$y}:exists ?? die() !! %Parents{$y} = Animatee.new; # unique only |
|||
for .parent.elements { |
|||
%Parents{$y}.todo = .attribs<attributeName>; |
|||
%Parents{$y}.from = .attribs<from>.split(/\s+/); |
|||
%Parents{$y}.to = .attribs<to>.split(/\s+/); |
|||
%Parents{$y}.begin = .attribs<begin>.match(/\d+/); |
|||
%Parents{$y}.dur = .attribs<dur>.match(/\d+/); |
|||
} |
|||
} |
|||
# use regex to strip SMIL tag and create a master template; sub-optimal approach |
|||
my $z = XML::XPath.new(xml => $smil.subst(/\<\/?smil\>/,'',:g:ii:ss)) or die; |
|||
for 0, 2, 4 -> $t { # task requires 0 & 2 only |
|||
my $clone = $z.clone; # work on a copy |
|||
for %Parents.kv -> $k,$v { |
|||
my @incre = ($v.to »-« $v.from) »/» $v.dur; # increment list |
|||
with $clone.find("//$k") { # moving attribute = @from + @increment*$t |
|||
.attribs{%Parents{$_.name}.todo} = $v.from »+« @incre »*» $t; |
|||
.removeChild($_); # ditch <animate> and friends |
|||
} |
|||
} |
|||
say "when t = ", $t; |
|||
say $clone.find("/"); |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>when t = 0 |
|||
<?xml version="1.0"?><X3D><Scene><Viewpoint position="0 0 8" orientation="0 0 1 0"/><PointLight location="0 2 0" color="1 1 1"/><Shape><Box size="2 1 2"/><Appearance><Material diffuseColor="0 0.6 1"/></Appearance></Shape></Scene></X3D> |
|||
when t = 2 |
|||
<?xml version="1.0"?><X3D><Scene><Viewpoint position="0 0 8" orientation="0 0 1 0"/><PointLight location="0 2 0" color="1 1 1"/><Shape><Box size="1.8 1.2 1.8"/><Appearance><Material diffuseColor="0.2 0.56 0.8"/></Appearance></Shape></Scene></X3D> |
|||
when t = 4 |
|||
<?xml version="1.0"?><X3D><Scene><Viewpoint position="0 0 8" orientation="0 0 1 0"/><PointLight location="0 2 0" color="1 1 1"/><Shape><Box size="1.6 1.4 1.6"/><Appearance><Material diffuseColor="0.4 0.52 0.6"/></Appearance></Shape></Scene></X3D> |
|||
</pre> |
|||
=={{header|Tcl}}== |
|||
{{works with|Tcl|8.6}} |
|||
{{libheader|tDOM}} |
|||
<syntaxhighlight lang="tcl">package require Tcl 8.6 |
|||
package require tdom |
|||
# Applies a time-based interpolation to generate a space-separated list |
|||
proc interpolate {time info} { |
|||
dict with info { |
|||
scan $begin "%fs" begin |
|||
scan $dur "%fs" dur |
|||
} |
|||
if {$time < $begin} { |
|||
return $from |
|||
} elseif {$time > $begin+$dur} { |
|||
return $to |
|||
} |
|||
set delta [expr {($time - $begin) / $dur}] |
|||
return [lmap f $from t $to {expr {$f + ($t-$f)*$delta}}] |
|||
} |
|||
# Applies SMIL <transform> elements to their container |
|||
proc applySMILtransform {sourceDocument time} { |
|||
set doc [dom parse [$sourceDocument asXML]] |
|||
foreach smil [$doc selectNodes //smil] { |
|||
foreach context [$smil selectNodes {//*[animate]}] { |
|||
set animator [$context selectNodes animate] |
|||
set animated [$context selectNodes @[$animator @attributeName]] |
|||
$context removeChild $animator |
|||
$context setAttribute [$animator @attributeName] \ |
|||
[interpolate $time [lindex [$animator asList] 1]] |
|||
} |
|||
if {[$smil parentNode] eq ""} { |
|||
set reparent 1 |
|||
} else { |
|||
[$smil parentNode] replaceChild $smil [$smil firstChild] |
|||
} |
|||
} |
|||
if {[info exist reparent]} { |
|||
set doc [dom parse [[$smil firstChild] asXML]] |
|||
} |
|||
return $doc |
|||
} |
|||
set t [expr {[lindex $argv 0] + 0.0}] |
|||
set result [applySMILtransform [dom parse [read stdin]] $t] |
|||
puts {<?xml version="1.0" ?>} |
|||
puts -nonewline [$result asXML -indent 2]</syntaxhighlight> |
|||
{{out|Demonstration}} |
|||
Note that <tt>input.smil</tt> contains the source document from the task description. |
|||
<pre> |
|||
$ tclsh8.6 applySmil.tcl 0 < input.smil |
|||
<?xml version="1.0" ?> |
|||
<X3D> |
|||
<Scene> |
|||
<Viewpoint position="0 0 8" orientation="0 0 1 0"/> |
|||
<PointLight color="1 1 1" location="0 2 0"/> |
|||
<Shape> |
|||
<Box size="2.0 1.0 2.0"/> |
|||
<Appearance> |
|||
<Material diffuseColor="0.0 0.6 1.0"/> |
|||
</Appearance> |
|||
</Shape> |
|||
</Scene> |
|||
</X3D> |
|||
$ tclsh8.6 applySmil.tcl 2 < input.smil |
|||
<?xml version="1.0" ?> |
|||
<X3D> |
|||
<Scene> |
|||
<Viewpoint position="0 0 8" orientation="0 0 1 0"/> |
|||
<PointLight color="1 1 1" location="0 2 0"/> |
|||
<Shape> |
|||
<Box size="1.8 1.2 1.8"/> |
|||
<Appearance> |
|||
<Material diffuseColor="0.2 0.5599999999999999 0.8"/> |
|||
</Appearance> |
|||
</Shape> |
|||
</Scene> |
|||
</X3D> |
|||
</pre> |
|||
=={{header|Wren}}== |
|||
{{libheader|Wren-pattern}} |
|||
As Wren lacks any kind of XML support (let alone SMIL), I've had to resort to string parsing to complete this task. |
|||
<syntaxhighlight lang="wren">import "./pattern" for Pattern |
|||
var xml = """ |
|||
<?xml version="1.0" ?> |
|||
<smil> |
<smil> |
||
<X3D> |
<X3D> |
||
<Scene> |
<Scene> |
||
<Viewpoint |
<Viewpoint position="0 0 8" orientation="0 0 1 0"/> |
||
<PointLight color='1 1 1' location='0 2 0'/> |
<PointLight color='1 1 1' location='0 2 0'/> |
||
<Shape> |
|||
<Transform translation='0.0 0.0 -3.0' scale='1 1 1'> |
|||
< |
<Box size='2 1 2'> |
||
< |
<animate attributeName="size" from="2 1 2" |
||
to="1 2 1" begin="0s" dur="10s"/> |
|||
<animate attributeName="size" from="2 1 2" |
|||
</Box> |
|||
to="1 2 1" begin="0s" dur="10s"/> |
|||
<Appearance> |
|||
< |
<Material diffuseColor='0.0 0.6 1.0'> |
||
< |
<animate attributeName="diffuseColor" from="0.0 0.6 1.0" |
||
to="1.0 0.4 0.0" begin="0s" dur="10s"/> |
|||
<animate attributeName="diffuseColor" from="0.0 0.6 1.0" |
|||
</Material> |
|||
to="1.0 0.4 0.0" begin="0s" dur="10s"/> |
|||
</Appearance> |
|||
</Shape> |
|||
</Shape> |
|||
</Transform> |
|||
</Scene> |
</Scene> |
||
</X3D |
</X3D> |
||
</smil> |
|||
""" |
|||
var extractValue = Fn.new { |s| s.replace("\"", "").split("=")[1] } |
|||
Try to make it possible to insert "animate" tags for animating every attribute of the base file. |
|||
var interpolate = Fn.new { |from, to, begin, dur, t| |
|||
if (begin >= t) return from |
|||
if (begin + t >= dur) return to |
|||
var mid = List.filled(3, 0) |
|||
for (i in 0..2) { |
|||
mid[i] = (from[i] * (dur - t) + to[i] * (t - begin)) / (dur - begin) |
|||
} |
|||
return mid |
|||
} |
|||
var smil = xml.indexOf("smil") > -1 |
|||
if (!smil) { |
|||
System.print(xml) |
|||
return |
|||
} |
|||
xml = xml.replace("<smil>\n", "").replace("\n</smil>", "") |
|||
var animate = Pattern.new("<animate +1^///>") |
|||
var matches = animate.findAll(xml) |
|||
var mcount = matches.count |
|||
if (mcount == 0) { |
|||
System.print(xml) |
|||
return |
|||
} |
|||
var elem = List.filled(mcount, null) |
|||
var attr = List.filled(mcount, null) |
|||
var from = List.filled(mcount, null) |
|||
var to = List.filled(mcount, null) |
|||
var begin = List.filled(mcount, 0) |
|||
var dur = List.filled(mcount, 0) |
|||
for (i in 0...mcount) { |
|||
var match = matches[i] |
|||
xml = xml.replace(match.text + "\n", "") |
|||
var p = Pattern.new("+1/s") |
|||
var items = p.splitAll(match.text[1..-3]) |
|||
attr[i] = extractValue.call(items[1]) |
|||
from[i] = [0] * 3 |
|||
from[i][0] = Num.fromString(items[2].replace("from=\"", "")) |
|||
from[i][1] = Num.fromString(items[3]) |
|||
from[i][2] = Num.fromString(items[4].replace("\"", "")) |
|||
to[i] = [0] * 3 |
|||
to[i][0] = Num.fromString(items[5].replace("to=\"", "")) |
|||
to[i][1] = Num.fromString(items[6]) |
|||
to[i][2] = Num.fromString(items[7].replace("\"", "")) |
|||
begin[i] = Num.fromString(extractValue.call(items[8]).replace("s", "")) |
|||
dur[i] = Num.fromString(extractValue.call(items[9]).replace("s", "")) |
|||
p = Pattern.new("<[+1/w] %(attr[i])/='[+1^']'>") |
|||
var matches2 = p.find(xml) |
|||
elem[i] = matches2.capsText[0] |
|||
p = Pattern.new(">\n+1/s<//%(elem[i])>") |
|||
xml = p.replaceAll(xml, "/>") |
|||
} |
|||
for (t in [0, 2]) { |
|||
var xml2 = xml |
|||
for (i in 0...mcount) { |
|||
var p = Pattern.new("<%(elem[i]) %(attr[i])/='[+1^']'//>") |
|||
var sfrom = p.find(xml2).capsText[0] |
|||
var mid = interpolate.call(from[i], to[i], begin[i], dur[i], t) |
|||
var smid = mid.map { |n| n.toString }.join(" ") |
|||
xml2 = xml2.replace("<%(elem[i]) %(attr[i])='%(sfrom)'/>", "<%(elem[i]) %(attr[i])='%(smid)'/>") |
|||
} |
|||
System.print("At t = %(t) seconds:\n") |
|||
System.print(xml2) |
|||
System.print() |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
At t = 0 seconds: |
|||
<?xml version="1.0" ?> |
|||
<X3D> |
|||
<Scene> |
|||
<Viewpoint position="0 0 8" orientation="0 0 1 0"/> |
|||
<PointLight color='1 1 1' location='0 2 0'/> |
|||
<Shape> |
|||
<Box size='2 1 2'/> |
|||
<Appearance> |
|||
<Material diffuseColor='0 0.6 1'/> |
|||
</Appearance> |
|||
</Shape> |
|||
</Scene> |
|||
</X3D> |
|||
At t = 2 seconds: |
|||
<?xml version="1.0" ?> |
|||
<X3D> |
|||
<Scene> |
|||
<Viewpoint position="0 0 8" orientation="0 0 1 0"/> |
|||
<PointLight color='1 1 1' location='0 2 0'/> |
|||
<Shape> |
|||
<Box size='1.8 1.2 1.8'/> |
|||
<Appearance> |
|||
<Material diffuseColor='0.2 0.56 0.8'/> |
|||
</Appearance> |
|||
</Shape> |
|||
</Scene> |
|||
</X3D> |
|||
</pre> |
Latest revision as of 10:22, 29 January 2024
Process SMIL directives in XML data is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
In order to represent evolutions of contents over time, the SMIL standard provides a solution to record the animation of data. Smil animations can be added to any kind of contents formated in XML.
The task is to create an utility that given the first Smiled XML file, would return the following ones:
<?xml version="1.0" ?>
<smil>
<X3D>
<Scene>
<Viewpoint position="0 0 8" orientation="0 0 1 0"/>
<PointLight color='1 1 1' location='0 2 0'/>
<Shape>
<Box size='2 1 2'>
<animate attributeName="size" from="2 1 2"
to="1 2 1" begin="0s" dur="10s"/>
</Box>
<Appearance>
<Material diffuseColor='0.0 0.6 1.0'>
<animate attributeName="diffuseColor" from="0.0 0.6 1.0"
to="1.0 0.4 0.0" begin="0s" dur="10s"/>
</Material>
</Appearance>
</Shape>
</Scene>
</X3D>
</smil>
At t = 0 second here is the expected output:
<?xml version="1.0" ?>
<X3D>
<Scene>
<Viewpoint position="0 0 8" orientation="0 0 1 0"/>
<PointLight color='1 1 1' location='0 2 0'/>
<Shape>
<Box size='2 1 2'/>
<Appearance>
<Material diffuseColor='0.0 0.6 1.0'/>
</Appearance>
</Shape>
</Scene>
</X3D>
At t = 2 second here is the expected output:
<?xml version="1.0" ?>
<X3D>
<Scene>
<Viewpoint position="0 0 8" orientation="0 0 1 0"/>
<PointLight color='1 1 1' location='0 2 0'/>
<Shape>
<Box size='1.8 1.2 1.8'/>
<Appearance>
<Material diffuseColor='0.2 0.56 0.8'/>
</Appearance>
</Shape>
</Scene>
</X3D>
Go
package main
import (
"fmt"
"github.com/beevik/etree"
"log"
"os"
"strconv"
"strings"
)
type animData struct {
element *etree.Element
attrib string
from string
to string
begin float64
dur float64
}
func check(err error) {
if err != nil {
log.Fatal(err)
}
}
func (ad *animData) AtTime(t float64) string {
beg := ad.begin
end := beg + ad.dur
if t < beg || t > end {
log.Fatalf("time must be in interval [%g, %g]", beg, end)
}
fromSplit := strings.Fields(ad.from)
toSplit := strings.Fields(ad.to)
le := len(fromSplit)
interSplit := make([]string, le)
for i := 0; i < le; i++ {
fromF, err := strconv.ParseFloat(fromSplit[i], 64)
check(err)
toF, err := strconv.ParseFloat(toSplit[i], 64)
check(err)
interF := (fromF*(end-t) + toF*(t-beg)) / ad.dur
interSplit[i] = fmt.Sprintf("%.2f", interF)
}
return strings.Join(interSplit, " ")
}
func main() {
doc := etree.NewDocument()
check(doc.ReadFromFile("smil.xml"))
smil := doc.SelectElement("smil")
if smil == nil {
log.Fatal("'smil' element not found")
}
x3d := smil.SelectElement("X3D")
if x3d == nil {
log.Fatal("'X3D' element not found")
}
doc.SetRoot(x3d) // remove 'smil' element
var ads []*animData
for _, a := range doc.FindElements("//animate") {
attrib := a.SelectAttrValue("attributeName", "?")
from := a.SelectAttrValue("from", "?")
to := a.SelectAttrValue("to", "?")
beginS := a.SelectAttrValue("begin", "?")
durS := a.SelectAttrValue("dur", "?")
if attrib == "?" || from == "?" || to == "?" ||
beginS == "?" || durS == "?" {
log.Fatal("an animate element has missing attribute(s)")
}
begin, err := strconv.ParseFloat(beginS[:len(beginS)-1], 64)
check(err)
dur, err := strconv.ParseFloat(durS[:len(durS)-1], 64)
check(err)
p := a.Parent()
if p == nil {
log.Fatal("an animate element has no parent")
}
pattrib := p.SelectAttrValue(attrib, "?")
if pattrib == "?" {
log.Fatal("an animate element's parent has missing attribute")
}
ads = append(ads, &animData{p, attrib, from, to, begin, dur})
p.RemoveChild(a) // remove 'animate' element
}
ts := []float64{0, 2}
for _, t := range ts {
for _, ad := range ads {
s := ad.AtTime(t)
ad.element.CreateAttr(ad.attrib, s)
}
doc.Indent(2)
fmt.Printf("At time = %g seconds:\n\n", t)
doc.WriteTo(os.Stdout)
fmt.Println()
}
}
- Output:
At time = 0 seconds: <?xml version="1.0" ?> <X3D> <Scene> <Viewpoint position="0 0 8" orientation="0 0 1 0"/> <PointLight color="1 1 1" location="0 2 0"/> <Shape> <Box size="2.00 1.00 2.00"/> <Appearance> <Material diffuseColor="0.00 0.60 1.00"/> </Appearance> </Shape> </Scene> </X3D> At time = 2 seconds: <?xml version="1.0" ?> <X3D> <Scene> <Viewpoint position="0 0 8" orientation="0 0 1 0"/> <PointLight color="1 1 1" location="0 2 0"/> <Shape> <Box size="1.80 1.20 1.80"/> <Appearance> <Material diffuseColor="0.20 0.56 0.80"/> </Appearance> </Shape> </Scene> </X3D>
Nim
import sequtils, strformat, strtabs, strutils, xmlparser, xmltree
type
Animation = object
attrName: string # Attribute to animate.
fromValues: seq[float] # List of "from" values.
toValues: seq[float] # List of "to" values.
begin: float # Animation starting time.
duration: float # Animation duration.
func getAnim(node: XmlNode): Animation =
## Build an animation object from an XML node.
result.attrName = node.attr("attributeName")
if result.attrName.len == 0:
raise newException(ValueError, "missing 'attributeName' attribute")
var str = node.attr("from")
if str.len == 0:
raise newException(ValueError, "missing 'from' attribute")
result.fromValues = str.splitWhitespace().map(parseFloat)
str = node.attr("to")
if str.len == 0:
raise newException(ValueError, "missing 'to' attribute")
result.toValues = str.splitWhitespace().map(parseFloat)
if result.fromValues.len != result.toValues.len:
raise newException(ValueError, "inconsistent number of values")
str = node.attr("begin")
if str.len == 0:
raise newException(ValueError, "missing 'begin' attribute")
if str[^1] != 's':
raise newException(ValueError, "missing unit")
result.begin = parseFloat(str[0..^2])
str = node.attr("dur")
if str.len == 0:
raise newException(ValueError, "missing 'dur' attribute")
if str[^1] != 's':
raise newException(ValueError, "missing unit")
result.duration = parseFloat(str[0..^2])
func buildXml(node: XmlNode; t: float): XmlNode =
## Build the XML tree of a SMIL animation at time "t".
if node.len > 0 and node[0].tag == "animate":
# Child is an animate node.
let anim = node[0].getAnim()
# Check attribute name.
if node.attr(anim.attrName).len == 0:
raise newException(ValueError, &"unable to find attribute '{anim.attrName}'")
# Check time.
if t notin anim.begin..(anim.begin + anim.duration):
raise newException(ValueError, "time value out of range")
# Build attribute value.
var aVal = newSeq[float](anim.fromValues.len)
for i in 0..aVal.high:
aVal[i] = (anim.fromValues[i] * (anim.begin + anim.duration - t) +
anim.toValues[i] * (t - anim.begin)) / anim.duration
# Set attributes.
let a = node.attrs
a[anim.attrName] = aVal.mapIt(it.formatFloat(ffDefault, 2)).join(" ")
result = newElement(node.tag)
result.attrs = a
else:
# Node child (if any) is not an animate node: copy the node and process children.
result = newElement(node.tag)
result.attrs = node.attrs
for child in node:
result.add child.buildXml(t)
when isMainModule:
let smil = loadXml("smil.xml")
if smil.tag != "smil":
raise newException(ValueError, "not a SMIL file")
let newRoot = smil[0]
echo "At time 0 second:\n"
echo xmlHeader, newRoot.buildXml(0)
echo "\nAt time 2 seconds:\n"
echo xmlHeader, newRoot.buildXml(2)
- Output:
At time 0 second: <?xml version="1.0" encoding="UTF-8" ?> <X3D> <Scene> <Viewpoint position="0 0 8" orientation="0 0 1 0" /> <PointLight color="1 1 1" location="0 2 0" /> <Shape> <Box size="2.0 1.0 2.0" /> <Appearance> <Material diffuseColor="0.0 0.60 1.0" /> </Appearance> </Shape> </Scene> </X3D> At time 2 seconds: <?xml version="1.0" encoding="UTF-8" ?> <X3D> <Scene> <Viewpoint position="0 0 8" orientation="0 0 1 0" /> <PointLight color="1 1 1" location="0 2 0" /> <Shape> <Box size="1.8 1.2 1.8" /> <Appearance> <Material diffuseColor="0.20 0.56 0.80" /> </Appearance> </Shape> </Scene> </X3D>
Perl
# 20201101 added Perl programming solution
use 5.014; # for s///r;
use strict;
use warnings;
BEGIN {
package Animatee;
use Moo;
has [qw(todo begin dur from to)] => ( is => 'rw');
$INC{"Animatee.pm"} = 1;
}
use Animatee;
use XML::Twig;
use List::AllUtils 'pairwise';
my $smil = <<'DATA';
<?xml version="1.0" ?> <smil><X3D><Scene><Viewpoint position="0 0 8" orientation="0 0 1 0" /><PointLight color="1 1 1" location="0 2 0" /><Shape><Box size="2 1 2"><animate attributeName="size" from="2 1 2" to="1 2 1" begin="0s" dur="10s" /></Box><Appearance><Material diffuseColor="0.0 0.6 1.0"><animate attributeName="diffuseColor" from="0.0 0.6 1.0" to="1.0 0.4 0.0" begin="0s" dur="10s" /></Material></Appearance></Shape></Scene></X3D></smil>
DATA
my %Parents;
my $x = XML::Twig->new->parse($smil);
for my $node ($x->findnodes("//animate")) {
my $y = $node->parent;
exists($Parents{$y}) ? (die) : ($Parents{my $k = $y->getName} = Animatee->new);
for my $animatee ($y->getChildNodes) {
my %h = %{$animatee->atts};
$Parents{$k}->todo($h{attributeName});
$Parents{$k}->from([ split(/\s+/,$h{from}) ]);
$Parents{$k}->to([ split(/\s+/,$h{to}) ]);
$Parents{$k}->begin( $h{begin} =~ m/\d+/g);
$Parents{$k}->dur ( $h{dur} =~ m/\d+/g);
}
}
my $z = XML::Twig->new->parse($smil =~ s/\<\/?smil\>//gr) or die;
for my $t ( 0, 2, 4 ) {
my $clone = $z;
while ( my( $k,$v ) = each %Parents) {
my @incre = pairwise { ($a-$b)/$v->dur } @{$v->to}, @{$v->from};
for my $f ($clone->findnodes("//$k")) {
my $c = join (' ', pairwise { $a+$b*$t } @{$v->from}, @incre);
$f->set_att($v->todo,$c);
}
for my $f ($clone->findnodes("//animate")) {
$f->delete
}
}
print "when t = $t\n";
print $clone->sprint,"\n";
}
- Output:
when t = 0 <?xml version="1.0"?> <X3D><Scene><Viewpoint orientation="0 0 1 0" position="0 0 8"/><PointLight color="1 1 1" location="0 2 0"/><Shape><Box size="2 1 2"></Box><Appearance><Material diffuseColor="0 0.6 1"></Material></Appearance></Shape></Scene></X3D> when t = 2 <?xml version="1.0"?> <X3D><Scene><Viewpoint orientation="0 0 1 0" position="0 0 8"/><PointLight color="1 1 1" location="0 2 0"/><Shape><Box size="1.8 1.2 1.8"></Box><Appearance><Material diffuseColor="0.2 0.56 0.8"></Material></Appearance></Shape></Scene></X3D> when t = 4 <?xml version="1.0"?> <X3D><Scene><Viewpoint orientation="0 0 1 0" position="0 0 8"/><PointLight color="1 1 1" location="0 2 0"/><Shape><Box size="1.6 1.4 1.6"></Box><Appearance><Material diffuseColor="0.4 0.52 0.6"></Material></Appearance></Shape></Scene></X3D>
Phix
with javascript_semantics include builtins\xml.e constant xml = """ <?xml version="1.0" ?> <smil> <X3D> <Scene> <Viewpoint position="0 0 8" orientation="0 0 1 0"/> <PointLight color='1 1 1' location='0 2 0'/> <Shape> <Box size='2 1 2'> <animate attributeName="size" from="2 1 2" to="1 2 1" begin="0s" dur="10s"/> </Box> <Appearance> <Material diffuseColor='0.0 0.6 1.0'> <animate attributeName="diffuseColor" from="0.0 0.6 1.0" to="1.0 0.4 0.0" begin="0s" dur="10s"/> </Material> </Appearance> </Shape> </Scene> </X3D> </smil> """ function animate_contents(sequence doc, atom t) doc = deep_copy(doc) sequence a = xml_get_nodes(doc,"animate") if a={} then for i=1 to length(doc[XML_CONTENTS]) do doc[XML_CONTENTS][i] = animate_contents(doc[XML_CONTENTS][i],t) end for else for i=1 to length(doc[XML_CONTENTS]) do if doc[XML_CONTENTS][i][XML_TAGNAME]="animate" then string name = xml_get_attribute(doc[XML_CONTENTS][i],"attributeName"), vfrm = xml_get_attribute(doc[XML_CONTENTS][i],"from"), v_to = xml_get_attribute(doc[XML_CONTENTS][i],"to"), sbeg = xml_get_attribute(doc[XML_CONTENTS][i],"begin"), sdur = xml_get_attribute(doc[XML_CONTENTS][i],"dur") sequence from = apply(split(vfrm),to_number), to_s = apply(split(v_to),to_number) atom begin = to_number(trim_tail(sbeg,"s")), durat = to_number(trim_tail(sdur,"s")), fj = begin+durat-t, tj = t-begin -- plenty more error handling possible here... if tj<0 or fj<0 or length(from)!=length(to_s) then ?9/0 end if for j=1 to length(from) do from[j] = sprintf("%.2f",(from[j]*fj+to_s[j]*tj)/durat) end for doc = xml_set_attribute(doc,name,join(from," ")) doc[XML_CONTENTS][i..i] = "" -- remove 'animate' exit end if end for end if return doc end function function animate(sequence doc, atom t) doc = deep_copy(doc) doc[XML_CONTENTS] = doc[XML_CONTENTS][XML_CONTENTS][1] -- remove smil doc[XML_CONTENTS] = animate_contents(doc[XML_CONTENTS],t) return doc end function sequence doc = xml_parse(xml) if doc[XML_DOCUMENT]!="document" or doc[XML_CONTENTS][XML_TAGNAME]!="smil" or length(doc[XML_CONTENTS][XML_CONTENTS])!=1 or doc[XML_CONTENTS][XML_CONTENTS][1][XML_TAGNAME]!="X3D" then ?9/0 end if printf(1,"At time = 0:\n\n") puts(1,xml_sprint(animate(doc,0))) printf(1,"\nAt time = 2:\n\n") puts(1,xml_sprint(animate(doc,2)))
- Output:
At time = 0: <?xml version="1.0" ?> <X3D> <Scene> <Viewpoint position="0 0 8" orientation="0 0 1 0" /> <PointLight color="1 1 1" location="0 2 0" /> <Shape> <Box size="2.00 1.00 2.00" /> <Appearance> <Material diffuseColor="0.00 0.60 1.00" /> </Appearance> </Shape> </Scene> </X3D> At time = 2: <?xml version="1.0" ?> <X3D> <Scene> <Viewpoint position="0 0 8" orientation="0 0 1 0" /> <PointLight color="1 1 1" location="0 2 0" /> <Shape> <Box size="1.80 1.20 1.80" /> <Appearance> <Material diffuseColor="0.20 0.56 0.80" /> </Appearance> </Shape> </Scene> </X3D>
Raku
(formerly Perl 6) A crude attempt that only works with task data.
use XML::XPath;
my $smil = q:to<DATA>; # cramped verison, modified from task data
<?xml version="1.0" ?> <smil><X3D><Scene><Viewpoint position="0 0 8" orientation="0 0 1 0" /><PointLight color="1 1 1" location="0 2 0" /><Shape><Box size="2 1 2"><animate attributeName="size" from="2 1 2" to="1 2 1" begin="0s" dur="10s" /></Box><Appearance><Material diffuseColor="0.0 0.6 1.0"><animate attributeName="diffuseColor" from="0.0 0.6 1.0" to="1.0 0.4 0.0" begin="0s" dur="10s" /></Material></Appearance></Shape></Scene></X3D></smil>
DATA
class Animatee { has ($.todo, @.from, @.to, $.begin, $.dur) is rw };
my %Parents; # keys store the parent tags that got <animate> child
my $x = XML::XPath.new(xml => $smil) or die;
for @($x.find("//animate")) { # strangely need .List or @ coercion to work
my $y = .parent.name;
%Parents{$y}:exists ?? die() !! %Parents{$y} = Animatee.new; # unique only
for .parent.elements {
%Parents{$y}.todo = .attribs<attributeName>;
%Parents{$y}.from = .attribs<from>.split(/\s+/);
%Parents{$y}.to = .attribs<to>.split(/\s+/);
%Parents{$y}.begin = .attribs<begin>.match(/\d+/);
%Parents{$y}.dur = .attribs<dur>.match(/\d+/);
}
}
# use regex to strip SMIL tag and create a master template; sub-optimal approach
my $z = XML::XPath.new(xml => $smil.subst(/\<\/?smil\>/,'',:g:ii:ss)) or die;
for 0, 2, 4 -> $t { # task requires 0 & 2 only
my $clone = $z.clone; # work on a copy
for %Parents.kv -> $k,$v {
my @incre = ($v.to »-« $v.from) »/» $v.dur; # increment list
with $clone.find("//$k") { # moving attribute = @from + @increment*$t
.attribs{%Parents{$_.name}.todo} = $v.from »+« @incre »*» $t;
.removeChild($_); # ditch <animate> and friends
}
}
say "when t = ", $t;
say $clone.find("/");
}
- Output:
when t = 0 <?xml version="1.0"?><X3D><Scene><Viewpoint position="0 0 8" orientation="0 0 1 0"/><PointLight location="0 2 0" color="1 1 1"/><Shape><Box size="2 1 2"/><Appearance><Material diffuseColor="0 0.6 1"/></Appearance></Shape></Scene></X3D> when t = 2 <?xml version="1.0"?><X3D><Scene><Viewpoint position="0 0 8" orientation="0 0 1 0"/><PointLight location="0 2 0" color="1 1 1"/><Shape><Box size="1.8 1.2 1.8"/><Appearance><Material diffuseColor="0.2 0.56 0.8"/></Appearance></Shape></Scene></X3D> when t = 4 <?xml version="1.0"?><X3D><Scene><Viewpoint position="0 0 8" orientation="0 0 1 0"/><PointLight location="0 2 0" color="1 1 1"/><Shape><Box size="1.6 1.4 1.6"/><Appearance><Material diffuseColor="0.4 0.52 0.6"/></Appearance></Shape></Scene></X3D>
Tcl
package require Tcl 8.6
package require tdom
# Applies a time-based interpolation to generate a space-separated list
proc interpolate {time info} {
dict with info {
scan $begin "%fs" begin
scan $dur "%fs" dur
}
if {$time < $begin} {
return $from
} elseif {$time > $begin+$dur} {
return $to
}
set delta [expr {($time - $begin) / $dur}]
return [lmap f $from t $to {expr {$f + ($t-$f)*$delta}}]
}
# Applies SMIL <transform> elements to their container
proc applySMILtransform {sourceDocument time} {
set doc [dom parse [$sourceDocument asXML]]
foreach smil [$doc selectNodes //smil] {
foreach context [$smil selectNodes {//*[animate]}] {
set animator [$context selectNodes animate]
set animated [$context selectNodes @[$animator @attributeName]]
$context removeChild $animator
$context setAttribute [$animator @attributeName] \
[interpolate $time [lindex [$animator asList] 1]]
}
if {[$smil parentNode] eq ""} {
set reparent 1
} else {
[$smil parentNode] replaceChild $smil [$smil firstChild]
}
}
if {[info exist reparent]} {
set doc [dom parse [[$smil firstChild] asXML]]
}
return $doc
}
set t [expr {[lindex $argv 0] + 0.0}]
set result [applySMILtransform [dom parse [read stdin]] $t]
puts {<?xml version="1.0" ?>}
puts -nonewline [$result asXML -indent 2]
- Demonstration:
Note that input.smil contains the source document from the task description.
$ tclsh8.6 applySmil.tcl 0 < input.smil <?xml version="1.0" ?> <X3D> <Scene> <Viewpoint position="0 0 8" orientation="0 0 1 0"/> <PointLight color="1 1 1" location="0 2 0"/> <Shape> <Box size="2.0 1.0 2.0"/> <Appearance> <Material diffuseColor="0.0 0.6 1.0"/> </Appearance> </Shape> </Scene> </X3D> $ tclsh8.6 applySmil.tcl 2 < input.smil <?xml version="1.0" ?> <X3D> <Scene> <Viewpoint position="0 0 8" orientation="0 0 1 0"/> <PointLight color="1 1 1" location="0 2 0"/> <Shape> <Box size="1.8 1.2 1.8"/> <Appearance> <Material diffuseColor="0.2 0.5599999999999999 0.8"/> </Appearance> </Shape> </Scene> </X3D>
Wren
As Wren lacks any kind of XML support (let alone SMIL), I've had to resort to string parsing to complete this task.
import "./pattern" for Pattern
var xml = """
<?xml version="1.0" ?>
<smil>
<X3D>
<Scene>
<Viewpoint position="0 0 8" orientation="0 0 1 0"/>
<PointLight color='1 1 1' location='0 2 0'/>
<Shape>
<Box size='2 1 2'>
<animate attributeName="size" from="2 1 2"
to="1 2 1" begin="0s" dur="10s"/>
</Box>
<Appearance>
<Material diffuseColor='0.0 0.6 1.0'>
<animate attributeName="diffuseColor" from="0.0 0.6 1.0"
to="1.0 0.4 0.0" begin="0s" dur="10s"/>
</Material>
</Appearance>
</Shape>
</Scene>
</X3D>
</smil>
"""
var extractValue = Fn.new { |s| s.replace("\"", "").split("=")[1] }
var interpolate = Fn.new { |from, to, begin, dur, t|
if (begin >= t) return from
if (begin + t >= dur) return to
var mid = List.filled(3, 0)
for (i in 0..2) {
mid[i] = (from[i] * (dur - t) + to[i] * (t - begin)) / (dur - begin)
}
return mid
}
var smil = xml.indexOf("smil") > -1
if (!smil) {
System.print(xml)
return
}
xml = xml.replace("<smil>\n", "").replace("\n</smil>", "")
var animate = Pattern.new("<animate +1^///>")
var matches = animate.findAll(xml)
var mcount = matches.count
if (mcount == 0) {
System.print(xml)
return
}
var elem = List.filled(mcount, null)
var attr = List.filled(mcount, null)
var from = List.filled(mcount, null)
var to = List.filled(mcount, null)
var begin = List.filled(mcount, 0)
var dur = List.filled(mcount, 0)
for (i in 0...mcount) {
var match = matches[i]
xml = xml.replace(match.text + "\n", "")
var p = Pattern.new("+1/s")
var items = p.splitAll(match.text[1..-3])
attr[i] = extractValue.call(items[1])
from[i] = [0] * 3
from[i][0] = Num.fromString(items[2].replace("from=\"", ""))
from[i][1] = Num.fromString(items[3])
from[i][2] = Num.fromString(items[4].replace("\"", ""))
to[i] = [0] * 3
to[i][0] = Num.fromString(items[5].replace("to=\"", ""))
to[i][1] = Num.fromString(items[6])
to[i][2] = Num.fromString(items[7].replace("\"", ""))
begin[i] = Num.fromString(extractValue.call(items[8]).replace("s", ""))
dur[i] = Num.fromString(extractValue.call(items[9]).replace("s", ""))
p = Pattern.new("<[+1/w] %(attr[i])/='[+1^']'>")
var matches2 = p.find(xml)
elem[i] = matches2.capsText[0]
p = Pattern.new(">\n+1/s<//%(elem[i])>")
xml = p.replaceAll(xml, "/>")
}
for (t in [0, 2]) {
var xml2 = xml
for (i in 0...mcount) {
var p = Pattern.new("<%(elem[i]) %(attr[i])/='[+1^']'//>")
var sfrom = p.find(xml2).capsText[0]
var mid = interpolate.call(from[i], to[i], begin[i], dur[i], t)
var smid = mid.map { |n| n.toString }.join(" ")
xml2 = xml2.replace("<%(elem[i]) %(attr[i])='%(sfrom)'/>", "<%(elem[i]) %(attr[i])='%(smid)'/>")
}
System.print("At t = %(t) seconds:\n")
System.print(xml2)
System.print()
}
- Output:
At t = 0 seconds: <?xml version="1.0" ?> <X3D> <Scene> <Viewpoint position="0 0 8" orientation="0 0 1 0"/> <PointLight color='1 1 1' location='0 2 0'/> <Shape> <Box size='2 1 2'/> <Appearance> <Material diffuseColor='0 0.6 1'/> </Appearance> </Shape> </Scene> </X3D> At t = 2 seconds: <?xml version="1.0" ?> <X3D> <Scene> <Viewpoint position="0 0 8" orientation="0 0 1 0"/> <PointLight color='1 1 1' location='0 2 0'/> <Shape> <Box size='1.8 1.2 1.8'/> <Appearance> <Material diffuseColor='0.2 0.56 0.8'/> </Appearance> </Shape> </Scene> </X3D>