├── Gemfile ├── lib ├── tickly │ ├── version.rb │ ├── node_processor.rb │ ├── curve.rb │ ├── evaluator.rb │ └── parser.rb └── tickly.rb ├── test ├── test-data │ ├── one_node_with_one_param.txt │ ├── nuke8_copypaste_without_linefeed_at_end.nk │ ├── nukenode.txt │ ├── nuke_group.txt │ ├── tracker_with_repeating_gaps.nk │ ├── one_tracker_with_break.nk │ ├── one_tracker_with_break_in_grp.nk │ ├── tracker_with_differing_gaps.nk │ ├── nuke7_tracker_2tracks.nk │ ├── three_nodes_and_roto.txt │ └── windows_linebreaks.nk ├── test_benchmark.rb ├── test_emitter.rb ├── helper.rb ├── test_node_processor.rb ├── test_curve.rb ├── test_evaluator.rb └── test_parser.rb ├── .travis.yml ├── .document ├── .gitignore ├── tickly.gemspec ├── LICENSE.txt ├── Rakefile └── README.md /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /lib/tickly/version.rb: -------------------------------------------------------------------------------- 1 | module Tickly 2 | VERSION = "2.1.7" 3 | end 4 | -------------------------------------------------------------------------------- /test/test-data/one_node_with_one_param.txt: -------------------------------------------------------------------------------- 1 | SomeNode { 2 | foo bar 3 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | rvm: 2 | - 1.9.3 3 | - 2.5.0 4 | cache: bundler 5 | sudo: false -------------------------------------------------------------------------------- /.document: -------------------------------------------------------------------------------- 1 | lib/**/*.rb 2 | bin/* 3 | - 4 | features/**/*.feature 5 | LICENSE.txt 6 | -------------------------------------------------------------------------------- /test/test_benchmark.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | require 'benchmark' 3 | 4 | class TestParserEvaluator < Test::Unit::TestCase 5 | 6 | 7 | end 8 | -------------------------------------------------------------------------------- /test/test-data/nuke8_copypaste_without_linefeed_at_end.nk: -------------------------------------------------------------------------------- 1 | set cut_paste_input [stack 0] 2 | version 8.0 v1 3 | push $cut_paste_input 4 | Tracker4 { 5 | ypos -177 6 | } -------------------------------------------------------------------------------- /test/test-data/nukenode.txt: -------------------------------------------------------------------------------- 1 | set cut_paste_input [stack 0] 2 | version 6.3 v4 3 | push $cut_paste_input 4 | Blur { 5 | size {{curve x1 0 x20 1.7 x33 3.9}} 6 | name Blur1 7 | label "With \"Escapes\"" 8 | selected true 9 | xpos -353 10 | ypos -33 11 | } -------------------------------------------------------------------------------- /lib/tickly.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + "/tickly/parser" 2 | require File.dirname(__FILE__) + "/tickly/evaluator" 3 | require File.dirname(__FILE__) + "/tickly/curve" 4 | require File.dirname(__FILE__) + "/tickly/node_processor" 5 | require File.dirname(__FILE__) + "/tickly/version" 6 | 7 | module Tickly 8 | end 9 | -------------------------------------------------------------------------------- /test/test-data/nuke_group.txt: -------------------------------------------------------------------------------- 1 | set cut_paste_input [stack 0] 2 | version 6.3 v4 3 | Group { 4 | inputs 0 5 | name Group1 6 | selected true 7 | } 8 | CheckerBoard2 { 9 | inputs 0 10 | name CheckerBoard1 11 | } 12 | Blur { 13 | size 42.5 14 | name Blur1 15 | } 16 | Output { 17 | name Output1 18 | } 19 | end_group -------------------------------------------------------------------------------- /test/test_emitter.rb: -------------------------------------------------------------------------------- 1 | require "helper" 2 | 3 | class TestEmitter < Test::Unit::TestCase 4 | def test_emitter_e 5 | assert_equal ["2", "2"], e("2", "2") 6 | end 7 | 8 | def test_emitter_le 9 | assert_equal [:c, "2", "2"], le("2", "2") 10 | end 11 | 12 | def test_emitter_be 13 | assert_equal [:b, "2", "2"], se("2", "2") 14 | end 15 | 16 | def test_emitter_be_with_subexpression 17 | assert_equal [:b, [:c,"2", "2"]], se(le("2", "2")) 18 | end 19 | 20 | end -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 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 | 12 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 13 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 14 | require 'tickly' 15 | 16 | class Test::Unit::TestCase 17 | # Provides the methods for quickly emitting the expression arrays, 18 | # is used in tests 19 | module Emitter #:nodoc :all 20 | def le(*elems) 21 | e(*elems).unshift :c 22 | end 23 | 24 | def e(*elems) 25 | elems 26 | end 27 | 28 | def se(*elems) 29 | e(*elems).unshift :b 30 | end 31 | end 32 | 33 | include Emitter 34 | end 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | profiler_calls.html 3 | 4 | # rcov generated 5 | coverage 6 | coverage.data 7 | 8 | # rdoc generated 9 | rdoc 10 | 11 | # yard generated 12 | doc 13 | .yardoc 14 | 15 | # bundler 16 | .bundle 17 | 18 | # jeweler generated 19 | pkg 20 | 21 | # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore: 22 | # 23 | # * Create a file at ~/.gitignore 24 | # * Include files you want ignored 25 | # * Run: git config --global core.excludesfile ~/.gitignore 26 | # 27 | # After doing this, these files will be ignored in all your git projects, 28 | # saving you from having to 'pollute' every project you touch with them 29 | # 30 | # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line) 31 | # 32 | # For MacOS: 33 | # 34 | #.DS_Store 35 | 36 | # For TextMate 37 | #*.tmproj 38 | #tmtags 39 | 40 | # For emacs: 41 | #*~ 42 | #\#* 43 | #.\#* 44 | 45 | # For vim: 46 | #*.swp 47 | 48 | # For redcar: 49 | #.redcar 50 | 51 | # For rubinius: 52 | #*.rbc 53 | -------------------------------------------------------------------------------- /tickly.gemspec: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/lib/tickly/version' 2 | 3 | Gem::Specification.new do |s| 4 | s.name = "tickly" 5 | s.version = Tickly::VERSION 6 | 7 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 8 | s.require_paths = ["lib"] 9 | s.authors = ["Julik Tarkhanov"] 10 | s.date = Time.now.utc.strftime("%Y-%m-%d") 11 | s.description = "Parses the subset of the TCL grammar needed for Nuke scripts" 12 | s.email = "me@julik.nl" 13 | s.extra_rdoc_files = [ 14 | "LICENSE.txt", 15 | "README.md" 16 | ] 17 | s.files = `git ls-files -z`.split("\x0").reject do |f| 18 | f.start_with? "test/test-data/" 19 | end 20 | s.homepage = "http://github.com/julik/tickly" 21 | s.licenses = ["MIT"] 22 | s.rubygems_version = "2.2.2" 23 | s.summary = "Assists in parsing Nuke scripts in TCL" 24 | 25 | s.specification_version = 4 26 | s.add_development_dependency("rake", "~> 10") 27 | s.add_development_dependency("rdoc", "~> 3") 28 | s.add_development_dependency("ruby-prof") 29 | s.add_development_dependency("test-unit") 30 | end 31 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 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. 21 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require 'rake/testtask' 3 | 4 | require 'rake/testtask' 5 | Rake::TestTask.new(:test) do |test| 6 | test.libs << 'lib' << 'test' 7 | test.pattern = 'test/**/test_*.rb' 8 | test.verbose = true 9 | end 10 | 11 | require 'rdoc/task' 12 | Rake::RDocTask.new do |rdoc| 13 | version = File.exist?('VERSION') ? File.read('VERSION') : "" 14 | 15 | rdoc.rdoc_dir = 'rdoc' 16 | rdoc.title = "tickly #{version}" 17 | rdoc.rdoc_files.include('README*') 18 | rdoc.rdoc_files.include('lib/**/*.rb') 19 | end 20 | 21 | desc "Benchmarks the parser" 22 | task :bench do 23 | require File.dirname(__FILE__) + "/lib/tickly" 24 | require 'benchmark' 25 | 26 | class Tracker3 27 | def initialize(n); end 28 | end 29 | 30 | pe = Tickly::NodeProcessor.new 31 | pe.add_node_handler_class(Tracker3) 32 | 33 | HUGE_SCRIPT = File.open(File.dirname(__FILE__) + "/test/test-data/huge_nuke_tcl.tcl", "rb") 34 | Benchmark.bm do | runner | 35 | runner.report("Parsing a huge Nuke script:") do 36 | counter = 0 37 | pe.parse(HUGE_SCRIPT) { counter += 1 } 38 | HUGE_SCRIPT.rewind 39 | end 40 | end 41 | end 42 | 43 | task :default => [:test, :bench] 44 | -------------------------------------------------------------------------------- /test/test_node_processor.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class TestParserEvaluator < Test::Unit::TestCase 4 | 5 | HUGE_SCRIPT = File.open(File.dirname(__FILE__) + "/test-data/huge_nuke_tcl.tcl") 6 | NUKE7_SCRIPT = File.open(File.dirname(__FILE__) + "/test-data/nuke7_tracker_2tracks.nk") 7 | MISSING_LF = File.open(File.dirname(__FILE__) + 8 | "/test-data/nuke8_copypaste_without_linefeed_at_end.nk") 9 | 10 | class Tracker4 11 | attr_reader :knobs 12 | def initialize(knobs) 13 | @knobs = knobs 14 | end 15 | end 16 | 17 | class NodeCaptured < RuntimeError; end 18 | 19 | def test_processes_nodes 20 | pe = Tickly::NodeProcessor.new 21 | pe.add_node_handler_class(Tracker4) 22 | 23 | assert_raise(NodeCaptured) do 24 | pe.parse(NUKE7_SCRIPT) do | node | 25 | assert_kind_of Tracker4, node 26 | assert_equal "Tracker1", node.knobs["name"] 27 | 28 | raise NodeCaptured 29 | end 30 | end 31 | end 32 | 33 | def test_processes_without_trailing_LF 34 | pe = Tickly::NodeProcessor.new 35 | pe.add_node_handler_class(Tracker4) 36 | 37 | assert_raise(NodeCaptured) do 38 | pe.parse(MISSING_LF) do | node | 39 | 40 | assert_kind_of Tracker4, node 41 | ref_knobs = {"ypos"=>"-177"} 42 | assert_equal ref_knobs, node.knobs 43 | 44 | raise NodeCaptured 45 | end 46 | end 47 | end 48 | 49 | def test_raises_without_a_block 50 | pe = Tickly::NodeProcessor.new 51 | assert_raise(LocalJumpError) { pe.parse(NUKE7_SCRIPT) } 52 | end 53 | 54 | 55 | end 56 | -------------------------------------------------------------------------------- /test/test_curve.rb: -------------------------------------------------------------------------------- 1 | require "helper" 2 | 3 | class TestCurve < Test::Unit::TestCase 4 | def test_parsing_nuke_curve 5 | curve = [:c] + %w( curve x742 888 890.2463989 891.6602783 6 | 893.5056763 895.6155396 s95 897.2791748 899.1762695 7 | x754 912.0731812 x755 913.7190552 916.0959473 918.1025391 920.0751953 922.1898804 ) 8 | 9 | p = Tickly::Curve.new(curve) 10 | result = p.to_a 11 | 12 | assert_kind_of Array, result 13 | assert_equal 13, result.length 14 | assert_equal 742, result[0][0] 15 | assert_equal 754, result[7][0] 16 | end 17 | 18 | def test_curve_plus 19 | curve = [:c] + %w( curve+5 x1 987 x32 989.5999756 ) 20 | 21 | p = Tickly::Curve.new(curve) 22 | result = p.to_a 23 | assert_kind_of Array, result 24 | assert_equal [[1, 992.0], [32, 994.5999756]], result 25 | end 26 | 27 | def test_invalid_curves 28 | assert_raise Tickly::Curve::InvalidCurveError do 29 | Tickly::Curve.new([]) 30 | end 31 | 32 | assert_raise Tickly::Curve::InvalidCurveError do 33 | Tickly::Curve.new([:c]) 34 | end 35 | 36 | assert_raise Tickly::Curve::InvalidCurveError do 37 | Tickly::Curve.new([:c, "curve"]) 38 | end 39 | end 40 | 41 | def test_curve_with_trailing_space_at_command_end 42 | atoms = [:c, "curve ", "x374", "1008.35", "899.289", "809.798", 43 | "742.572", "825.061", "1013.43", "1238.31", "1490.91", 44 | "1698.4", "1848.96", "1889.24", "1961.12", "2024.13", 45 | "2090.3", "2114.74", "2164.57", "2227.17", "2309.3"] 46 | 47 | c = Tickly::Curve.new(atoms) 48 | assert_kind_of Tickly::Curve, c 49 | end 50 | end -------------------------------------------------------------------------------- /test/test_evaluator.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class TestEvaluator < Test::Unit::TestCase 4 | 5 | def test_does_nothing_without_handlers 6 | stack = le("Tracker4", le(le("enabled", "true"))) 7 | e = Tickly::Evaluator.new 8 | e.evaluate(stack) 9 | end 10 | 11 | class ShouldNotBeInstantiated 12 | def initialize 13 | raise "You failed" 14 | end 15 | end 16 | 17 | def test_does_not_send_anything_when_the_expression_passed_does_not_match_pattern 18 | stack = e("ShouldNotBeInstantiated") 19 | e = Tickly::Evaluator.new 20 | e.add_node_handler_class(ShouldNotBeInstantiated) 21 | assert_nothing_raised { e.evaluate(stack) } 22 | end 23 | 24 | class SomeNode 25 | attr_reader :options 26 | def initialize(options_hash) 27 | @options = options_hash 28 | end 29 | end 30 | 31 | def test_instantiates_handler_class 32 | stack = e("SomeNode", le(e("foo", "bar"), e("baz", "bad"))) 33 | e = Tickly::Evaluator.new 34 | e.add_node_handler_class(SomeNode) 35 | node = e.evaluate(stack) 36 | 37 | assert_kind_of SomeNode, node 38 | ref_o = {"foo" => "bar", "baz" => "bad"} 39 | assert_equal ref_o, node.options 40 | end 41 | 42 | class TargetError < RuntimeError 43 | end 44 | 45 | def test_yields_the_handler_instance 46 | stack = e("SomeNode", le(e("foo", "bar"), e("baz", "bad"))) 47 | e = Tickly::Evaluator.new 48 | e.add_node_handler_class(SomeNode) 49 | ref_o = {"foo" => "bar", "baz" => "bad"} 50 | 51 | assert_raise(TargetError) do 52 | e.evaluate(stack) do | some_node | 53 | assert_kind_of SomeNode, some_node 54 | raise TargetError 55 | end 56 | end 57 | end 58 | 59 | def test_will_capture 60 | e = Tickly::Evaluator.new 61 | e.add_node_handler_class(SomeNode) 62 | 63 | valid = e("SomeNode", le(e("foo", "bar"), e("baz", "bad"))) 64 | assert e.will_capture?(valid) 65 | assert !e.will_capture?([]) 66 | assert !e.will_capture?(e("SomeNode")) 67 | end 68 | 69 | end 70 | -------------------------------------------------------------------------------- /test/test-data/tracker_with_repeating_gaps.nk: -------------------------------------------------------------------------------- 1 | #! /Applications/Nuke5.2v3/Nuke5.2v3.app/Contents/MacOS/Nuke5.2v3 -nx 2 | version 5.2300 3 | define_window_layout_xml { 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | } 35 | Root { 36 | inputs 0 37 | name /Code/apps/tracksperanto/test/import/samples/nuke/tracker_with_repeating_gaps.nk 38 | frame 12 39 | format "2048 1556 0 0 2048 1556 1 2K_Super_35(full-ap)" 40 | proxy_type scale 41 | proxy_format "1024 778 0 0 1024 778 1 1K_Super_35(full-ap)" 42 | } 43 | Viewer { 44 | inputs 0 45 | frame 12 46 | name Viewer1 47 | xpos -40 48 | ypos -10 49 | } 50 | Constant { 51 | inputs 0 52 | channels rgb 53 | name Constant1 54 | xpos -265 55 | ypos -191 56 | } 57 | Tracker3 { 58 | track1 {{curve x1 196 326 440 x6 554 658 752} {curve x1 574 556 534 x6 512 492 476}} 59 | offset1 {0 0} 60 | pattern1 {-32 -32 32 32} 61 | search1 {-22 -22 22 22} 62 | track2 {1126 676} 63 | offset2 {0 0} 64 | pattern2 {-32 -32 32 32} 65 | search2 {-22 -22 22 22} 66 | track3 {1126 880} 67 | offset3 {0 0} 68 | pattern3 {-32 -32 32 32} 69 | search3 {-22 -22 22 22} 70 | track4 {922 880} 71 | offset4 {0 0} 72 | pattern4 {-32 -32 32 32} 73 | search4 {-22 -22 22 22} 74 | translate {{curve x1 0 130 244 x6 358 462 556} {curve x1 0 -18 -40 x6 -62 -82 -98}} 75 | center {{curve x1 196 196 196 x6 196 196 196} {curve x1 574 574 574 x6 574 574 574}} 76 | name Tracker1 77 | xpos -265 78 | ypos -119 79 | } 80 | -------------------------------------------------------------------------------- /lib/tickly/node_processor.rb: -------------------------------------------------------------------------------- 1 | module Tickly 2 | # A combination of a Parser and an Evaluator 3 | # Evaluates a passed Nuke script without expanding it's inner arguments. 4 | # The TCL should look like Nuke's node commands: 5 | # 6 | # NodeClass { 7 | # foo bar 8 | # baz bad 9 | # } 10 | # 11 | # You have to add the Classes that you want to instantiate for nodes using add_node_handler_class 12 | # and every time the parser encounters that node the node will be instantiated 13 | # and the node options (actually TCL commands) will be passed to the constructor, 14 | # as a Ruby Hash with string keys. 15 | # Every value of the knobs hash will be the AST as returned by the Parser. 16 | # 17 | # class Blur 18 | # def initialize(knobs_hash) 19 | # puts knobs_hash.inspect 20 | # end 21 | # end 22 | # 23 | # e = Tickly::NodeProcessor.new 24 | # e.add_node_handler_class Blur 25 | # e.parse(File.open("/path/to/script.nk")) do | blur_node | 26 | # # do whatever you want to the node instance 27 | # end 28 | class NodeProcessor 29 | def initialize 30 | @evaluator = Tickly::Evaluator.new 31 | @parser = Ratchet.new 32 | @parser.expr_callback = method(:filter_expression) 33 | end 34 | 35 | # Add a Class object that can instantiate node handlers. The last part of the class name 36 | # has to match the name of the Nuke node that you want to capture. 37 | # For example, to capture Tracker3 nodes a name like this will do: 38 | # Whatever::YourModule::Better::Tracker3 39 | def add_node_handler_class(class_object) 40 | @evaluator.add_node_handler_class(class_object) 41 | end 42 | 43 | # Parses from the passed IO or string and yields every node 44 | # that has been instantiated 45 | def parse(io_or_str, &nuke_node_callback) 46 | raise LocalJumpError, "NodeProcesssor#parse totally requires a block" unless block_given? 47 | @node_handler = nuke_node_callback 48 | @parser.parse(io_or_str) 49 | end 50 | 51 | private 52 | 53 | class Ratchet < Parser #:nodoc: :all 54 | attr_accessor :expr_callback 55 | def compact_subexpr(expr, at_depth) 56 | expr_callback.call(expr, at_depth) 57 | end 58 | end 59 | 60 | def filter_expression(expression, at_depth) 61 | # Leave all expressions which are deeper than 1 62 | # intact 63 | return expression if at_depth > 1 64 | 65 | # Skip all nodes which are not interesting for 66 | # the evaluator to do 67 | unless @evaluator.will_capture?(expression) 68 | return nil # Do not even keep it in memory 69 | end 70 | 71 | # And immediately evaluate 72 | # TODO: also yield it! 73 | node_instance = @evaluator.evaluate(expression) 74 | @node_handler.call(node_instance) 75 | 76 | # Still return nil 77 | return nil 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /lib/tickly/curve.rb: -------------------------------------------------------------------------------- 1 | module Tickly 2 | # A shorthand class for Nuke's animation curves. 3 | # Will convert a passed Curve expression into a set of values, 4 | # where all the values are baked per integer frames on the whole 5 | # stretch of time where curve is defined 6 | class Curve 7 | 8 | class InvalidCurveError < RuntimeError; end 9 | 10 | include Enumerable 11 | 12 | SECTION_START = /^x(\d+)$/ 13 | KEYFRAME = /^([-\d\.]+)$/ 14 | 15 | # The constructor accepts a Curve expression as returned by the Parser 16 | # Normally it looks like this 17 | # [:c, "curve", "x1", "123", "456", ...] 18 | def initialize(curve_expression) 19 | raise InvalidCurveError, "A curve expression should have :c as it's first symbol" unless curve_expression[0] == :c 20 | raise InvalidCurveError, "Curve expression contained no values" unless curve_expression[2] 21 | 22 | # Nuke7 sometimes produces curves where the command is a string literal 23 | # within quotes, and it contains a trailing space 24 | cmd = curve_expression[1].to_s.strip 25 | raise InvalidCurveError, "Curve expression should start with a 'curve' command" unless cmd =~ /^curve/ 26 | 27 | # Compute the curve increment or decrement. It looks like a modifier: 28 | # "curve+5" means we have to add 5 to every value on the curve 29 | xformer = lambda { |v| v} # Identity 30 | if cmd =~ /^(curve)([+-])([\d\.]+)$/ 31 | operator = $2[0..1] # Ensure only one character gets through 32 | modifier = $3.to_f 33 | xformer = lambda{|v| v.send(operator, modifier) } 34 | end 35 | 36 | expand_curve(curve_expression, &xformer) 37 | end 38 | 39 | # Returns each defined keyframe as a pair of a frame number and a value 40 | def each(&blk) 41 | @tuples.each(&blk) 42 | end 43 | 44 | private 45 | 46 | def expand_curve(curve_expression, &post_lambda) 47 | # Replace the closing curly brace with a curly brace with space so that it gets caught by split 48 | atoms = curve_expression[2..-1] # remove the :c curly designator and the "curve" keyword 49 | 50 | @tuples = [] 51 | # Nuke saves curves very efficiently. x(keyframe_number) means that an 52 | # uninterrupted sequence of values will start, after which values follow. 53 | # When the curve is interrupted in some way a new x(keyframe_number) will 54 | # signify that we skip to that specified keyframe and the curve continues 55 | # from there, in gap size defined by the last fragment. That is, 56 | # x1 1 x3 2 3 4 will place 2, 3 and 4 at 2-frame increments. 57 | # Thanks to Michael Lester for explaining this. 58 | last_processed_keyframe = 1 59 | intraframe_gap_size = 1 60 | while atom = atoms.shift 61 | if atom =~ SECTION_START 62 | last_processed_keyframe = $1.to_i 63 | if @tuples.any? 64 | last_captured_frame = @tuples[-1][0] 65 | intraframe_gap_size = last_processed_keyframe - last_captured_frame 66 | end 67 | elsif atom =~ KEYFRAME 68 | @tuples << [last_processed_keyframe, yield($1.to_f)] 69 | last_processed_keyframe += intraframe_gap_size 70 | end 71 | end 72 | end 73 | end 74 | end -------------------------------------------------------------------------------- /lib/tickly/evaluator.rb: -------------------------------------------------------------------------------- 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: 4 | # 5 | # NodeClass { 6 | # foo bar 7 | # baz bad 8 | # } 9 | # 10 | # You have to add the Classes that you want to instantiate for nodes using add_node_handler_class 11 | # and the evaluator will instantiate the classes it finds in the passed expression and pass the 12 | # node options (actually TCL commands) to the constructor, as a Ruby Hash with string keys. 13 | # Every value of the knobs hash will be the AST as returned by the Parser. 14 | # You have to pass every expression returned by Tickly::Parser#parse separately. 15 | # 16 | # class Blur 17 | # def initialize(knobs_hash) 18 | # puts knobs_hash.inspect 19 | # end 20 | # end 21 | # 22 | # e = Tickly::Evaluator.new 23 | # e.add_node_handler_class Blur 24 | # p = Tickly::Parser.new 25 | # 26 | # expressions = p.parse(some_nuke_script) 27 | # expressions.each do | expr | 28 | # # If expr is a Nuke node constructor, a new Blur will be created and yielded 29 | # e.evaluate(expr) do | node_instance| 30 | # # do whatever you want to the node instance 31 | # end 32 | # end 33 | class Evaluator 34 | def initialize 35 | @node_handlers = {} 36 | end 37 | 38 | # Add a Class object that can instantiate node handlers. The last part of the class name 39 | # has to match the name of the Nuke node that you want to capture. 40 | # For example, to capture Tracker3 nodes a name like this will do: 41 | # Whatever::YourModule::Better::Tracker3 42 | def add_node_handler_class(handler_class) 43 | @node_handlers[class_name_without_modules(handler_class)] = handler_class 44 | end 45 | 46 | # Evaluates a single Nuke TCL command, and if it is a node constructor 47 | # and a class with a corresponding name has been added using add_node_handler_class 48 | # the class will be instantiated and yielded to the block. The instance will also be returned 49 | # at the end of the method. This method evaluates one expression at a time 50 | # (it's more of a pattern matcher really) 51 | def evaluate(expr) 52 | if will_capture?(expr) 53 | handler_class = @node_handlers[expr[0]] 54 | handler_arguments = expr[1] 55 | hash_of_args = {} 56 | # Use 1..-1 to skip the curly brace symbol 57 | expr[1][1..-1].map do | e | 58 | # The name of the command is the first element, always 59 | hash_of_args[e[0]] = e[1] 60 | end 61 | 62 | # Instantiate the handler with the options 63 | handler_instance = handler_class.new(hash_of_args) 64 | 65 | # Both return and yield it 66 | yield handler_instance if block_given? 67 | handler_instance 68 | end 69 | end 70 | 71 | # Tells whether this Evaluator will actually instantiate 72 | # anything from the passed expression 73 | def will_capture?(expr) 74 | multiple_atoms?(expr) && has_subcommand?(expr) && has_handler?(expr) 75 | end 76 | 77 | private 78 | 79 | def multiple_atoms?(expr) 80 | expr.length > 1 81 | end 82 | 83 | def has_handler?(expr) 84 | @node_handlers.has_key? expr[0] 85 | end 86 | 87 | def class_name_without_modules(some_module) 88 | some_module.to_s.split('::').pop 89 | end 90 | 91 | def has_subcommand?(expr) 92 | expr[1][0] == :c 93 | end 94 | end 95 | end -------------------------------------------------------------------------------- /test/test-data/one_tracker_with_break.nk: -------------------------------------------------------------------------------- 1 | #! /Applications/Nuke5.1v2/Nuke5.1v2.app/Contents/MacOS/Nuke5.1v2 -nx 2 | version 5.1200 3 | define_window_layout_xml { 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | } 34 | Root { 35 | inputs 0 36 | name /Code/apps/tracksperanto/test/samples/one_tracker_with_break.nk 37 | frame 763 38 | last_frame 1580 39 | lock_range true 40 | format "640 480 0 0 640 480 1 PC_Video" 41 | proxy_type scale 42 | proxy_format "640 480 0 0 640 480 1 PC_Video" 43 | } 44 | Read { 45 | inputs 0 46 | file /Users/julik/Desktop/Storm/E036/E036_L005.%04d.dpx 47 | format "2048 1176 0 0 2048 1176 1 " 48 | last 1580 49 | timecode 03:38:32:07 50 | edge_code "02 05 17 3388 6703 60" 51 | name Read1 52 | xpos -271 53 | ypos -90 54 | } 55 | Tracker3 { 56 | track1 {{curve i x742 888 890.2463989 891.6602783 893.5056763 895.6155396 s95 897.2791748 899.1762695 901.6463623 903.6976318 905.1331177 907.3597412 909.7727051 912.0731812 913.7190552 916.0959473 918.1025391 920.0751953 922.1898804 924.6269531 926.2683716 928.2131348 930.7736206} {curve i x742 406 405.3980103 404.867981 404.1585083 403.6867065 403.1013489 402.463623 401.933136 401.3148499 400.4875183 399.8830566 399.3424377 398.5214233 398.1871033 397.5262756 396.8475647 396.2599182 395.5371399 395.1407166 394.3450928 393.7192078 392.8123474}} 57 | enable2 true 58 | track2 {{curve x742 1105 1107.277466 1108.645996 1110.494629 1112.658691 1114.258179 1116.237061 1118.607788 1120.709717 1122.134155 1124.373779 1126.885132 1129.21936 1130.868164 1133.252686 1135.243896 1137.268188 1139.385376 1141.824097 1143.422974 1145.289551 1147.972412} {curve x742 524 523.3273926 522.723999 521.8934326 521.4053955 520.6884766 520.0819092 519.5550537 518.897644 518.0953369 517.442688 516.8226929 516.0817871 515.673584 515.0698853 514.3513184 513.664917 512.9036865 512.5012817 511.5973511 510.9969482 510.1079407}} 59 | translate {{curve i x742 0 2.246398926 3.66027832 5.50567627 7.615539551 9.279174805 11.17626953 13.6463623 15.69763184 17.13311768 19.35974121 21.77270508 24.07318115 25.71905518 28.09594727 30.10253906 32.07519531 34.18988037 36.62695312 38.26837158 40.21313477 42.77362061} {curve i x742 0 -0.6019897461 -1.132019043 -1.841491699 -2.313293457 -2.898651123 -3.536376953 -4.066864014 -4.685150146 -5.512481689 -6.116943359 -6.657562256 -7.47857666 -7.812896729 -8.473724365 -9.152435303 -9.740081787 -10.46286011 -10.85928345 -11.65490723 -12.28079224 -13.18765259}} 60 | center {{curve i x742 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888} {curve i x742 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406}} 61 | name Tracker1 62 | selected true 63 | xpos -271 64 | ypos -4 65 | } 66 | Viewer { 67 | frame 763 68 | name Viewer1 69 | xpos 10 70 | ypos -10 71 | } 72 | -------------------------------------------------------------------------------- /test/test-data/one_tracker_with_break_in_grp.nk: -------------------------------------------------------------------------------- 1 | #! /Applications/Nuke5.1v2/Nuke5.1v2.app/Contents/MacOS/Nuke5.1v2 -nx 2 | version 5.1200 3 | define_window_layout_xml { 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | } 35 | Root { 36 | inputs 0 37 | name /Code/apps/tracksperanto/test/samples/one_tracker_with_break_in_grp.nk 38 | frame 763 39 | last_frame 1580 40 | lock_range true 41 | format "640 480 0 0 640 480 1 PC_Video" 42 | proxy_type scale 43 | proxy_format "640 480 0 0 640 480 1 PC_Video" 44 | } 45 | Read { 46 | inputs 0 47 | file /Users/julik/Desktop/Storm/E036/E036_L005.%04d.dpx 48 | format "2048 1176 0 0 2048 1176 1 " 49 | last 1580 50 | timecode 03:38:32:07 51 | edge_code "02 05 17 3388 6703 60" 52 | name Read1 53 | xpos -271 54 | ypos -195 55 | } 56 | Group { 57 | name Group1 58 | selected true 59 | xpos -171 60 | ypos -93 61 | } 62 | Input { 63 | inputs 0 64 | name Input1 65 | xpos -227 66 | ypos -113 67 | } 68 | Tracker3 { 69 | track1 {{curve x742 888 890.2463989 891.6602783 893.5056763 895.6155396 s95 897.2791748 899.1762695 901.6463623 903.6976318 905.1331177 907.3597412 909.7727051 912.0731812 913.7190552 916.0959473 918.1025391 920.0751953 922.1898804 924.6269531 926.2683716 928.2131348 930.7736206} {curve x742 406 405.3980103 404.867981 404.1585083 403.6867065 403.1013489 402.463623 401.933136 401.3148499 400.4875183 399.8830566 399.3424377 398.5214233 398.1871033 397.5262756 396.8475647 396.2599182 395.5371399 395.1407166 394.3450928 393.7192078 392.8123474}} 70 | enable2 true 71 | track2 {{curve i x742 1105 1107.277466 1108.645996 1110.494629 1112.658691 1114.258179 1116.237061 1118.607788 1120.709717 1122.134155 1124.373779 1126.885132 1129.21936 1130.868164 1133.252686 1135.243896 1137.268188 1139.385376 1141.824097 1143.422974 1145.289551 1147.972412} {curve i x742 524 523.3273926 522.723999 521.8934326 521.4053955 520.6884766 520.0819092 519.5550537 518.897644 518.0953369 517.442688 516.8226929 516.0817871 515.673584 515.0698853 514.3513184 513.664917 512.9036865 512.5012817 511.5973511 510.9969482 510.1079407}} 72 | translate {{curve i x742 0 2.246398926 3.66027832 5.50567627 7.615539551 9.279174805 11.17626953 13.6463623 15.69763184 17.13311768 19.35974121 21.77270508 24.07318115 25.71905518 28.09594727 30.10253906 32.07519531 34.18988037 36.62695312 38.26837158 40.21313477 42.77362061} {curve i x742 0 -0.6019897461 -1.132019043 -1.841491699 -2.313293457 -2.898651123 -3.536376953 -4.066864014 -4.685150146 -5.512481689 -6.116943359 -6.657562256 -7.47857666 -7.812896729 -8.473724365 -9.152435303 -9.740081787 -10.46286011 -10.85928345 -11.65490723 -12.28079224 -13.18765259}} 73 | center {{curve i x742 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888} {curve i x742 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406 406}} 74 | name Tracker1 75 | xpos -227 76 | ypos -73 77 | } 78 | Output { 79 | name Output1 80 | xpos -227 81 | ypos 27 82 | } 83 | end_group 84 | push 0 85 | Viewer { 86 | inputs 2 87 | frame 763 88 | name Viewer1 89 | xpos -271 90 | ypos -6 91 | } 92 | -------------------------------------------------------------------------------- /test/test-data/tracker_with_differing_gaps.nk: -------------------------------------------------------------------------------- 1 | #! /Applications/Nuke5.2v3/Nuke5.2v3.app/Contents/MacOS/Nuke5.2v3 -nx 2 | version 5.2300 3 | define_window_layout_xml { 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | } 35 | Root { 36 | inputs 0 37 | name /Code/apps/tracksperanto/test/import/samples/nuke/tracker_with_differing_gaps.nk 38 | frame 110 39 | last_frame 340 40 | lock_range true 41 | format "2048 1556 0 0 2048 1556 1 2K_Super_35(full-ap)" 42 | proxy_type scale 43 | proxy_format "1024 778 0 0 1024 778 1 1K_Super_35(full-ap)" 44 | } 45 | Read { 46 | inputs 0 47 | file /Users/julik/Desktop/VFX_Projectery/Target/s31/imp/s31_toLin_DIST.%04d.jpg 48 | format "2048 3112 0 0 2048 3112 1 " 49 | last 340 50 | name Read1 51 | xpos -315 52 | ypos -176 53 | } 54 | Tracker3 { 55 | track1 {{curve x22 682 677.8408813 676.0637207 671.6843262 667.552002 666.1954956 661.3134155 658.52771 656.0322266 651.600769 649.4697266 646.7433472 642.364563 641.3949585 637.7145996 634.6853027 633.1293335 629.868103 626.6664429 625.713501 621.2104492 619.5162964 617.4292603 613.7619629 611.5888062 x61 555 x62 548.7984009 545.6205444 539.6292114 532.8546753 529.2728271 522.1868896 516.2642822 511.1176453 503.9720764 497.4740295 492.9031982 484.4396362 x100 318 x103 304 x105 291.7999878 x106 286.2000122 278.7999878 x109 269.2000122 x110 263.2000122} {curve x22 1156 1154.666138 1155.331299 1156.672974 1153.877563 1155.259521 1154.65918 1152.168335 1152.477051 1152.115234 1149.824341 1150.614502 1147.347046 1146.821167 1144.796631 1140.738037 1139.098267 1137.47876 1133.239502 1132.942261 1130.466919 1126.970337 1125.021606 1122.027222 1118.608521 x61 1085 x62 1081.507446 1079.341919 1078.684082 1073.892944 1073.416748 1071.084717 1066.524414 1066.126099 1063.667603 1058.772217 1059.141846 1054.595581 x100 960 x103 967.7999878 x105 970 x106 971.7999878 971.7999878 x109 979.2000122 x110 975.7999878}} 56 | offset1 {0 0} 57 | pattern1 {-14 -13 14 13} 58 | search1 {-12 -10 12 10} 59 | track2 {1126 1454} 60 | offset2 {0 0} 61 | pattern2 {-32 -32 32 32} 62 | search2 {-22 -22 22 22} 63 | track3 {1126 1658} 64 | offset3 {0 0} 65 | pattern3 {-32 -32 32 32} 66 | search3 {-22 -22 22 22} 67 | track4 {922 1658} 68 | offset4 {0 0} 69 | pattern4 {-32 -32 32 32} 70 | search4 {-22 -22 22 22} 71 | translate {{curve x22 0 -4.159118652 -5.936279297 -10.31567383 -14.44799805 -15.80450439 -20.68658447 -23.47229004 -25.96777344 -30.39923096 -32.53027344 -35.25665283 -39.63543701 -40.6050415 -44.28540039 -47.31469727 -48.8706665 -52.13189697 -55.33355713 -56.28649902 -60.78955078 -62.48370361 -64.57073975 -68.23803711 -70.41119385 x61 -127 x62 -133.2015991 -136.3794556 -142.3707886 -149.1453247 -152.7271729 -159.8131104 -165.7357178 -170.8823547 -178.0279236 -184.5259705 -189.0968018 -197.5603638 x100 -364 x103 -378 x105 -390.2000122 x106 -395.7999878 -403.2000122 x109 -412.7999878 x110 -418.7999878} {curve x22 0 -1.333862305 -0.6687011719 0.6729736328 -2.122436523 -0.7404785156 -1.340820312 -3.831665039 -3.522949219 -3.884765625 -6.17565918 -5.385498047 -8.652954102 -9.178833008 -11.20336914 -15.26196289 -16.9017334 -18.52124023 -22.76049805 -23.05773926 -25.53308105 -29.02966309 -30.97839355 -33.97277832 -37.39147949 x61 -71 x62 -74.49255371 -76.65808105 -77.31591797 -82.10705566 -82.58325195 -84.9152832 -89.47558594 -89.87390137 -92.33239746 -97.2277832 -96.8581543 -101.4044189 x100 -196 x103 -188.2000122 x105 -186 x106 -184.2000122 -184.2000122 x109 -176.7999878 x110 -180.2000122}} 72 | center {{curve x22 682 682 682 682 682 682 682 682 682 682 682 682 682 682 682 682 682 682 682 682 682 682 682 682 682 x61 682 x62 682 682 682 682 682 682 682 682 682 682 682 682 x100 682 x103 682 x105 682 x106 682 682 x109 682 x110 682} {curve x22 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 x61 1156 x62 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 1156 x100 1156 x103 1156 x105 1156 x106 1156 1156 x109 1156 x110 1156}} 73 | name Tracker1 74 | xpos -255 75 | ypos -82 76 | } 77 | Viewer { 78 | frame 110 79 | name Viewer1 80 | xpos -41 81 | ypos -9 82 | } 83 | -------------------------------------------------------------------------------- /test/test_parser.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/helper' 2 | 3 | class TestParser < Test::Unit::TestCase 4 | P = Tickly::Parser.new 5 | 6 | def test_parse_single_int_as_a_stack_with_string_token 7 | assert_equal e(e("2")), P.parse('2') 8 | end 9 | 10 | def test_parse_single_int_and_discard_whitespace 11 | p = P.parse(' 2 ') 12 | assert_equal e(e("2")), p 13 | end 14 | 15 | def test_parse_multiple_ints_and_strings_as_stack_of_expressions 16 | assert_equal e(e("2", "foo", "bar", "baz")), P.parse('2 foo bar baz') 17 | end 18 | 19 | def test_parse_and_expand_a_string_in_double_quotes 20 | p = P.parse('"This is a string literal with spaces"') 21 | assert_equal e(e("This is a string literal with spaces")), p 22 | 23 | p = P.parse('"This is a string literal \"escaped\" with spaces"') 24 | assert_equal e(e("This is a string literal \"escaped\" with spaces")), p 25 | end 26 | 27 | def test_parse_string_expression 28 | assert_equal e(e(se("1", "2", "3"))), P.parse("[1 2 3]") 29 | end 30 | 31 | def test_parse_multiple_string_expressions_in_one_expression 32 | p = P.parse("[1 2 3] [3 4 5 foo]") 33 | assert_equal e(e(se("1", "2", "3"), se("3", "4", "5", "foo"))), p 34 | end 35 | 36 | def test_parse_multiline_statements_as_literal_expressions 37 | p = P.parse("2\n2") 38 | assert_equal e(e("2"), e("2")), p 39 | end 40 | 41 | def test_parse_expr 42 | expr = '{4 + 5}' 43 | p = P.parse(expr) 44 | assert_equal e(e(le("4", "+", "5"))), p 45 | end 46 | 47 | def test_raises_on_unterminated_string 48 | expr = '"Literal with no closing quote' 49 | assert_raise(Tickly::Parser::Error) { P.parse(expr) } 50 | end 51 | 52 | def test_raises_on_unterminated_subexpressions 53 | expr = 'a {b' 54 | assert_raise(Tickly::Parser::Error) { P.parse(expr) } 55 | 56 | expr = 'a [b' 57 | assert_raise(Tickly::Parser::Error) { P.parse(expr) } 58 | end 59 | 60 | def test_curlies_after_expr 61 | expr = 'a{4 + 5}b' 62 | p = P.parse(expr) 63 | assert_equal [["a", [:c, "4", "+", "5"], "b"]], p 64 | end 65 | 66 | def test_parsing_a_nuke_node 67 | f = File.open(File.dirname(__FILE__) + "/test-data/nukenode.txt") 68 | p = P.parse(f) 69 | script = e( 70 | e("set", "cut_paste_input", se("stack", "0")), 71 | e("version", "6.3", "v4"), 72 | e("push", "$cut_paste_input"), 73 | e("Blur", 74 | le( 75 | e("size", 76 | le( 77 | le("curve", "x1", "0", "x20", "1.7", "x33", "3.9") 78 | ) 79 | ), 80 | e("name", "Blur1"), 81 | e("label", "With \"Escapes\""), 82 | e("selected", "true"), 83 | e("xpos", "-353"), 84 | e("ypos", "-33") 85 | ) 86 | ) 87 | ) 88 | assert_equal script, p 89 | end 90 | 91 | def test_parse_a_simple_Nuke_script_and_internalize_the_RotoPaint 92 | f = File.open(File.dirname(__FILE__) + "/test-data/three_nodes_and_roto.txt") 93 | p = P.parse(f) 94 | # Should pass through the rotopaint node and get to the blur properly 95 | blur = e("Blur", 96 | le( 97 | e("size", 98 | le( 99 | le("curve", "i", "x1", "0", "x20", "1.7", "x33", "3.9") 100 | ) 101 | ), 102 | e("name", "Blur1"), 103 | e("label", "With \"Escapes\""), 104 | e("selected", "true"), 105 | e("xpos", "-212"), 106 | e("ypos", "-24") 107 | ) 108 | ) 109 | assert_equal blur, p[4] 110 | end 111 | 112 | class Discarder < Tickly::Parser 113 | def compact_subexpr(expr, depth) 114 | return :discarded 115 | end 116 | end 117 | 118 | class Eater < Tickly::Parser 119 | def compact_subexpr(e, d) 120 | nil 121 | end 122 | end 123 | 124 | def test_passes_expressions_via_compact_subexpr 125 | f = File.open(File.dirname(__FILE__) + "/test-data/three_nodes_and_roto.txt") 126 | p = Discarder.new.parse(f) 127 | assert_equal [:discarded, :discarded, :discarded, :discarded, :discarded], p 128 | end 129 | 130 | def test_removes_all_the_expressions_compacted_into_nil 131 | f = File.open(File.dirname(__FILE__) + "/test-data/three_nodes_and_roto.txt") 132 | p = Eater.new.parse(f) 133 | assert_equal [], p 134 | end 135 | 136 | def test_parsing_nuke_script_with_shitdows_line_breaks 137 | f = File.open(File.dirname(__FILE__) + "/test-data/windows_linebreaks.nk") 138 | p = P.parse(f) 139 | 140 | first_expr = p[0] 141 | assert_equal ["set", "cut_paste_input", [:b, "stack", "0"]], first_expr, "Should have chopped off the " 142 | end 143 | 144 | def test_parsing_nuke_script_with_indentations 145 | f = File.open(File.dirname(__FILE__) + "/test-data/nuke_group.txt") 146 | p = P.parse(f) 147 | grp = e( 148 | e("set", "cut_paste_input", se("stack", "0")), 149 | e("version", "6.3", "v4"), 150 | e("Group", 151 | le( 152 | e("inputs", "0"), 153 | e("name", "Group1"), 154 | e("selected", "true") 155 | ) 156 | ), 157 | e("CheckerBoard2", 158 | le( 159 | e("inputs", "0"), 160 | e("name", "CheckerBoard1") 161 | ) 162 | ), 163 | e("Blur", 164 | le( 165 | e("size", "42.5"), 166 | e("name", "Blur1") 167 | ) 168 | ), 169 | e("Output", 170 | le( 171 | e("name", "Output1") 172 | ) 173 | ), 174 | e("end_group") 175 | ) 176 | assert_equal grp, p 177 | end 178 | 179 | def test_one_node_parsing 180 | f = File.open(File.dirname(__FILE__) + "/test-data/one_node_with_one_param.txt") 181 | p = P.parse(f) 182 | ref = e(e("SomeNode", 183 | le( 184 | e("foo", "bar") 185 | ) 186 | )) 187 | 188 | assert_equal ref, p 189 | end 190 | 191 | end 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/julik/tickly.svg?branch=master)](https://travis-ci.org/julik/tickly) 2 | 3 | A highly simplistic TCL parser and evaluator (primarily designed for parsing Nuke scripts). 4 | It transforms the passed Nuke scripts into a TCL AST. 5 | It also supports some cheap tricks to discard the nodes you are not interested in, since Nuke 6 | scripts easily grow into tens of megabytes. 7 | 8 | The AST format is extremely simple (nested arrays). 9 | 10 | ## Plain parsing 11 | 12 | Create a Parser object and pass TCL expressions/scripts to it. You can pass IO obejcts or strings. Note that parse() 13 | will always return an Array of expressions, even if you only fed it one expression line. For example: 14 | 15 | p = Tickly::Parser.new 16 | 17 | # One expression, even if it's invalid (2 is not a valid TCL bareword - doesn't matter) 18 | p.parse '2' #=> [["2"]] 19 | 20 | # TCL command 21 | p.parse "tail $list" #=> [["tail", "$list"]] 22 | 23 | # Multiple expressions 24 | p.parse "2\n2" #=> [["2"], ["2"]] 25 | 26 | # Expressions in curly braces 27 | p.parse '{2 2}' #=> [[:c, "2", "2"]] 28 | 29 | # Expressions in square brackets 30 | p.parse '{exec cmd [fileName]}' #=> [[:c, "exec", "cmd", [:b, "fileName"]]] 31 | 32 | The AST is represented by simple arrays. Each TCL expression becomes an array. An array starting 33 | with the `:c` symbol ("c" for "curlies") is a literal expression in curly braces (`{}`). 34 | An array with the `:b` symbol at the beginning is an expression with string interpolations 35 | (square brackets). 36 | All the other array elements are guaranteed to be strings or innner expressions (arrays). 37 | 38 | String literals are expanded to string array elements. 39 | 40 | p.parse( '"a string with \"quote"') #=> [['a string with "quote']] 41 | 42 | Multiple expressions separated by semicolons or newlines will be accumulated as multiple arrays. 43 | 44 | Lots and lots of TCL features are probably not supported - remember that most Nuke scripts are 45 | machine-generated and they do not use most of the esoteric language features. 46 | 47 | ## Evaulating nodes in Nuke scripts 48 | 49 | What you are likely to use Tickly for is parsing Nuke scripts. They got multiple node definitions, which 50 | are actially arguments for a node constructor written out in TCL. Consider this ubiquitous fragment for a 51 | hypothetic SomeNode in your script: 52 | 53 | SomeNode { 54 | name SomeNode4 55 | someknob 15 56 | anotherknob 3 57 | animation {curve x1 12 45 67} 58 | x_pos 123 59 | y_pos -10 60 | } 61 | 62 | and so on. You can use a `NodeProcessor` to capture these node constructors right as they are being parsed. 63 | The advantage of this workflow is that the processor will discard all the nodes you don't need, saving time 64 | and memory. 65 | 66 | To match nodes you create Ruby classes matching the node classes by name. It doesn't matter if your 67 | custom node handler is inside a module since the processor will only use the last part of the name. 68 | 69 | For example, to capture every `Blur` node in your script: 70 | 71 | # Remember, only the last part of the class name matters 72 | class MyAwesomeDirtyScript::Blur 73 | attr_reader :knobs 74 | def initialize(string_keyed_knobs_hash) 75 | @knobs = string_keyed_knobs_hash 76 | end 77 | end 78 | 79 | # Instantiate a new processor 80 | e = Tickly::NodeProcessor.new 81 | 82 | # Add the class 83 | e.add_node_handler_class SomeNode 84 | 85 | # Open the ginormous Nuke script 86 | file = File.open("/mnt/raid/nuke/scripts/HugeShot_123.nk") 87 | 88 | e.parse(file) do | blur_node | 89 | # Everytime a Blur node is found in the script it will be instantiated, 90 | # and the knobs of the node will be passed to the constructor that you define 91 | kernel_size = blur_node.knobs["radius"] 92 | ... 93 | end 94 | 95 | Of course you can capture multiple node classes. This is how Tracksperanto parses various 96 | nodes containing tracking data: 97 | 98 | parser = Tickly::NodeProcessor.new 99 | parser.add_node_handler_class(Tracker3) 100 | parser.add_node_handler_class(Reconcile3D) 101 | parser.add_node_handler_class(PlanarTracker1_0) 102 | parser.add_node_handler_class(Tracker4) 103 | 104 | Then you will need to handle switching between node types during parsing 105 | 106 | e.parse(file) do | detected_node | 107 | if detected_node.is_a?(Tracker3) 108 | ... 109 | else 110 | ... 111 | end 112 | end 113 | 114 | Node clones are not supported. 115 | 116 | ## Animation curves 117 | 118 | You can parse Nuke's animation curves using `Tickly::Curve`. This will give you a way to iterate over every defined keyframe. 119 | This currently does not happen automatically for things passing through the parser. 120 | 121 | ## Tip: Speeding up parsing 122 | 123 | Normally, Tickly will accept strings and IO objects as sources. However if you want a little performance boost 124 | when parsing actual _files_ (or long IO objects that can be prebuffered) you should use it together 125 | with [bychar](http://rubygems.org/gems/bychar), version 3 or newer - like so: 126 | 127 | p = Tickly::Parser.new 128 | File.open("/mnt/raid/comps/s023_v23.nk", "r") do | f | 129 | expressions = p.parse(Bychar.wrap(f)) 130 | ... 131 | end 132 | 133 | This way some of the IO will be prebuffered for you and give you improved reading performance when parsing. 134 | 135 | Tracksperanto does the bychar wrapping thing automatically, so no need to worry about that. 136 | 137 | 138 | 139 | ## Contributing to tickly 140 | 141 | Just like tracksperanto Tickly no longer ships with test data in gem format (since the test data amounts to 142 | to a substantial increase in package size). To obtain the test data, check the repo out. 143 | 144 | * Check out the latest master to obtain the test data. 145 | * Make sure the feature hasn't been implemented or the bug hasn't been fixed yet. 146 | * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it. 147 | * Fork the project. 148 | * Start a feature/bugfix branch. 149 | * Commit and push until you are happy with your contribution. 150 | * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. 151 | * 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. 152 | 153 | ## Copyright 154 | 155 | Copyright (c) 2013 Julik Tarkhanov. See LICENSE.txt for 156 | further details. 157 | 158 | -------------------------------------------------------------------------------- /lib/tickly/parser.rb: -------------------------------------------------------------------------------- 1 | require 'stringio' 2 | 3 | module Tickly 4 | # Simplistic, incomplete and most likely incorrect TCL parser 5 | class Parser 6 | 7 | # Used as an IO wrapper, similar to what Bychar has. 8 | class R < Struct.new(:io) 9 | def read_one_char; io.read(1); end 10 | end 11 | 12 | # Gets raised on invalid input 13 | class Error < RuntimeError 14 | end 15 | 16 | # Returns the given String or IO object wrapped in an object that has 17 | # one method, read_one_char - that gets used by all the subsequent 18 | # parsing steps 19 | def wrap_io_or_string(io_or_str) 20 | return io_or_str if io_or_str.respond_to?(:read_one_char) # Bychar or R 21 | return R.new(io_or_str) if io_or_str.respond_to?(:read) 22 | R.new(StringIO.new(io_or_str)) 23 | end 24 | 25 | # Parses a piece of TCL and returns it converted into internal expression 26 | # structures. A basic TCL expression is just an array of Strings. An expression 27 | # in curly braces will have the symbol :c tacked onto the beginning of the array. 28 | # An expression in square braces will have the symbol :b tacked onto the beginning. 29 | # This method always returns a Array of expressions. If you only fed it one expression, 30 | # this expression will be the only element of the array. 31 | # The correct way to use the returned results is thusly: 32 | # 33 | # p = Tickly::Parser.new 34 | # expressions = p.parse("2 + 2") #=> [["2", "+", "2"]] 35 | # expression = expressions[0] #=> ["2", "2"] 36 | def parse(io_or_str) 37 | reader = wrap_io_or_string(io_or_str) 38 | # Use multiple_expressions = true so that the top-level parsed script 39 | # is always an array of expressions 40 | parse_expr(reader, stop_char = nil, stack_depth = 0, multiple_expressions = true) 41 | end 42 | 43 | # Override this to remove any unneeded subexpressions. 44 | # Return the modified expression. If you return nil, the result 45 | # will not be added to the expression list. You can also use this 46 | # method for bottom-up expression evaluation, returning the result 47 | # of the expression being evaluated. This method will be first called 48 | # for the innermost expressions and then proceed up the call stack. 49 | def compact_subexpr(expr, at_depth) 50 | expr 51 | end 52 | 53 | private 54 | 55 | TERMINATORS = ["\n", ";"] 56 | ESC = 92.chr # Backslash (\) 57 | QUOTES = %w( " ' ) 58 | 59 | # Package the expressions, stack and buffer. 60 | # We use a special flag to tell us whether we need multuple expressions. 61 | # If we do, the expressions will be returned. If not, just the stack. 62 | # Also, anything that remains on the stack will be put on the expressions 63 | # list if multiple_expressions is true. 64 | def wrap_up(expressions, stack, buf, stack_depth, multiple_expressions) 65 | stack << buf if (buf.length > 0) 66 | return stack unless multiple_expressions 67 | 68 | expressions << stack if stack.any? 69 | 70 | # Make sure that all of the expresisons get collapsed 71 | expressions = expressions.map do | e | 72 | compact_subexpr(e, stack_depth + 1) 73 | end 74 | 75 | return expressions 76 | end 77 | 78 | # If the passed buf contains any bytes, put them on the stack and 79 | # empty the buffer 80 | def consume_remaining_buffer(stack, buf) 81 | return if buf.length == 0 82 | stack << buf.dup 83 | buf.replace('') 84 | end 85 | 86 | # Parse from a passed IO object either until an unescaped stop_char is reached 87 | # or until the IO is exhausted. The last argument is the class used to 88 | # compose the subexpression being parsed. The subparser is reentrant and not 89 | # destructive for the object containing it. 90 | def parse_expr(io, stop_char = nil, stack_depth = 0, multiple_expressions = false) 91 | # A standard stack is an expression that does not evaluate to a string 92 | expressions = [] 93 | stack = [] 94 | buf = '' 95 | 96 | loop do 97 | char = io.read_one_char 98 | 99 | # Ignore carriage returns 100 | next if char == "\r" 101 | 102 | if stop_char && char.nil? 103 | raise Error, "IO ran out when parsing a subexpression (expected to end on #{stop_char.inspect})" 104 | elsif char == stop_char # Bail out of a subexpr or bail out on nil 105 | # TODO: default stop_char is nil, and this is also what gets returned from a depleted 106 | # IO on IO#read(). We should do that in Bychar. 107 | # Handle any remaining subexpressions 108 | return wrap_up(expressions, stack, buf, stack_depth, multiple_expressions) 109 | elsif char == " " || char == "\n" # Space 110 | if buf.length > 0 111 | stack << buf 112 | buf = '' 113 | end 114 | if TERMINATORS.include?(char) && stack.any? # Introduce a stack separator! This is a new line 115 | 116 | # First get rid of the remaining buffer data 117 | consume_remaining_buffer(stack, buf) 118 | 119 | # Since we now finished an expression and it is on the stack, 120 | # we can run this expression through the filter 121 | filtered_expr = compact_subexpr(stack, stack_depth + 1) 122 | 123 | # Only preserve the parsed expression if it's not nil 124 | expressions << filtered_expr unless filtered_expr.nil? 125 | 126 | # Reset the stack for the next expression 127 | stack = [] 128 | 129 | # Note that we will return multiple expressions instead of one 130 | multiple_expressions = true 131 | end 132 | elsif char == '[' # Opens a new string expression 133 | consume_remaining_buffer(stack, buf) 134 | stack << [:b] + parse_expr(io, ']', stack_depth + 1) 135 | elsif char == '{' # Opens a new literal expression 136 | consume_remaining_buffer(stack, buf) 137 | stack << [:c] + parse_expr(io, '}', stack_depth + 1) 138 | elsif QUOTES.include?(char) # String 139 | consume_remaining_buffer(stack, buf) 140 | stack << parse_str(io, char) 141 | else 142 | buf << char 143 | end 144 | end 145 | 146 | raise Error, "Should never happen" 147 | end 148 | 149 | # Parse a string literal, in single or double quotes. 150 | def parse_str(io, stop_quote) 151 | buf = '' 152 | loop do 153 | c = io.read_one_char 154 | if c.nil? 155 | raise Error, "The IO ran out before the end of a literal string" 156 | elsif buf.length > 0 && buf[-1..-1] == ESC # If this char was escaped 157 | # Trim the escape character at the end of the buffer 158 | buf = buf[0..-2] 159 | buf << c 160 | elsif c == stop_quote 161 | return buf 162 | else 163 | buf << c 164 | end 165 | end 166 | end 167 | end 168 | end -------------------------------------------------------------------------------- /test/test-data/nuke7_tracker_2tracks.nk: -------------------------------------------------------------------------------- 1 | version 7.0 v2 2 | define_window_layout_xml { 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 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 | } 117 | -------------------------------------------------------------------------------- /test/test-data/three_nodes_and_roto.txt: -------------------------------------------------------------------------------- 1 | set cut_paste_input [stack 0] 2 | version 6.3 v4 3 | CheckerBoard2 { 4 | inputs 0 5 | name CheckerBoard1 6 | selected true 7 | xpos -212 8 | ypos -120 9 | } 10 | RotoPaint { 11 | curves {AnimTree: "" { 12 | Version: 1.2 13 | Flag: 0 14 | RootNode: 1 15 | Node: { 16 | NodeName: "Root" { 17 | Flag: 512 18 | NodeType: 1 19 | Transform: 0 0 S 0 0 S 0 0 S 0 0 S 0 1 S 0 1 S 0 0 S 0 1024 S 0 778 20 | NumOfAttributes: 11 21 | "vis" S 0 1 "opc" S 0 1 "mbo" S 0 1 "mb" S 0 1 "mbs" S 0 0.5 "fo" S 0 1 "fx" S 0 0 "fy" S 0 0 "ff" S 0 1 "ft" S 0 0 "pt" S 0 0 22 | } 23 | NumOfChildren: 4 24 | Node: { 25 | NodeName: "Bezier1" { 26 | Flag: 576 27 | NodeType: 3 28 | CurveGroup: "" { 29 | Transform: 0 0 S 1 33 0 S 1 33 0 S 1 33 0 S 1 33 1 S 1 33 1 S 1 33 0 S 1 33 826.667 S 1 33 984.667 30 | Flag: 0 31 | NumOfCubicCurves: 2 32 | CubicCurve: "" { 33 | Type: 0 Flag: 8192 Dim: 2 34 | NumOfPoints: 9 35 | 0 S 1 33 0 S 1 33 320 0 0 S 1 33 620 S 1 33 1152 0 0 S 1 33 0 S 1 33 -320 0 0 S 1 33 -168 S 1 33 -128 0 0 S 1 33 812 S 1 33 730 0 0 S 1 33 168 S 1 33 128 0 0 S 1 33 34 S 1 33 -2 0 0 S 1 33 1048 S 1 33 1072 0 0 S 1 33 -34 S 1 33 2 0 36 | } 37 | CubicCurve: "" { 38 | Type: 0 Flag: 8192 Dim: 2 39 | NumOfPoints: 9 40 | 0 S 1 33 0 S 1 33 320 0 0 S 1 33 0 S 1 33 0 0 0 S 1 33 0 S 1 33 -320 0 0 S 1 33 -168 S 1 33 -128 0 0 S 1 33 0 S 1 33 0 0 0 S 1 33 168 S 1 33 128 0 0 S 1 33 34 S 1 33 -2 0 0 S 1 33 0 S 1 33 0 0 0 S 1 33 -34 S 1 33 2 0 41 | } 42 | NumOfAttributes: 44 43 | "vis" S 0 1 "r" S 0 1 "g" S 0 1 "b" S 0 1 "a" S 0 1 "ro" S 0 0 "go" S 0 0 "bo" S 0 0 "ao" S 0 0 "opc" S 0 1 "bm" S 0 0 "inv" S 0 0 "mbo" S 0 0 "mb" S 0 1 "mbs" S 0 0.5 "mbsot" S 0 0 "mbso" S 0 0 "fo" S 0 1 "fx" S 0 0 "fy" S 0 0 "ff" S 0 1 "ft" S 0 0 "src" S 0 0 "stx" S 0 0 "sty" S 0 0 "str" S 0 0 "sr" S 0 0 "ssx" S 0 1 "ssy" S 0 1 "ss" S 0 0 "spx" S 0 1024 "spy" S 0 778 "stot" S 0 0 "sto" S 0 0 "sv" S 0 0 "sf" S 0 1 "sb" S 0 1 "nv" S 0 1 "view1" S 0 1 "ltn" S 0 33 "ltm" S 0 33 "ltt" S 0 0 "tt" S 0 4 "pt" S 0 0 44 | } 45 | } 46 | NumOfChildren: 0 47 | } 48 | Node: { 49 | NodeName: "Brush3" { 50 | Flag: 512 51 | NodeType: 5 52 | CubicCurve: "" { 53 | Transform: 0 0 S 1 33 0 S 1 33 0 S 1 33 0 S 1 33 1 S 1 33 1 S 1 33 0 S 1 33 882.24 S 1 33 789.92 54 | Type: 3 Flag: 2080 Dim: 3 55 | NumOfPoints: 25 56 | 0 S 1 33 332 S 1 33 1018 S 1 33 1 0 0 S 1 33 342 S 1 33 1018 S 1 33 1 0 0 S 1 33 386 S 1 33 1018 S 1 33 1 0 0 S 1 33 482 S 1 33 984 S 1 33 1 0 0 S 1 33 602 S 1 33 936 S 1 33 1 0 0 S 1 33 716 S 1 33 896 S 1 33 1 0 0 S 1 33 798 S 1 33 868 S 1 33 1 0 0 S 1 33 894 S 1 33 840 S 1 33 1 0 0 S 1 33 930 S 1 33 838 S 1 33 1 0 0 S 1 33 966 S 1 33 834 S 1 33 1 0 0 S 1 33 980 S 1 33 834 S 1 33 1 0 0 S 1 33 998 S 1 33 834 S 1 33 1 0 0 S 1 33 1014 S 1 33 834 S 1 33 1 0 0 S 1 33 1038 S 1 33 834 S 1 33 1 0 0 S 1 33 1048 S 1 33 828 S 1 33 1 0 0 S 1 33 1060 S 1 33 822 S 1 33 1 0 0 S 1 33 1068 S 1 33 816 S 1 33 1 0 0 S 1 33 1080 S 1 33 802 S 1 33 1 0 0 S 1 33 1092 S 1 33 778 S 1 33 1 0 0 S 1 33 1098 S 1 33 748 S 1 33 1 0 0 S 1 33 1108 S 1 33 678 S 1 33 1 0 0 S 1 33 1092 S 1 33 570 S 1 33 1 0 0 S 1 33 1028 S 1 33 448 S 1 33 1 0 0 S 1 33 978 S 1 33 376 S 1 33 1 0 0 S 1 33 926 S 1 33 296 S 1 33 1 0 57 | NumOfAttributes: 46 58 | "vis" S 0 1 "r" S 0 1 "g" S 0 1 "b" S 0 1 "a" S 0 1 "ro" S 0 1 "go" S 0 1 "bo" S 0 1 "ao" S 0 1 "opc" S 0 1 "bs" S 0 25 "bsp" S 0 0.05 "h" S 0 0.2 "bm" S 0 0 "dt" S 0 1 "ds" S 0 0 "dh" S 0 0 "bt" S 0 0 "bu" S 0 1 "inv" S 0 0 "src" S 0 0 "stx" S 0 0 "sty" S 0 0 "str" S 0 0 "sr" S 0 0 "ssx" S 0 1 "ssy" S 0 1 "ss" S 0 0 "spx" S 0 1024 "spy" S 0 778 "stot" S 0 0 "sto" S 0 0 "sv" S 0 0 "sf" S 0 1 "sb" S 0 1 "ltn" S 0 33 "ltm" S 0 33 "ltt" S 0 2 "ws" S 0 0 "we" S 0 1 "ep1" S 0 0 "ep2" S 0 0 "ep3" S 0 0 "tt" S 0 14 "nv" S 0 1 "view1" S 0 1 59 | } 60 | } 61 | NumOfChildren: 0 62 | } 63 | Node: { 64 | NodeName: "Brush2" { 65 | Flag: 512 66 | NodeType: 5 67 | CubicCurve: "" { 68 | Transform: 0 0 S 1 33 0 S 1 33 0 S 1 33 0 S 1 33 1 S 1 33 1 S 1 33 0 S 1 33 664.487 S 1 33 404.973 69 | Type: 3 Flag: 2080 Dim: 3 70 | NumOfPoints: 37 71 | 0 S 1 33 968 S 1 33 454 S 1 33 1 0 0 S 1 33 956 S 1 33 454 S 1 33 1 0 0 S 1 33 894 S 1 33 454 S 1 33 1 0 0 S 1 33 762 S 1 33 454 S 1 33 1 0 0 S 1 33 540 S 1 33 476 S 1 33 1 0 0 S 1 33 354 S 1 33 510 S 1 33 1 0 0 S 1 33 252 S 1 33 532 S 1 33 1 0 0 S 1 33 204 S 1 33 540 S 1 33 1 0 0 S 1 33 168 S 1 33 546 S 1 33 1 0 0 S 1 33 152 S 1 33 546 S 1 33 1 0 0 S 1 33 152 S 1 33 540 S 1 33 1 0 0 S 1 33 170 S 1 33 526 S 1 33 1 0 0 S 1 33 210 S 1 33 516 S 1 33 1 0 0 S 1 33 260 S 1 33 512 S 1 33 1 0 0 S 1 33 312 S 1 33 516 S 1 33 1 0 0 S 1 33 372 S 1 33 562 S 1 33 1 0 0 S 1 33 438 S 1 33 600 S 1 33 1 0 0 S 1 33 518 S 1 33 648 S 1 33 1 0 0 S 1 33 600 S 1 33 688 S 1 33 1 0 0 S 1 33 710 S 1 33 732 S 1 33 1 0 0 S 1 33 800 S 1 33 754 S 1 33 1 0 0 S 1 33 842 S 1 33 760 S 1 33 1 0 0 S 1 33 888 S 1 33 760 S 1 33 1 0 0 S 1 33 918 S 1 33 760 S 1 33 1 0 0 S 1 33 954 S 1 33 696 S 1 33 1 0 0 S 1 33 996 S 1 33 498 S 1 33 1 0 0 S 1 33 1002 S 1 33 300 S 1 33 1 0 0 S 1 33 1002 S 1 33 36 S 1 33 1 0 0 S 1 33 960 S 1 33 -132 S 1 33 1 0 0 S 1 33 932 S 1 33 -208 S 1 33 1 0 0 S 1 33 914 S 1 33 -252 S 1 33 1 0 0 S 1 33 908 S 1 33 -260 S 1 33 1 0 0 S 1 33 902 S 1 33 -246 S 1 33 1 0 0 S 1 33 896 S 1 33 -150 S 1 33 1 0 0 S 1 33 890 S 1 33 64 S 1 33 1 0 0 S 1 33 890 S 1 33 300 S 1 33 1 0 0 S 1 33 900 S 1 33 498 S 1 33 1 0 72 | NumOfAttributes: 46 73 | "vis" S 0 1 "r" S 0 1 "g" S 0 1 "b" S 0 1 "a" S 0 1 "ro" S 0 1 "go" S 0 1 "bo" S 0 1 "ao" S 0 1 "opc" S 0 1 "bs" S 0 25 "bsp" S 0 0.05 "h" S 0 0.2 "bm" S 0 0 "dt" S 0 1 "ds" S 0 0 "dh" S 0 0 "bt" S 0 0 "bu" S 0 1 "inv" S 0 0 "src" S 0 0 "stx" S 0 0 "sty" S 0 0 "str" S 0 0 "sr" S 0 0 "ssx" S 0 1 "ssy" S 0 1 "ss" S 0 0 "spx" S 0 1024 "spy" S 0 778 "stot" S 0 0 "sto" S 0 0 "sv" S 0 0 "sf" S 0 1 "sb" S 0 1 "ltn" S 0 33 "ltm" S 0 33 "ltt" S 0 2 "ws" S 0 0 "we" S 0 1 "ep1" S 0 0 "ep2" S 0 0 "ep3" S 0 0 "tt" S 0 14 "nv" S 0 1 "view1" S 0 1 74 | } 75 | } 76 | NumOfChildren: 0 77 | } 78 | Node: { 79 | NodeName: "Brush1" { 80 | Flag: 512 81 | NodeType: 5 82 | CubicCurve: "" { 83 | Transform: 0 0 S 1 33 0 S 1 33 0 S 1 33 0 S 1 33 1 S 1 33 1 S 1 33 0 S 1 33 730.692 S 1 33 712.038 84 | Type: 3 Flag: 2080 Dim: 3 85 | NumOfPoints: 52 86 | 0 S 1 33 446 S 1 33 1062 S 1 33 1 0 0 S 1 33 456 S 1 33 1068 S 1 33 1 0 0 S 1 33 480 S 1 33 1080 S 1 33 1 0 0 S 1 33 542 S 1 33 1116 S 1 33 1 0 0 S 1 33 632 S 1 33 1152 S 1 33 1 0 0 S 1 33 684 S 1 33 1170 S 1 33 1 0 0 S 1 33 714 S 1 33 1180 S 1 33 1 0 0 S 1 33 738 S 1 33 1186 S 1 33 1 0 0 S 1 33 744 S 1 33 1186 S 1 33 1 0 0 S 1 33 746 S 1 33 1186 S 1 33 1 0 0 S 1 33 746 S 1 33 1176 S 1 33 1 0 0 S 1 33 746 S 1 33 1104 S 1 33 1 0 0 S 1 33 728 S 1 33 960 S 1 33 1 0 0 S 1 33 666 S 1 33 808 S 1 33 1 0 0 S 1 33 620 S 1 33 690 S 1 33 1 0 0 S 1 33 600 S 1 33 640 S 1 33 1 0 0 S 1 33 582 S 1 33 600 S 1 33 1 0 0 S 1 33 572 S 1 33 588 S 1 33 1 0 0 S 1 33 584 S 1 33 606 S 1 33 1 0 0 S 1 33 666 S 1 33 702 S 1 33 1 0 0 S 1 33 744 S 1 33 796 S 1 33 1 0 0 S 1 33 810 S 1 33 874 S 1 33 1 0 0 S 1 33 842 S 1 33 916 S 1 33 1 0 0 S 1 33 866 S 1 33 942 S 1 33 1 0 0 S 1 33 872 S 1 33 948 S 1 33 1 0 0 S 1 33 876 S 1 33 948 S 1 33 1 0 0 S 1 33 876 S 1 33 892 S 1 33 1 0 0 S 1 33 800 S 1 33 714 S 1 33 1 0 0 S 1 33 684 S 1 33 476 S 1 33 1 0 0 S 1 33 648 S 1 33 408 S 1 33 1 0 0 S 1 33 618 S 1 33 346 S 1 33 1 0 0 S 1 33 606 S 1 33 320 S 1 33 1 0 0 S 1 33 636 S 1 33 366 S 1 33 1 0 0 S 1 33 656 S 1 33 394 S 1 33 1 0 0 S 1 33 746 S 1 33 528 S 1 33 1 0 0 S 1 33 824 S 1 33 630 S 1 33 1 0 0 S 1 33 872 S 1 33 690 S 1 33 1 0 0 S 1 33 902 S 1 33 724 S 1 33 1 0 0 S 1 33 914 S 1 33 730 S 1 33 1 0 0 S 1 33 918 S 1 33 726 S 1 33 1 0 0 S 1 33 918 S 1 33 654 S 1 33 1 0 0 S 1 33 848 S 1 33 476 S 1 33 1 0 0 S 1 33 788 S 1 33 378 S 1 33 1 0 0 S 1 33 762 S 1 33 330 S 1 33 1 0 0 S 1 33 750 S 1 33 306 S 1 33 1 0 0 S 1 33 746 S 1 33 300 S 1 33 1 0 0 S 1 33 750 S 1 33 300 S 1 33 1 0 0 S 1 33 770 S 1 33 316 S 1 33 1 0 0 S 1 33 798 S 1 33 328 S 1 33 1 0 0 S 1 33 812 S 1 33 334 S 1 33 1 0 0 S 1 33 824 S 1 33 336 S 1 33 1 0 0 S 1 33 828 S 1 33 340 S 1 33 1 0 87 | NumOfAttributes: 46 88 | "vis" S 0 1 "r" S 0 1 "g" S 0 1 "b" S 0 1 "a" S 0 1 "ro" S 0 1 "go" S 0 1 "bo" S 0 1 "ao" S 0 1 "opc" S 0 1 "bs" S 0 25 "bsp" S 0 0.05 "h" S 0 0.2 "bm" S 0 0 "dt" S 0 1 "ds" S 0 0 "dh" S 0 0 "bt" S 0 0 "bu" S 0 1 "inv" S 0 0 "src" S 0 0 "stx" S 0 0 "sty" S 0 0 "str" S 0 0 "sr" S 0 0 "ssx" S 0 1 "ssy" S 0 1 "ss" S 0 0 "spx" S 0 1024 "spy" S 0 778 "stot" S 0 0 "sto" S 0 0 "sv" S 0 0 "sf" S 0 1 "sb" S 0 1 "ltn" S 0 33 "ltm" S 0 33 "ltt" S 0 2 "ws" S 0 0 "we" S 0 1 "ep1" S 0 0 "ep2" S 0 0 "ep3" S 0 0 "tt" S 0 14 "nv" S 0 1 "view1" S 0 1 89 | } 90 | } 91 | NumOfChildren: 0 92 | } 93 | } 94 | } 95 | } 96 | toolbox {selectAll { 97 | { selectAll ssx 1 ssy 1 sf 1 } 98 | { createBezier ssx 1 ssy 1 sf 1 sb 1 tt 4 } 99 | { createBSpline ssx 1 ssy 1 sf 1 sb 1 } 100 | { createEllipse ssx 1 ssy 1 sf 1 sb 1 } 101 | { createRectangle ssx 1 ssy 1 sf 1 sb 1 } 102 | { brush ssx 1 ssy 1 sf 1 sb 1 ltn 33 ltm 33 tt 14 } 103 | { eraser src 2 ssx 1 ssy 1 sf 1 sb 1 } 104 | { clone src 1 ssx 1 ssy 1 sf 1 sb 1 } 105 | { reveal src 3 ssx 1 ssy 1 sf 1 sb 1 } 106 | { dodge src 1 ssx 1 ssy 1 sf 1 sb 1 } 107 | { burn src 1 ssx 1 ssy 1 sf 1 sb 1 } 108 | { blur src 1 ssx 1 ssy 1 sf 1 sb 1 } 109 | { sharpen src 1 ssx 1 ssy 1 sf 1 sb 1 } 110 | { smear src 1 ssx 1 ssy 1 sf 1 sb 1 } 111 | } } 112 | toolbar_brush_hardness 0.200000003 113 | toolbar_lifetime_type all 114 | toolbar_source_transform_scale {1 1} 115 | toolbar_source_transform_center {320 240} 116 | colorOverlay 0 117 | lifetime_type "all frames" 118 | lifetime_start 33 119 | lifetime_end 33 120 | motionblur_shutter_offset_type centred 121 | brush_spacing 0.05000000075 122 | brush_hardness 0.200000003 123 | source_black_outside true 124 | createNewTrack {{-1} "-1\t(none)\t-1" "1000\tNew Track Layer\t1000"} 125 | name RotoPaint1 126 | selected true 127 | xpos -212 128 | ypos -48 129 | } 130 | Blur { 131 | size {{curve i x1 0 x20 1.7 x33 3.9}} 132 | name Blur1 133 | label "With \"Escapes\"" 134 | selected true 135 | xpos -212 136 | ypos -24 137 | } 138 | -------------------------------------------------------------------------------- /test/test-data/windows_linebreaks.nk: -------------------------------------------------------------------------------- 1 | set cut_paste_input [stack 0] 2 | version 6.2 v2 3 | push $cut_paste_input 4 | Tracker3 { 5 | track1 {{curve 6 | x1 694.030864 7 | x2 700.201011 8 | x3 707.172296 9 | x4 715.788863 10 | x5 722.901890 11 | x6 721.822500 12 | x7 715.908979 13 | x8 712.514454 14 | x9 711.648261 15 | x10 710.065840 16 | x11 707.204060 17 | x12 706.152124 18 | x13 707.472598 19 | x14 709.102548 20 | x15 714.110512 21 | x16 718.288421 22 | x17 719.345575 23 | x18 724.099640 24 | x19 730.914115 25 | x20 733.337901 26 | x21 737.339629 27 | x22 738.838096 28 | x23 740.454469 29 | x24 741.771548 30 | x25 740.902005 31 | x26 739.800864 32 | x27 738.909122 33 | x28 738.342190 34 | x29 738.381453 35 | x30 741.449245 36 | x31 745.054920 37 | x32 742.336236 38 | x33 739.075044 39 | x34 737.594719 40 | x35 735.571338 41 | x36 729.881996 42 | x37 722.423313 43 | x38 717.788917 44 | x39 712.344594 45 | x40 707.466955 46 | x41 701.123530 47 | x42 694.879896 48 | x43 685.514318 49 | x44 677.501890 50 | x45 672.662437 51 | x46 669.186831 52 | x47 663.298583 53 | x48 656.230674 54 | x49 652.497001 55 | x50 651.890143 56 | x51 655.778831 57 | x52 657.026014 58 | x53 655.512278 59 | x54 652.790760 60 | x55 653.010935 61 | x56 647.702970 62 | x57 647.562904 63 | x58 645.088767 64 | x59 645.049822 65 | x60 645.908309 66 | x61 642.919780 67 | x62 641.660146 68 | x63 647.714471 69 | x64 651.929355 70 | x65 650.136193 71 | x66 649.342078 72 | x67 647.781939 73 | x68 648.138297 74 | x69 649.384348 75 | x70 653.137958 76 | x71 659.113774 77 | x72 667.734417 78 | x73 676.521920 79 | x74 685.702801 80 | x75 698.193293 81 | x76 708.793151 82 | x77 715.846494 83 | x78 724.548759 84 | x79 735.456115 85 | x80 749.351477 86 | x81 756.510870 87 | x82 758.601279 88 | x83 761.528826 89 | x84 766.964672 90 | x85 772.608834 91 | x86 779.164072 92 | x87 784.107053 93 | x88 782.582546 94 | x89 781.843089 95 | x90 786.844467 96 | x91 787.121805 97 | x92 777.496357 98 | x93 761.717752 99 | x94 748.443882 100 | x95 735.943792 101 | x96 719.977581 102 | x97 699.772269 103 | x98 676.587734 104 | x99 660.962854 105 | x100 646.720478 106 | x101 637.453393 107 | x102 633.770700 108 | x103 625.129900 109 | x104 612.417602 110 | x105 603.328081 111 | x106 599.483633 112 | x107 593.222816 113 | x108 589.326775 114 | x109 593.936718 115 | x110 602.287582 116 | x111 611.152966 117 | x112 615.789446 118 | x113 617.278439 119 | x114 618.288464 120 | x115 613.925111 121 | x116 611.169479 122 | x117 609.688863 123 | x118 604.631939 124 | x119 598.065648 125 | x120 592.554290 126 | x121 587.575658 127 | x122 583.872157 128 | x123 582.739797 129 | x124 581.328776 130 | x125 581.446490 131 | x126 588.270868 132 | x127 598.708699 133 | x128 613.216142 134 | x129 624.247747 135 | x130 634.581174 136 | x131 639.123530 137 | x132 642.235183 138 | x133 660.053620 139 | x134 676.945688 140 | x135 687.854642 141 | x136 695.386093 142 | x137 705.432339 143 | x138 720.691248 144 | x139 731.539595 145 | x140 734.719649 146 | x141 730.888634 147 | x142 718.633110 148 | x143 710.680224 149 | x144 708.665614 150 | x145 708.059560 151 | x146 697.643576 152 | x147 688.708068 153 | x148 679.971977 154 | x149 667.024330 155 | x150 645.808885 156 | x151 625.863454 157 | x152 620.140884 158 | x153 619.185364 159 | x154 615.362668 160 | x155 607.314216 161 | x156 596.838536 162 | x157 587.729188 163 | x158 583.308313 164 | x159 586.758951 165 | x160 600.719237 166 | x161 604.429537 167 | x162 597.829911 168 | x163 588.751269 169 | x164 583.694095 170 | x165 591.312250 171 | x166 610.392214 172 | x167 623.132824 173 | x168 616.807715 174 | x169 602.956060 175 | x170 590.656253 176 | x171 583.201793 177 | x172 577.556617 178 | x173 570.478947 179 | x174 567.140346 180 | x175 568.869913 181 | x176 572.841418 182 | x177 582.398973 183 | x178 596.845580 184 | x179 614.473604 185 | x180 634.502515 186 | x181 644.175646 187 | x182 647.724899 188 | x183 660.787810 189 | x184 676.632771 190 | x185 687.705990 191 | x186 698.742843 192 | x187 707.607572 193 | x188 712.388965 194 | x189 715.116699 195 | x190 711.551673 196 | x191 709.583512 197 | x192 709.510520 198 | x193 712.159491 199 | x194 708.621360 200 | x195 704.261699 201 | x196 700.146554 202 | x197 697.655167 203 | x198 694.517990 204 | x199 690.050403 205 | x200 682.014942 206 | x201 674.403724 207 | x202 669.633850 208 | x203 665.045464 209 | x204 659.232506 210 | x205 649.674325 211 | x206 641.352660 212 | x207 645.220211 213 | x208 644.510370 214 | x209 640.188576 215 | x210 627.229561 216 | x211 624.364422 217 | x212 638.438230 218 | x213 641.408894 219 | x214 641.259931 220 | x215 637.174009 221 | x216 613.709915 222 | x217 606.336767 223 | x218 610.623648 224 | x219 608.800905 225 | x220 605.241131 226 | x221 600.630766 227 | x222 597.697443 228 | x223 593.414693 229 | x224 590.285756 230 | x225 588.190505 231 | x226 588.903201 232 | x227 591.754033 233 | x228 598.261078 234 | x229 605.625685 235 | x230 617.179642 236 | x231 631.501696 237 | x232 646.015835 238 | x233 657.064618 239 | x234 656.820903 240 | x235 663.132728 241 | x236 673.955389 242 | x237 686.591170 243 | x238 696.713684 244 | x239 708.571567 245 | x240 723.211516 246 | x241 727.505421 247 | x242 726.887848 248 | x243 726.436440 249 | x244 719.462479 250 | x245 713.163462 251 | x246 706.214690 252 | x247 701.371477 253 | x248 700.224496 254 | x249 702.029787 255 | x250 698.992798 256 | x251 691.335616 257 | x252 679.782936 258 | x253 671.248261 259 | x254 666.661295 260 | x255 660.268267 261 | x256 654.045304 262 | x257 644.745472 263 | x258 637.336797 264 | x259 633.129377 265 | x260 627.261128 266 | x261 621.263614 267 | x262 617.222243 268 | x263 611.581700 269 | x264 605.561946 270 | x265 595.988057 271 | x266 584.462163 272 | x267 582.358852 273 | x268 591.663335 274 | x269 597.379140 275 | x270 590.624020 276 | x271 578.024747 277 | x272 571.138624 278 | x273 564.100024 279 | x274 566.518975 280 | x275 567.320387 281 | x276 563.817290 282 | x277 558.515609 283 | x278 562.080061 284 | x279 566.280673 285 | x280 571.437590 286 | x281 574.806807 287 | x282 576.111884 288 | x283 584.195004 289 | x284 589.093797 290 | x285 591.763374 291 | x286 602.207433 292 | x287 612.540451 293 | x288 624.431611 294 | x289 632.016752 295 | x290 639.716387 296 | x291 644.317108 297 | x292 654.401807 298 | x293 661.844357 299 | x294 668.335782 300 | x295 677.289844 301 | x296 684.279232 302 | x297 689.788250 303 | x298 704.283336 304 | x299 720.069380 305 | x300 727.984445 306 | x301 730.895905 307 | x302 727.052053 308 | x303 720.898144 309 | x304 715.713171 310 | x305 706.792145 311 | x306 699.676117 312 | x307 694.941360 313 | x308 687.439169 314 | x309 679.390184 315 | x310 672.205209 316 | x311 669.603952 317 | x312 666.762121 318 | x313 659.826193 319 | x314 653.624858 320 | x315 637.893617 321 | x316 618.382849 322 | x317 618.940753 323 | x318 623.443290 324 | x319 621.501264 325 | x320 616.755222 326 | x321 612.180493 327 | x322 612.866479 328 | x323 617.386513 329 | x324 619.411137 330 | x325 623.477493 331 | x326 626.440735 332 | x327 632.199083 333 | x328 633.632408 334 | x329 633.104642 335 | x330 634.550684 336 | x331 637.704148 337 | x332 644.777013 338 | x333 647.800461 339 | x334 650.963234 340 | x335 654.565027 341 | x336 657.423433 342 | x337 658.747097 343 | x338 664.687875 344 | x339 668.564091 345 | x340 675.263946 346 | x341 677.667764 347 | x342 675.295095 348 | x343 675.570968 349 | x344 679.318510 350 | x345 683.673927 351 | x346 689.301008 352 | x347 697.928304 353 | x348 706.263592 354 | x349 709.817936 355 | x350 710.138611 356 | x351 712.007839 357 | x352 711.619025 358 | x353 715.087637 359 | x354 719.212291 360 | x355 718.537896 361 | x356 715.864568 362 | x357 710.490697 363 | x358 706.384581 364 | x359 705.677684 365 | x360 700.551707 366 | x361 688.217359 367 | x362 670.450214 368 | x363 660.128051 369 | x364 654.884848 370 | x365 646.907521 371 | x366 655.290469 372 | x367 654.494923 373 | x368 638.216442 374 | x369 623.589627 375 | x370 621.072575 376 | x371 621.440408 377 | x372 624.419685 378 | x373 622.018745 379 | x374 623.830715 380 | x375 625.237907 381 | x376 623.338670 382 | x377 627.757165 383 | x378 633.797454 384 | x379 643.381429 385 | x380 657.628544 386 | x381 668.357308 387 | x382 677.328563 388 | x383 693.696570 389 | x384 717.678906 390 | x385 729.923260 391 | x386 737.300798 392 | x387 743.141546 393 | x388 744.425429 394 | x389 747.689472 395 | x390 754.662679 396 | x391 762.972455 397 | x392 767.680051 398 | x393 770.022101 399 | x394 768.269202 400 | x395 766.480932 401 | x396 763.178620 402 | x397 762.577255 403 | x398 759.357247 404 | x399 751.850621 405 | x400 744.383684 406 | x401 737.866698 407 | x402 732.241169 408 | x403 725.717038 409 | x404 718.661005 410 | x405 707.496414 411 | x406 693.671348 412 | x407 685.239024 413 | x408 683.952263 414 | x409 685.978077 415 | x410 684.779877 416 | x411 676.829056 417 | x412 664.711285 418 | x413 648.819675 419 | x414 633.301749 420 | x415 622.853338 421 | x416 611.896711 422 | x417 602.636330 423 | x418 597.530204 424 | x419 594.933681 425 | x420 597.186555 426 | x421 597.905383 427 | x422 597.441862 428 | x423 600.820842 429 | x424 608.048990 430 | x425 621.878697 431 | x426 638.815631 432 | x427 649.487866 433 | x428 665.857521 434 | x429 686.562114 435 | x430 696.933555 436 | x431 703.099095 437 | x432 714.089137 438 | x433 727.232067 439 | x434 737.876405 440 | x435 745.210260 441 | x436 747.389638 442 | x437 747.364621 443 | x438 744.418293 444 | x439 738.895269 445 | x440 734.462036 446 | x441 730.079010 447 | x442 720.625319 448 | x443 715.903133 449 | x444 713.275073 450 | x445 709.172655 451 | x446 699.961654 452 | x447 690.708705 453 | x448 680.343761 454 | x449 671.158195 455 | x450 657.864973 456 | x451 638.614461 457 | x452 619.396148 458 | x453 608.087588 459 | x454 602.672922 460 | x455 601.984263 461 | x456 603.387493 462 | x457 600.301655 463 | x458 594.201159 464 | x459 588.329460 465 | x460 582.168839 466 | x461 580.053076 467 | x462 582.231925 468 | x463 586.205258 469 | x464 590.596599 470 | x465 595.753878 471 | x466 600.396831 472 | x467 608.314982 473 | x468 617.314479 474 | x469 631.456765 475 | x470 643.696301 476 | x471 652.536377 477 | x472 663.673915 478 | x473 682.864544 479 | x474 699.827430 480 | x475 703.417209 481 | x476 710.428817 482 | x477 720.177000 483 | x478 729.890423 484 | x479 733.963661 485 | x480 734.181958 486 | x481 725.165697 487 | x482 714.539224 488 | x483 705.770888 489 | x484 701.573445 490 | x485 700.410311 491 | x486 698.491783 492 | x487 698.386473 493 | x488 700.828975 494 | x489 698.373315 495 | x490 695.412572 496 | x491 690.553606 497 | x492 684.953362 498 | x493 679.061730 499 | x494 669.518421 500 | x495 651.807922 501 | x496 630.974882 502 | x497 614.525797 503 | x498 600.907483 504 | x499 593.128782 505 | x500 586.593608 506 | x501 580.326617 507 | x502 570.141641 508 | x503 566.705180 509 | x504 570.740276 510 | x505 574.938523 511 | x506 577.814535 512 | x507 581.442293 513 | x508 586.585992 514 | x509 592.459616 515 | x510 600.277980 516 | x511 610.784063 517 | x512 625.000826 518 | x513 640.652825 519 | x514 654.433264 520 | x515 666.135137 521 | x516 680.217569 522 | x517 694.619548 523 | x518 703.846123 524 | x519 700.276777 525 | x520 697.474831 526 | x521 694.068218 527 | x522 692.681773 528 | x523 690.621114 529 | x524 691.448321 530 | x525 689.785431 531 | x526 683.730678 532 | x527 676.988086 533 | x528 672.697166 534 | x529 668.074091 535 | x530 663.882683 536 | x531 660.054834 537 | x532 654.335220 538 | x533 646.642530 539 | x534 641.851827 540 | x535 638.486283 541 | x536 636.321501 542 | x537 636.559825 543 | x538 636.526046 544 | x539 626.791903 545 | x540 620.633680 546 | x541 621.305621 547 | x542 625.075216 548 | x543 629.748495 549 | x544 627.634835 550 | x545 619.016830 551 | x546 607.694070 552 | x547 602.095158 553 | x548 598.051416 554 | x549 594.285481 555 | x550 593.936662 556 | x551 595.123837 557 | x552 593.959156 558 | x553 594.874141 559 | x554 599.111889 560 | x555 608.313589 561 | x556 622.895508 562 | x557 634.307416 563 | x558 645.429851 564 | x559 650.158963 565 | x560 662.144099 566 | x561 682.965478 567 | x562 693.047571 568 | x563 694.509435 569 | x564 695.731695 570 | x565 697.629385 571 | x566 706.587655 572 | x567 711.133760 573 | x568 716.652848 574 | x569 720.968533 575 | x570 721.970191 576 | x571 720.429715 577 | x572 724.211712 578 | x573 724.995708 579 | x574 720.408761 580 | x575 718.036010 581 | x576 710.599597 582 | x577 702.271616 583 | x578 695.744181 584 | x579 691.086153 585 | x580 684.674953 586 | x581 675.992511 587 | x582 666.494751 588 | x583 654.051158 589 | x584 634.663481 590 | x585 623.871157 591 | x586 616.701209 592 | x587 615.255118 593 | x588 614.413526 594 | x589 603.484777 595 | x590 593.260444 596 | x591 583.411565 597 | x592 577.967535 598 | x593 572.297260 599 | x594 563.091274 600 | x595 555.581370 601 | x596 557.502663 602 | x597 562.472347 603 | x598 568.893551 604 | x599 572.073067 605 | x600 576.399174 606 | x601 585.754812 607 | x602 600.126491 608 | x603 609.908462 609 | x604 616.050915 610 | x605 623.954830 611 | x606 638.500883 612 | x607 656.740461 613 | x608 679.435803 614 | x609 702.206532 615 | x610 711.215841 616 | x611 711.188787 617 | x612 721.943509 618 | x613 735.859767 619 | x614 746.920229 620 | x615 751.828696 621 | x616 749.686947 622 | x617 741.309292 623 | x618 738.818386 624 | x619 734.732755 625 | x620 726.896852 626 | x621 720.328886 627 | x622 717.633797 628 | x623 715.355945 629 | x624 713.661457 630 | x625 713.954324 631 | x626 714.789036 632 | x627 713.261458 633 | x628 710.229825 634 | x629 704.101389 635 | x630 694.070851 636 | x631 680.092328 637 | x632 669.105115 638 | x633 657.353296 639 | x634 645.990595 640 | x635 636.964485 641 | x636 627.686405 642 | x637 617.149860 643 | x638 616.404198 644 | x639 609.515533 645 | x640 598.028788 646 | x641 590.853762 647 | x642 585.980464 648 | x643 583.798179 649 | x644 583.703036 650 | x645 583.120746 651 | x646 583.615279 652 | x647 582.783362 653 | x648 579.409035 654 | x649 575.176822 655 | x650 572.974508 656 | x651 569.761046 657 | x652 567.642264 658 | x653 567.297269 659 | x654 571.052560 660 | x655 575.431997 661 | x656 580.676486 662 | x657 588.406046 663 | x658 597.621375 664 | x659 607.935658 665 | x660 619.465195 666 | x661 630.299552 667 | x662 638.463707 668 | x663 647.122678 669 | x664 651.903639 670 | x665 658.964984 671 | x666 664.723544 672 | x667 671.304124 673 | x668 676.312951 674 | x669 680.539639 675 | x670 683.991040 676 | x671 685.018283 677 | x672 686.613841 678 | x673 690.799116 679 | x674 695.479809 680 | x675 696.323470 681 | x676 697.849613 682 | x677 701.113490 683 | x678 703.399933 684 | x679 703.705965 685 | x680 703.669260 686 | x681 704.111914 687 | x682 706.604469 688 | x683 709.403805 689 | x684 712.161812 690 | x685 715.648238 691 | x686 718.893950 692 | x687 721.631663 693 | x688 724.175428 694 | x689 725.844256 695 | x690 729.254366 696 | x691 731.108544 697 | x692 733.656458 698 | x693 733.258494 699 | x694 733.097532 700 | x695 733.360587 701 | x696 731.119295 702 | x697 730.405018 703 | x698 728.790654 704 | x699 727.104845 705 | x700 727.927867 706 | x701 727.309580 707 | x702 727.301440 708 | x703 727.111814 709 | x704 727.477263 710 | x705 727.459211 711 | x706 727.408148 712 | x707 727.199667 713 | x708 726.167070 714 | x709 726.977865 715 | x710 726.909614 716 | x711 727.876808 717 | x712 728.205969 718 | x713 728.203529 719 | x714 728.544281 720 | x715 728.158138 721 | x716 726.791760 722 | x717 727.559317 723 | x718 726.289385 724 | x719 725.978774 725 | x720 727.083266 726 | x721 727.250921 727 | x722 728.535163 728 | x723 728.940732 729 | x724 728.479415 730 | x725 730.097371 731 | x726 730.529838 732 | x727 731.305491 733 | x728 733.681993 734 | x729 733.805644 735 | x730 734.960481 736 | x731 736.536087 737 | x732 736.982050 738 | x733 738.001326 739 | x734 739.197830 740 | x735 739.304316 741 | x736 740.091846 742 | x737 739.490873 743 | x738 740.211551 744 | x739 742.732160 745 | x740 745.771279 746 | x741 748.229353 747 | x742 752.197913 748 | x743 754.471050 749 | x744 753.369483 750 | x745 752.545103 751 | x746 751.973175 752 | x747 752.655712 753 | x748 749.821300 754 | x749 747.354181 755 | x750 744.318160 756 | x751 742.284357 757 | x752 740.410938 758 | x753 739.832251 759 | x754 737.376578 760 | x755 735.259568 761 | x756 733.833284 762 | x757 731.818342 763 | x758 729.114507 764 | x759 727.516112 765 | x760 728.912502 766 | x761 728.507177 767 | x762 729.955712 768 | x763 731.004815 769 | x764 729.291510 770 | x765 728.808882 771 | x766 728.333063 772 | x767 728.188195 773 | x768 727.508188 774 | x769 727.925290 775 | x770 725.138234 776 | x771 726.030530 777 | x772 724.801300 778 | x773 728.136863 779 | x774 730.023786 780 | x775 730.494844 781 | x776 731.738950 782 | x777 734.087366 783 | x778 735.131468 784 | x779 733.736702 785 | x780 734.886150 786 | x781 735.788446 787 | x782 736.426526 788 | x783 739.185305 789 | x784 741.490908 790 | x785 742.730984 791 | x786 745.895788 792 | x787 745.969076 793 | x788 744.457941 794 | x789 743.504917 795 | x790 742.816615 796 | x791 743.606647 797 | x792 745.911474 798 | x793 746.691621 799 | x794 748.182790 800 | x795 751.282311 801 | x796 751.040410 802 | x797 751.281441 803 | x798 753.623101 804 | x799 755.282256 805 | x800 754.640193 806 | x801 756.522302 807 | x802 757.805055 808 | x803 756.347457 809 | x804 758.336816 810 | x805 758.152541 811 | x806 757.520420 812 | x807 757.538508 813 | x808 759.453400 814 | x809 761.761162 815 | x810 761.425607 816 | x811 763.745262 817 | x812 763.770220 818 | x813 764.417361 819 | x814 763.960020 820 | x815 763.804240 821 | x816 763.137009 822 | x817 761.113949 823 | x818 759.816619 824 | x819 758.607813 825 | x820 761.263171 826 | x821 761.500971 827 | x822 760.210013 828 | x823 757.749950 829 | x824 757.715917 830 | x825 755.596573 831 | x826 756.057660 832 | x827 757.996880 833 | x828 758.898347 834 | x829 758.134853 835 | x830 758.914445 836 | x831 761.505708 837 | x832 765.172376 838 | x833 770.733293 839 | x834 773.711611 840 | x835 772.148390 841 | x836 772.119570 842 | x837 773.840577 843 | x838 773.995419 844 | x839 773.212678 845 | x840 774.217898 846 | x841 774.115534 847 | x842 771.690926 848 | x843 770.085288 849 | x844 763.428270 850 | x845 759.139771 851 | x846 757.330411 852 | x847 752.693359 853 | x848 751.859615 854 | x849 750.013747 855 | x850 747.673393 856 | x851 747.400695 857 | x852 746.641026 858 | x853 744.088493 859 | x854 742.864285 860 | x855 739.480101 861 | x856 737.654260 862 | x857 736.437168 863 | x858 737.429021 864 | x859 746.738666 865 | x860 763.828862 866 | x861 765.475837 867 | x862 758.078158 868 | x924 704.847042 869 | x925 713.747281 870 | x926 724.735512 871 | x927 729.947577 872 | x928 729.573385 873 | x929 731.212156 874 | x930 740.185438 875 | x931 747.268432 876 | x932 750.182590 877 | x933 749.512849 878 | x934 751.795032 879 | x935 757.136660 880 | x936 755.499809 881 | x937 755.009492 882 | x938 757.175025 883 | x939 753.654396 884 | x940 749.826131 885 | x941 743.968086 886 | x942 736.841606 887 | x943 730.062952 888 | x944 722.244737 889 | x945 715.449711 890 | x946 707.317028 891 | x947 697.572391 892 | x948 687.958100 893 | x949 680.634032 894 | x950 677.684137 895 | x951 680.914472 896 | x952 680.434879 897 | } 898 | {curve 899 | x1 360.687853 900 | x2 349.672035 901 | x3 342.513585 902 | x4 345.903744 903 | x5 355.215362 904 | x6 364.621008 905 | x7 368.185242 906 | x8 369.102679 907 | x9 373.489436 908 | x10 376.348019 909 | x11 375.279603 910 | x12 368.167388 911 | x13 362.024074 912 | x14 362.456248 913 | x15 363.351781 914 | x16 361.965567 915 | x17 361.814327 916 | x18 361.510223 917 | x19 359.355200 918 | x20 361.350724 919 | x21 360.669351 920 | x22 358.826697 921 | x23 358.438048 922 | x24 360.917755 923 | x25 360.815120 924 | x26 364.622643 925 | x27 367.709653 926 | x28 371.005648 927 | x29 372.958349 928 | x30 377.750458 929 | x31 380.398618 930 | x32 380.668988 931 | x33 383.987647 932 | x34 386.191354 933 | x35 390.025421 934 | x36 391.393041 935 | x37 393.204680 936 | x38 394.620830 937 | x39 398.093912 938 | x40 400.766923 939 | x41 403.147072 940 | x42 398.535724 941 | x43 400.253985 942 | x44 404.202560 943 | x45 409.044536 944 | x46 414.706399 945 | x47 418.025703 946 | x48 415.965011 947 | x49 416.432668 948 | x50 413.570168 949 | x51 413.243068 950 | x52 413.380481 951 | x53 413.147965 952 | x54 409.848721 953 | x55 411.018793 954 | x56 421.450855 955 | x57 430.815968 956 | x58 437.298593 957 | x59 433.705644 958 | x60 415.709033 959 | x61 404.055101 960 | x62 399.967519 961 | x63 403.293606 962 | x64 408.536638 963 | x65 404.777074 964 | x66 396.821506 965 | x67 393.073420 966 | x68 388.693495 967 | x69 390.441585 968 | x70 399.377514 969 | x71 401.015042 970 | x72 403.018858 971 | x73 402.907563 972 | x74 398.049139 973 | x75 397.747678 974 | x76 410.080707 975 | x77 422.425082 976 | x78 433.167389 977 | x79 438.940510 978 | x80 440.746537 979 | x81 436.860745 980 | x82 442.245036 981 | x83 445.958212 982 | x84 449.827284 983 | x85 453.737594 984 | x86 460.690381 985 | x87 459.362297 986 | x88 455.062161 987 | x89 457.232193 988 | x90 456.276913 989 | x91 458.734313 990 | x92 464.450715 991 | x93 466.426907 992 | x94 468.333225 993 | x95 474.670216 994 | x96 479.285531 995 | x97 484.192519 996 | x98 486.124476 997 | x99 486.251869 998 | x100 486.319670 999 | x101 488.179782 1000 | x102 490.456186 1001 | x103 492.931216 1002 | x104 490.494518 1003 | x105 479.883399 1004 | x106 463.586486 1005 | x107 445.179822 1006 | x108 429.664474 1007 | x109 425.974298 1008 | x110 435.414947 1009 | x111 441.505413 1010 | x112 442.116416 1011 | x113 443.048132 1012 | x114 443.020452 1013 | x115 446.034014 1014 | x116 447.025659 1015 | x117 446.495745 1016 | x118 449.054331 1017 | x119 453.191248 1018 | x120 455.257874 1019 | x121 458.249794 1020 | x122 457.078929 1021 | x123 456.343622 1022 | x124 454.244236 1023 | x125 446.467625 1024 | x126 443.829485 1025 | x127 448.029583 1026 | x128 449.580226 1027 | x129 452.637464 1028 | x130 453.419376 1029 | x131 451.573075 1030 | x132 449.892177 1031 | x133 458.079208 1032 | x134 466.104454 1033 | x135 476.938691 1034 | x136 480.286102 1035 | x137 478.774479 1036 | x138 478.817560 1037 | x139 481.587727 1038 | x140 478.359878 1039 | x141 472.833973 1040 | x142 479.452281 1041 | x143 481.472711 1042 | x144 484.584350 1043 | x145 480.200776 1044 | x146 476.946664 1045 | x147 472.740253 1046 | x148 467.606918 1047 | x149 465.518309 1048 | x150 460.173319 1049 | x151 458.273903 1050 | x152 462.532541 1051 | x153 472.090309 1052 | x154 480.487343 1053 | x155 484.044723 1054 | x156 478.770196 1055 | x157 477.525489 1056 | x158 475.813760 1057 | x159 472.984344 1058 | x160 467.325981 1059 | x161 455.410103 1060 | x162 425.718297 1061 | x163 402.981698 1062 | x164 394.669985 1063 | x165 403.031765 1064 | x166 415.711847 1065 | x167 425.377130 1066 | x168 432.173707 1067 | x169 431.908418 1068 | x170 439.700152 1069 | x171 448.961823 1070 | x172 446.873340 1071 | x173 448.993838 1072 | x174 447.112037 1073 | x175 444.942920 1074 | x176 441.691900 1075 | x177 440.109701 1076 | x178 440.087490 1077 | x179 447.620083 1078 | x180 455.068991 1079 | x181 453.672391 1080 | x182 452.319428 1081 | x183 456.592665 1082 | x184 467.297197 1083 | x185 488.764341 1084 | x186 492.414409 1085 | x187 491.806995 1086 | x188 491.528026 1087 | x189 494.208013 1088 | x190 500.355792 1089 | x191 508.366942 1090 | x192 509.658762 1091 | x193 508.862775 1092 | x194 506.689231 1093 | x195 502.570414 1094 | x196 501.862848 1095 | x197 503.720867 1096 | x198 505.652723 1097 | x199 505.940422 1098 | x200 506.848452 1099 | x201 507.635748 1100 | x202 508.912550 1101 | x203 511.117173 1102 | x204 513.989822 1103 | x205 514.077452 1104 | x206 514.723009 1105 | x207 520.454296 1106 | x208 531.033961 1107 | x209 525.179689 1108 | x210 519.776467 1109 | x211 536.806464 1110 | x212 544.331185 1111 | x213 542.977207 1112 | x214 531.729848 1113 | x215 512.649267 1114 | x216 508.664506 1115 | x217 504.786866 1116 | x218 514.027722 1117 | x219 525.163933 1118 | x220 525.676368 1119 | x221 528.826664 1120 | x222 525.028474 1121 | x223 519.114313 1122 | x224 513.108057 1123 | x225 509.272256 1124 | x226 506.751165 1125 | x227 505.229778 1126 | x228 505.926253 1127 | x229 501.894322 1128 | x230 501.723175 1129 | x231 502.842368 1130 | x232 506.086826 1131 | x233 503.271923 1132 | x234 507.949134 1133 | x235 512.813469 1134 | x236 514.979129 1135 | x237 522.567019 1136 | x238 527.314773 1137 | x239 529.846327 1138 | x240 531.816879 1139 | x241 529.728459 1140 | x242 529.055969 1141 | x243 525.126881 1142 | x244 522.626131 1143 | x245 517.039582 1144 | x246 515.223131 1145 | x247 518.498678 1146 | x248 523.418647 1147 | x249 522.530120 1148 | x250 525.441830 1149 | x251 523.319636 1150 | x252 522.536093 1151 | x253 524.398994 1152 | x254 526.075093 1153 | x255 531.106145 1154 | x256 530.190326 1155 | x257 524.172946 1156 | x258 523.923752 1157 | x259 523.270822 1158 | x260 524.474245 1159 | x261 522.976809 1160 | x262 524.644902 1161 | x263 523.423152 1162 | x264 522.389461 1163 | x265 514.707262 1164 | x266 502.114101 1165 | x267 502.577417 1166 | x268 514.725542 1167 | x269 520.674168 1168 | x270 518.038936 1169 | x271 508.335995 1170 | x272 509.866625 1171 | x273 521.264164 1172 | x274 527.516478 1173 | x275 534.585054 1174 | x276 538.488793 1175 | x277 539.404105 1176 | x278 538.893258 1177 | x279 544.498700 1178 | x280 539.586790 1179 | x281 531.197417 1180 | x282 525.481765 1181 | x283 519.503440 1182 | x284 518.899588 1183 | x285 520.161642 1184 | x286 521.433137 1185 | x287 526.365014 1186 | x288 534.529560 1187 | x289 548.266807 1188 | x290 543.901479 1189 | x291 548.358071 1190 | x292 547.429889 1191 | x293 544.012927 1192 | x294 543.555105 1193 | x295 542.136415 1194 | x296 543.787986 1195 | x297 547.629044 1196 | x298 545.553371 1197 | x299 546.800819 1198 | x300 551.391889 1199 | x301 553.260957 1200 | x302 554.085548 1201 | x303 550.034454 1202 | x304 546.175411 1203 | x305 545.383471 1204 | x306 550.127284 1205 | x307 558.491170 1206 | x308 564.260685 1207 | x309 565.223592 1208 | x310 563.730188 1209 | x311 557.586409 1210 | x312 548.593256 1211 | x313 544.587114 1212 | x314 537.882896 1213 | x315 525.625863 1214 | x316 520.510132 1215 | x317 526.615135 1216 | x318 532.699275 1217 | x319 533.500158 1218 | x320 528.159141 1219 | x321 527.257301 1220 | x322 525.462879 1221 | x323 524.671319 1222 | x324 529.219271 1223 | x325 536.729182 1224 | x326 543.635526 1225 | x327 549.809953 1226 | x328 550.873990 1227 | x329 547.398144 1228 | x330 548.407366 1229 | x331 549.561614 1230 | x332 550.527254 1231 | x333 554.585942 1232 | x334 559.349728 1233 | x335 560.251019 1234 | x336 561.013284 1235 | x337 557.354385 1236 | x338 551.384813 1237 | x339 553.378606 1238 | x340 559.187193 1239 | x341 558.023432 1240 | x342 548.789766 1241 | x343 536.781494 1242 | x344 536.658020 1243 | x345 536.904555 1244 | x346 537.868994 1245 | x347 544.771741 1246 | x348 549.760049 1247 | x349 554.330505 1248 | x350 553.459538 1249 | x351 551.095759 1250 | x352 551.362764 1251 | x353 554.443706 1252 | x354 557.038781 1253 | x355 558.509575 1254 | x356 554.782984 1255 | x357 554.120385 1256 | x358 556.703980 1257 | x359 555.821556 1258 | x360 551.368608 1259 | x361 543.308548 1260 | x362 529.722340 1261 | x363 525.064031 1262 | x364 525.013550 1263 | x365 525.862458 1264 | x366 525.150223 1265 | x367 517.370733 1266 | x368 509.009555 1267 | x369 506.018203 1268 | x370 516.685043 1269 | x371 532.707212 1270 | x372 541.258460 1271 | x373 545.682043 1272 | x374 548.914145 1273 | x375 552.752859 1274 | x376 555.624204 1275 | x377 560.177872 1276 | x378 566.315702 1277 | x379 571.513555 1278 | x380 572.250868 1279 | x381 567.718897 1280 | x382 560.494635 1281 | x383 546.297075 1282 | x384 538.283562 1283 | x385 530.159105 1284 | x386 524.582929 1285 | x387 524.835190 1286 | x388 524.349222 1287 | x389 527.114651 1288 | x390 537.502773 1289 | x391 551.403511 1290 | x392 556.846024 1291 | x393 560.127339 1292 | x394 568.491579 1293 | x395 573.898558 1294 | x396 577.176922 1295 | x397 580.363890 1296 | x398 581.653452 1297 | x399 583.721338 1298 | x400 584.044969 1299 | x401 586.711678 1300 | x402 588.766114 1301 | x403 587.556369 1302 | x404 584.336402 1303 | x405 576.486620 1304 | x406 565.496287 1305 | x407 560.526248 1306 | x408 563.649169 1307 | x409 567.487639 1308 | x410 560.491435 1309 | x411 557.344347 1310 | x412 554.123251 1311 | x413 550.658161 1312 | x414 556.944621 1313 | x415 566.569307 1314 | x416 573.041487 1315 | x417 584.700782 1316 | x418 591.693408 1317 | x419 596.903038 1318 | x420 605.462442 1319 | x421 612.139889 1320 | x422 616.497121 1321 | x423 617.722304 1322 | x424 623.264026 1323 | x425 626.945493 1324 | x426 628.271601 1325 | x427 625.492042 1326 | x428 619.255708 1327 | x429 618.403671 1328 | x430 617.933233 1329 | x431 614.778423 1330 | x432 606.420255 1331 | x433 603.417208 1332 | x434 612.057482 1333 | x435 623.844826 1334 | x436 635.349290 1335 | x437 637.786869 1336 | x438 635.225970 1337 | x439 632.835851 1338 | x440 635.215511 1339 | x441 636.918929 1340 | x442 635.925610 1341 | x443 637.637574 1342 | x444 644.429883 1343 | x445 653.204828 1344 | x446 656.803256 1345 | x447 658.971885 1346 | x448 658.338302 1347 | x449 655.366625 1348 | x450 647.321481 1349 | x451 633.399720 1350 | x452 629.177391 1351 | x453 628.589559 1352 | x454 631.428998 1353 | x455 632.290668 1354 | x456 624.504104 1355 | x457 616.324188 1356 | x458 612.388478 1357 | x459 608.673651 1358 | x460 609.600327 1359 | x461 609.850814 1360 | x462 612.449724 1361 | x463 614.080112 1362 | x464 612.703117 1363 | x465 609.631745 1364 | x466 610.568687 1365 | x467 612.449833 1366 | x468 615.347330 1367 | x469 617.002592 1368 | x470 615.262108 1369 | x471 614.831501 1370 | x472 607.185163 1371 | x473 598.260637 1372 | x474 587.312744 1373 | x475 587.267340 1374 | x476 584.267896 1375 | x477 582.167767 1376 | x478 591.524232 1377 | x479 601.287847 1378 | x480 607.703274 1379 | x481 611.565462 1380 | x482 605.039690 1381 | x483 596.487909 1382 | x484 597.953880 1383 | x485 595.695810 1384 | x486 595.664703 1385 | x487 601.404813 1386 | x488 605.133569 1387 | x489 613.436591 1388 | x490 618.946501 1389 | x491 621.941702 1390 | x492 621.950805 1391 | x493 622.036647 1392 | x494 613.251063 1393 | x495 600.418999 1394 | x496 591.682086 1395 | x497 590.386301 1396 | x498 592.156093 1397 | x499 595.940592 1398 | x500 595.347698 1399 | x501 589.474731 1400 | x502 581.297321 1401 | x503 577.059354 1402 | x504 572.776027 1403 | x505 570.179472 1404 | x506 567.342718 1405 | x507 565.460171 1406 | x508 569.484109 1407 | x509 579.232500 1408 | x510 589.074166 1409 | x511 600.374726 1410 | x512 612.477091 1411 | x513 615.265357 1412 | x514 614.980932 1413 | x515 611.498738 1414 | x516 598.834066 1415 | x517 598.620903 1416 | x518 592.354856 1417 | x519 588.812499 1418 | x520 584.134320 1419 | x521 589.764323 1420 | x522 597.200974 1421 | x523 602.054452 1422 | x524 604.580236 1423 | x525 603.841620 1424 | x526 602.249467 1425 | x527 603.309260 1426 | x528 607.346946 1427 | x529 610.808349 1428 | x530 612.169336 1429 | x531 616.065283 1430 | x532 616.682168 1431 | x533 615.615444 1432 | x534 619.440487 1433 | x535 619.355387 1434 | x536 615.388203 1435 | x537 614.918864 1436 | x538 616.774773 1437 | x539 602.374370 1438 | x540 584.435998 1439 | x541 573.900189 1440 | x542 565.466271 1441 | x543 564.482638 1442 | x544 563.205117 1443 | x545 558.406403 1444 | x546 556.844493 1445 | x547 560.551643 1446 | x548 566.608955 1447 | x549 576.930998 1448 | x550 583.495408 1449 | x551 589.950963 1450 | x552 595.012493 1451 | x553 600.333384 1452 | x554 606.196354 1453 | x555 614.259243 1454 | x556 618.379239 1455 | x557 616.919631 1456 | x558 610.281052 1457 | x559 606.647467 1458 | x560 598.843429 1459 | x561 595.907824 1460 | x562 597.093951 1461 | x563 599.972017 1462 | x564 604.789627 1463 | x565 614.080624 1464 | x566 617.814374 1465 | x567 621.281212 1466 | x568 625.042430 1467 | x569 623.345113 1468 | x570 618.836478 1469 | x571 618.109798 1470 | x572 621.969751 1471 | x573 621.647455 1472 | x574 622.926020 1473 | x575 622.922869 1474 | x576 624.529863 1475 | x577 627.628539 1476 | x578 629.573048 1477 | x579 631.242369 1478 | x580 628.159704 1479 | x581 617.921301 1480 | x582 605.725486 1481 | x583 589.402997 1482 | x584 581.441690 1483 | x585 572.315168 1484 | x586 569.078869 1485 | x587 567.920765 1486 | x588 558.735281 1487 | x589 546.008818 1488 | x590 536.462057 1489 | x591 538.858812 1490 | x592 546.819412 1491 | x593 557.851367 1492 | x594 557.001721 1493 | x595 552.597816 1494 | x596 547.741233 1495 | x597 546.868322 1496 | x598 548.650959 1497 | x599 554.602073 1498 | x600 563.438592 1499 | x601 577.849767 1500 | x602 591.841729 1501 | x603 595.091702 1502 | x604 594.137983 1503 | x605 596.291216 1504 | x606 596.494102 1505 | x607 595.991368 1506 | x608 596.661757 1507 | x609 585.371717 1508 | x610 572.649622 1509 | x611 575.039117 1510 | x612 578.952353 1511 | x613 581.902711 1512 | x614 583.301094 1513 | x615 582.745940 1514 | x616 574.655953 1515 | x617 573.825289 1516 | x618 576.870281 1517 | x619 579.570665 1518 | x620 583.997499 1519 | x621 588.176661 1520 | x622 588.873108 1521 | x623 593.400644 1522 | x624 599.284746 1523 | x625 609.030239 1524 | x626 619.419508 1525 | x627 625.791779 1526 | x628 627.914695 1527 | x629 629.243558 1528 | x630 625.048084 1529 | x631 620.219579 1530 | x632 618.577556 1531 | x633 614.105704 1532 | x634 605.623579 1533 | x635 596.845798 1534 | x636 592.001307 1535 | x637 592.039595 1536 | x638 594.946100 1537 | x639 599.004658 1538 | x640 599.561880 1539 | x641 591.297884 1540 | x642 586.670062 1541 | x643 582.593865 1542 | x644 583.336161 1543 | x645 585.510473 1544 | x646 587.029782 1545 | x647 587.233690 1546 | x648 587.735659 1547 | x649 589.363159 1548 | x650 590.608562 1549 | x651 591.903643 1550 | x652 594.067304 1551 | x653 596.366221 1552 | x654 598.261863 1553 | x655 597.677833 1554 | x656 596.224693 1555 | x657 595.650906 1556 | x658 595.959912 1557 | x659 591.666447 1558 | x660 590.356232 1559 | x661 585.490589 1560 | x662 588.484898 1561 | x663 589.577936 1562 | x664 587.317227 1563 | x665 586.555771 1564 | x666 585.011442 1565 | x667 583.446210 1566 | x668 581.227220 1567 | x669 577.317130 1568 | x670 571.765377 1569 | x671 572.932790 1570 | x672 572.781431 1571 | x673 575.604101 1572 | x674 576.581110 1573 | x675 580.627133 1574 | x676 582.411678 1575 | x677 585.035322 1576 | x678 586.234369 1577 | x679 584.786821 1578 | x680 579.380032 1579 | x681 576.828867 1580 | x682 571.573774 1581 | x683 568.925115 1582 | x684 567.029615 1583 | x685 566.583646 1584 | x686 565.388728 1585 | x687 564.058519 1586 | x688 563.102886 1587 | x689 562.264383 1588 | x690 563.484189 1589 | x691 561.915844 1590 | x692 560.188087 1591 | x693 555.356913 1592 | x694 551.283388 1593 | x695 547.680760 1594 | x696 545.241900 1595 | x697 543.701855 1596 | x698 542.077092 1597 | x699 539.367518 1598 | x700 536.810781 1599 | x701 532.482513 1600 | x702 528.832291 1601 | x703 524.051384 1602 | x704 518.276282 1603 | x705 512.501813 1604 | x706 506.615980 1605 | x707 500.628833 1606 | x708 495.346696 1607 | x709 490.768612 1608 | x710 486.346982 1609 | x711 483.106954 1610 | x712 478.681531 1611 | x713 472.449095 1612 | x714 467.831152 1613 | x715 465.455715 1614 | x716 464.385839 1615 | x717 463.060854 1616 | x718 458.845896 1617 | x719 455.402984 1618 | x720 449.552352 1619 | x721 444.590384 1620 | x722 439.817268 1621 | x723 435.714517 1622 | x724 431.614806 1623 | x725 426.363817 1624 | x726 422.141298 1625 | x727 415.803631 1626 | x728 407.804846 1627 | x729 401.237950 1628 | x730 392.330970 1629 | x731 383.862177 1630 | x732 374.300701 1631 | x733 366.558441 1632 | x734 360.169061 1633 | x735 353.295702 1634 | x736 348.667372 1635 | x737 342.851920 1636 | x738 341.991312 1637 | x739 336.887525 1638 | x740 332.274600 1639 | x741 324.706783 1640 | x742 315.734102 1641 | x743 304.505751 1642 | x744 294.093428 1643 | x745 282.414212 1644 | x746 273.703727 1645 | x747 261.764588 1646 | x748 251.476377 1647 | x749 237.539744 1648 | x750 225.500296 1649 | x751 214.901526 1650 | x752 207.612373 1651 | x753 202.442852 1652 | x754 198.074173 1653 | x755 190.071054 1654 | x756 186.326226 1655 | x757 181.822673 1656 | x758 177.621268 1657 | x759 172.805055 1658 | x760 167.098201 1659 | x761 163.177875 1660 | x762 157.573388 1661 | x763 152.581368 1662 | x764 148.999015 1663 | x765 144.507722 1664 | x766 141.916793 1665 | x767 136.714781 1666 | x768 131.953030 1667 | x769 127.983804 1668 | x770 123.233213 1669 | x771 121.751054 1670 | x772 116.940333 1671 | x773 112.580672 1672 | x774 107.061722 1673 | x775 106.063464 1674 | x776 107.433028 1675 | x777 111.241212 1676 | x778 115.426443 1677 | x779 117.703033 1678 | x780 117.077161 1679 | x781 114.752581 1680 | x782 111.951283 1681 | x783 109.747647 1682 | x784 107.734769 1683 | x785 108.550498 1684 | x786 104.602645 1685 | x787 103.287010 1686 | x788 100.505791 1687 | x789 98.897613 1688 | x790 94.719251 1689 | x791 93.839575 1690 | x792 91.782999 1691 | x793 94.374905 1692 | x794 95.186302 1693 | x795 97.328987 1694 | x796 99.141722 1695 | x797 101.088389 1696 | x798 103.188528 1697 | x799 109.740053 1698 | x800 113.968342 1699 | x801 117.331773 1700 | x802 118.431954 1701 | x803 117.773763 1702 | x804 122.482609 1703 | x805 127.129841 1704 | x806 131.510709 1705 | x807 137.293900 1706 | x808 141.473180 1707 | x809 148.585990 1708 | x810 154.284861 1709 | x811 161.341361 1710 | x812 168.098843 1711 | x813 174.759946 1712 | x814 182.853842 1713 | x815 189.075463 1714 | x816 196.322225 1715 | x817 204.421832 1716 | x818 213.251250 1717 | x819 224.206957 1718 | x820 233.444934 1719 | x821 240.944059 1720 | x822 244.981337 1721 | x823 248.500929 1722 | x824 253.912217 1723 | x825 261.894068 1724 | x826 272.873566 1725 | x827 281.750646 1726 | x828 288.872630 1727 | x829 295.666775 1728 | x830 305.141029 1729 | x831 315.536585 1730 | x832 327.481294 1731 | x833 334.659400 1732 | x834 343.445837 1733 | x835 352.080418 1734 | x836 361.642436 1735 | x837 374.477726 1736 | x838 385.313669 1737 | x839 395.369031 1738 | x840 403.538841 1739 | x841 409.716845 1740 | x842 418.085831 1741 | x843 424.183697 1742 | x844 429.309745 1743 | x845 432.236207 1744 | x846 435.860022 1745 | x847 440.744587 1746 | x848 444.830986 1747 | x849 448.801801 1748 | x850 452.851408 1749 | x851 454.346115 1750 | x852 458.707254 1751 | x853 460.783802 1752 | x854 467.624049 1753 | x855 473.682922 1754 | x856 481.901351 1755 | x857 492.338995 1756 | x858 503.618711 1757 | x859 517.063356 1758 | x860 531.389033 1759 | x861 536.506260 1760 | x862 535.435738 1761 | x924 589.973611 1762 | x925 581.482567 1763 | x926 580.063268 1764 | x927 580.185445 1765 | x928 576.674707 1766 | x929 564.258781 1767 | x930 558.734493 1768 | x931 563.345984 1769 | x932 573.192046 1770 | x933 582.447087 1771 | x934 590.938690 1772 | x935 596.595284 1773 | x936 599.530639 1774 | x937 598.410834 1775 | x938 598.663567 1776 | x939 598.254646 1777 | x940 599.277347 1778 | x941 599.355295 1779 | x942 601.254410 1780 | x943 602.848509 1781 | x944 605.321266 1782 | x945 605.439356 1783 | x946 606.673492 1784 | x947 605.759628 1785 | x948 607.506836 1786 | x949 604.434708 1787 | x950 609.307610 1788 | x951 612.563824 1789 | x952 619.206589 1790 | }} 1791 | name Tr_01 1792 | selected true 1793 | } 1794 | 1795 | --------------------------------------------------------------------------------