Process SMIL directives in XML data

From Rosetta Code
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>

Tcl[edit]

Works with: Tcl version 8.6
Library: tDOM
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>