tickly 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "rdoc", "~> 3.12"
11
+ gem "bundler"
12
+ gem "jeweler", "~> 1.8.3"
13
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Julik Tarkhanov
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ = tickly
2
+
3
+ A highly simplistic TCL parser and evaluator (primarily designed for parsing Nuke scripts)
4
+
5
+ == Contributing to tickly
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
9
+ * Fork the project.
10
+ * Start a feature/bugfix branch.
11
+ * Commit and push until you are happy with your contribution.
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2013 Julik Tarkhanov. See LICENSE.txt for
18
+ further details.
19
+
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ require File.dirname(__FILE__) + "/lib/tickly"
6
+
7
+ begin
8
+ Bundler.setup(:default, :development)
9
+ rescue Bundler::BundlerError => e
10
+ $stderr.puts e.message
11
+ $stderr.puts "Run `bundle install` to install missing gems"
12
+ exit e.status_code
13
+ end
14
+ require 'rake'
15
+
16
+ require 'jeweler'
17
+ Jeweler::Tasks.new do |gem|
18
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
19
+ gem.version = Tickly::VERSION
20
+ gem.name = "tickly"
21
+ gem.homepage = "http://github.com/julik/tickly"
22
+ gem.license = "MIT"
23
+ gem.summary = %Q{Assists in parsing Nuke scripts in TCL}
24
+ gem.description = %Q{Parses the subset of the TCL grammar needed for Nuke scripts}
25
+ gem.email = "me@julik.nl"
26
+ gem.authors = ["Julik Tarkhanov"]
27
+ # dependencies defined in Gemfile
28
+ end
29
+ Jeweler::RubygemsDotOrgTasks.new
30
+
31
+ require 'rake/testtask'
32
+ Rake::TestTask.new(:test) do |test|
33
+ test.libs << 'lib' << 'test'
34
+ test.pattern = 'test/**/test_*.rb'
35
+ test.verbose = true
36
+ end
37
+
38
+ task :default => :test
39
+
40
+ require 'rdoc/task'
41
+ Rake::RDocTask.new do |rdoc|
42
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
43
+
44
+ rdoc.rdoc_dir = 'rdoc'
45
+ rdoc.title = "tickly #{version}"
46
+ rdoc.rdoc_files.include('README*')
47
+ rdoc.rdoc_files.include('lib/**/*.rb')
48
+ end
data/lib/tickly.rb ADDED
@@ -0,0 +1,91 @@
1
+ require File.dirname(__FILE__) + "/tickly/parser"
2
+ require File.dirname(__FILE__) + "/tickly/evaluator"
3
+ require 'forwardable'
4
+
5
+ module Tickly
6
+ VERSION = '0.0.1'
7
+
8
+ # Represents a TCL expression with it's arguments (in curly or square braces)
9
+ class Expr
10
+ extend Forwardable
11
+
12
+ def_delegators :@e, :push, :<<, :any?, :reject!, :map!, :[], :delete_at, :include?, :each, :empty?, :join, :length
13
+
14
+ def initialize(elements = [])
15
+ @e = elements
16
+ end
17
+
18
+ def map(&blk)
19
+ self.class.new(@e.map(&blk))
20
+ end
21
+
22
+ def to_a
23
+ @e
24
+ end
25
+
26
+ def ==(another)
27
+ another.to_a == to_a
28
+ end
29
+
30
+ def inspect
31
+ @e.map{|e| e.inspect }.join(', ')
32
+ end
33
+
34
+ end
35
+
36
+ # Represents an expression between curly braces (within which no text substitution will be done)
37
+ # like { 1 2 3 }
38
+ class LiteralExpr < Expr
39
+ def inspect
40
+ "le(%s)" % super
41
+ end
42
+ end
43
+
44
+ # Represents an expression between square brackets (where text substitution will be done)
45
+ # like [1 2 3]
46
+ class StringExpr < Expr
47
+ def inspect
48
+ "se(%s)" % super
49
+ end
50
+ end
51
+
52
+ # Provides the methods for quickly emitting the LiteralExpr and StringExpr objects
53
+ module Emitter
54
+ def le(*elems)
55
+ LiteralExpr.new(elems)
56
+ end
57
+
58
+ def se(*elems)
59
+ LiteralExpr.new(elems)
60
+ end
61
+ end
62
+
63
+
64
+ def self.to_tcl(e)
65
+ if e.is_a?(Tickly::LiteralExpr)
66
+ '{%s}' % e.map{|e| to_tcl(e)}.join(' ')
67
+ elsif e.is_a?(Tickly::StringExpr)
68
+ '[%s]' % e.map{|e| to_tcl(e)}.join(' ')
69
+ elsif e.is_a?(String) && (e.include?('"') || e.include?("'"))
70
+ e.inspect
71
+ else
72
+ e.to_s
73
+ end
74
+ end
75
+
76
+ def self.split_array(arr, separator = nil)
77
+ return arr unless arr.include?(separator)
78
+
79
+ subarrays = arr.class.new
80
+ subarrays.push(arr.class.new)
81
+
82
+ arr.each do | element |
83
+ if element == separator
84
+ subarrays.push(arr.class.new)
85
+ else
86
+ subarrays[-1].push(element)
87
+ end
88
+ end
89
+ return subarrays
90
+ end
91
+ end
@@ -0,0 +1,47 @@
1
+ module Tickly
2
+ # Evaluates a passed TCL expression without expanding it's inner arguments.
3
+ # The TCL should look like Nuke's node commands (i.e. NodeClass { foo bar; baz bad; } and so on)
4
+ # You have to add the Classes that you want to instantiate for nodes using add_node_handler_class
5
+ # and the evaluator will instantiate the classes it finds in the passed expression and pass the
6
+ # node options (actually TCL commands) to the constructor, as a Ruby Hash with string keys.
7
+ class Evaluator
8
+ def initialize
9
+ @node_handlers = []
10
+ end
11
+
12
+ def add_node_handler_class(handler_class)
13
+ @node_handlers << handler_class
14
+ end
15
+
16
+ def evaluate(expr)
17
+ if multiple_atoms?(expr) && has_subcommand?(expr) && has_handler?(expr)
18
+ handler_class = @node_handlers.find{|e| unconst_name(e) == expr[0]}
19
+ handler_arguments = expr[1]
20
+ hash_of_args = {}
21
+ expr[1].map do | e |
22
+ # The name of the command is the first element, always
23
+ hash_of_args[e[0]] = e[1]
24
+ end
25
+
26
+ # Instantiate the handler with the options
27
+ handler_class.new(hash_of_args)
28
+ end
29
+ end
30
+
31
+ def multiple_atoms?(expr)
32
+ expr.length > 1
33
+ end
34
+
35
+ def has_handler?(expr)
36
+ @node_handlers.map{|handler_class| unconst_name(handler_class) }.include?(expr[0])
37
+ end
38
+
39
+ def unconst_name(some_module)
40
+ some_module.to_s.split('::').pop
41
+ end
42
+
43
+ def has_subcommand?(expr)
44
+ expr[1].is_a?(LiteralExpr)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,123 @@
1
+ require 'stringio'
2
+
3
+ module Tickly
4
+
5
+ class Parser
6
+
7
+ # Parses a piece of TCL and returns it converted into internal expression
8
+ # structures (nested StringExpr or LiteralExp objects).
9
+ def parse(io_or_str)
10
+ io = io_or_str.respond_to?(:readchar) ? io_or_str : StringIO.new(io_or_str)
11
+ sub_parse(io)
12
+ end
13
+
14
+ private
15
+
16
+ # Parse from a passed IO object either until an unescaped stop_char is reached
17
+ # or until the IO is exhausted. The last argument is the class used to
18
+ # compose the subexpression being parsed. The subparser is reentrant and not
19
+ # destructive for the object containing it.
20
+ def sub_parse(io, stop_char = nil, expr_class = LiteralExpr)
21
+ # A standard stack is an expression that does not evaluate to a string
22
+ stack = expr_class.new
23
+ buf = ''
24
+ until io.eof?
25
+ char = io.readchar
26
+
27
+ if buf[-1] != ESC
28
+ if char == stop_char # Bail out of a subexpr
29
+ stack << buf if (buf.length > 0)
30
+ return cleanup(stack)
31
+ elsif char == " " || char == "\n" # Space
32
+ if buf.length > 0
33
+ stack << buf
34
+ buf = ''
35
+ end
36
+ if char == "\n" # Introduce a stack separator! This is a new line
37
+ stack << nil
38
+ end
39
+ elsif char == '[' # Opens a new string expression
40
+ stack << buf if (buf.length > 0)
41
+ stack << sub_parse(io, ']', StringExpr)
42
+ elsif char == '{' # Opens a new literal expression
43
+ stack << buf if (buf.length > 0)
44
+ stack << sub_parse(io, '}', LiteralExpr)
45
+ elsif char == '"'
46
+ stack << buf if (buf.length > 0)
47
+ stack << parse_str(io, '"')
48
+ elsif char == "'"
49
+ stack << buf if (buf.length > 0)
50
+ stack << parse_str(io, "'")
51
+ else
52
+ buf << char
53
+ end
54
+ else
55
+ buf << char
56
+ end
57
+ end
58
+
59
+ # Ramass any remaining buffer contents
60
+ stack << buf if (buf.length > 0)
61
+
62
+ cleanup(stack)
63
+ end
64
+
65
+ private
66
+
67
+ ESC = 92.chr # Backslash (\)
68
+
69
+ def parse_str(io, stop_char)
70
+ buf = ''
71
+ until io.eof?
72
+ c = io.readchar
73
+ if c == stop_char && buf[-1] != ESC
74
+ return buf
75
+ elsif buf[-1] == ESC # Eat out the escape char
76
+ buf = buf[0..-2] # Trim the escape character at the end of the buffer
77
+ buf << c
78
+ else
79
+ buf << c
80
+ end
81
+ end
82
+
83
+ return buf
84
+ end
85
+
86
+ def expand_one_elements!(expr)
87
+ expr.map! do | element |
88
+ if expr?(element) && element.length == 1 && element[0].class == element.class
89
+ element[0]
90
+ else
91
+ element
92
+ end
93
+ end
94
+ end
95
+
96
+ def remove_empty_elements!(expr)
97
+ expr.reject! {|e| expr?(e) && e.empty? }
98
+ end
99
+
100
+ # Tells whether a passed object is a StringExpr or LiteralExpr
101
+ def expr?(something)
102
+ [StringExpr, LiteralExpr].include?(something.class)
103
+ end
104
+
105
+ # Cleans up a subexpression stack. Currently it only removes nil objects
106
+ # in-between items (which act as line separators)
107
+ def cleanup(expr)
108
+ # Expand one-element expressions of the same class
109
+ #expand_one_elements!(expr)
110
+
111
+ # Remove empty subexprs
112
+ #remove_empty_elements!(expr)
113
+
114
+ # Squeeze out the leading and trailing nils
115
+ expr.delete_at(0) while (expr.any? && expr[0].nil?)
116
+ expr.delete_at(-1) while (expr.any? && expr[-1].nil?)
117
+
118
+ # Convert line breaks into subexpressions
119
+ Tickly.split_array(expr)
120
+ end
121
+
122
+ end
123
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'tickly'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,116 @@
1
+ version 7.0 v2
2
+ define_window_layout_xml {<?xml version="1.0" encoding="UTF-8"?>
3
+ <layout version="1.0">
4
+ <window x="0" y="25" w="1920" h="1150" screen="0">
5
+ <splitter orientation="1">
6
+ <split size="1348"/>
7
+ <splitter orientation="2">
8
+ <split size="1099"/>
9
+ <dock id="" activePageId="Viewer.1">
10
+ <page id="Viewer.1"/>
11
+ <page id="Curve Editor.1"/>
12
+ </dock>
13
+ </splitter>
14
+ <split size="560"/>
15
+ <splitter orientation="2">
16
+ <split size="112"/>
17
+ <dock id="" activePageId="Progress.1">
18
+ <page id="Progress.1"/>
19
+ </dock>
20
+ <split size="983"/>
21
+ <dock id="" activePageId="Properties.1">
22
+ <page id="Properties.1"/>
23
+ </dock>
24
+ </splitter>
25
+ </splitter>
26
+ </window>
27
+ <window x="2034" y="25" w="1294" h="1084" screen="1">
28
+ <splitter orientation="1">
29
+ <split size="40"/>
30
+ <dock id="" hideTitles="1" activePageId="Toolbar.1">
31
+ <page id="Toolbar.1"/>
32
+ </dock>
33
+ <split size="1242"/>
34
+ <dock id="" activePageId="DAG.1">
35
+ <page id="DAG.1"/>
36
+ </dock>
37
+ </splitter>
38
+ </window>
39
+ </layout>
40
+ }
41
+ Root {
42
+ inputs 0
43
+ name /mnt/flame-io/Julik/nuke7_tracker_2tracks.nk
44
+ frame 14
45
+ last_frame 56
46
+ lock_range true
47
+ format "2048 1556 0 0 2048 1556 1 2K_Super_35(full-ap)"
48
+ proxy_type scale
49
+ proxy_format "1024 778 0 0 1024 778 1 1K_Super_35(full-ap)"
50
+ }
51
+ Read {
52
+ inputs 0
53
+ file /mnt/flame-io/Julik/BUIENRADAR/e010.##.jpg
54
+ format "1280 720 0 0 1280 720 1 "
55
+ last 56
56
+ origlast 56
57
+ origset true
58
+ name Read1
59
+ xpos -184
60
+ ypos -218
61
+ }
62
+ Tracker4 {
63
+ tracks { { 1 31 2 }
64
+ { { 5 1 20 enable e }
65
+ { 3 1 75 name name }
66
+ { 2 1 58 track_x track_x }
67
+ { 2 1 58 track_y track_y }
68
+ { 2 1 63 offset_x offset_x }
69
+ { 2 1 63 offset_y offset_y }
70
+ { 4 1 27 T T }
71
+ { 4 1 27 R R }
72
+ { 4 1 27 S S }
73
+ { 2 0 45 error error }
74
+ { 1 1 0 error_min error_min }
75
+ { 1 1 0 error_max error_max }
76
+ { 1 1 0 pattern_x pattern_x }
77
+ { 1 1 0 pattern_y pattern_y }
78
+ { 1 1 0 pattern_r pattern_r }
79
+ { 1 1 0 pattern_t pattern_t }
80
+ { 1 1 0 search_x search_x }
81
+ { 1 1 0 search_y search_y }
82
+ { 1 1 0 search_r search_r }
83
+ { 1 1 0 search_t search_t }
84
+ { 2 1 0 key_track key_track }
85
+ { 2 1 0 key_search_x key_search_x }
86
+ { 2 1 0 key_search_y key_search_y }
87
+ { 2 1 0 key_search_r key_search_r }
88
+ { 2 1 0 key_search_t key_search_t }
89
+ { 2 1 0 key_track_x key_track_x }
90
+ { 2 1 0 key_track_y key_track_y }
91
+ { 2 1 0 key_track_r key_track_r }
92
+ { 2 1 0 key_track_t key_track_t }
93
+ { 2 1 0 key_centre_offset_x key_centre_offset_x }
94
+ { 2 1 0 key_centre_offset_y key_centre_offset_y }
95
+ }
96
+ {
97
+ { {curve K x1 1} "track 1" {curve x1 322 328.270813 328.0776978 326.0780029 326.5220032 324.8073425 358.2276306 392.5158691 392.4506226 401.6607666 401.6607666 x18 249 x19 242.1311951 235.2490845 229.473938 224.0309448 221.7980194 220.8138733 220.3097687 215.9066162 216.5221558 218.1231995 214.0824127 207.1420898 204.0522003 210.0070496 217.8195801 214.3830719 207.4842224 204.8947906 198.1132812 193.6152954 184.8657379 171.0793915 162.1078033 155.2776642 148.0143738 141.5550842 135.0557556 127.2473297 117.5063477 111.3422699 110.9694901 113.7087555 118.9628067 127.8691406 135.8892517 139.4182281 144.1413269 155.4910736} {curve x1 448 445.8141174 446.9832458 448.1704102 441.0701904 437.9360657 556.8189697 678.854187 681.770813 717.4888306 717.4888306 x18 409 x19 390.3128357 370.7902832 351.7124939 328.2048035 297.6690063 268.4028015 247.1441345 233.0551147 218.6214294 202.251358 188.6234741 183.5131378 179.0403137 173.3173218 168.2889252 165.387619 161.8663025 159.7026215 159.069519 159.5846252 160.325119 159.0343628 157.5151825 152.0431976 145.3804932 139.6097717 135.7308655 134.8005676 135.1534119 133.8771973 136.3284607 142.3200989 146.5865173 147.2932434 142.7356567 139.2093506 138.0481262 136.0488892} {curve K x1 0} {curve K x1 0} 1 0 0 {curve x1 0 9.74636543e-06 1.760418285e-05 2.161071201e-05 2.798796773e-05 3.497911246e-05 0.0003542814737 0.0007139164942 0.0005931421783 0.0005755083276 1 x18 0 x19 1.87521598e-05 0 8.180780791e-06 4.05472592e-05 6.728636954e-05 4.514258642e-05 3.507300538e-05 5.679712691e-05 8.744039287e-05 8.901520371e-05 0.00014483844 0.0001974749447 0.0002397972595 0.0002381663772 0.0002376472995 0.000209526033 0.0002295304103 0.0002616869801 0.0002456147812 0.0002774110437 0.0002656264675 0.0003155143973 0.0003469124189 0.0003644985254 0.0003972521665 0.000450609575 0.0004647645682 0.0005169142432 0.0005581540428 0.0006074070912 0.0006259974691 0.0005996763002 0.0005906001679 0.0005627727423 0.0005342494754 0.0005779165788 0.0005351592621 0.000515979146} 0 0.000713916 -17 -21 17 21 -32.7509 -32.7903 32.7509 32.7903 {} {curve x1 88 x18 29 x20 185} {curve x1 264 x18 236 x20 317} {curve x1 555 x18 468 x20 284} {curve x1 631 x18 581 x20 424} {curve x1 291 x18 232 x20 218} {curve x1 416 x18 388 x20 350} {curve x1 352 x18 265 x20 251} {curve x1 479 x18 429 x20 391} {curve x1 30.5 x18 16.5 x20 16.74908447} {curve x1 31.5 x18 20.5 x20 20.2902832} }
98
+ { {curve K x5 1} "track 2" {curve x5 118 112.3128128 105.5753326 105.4177399 110.1507874 106.7580414 95.44088745 84.42394257 78.0067749 70.42266846 62.3243866 55.09116364 45.5963974 33.77041626 28} {curve x5 64 64.09799194 70.61010742 74.58335876 75.47390747 77.01951599 77.52868652 71.32130432 59.49882126 53.70751572 50.4881134 41.80975723 29.1135006 13.10626316 0} {curve K x5 0} {curve K x5 0} 0 0 0 {curve x5 0 0.0001074973855 0.0002505811277 0.0003459765825 0.0001592934367 7.501828633e-05 0.0001198931179 0.0001049462949 8.04409813e-05 7.558142753e-05 8.95837562e-05 0.0001356712125 0.000246491915 0.0003578705189 0} 0 0.000357871 -11 -13 11 13 -46 -41 46 41 {} {curve x5 61 x19 -29} {curve x5 10 x19 -54} {curve x5 174 x19 84} {curve x5 117 x19 53} {curve x5 107 x19 17} {curve x5 51 x19 -13} {curve x5 128 x19 38} {curve x5 76 x19 12} {curve x5 10.5 x19 10.5} {curve x5 12.5 x19 12.5} }
99
+ }
100
+ }
101
+
102
+ translate {{curve x1 0 6.270812988 6.077697754 4.07800293 4.522003174 2.807342529 36.22763062 70.51586914 70.45062256 79.6607666 79.6607666 x18 -73 x19 -79.86880493 -86.75091553 -92.52606201 -97.96905518 -100.2019806 -101.1861267 -101.6902313 -106.0933838 -105.4778442 -103.8768005 -107.9175873 -114.8579102 -117.9477997 -111.9929504 -104.1804199 -107.6169281 -114.5157776 -117.1052094 -123.8867188 -128.3847046 -137.1342621 -150.9206085 -159.8921967 -166.7223358 -173.9856262 -180.4449158 -186.9442444 -194.7526703 -204.4936523 -210.6577301 -211.0305176 -208.2912445 -203.0372009 -194.1308594 -186.1107483 -182.5817719 -177.8586731 -166.5089264} {curve x1 0 -2.185882568 -1.01675415 0.1704101562 -6.92980957 -10.06393433 108.8189697 230.854187 233.770813 269.4888306 269.4888306 x18 -39 x19 -57.68716431 -77.2097168 -96.2875061 -119.7951965 -150.3309937 -179.5971985 -200.8558655 -214.9448853 -229.3785706 -245.748642 -259.3765259 -264.4868774 -268.9596863 -274.6826782 -279.7110596 -282.6123657 -286.1336975 -288.2973633 -288.930481 -288.4153748 -287.6748657 -288.9656372 -290.4848022 -295.9567871 -302.6195068 -308.3902283 -312.2691345 -313.1994324 -312.8465881 -314.1228027 -311.6715393 -305.6799011 -301.4134827 -300.7067566 -305.2643433 -308.7906494 -309.9518738 -311.9511108}}
103
+ center {{curve x1 322 322 322 322 322 322 322 322 322 322 322 x18 322 x19 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322 322} {curve x1 448 448 448 448 448 448 448 448 448 448 448 x18 448 x19 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448 448}}
104
+ selected_tracks 0
105
+ name Tracker1
106
+ selected true
107
+ xpos -237
108
+ ypos -62
109
+ }
110
+ Viewer {
111
+ frame 14
112
+ input_process false
113
+ name Viewer1
114
+ xpos -40
115
+ ypos -10
116
+ }