RUIC 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/HISTORY +23 -0
  3. data/README.md +2 -2
  4. data/lib/ruic.rb +11 -3
  5. data/lib/ruic/application.rb +28 -22
  6. data/lib/ruic/attributes.rb +10 -2
  7. data/lib/ruic/behaviors.rb +2 -15
  8. data/lib/ruic/effect.rb +32 -0
  9. data/lib/ruic/interfaces.rb +51 -13
  10. data/lib/ruic/presentation.rb +46 -20
  11. data/lib/ruic/renderplugin.rb +18 -0
  12. data/lib/ruic/ripl.rb +45 -0
  13. data/lib/ruic/statemachine.rb +142 -25
  14. data/lib/ruic/version.rb +1 -1
  15. data/test/customclasses.ruic +0 -1
  16. data/test/filtering.ruic +0 -1
  17. data/test/futureassets.ruic +0 -1
  18. data/test/nonmaster.ruic +0 -1
  19. data/test/paths.ruic +0 -2
  20. data/test/projects/MissingAssets/Existing.uip +87 -0
  21. data/test/projects/MissingAssets/MissingAssets.uia +21 -0
  22. data/test/projects/MissingAssets/MissingAssets.uia-user +14 -0
  23. data/test/projects/MissingAssets/RoundedPlane-1/RoundedPlane-1.import +18 -0
  24. data/test/projects/MissingAssets/RoundedPlane-1/meshes/RoundedPlane.mesh +0 -0
  25. data/test/projects/MissingAssets/effects/effects.txt +3 -0
  26. data/test/projects/MissingAssets/effects/existing.effect +38 -0
  27. data/test/projects/MissingAssets/fonts/Arimo-Regular.ttf +0 -0
  28. data/test/projects/MissingAssets/fonts/Chivo-Black.ttf +0 -0
  29. data/test/projects/MissingAssets/fonts/OFL.txt +92 -0
  30. data/test/projects/MissingAssets/maps/effects/brushnoise.dds +0 -0
  31. data/test/projects/MissingAssets/maps/existing.png +0 -0
  32. data/test/projects/MissingAssets/maps/existing2.png +0 -0
  33. data/test/projects/MissingAssets/maps/maps.txt +2 -0
  34. data/test/projects/MissingAssets/maps/materials/concrete_plain.png +0 -0
  35. data/test/projects/MissingAssets/maps/materials/concrete_plain_bump.png +0 -0
  36. data/test/projects/MissingAssets/maps/materials/spherical_checker.png +0 -0
  37. data/test/projects/MissingAssets/maps/unused.png +0 -0
  38. data/test/projects/MissingAssets/materials/concrete.material +251 -0
  39. data/test/projects/MissingAssets/materials/materials.txt +3 -0
  40. data/test/projects/MissingAssets/scripts/existing1.lua +2 -0
  41. data/test/projects/MissingAssets/scripts/existing2.lua +470 -0
  42. data/test/projects/MissingAssets/states/existing.scxml +4 -0
  43. data/test/projects/MissingAssets/tests/interface-navigation.scxml-test +3 -0
  44. data/test/properties.ruic +0 -2
  45. data/test/referencematerials.ruic +0 -2
  46. data/test/usage.ruic +38 -4
  47. metadata +53 -3
  48. data/lib/ruic/ripl-after-result.rb +0 -25
@@ -0,0 +1,18 @@
1
+ class UIC::RenderPlugin
2
+ include UIC::FileBacked
3
+ def initialize( file )
4
+ self.file = file
5
+ end
6
+ end
7
+
8
+ class UIC::Application::RenderPlugin < UIC::RenderPlugin
9
+ include UIC::ElementBacked
10
+ # @!parse extend UIC::ElementBacked::ClassMethods
11
+ xmlattribute :id
12
+ xmlattribute :src
13
+ def initialize(application,el)
14
+ self.owner = application
15
+ self.el = el
16
+ super( application.absolute_path(src) )
17
+ end
18
+ end
@@ -0,0 +1,45 @@
1
+ module Ripl; end
2
+
3
+ # Allows [Ripl](https://github.com/cldwalker/ripl) to execute code or print a message after every result.
4
+ #
5
+ # @example 1) Printing a blank line after each result
6
+ # require_relative 'ripl-after-result'
7
+ # Ripl::Shell.include Ripl::AfterResult
8
+ # Ripl.config.merge! after_result:"\n"
9
+ #
10
+ # @example 2) Executing arbitrary code with the result
11
+ # results = []
12
+ # require_relative 'ripl-after-result'
13
+ # Ripl::Shell.include Ripl::AfterResult
14
+ # Ripl.config.merge! after_result:proc{ |result| results << result }
15
+ module Ripl::AfterResult
16
+ # @private no need to document the method
17
+ def print_result(result)
18
+ super unless result.nil? && config[:skip_nil_results]
19
+ if after=config[:after_result]
20
+ if after.respond_to?(:call)
21
+ after.call(result)
22
+ else
23
+ puts after
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ # Allows [Ripl](https://github.com/cldwalker/ripl) to wrap lines of output
30
+ # and/or prefix each line of the result with the `result_prompt`.
31
+ module Ripl::FormatResult
32
+ # @private no need to document the method
33
+ def format_result(result,conf={})
34
+ conf=config if respond_to? :config
35
+ result = conf[:to_s] ? result.to_s : result.inspect
36
+ result = result.dup if result.frozen?
37
+ if limit=conf[:result_line_limit]
38
+ match = /^.{#{limit}}.+/o
39
+ :go while result.sub!(match){ |line| line.sub /^(?:(.{,#{limit}})[ \t]+|(.{#{limit}}))/o, "\\1\\2\n" }
40
+ end
41
+
42
+ result.gsub( conf[:prefix_result_lines] ? /^/ : /\A/, conf[:result_prompt] )
43
+ end
44
+ module_function :format_result
45
+ end
@@ -1,15 +1,10 @@
1
1
  class UIC::StateMachine
2
- include UIC::FileBacked
3
- def initialize( xml )
4
- @doc = Nokogiri.XML( xml )
2
+ include UIC::XMLFileBacked
3
+ def initialize( scxml )
4
+ self.file = scxml
5
5
  end
6
-
7
- def errors?
8
- !errors.empty?
9
- end
10
-
11
- def errors
12
- file_found? ? [] : ["File not found: '#{file}'"]
6
+ def inspect
7
+ "<#{self.class} #{file}>"
13
8
  end
14
9
  end
15
10
 
@@ -29,12 +24,13 @@ class UIC::Application::StateMachine < UIC::StateMachine
29
24
  def initialize(application,el)
30
25
  self.owner = application
31
26
  self.el = el
32
- self.file = application.resolve_file_path(src)
33
- super( File.read( file, encoding:'utf-8' ) )
34
- @visuals = @doc.at( "/application/statemachine[@ref='##{id}']/visual-states" )
35
- @visuals ||= @doc.root.add_child("<statemachine ref='##{id}'><visual-states/></statemachine>")
27
+
28
+ @visuals = app.doc.at( "/xmlns:application/xmlns:statemachine[@ref='##{id}']/xmlns:visual-states" )
29
+ @visuals ||= app.doc.root.add_child("<statemachine ref='##{id}'><visual-states/></statemachine>")
36
30
  @visual_states = VisualStates.new( self, @visuals )
37
31
  @visual_transitions = VisualTransitions.new( self, @visuals )
32
+
33
+ self.file = application.absolute_path(src)
38
34
  end
39
35
  alias_method :app, :owner
40
36
 
@@ -48,12 +44,32 @@ class UIC::Application::StateMachine < UIC::StateMachine
48
44
  visual_action.value[/\A(['"])[^'"]+\1\Z/] && # ensure that it's a simple string value
49
45
  visual_action.element.properties[ visual_action.attribute ].is_a?( UIC::Property::Image )
50
46
  end.group_by do |visual_action,owner|
51
- visual_action.value[/\A(['"])([^'"]+)\1\Z/,2]
47
+ app.absolute_path( visual_action.value[/\A(['"])([^'"]+)\1\Z/,2] )
52
48
  end.each do |image_path,array|
53
49
  array.map!(&:last)
54
50
  end
55
51
  end
56
52
 
53
+ # @return [Array<VisualAction>] all visual actions in the `.uia` for this state machine.
54
+ def visual_actions
55
+ visual_states.flat_map(&:enter_actions) +
56
+ visual_states.flat_map(&:exit_actions) +
57
+ visual_transitions.flat_map(&:actions)
58
+ end
59
+
60
+ def referenced_files
61
+ visual_actions.map do |action|
62
+ if action.is_a?(VisualAction::SetAttribute) && (el=action.element) && (path=action.value[ /\A(['"])([^'"]+)\1\Z/, 2 ])
63
+ type=el.properties[ action.attribute ].type
64
+ if action.attribute=='sourcepath' || action.attribute=='importfile' || type=='Texture'
65
+ app.absolute_path( path.sub(/#.+$/,'') )
66
+ elsif type=='Font'
67
+ app.absolute_path( File.join( 'fonts', path.sub(/$/,'.ttf') ) )
68
+ end
69
+ end
70
+ end.compact.uniq
71
+ end
72
+
57
73
  class UIC::Application::StateMachine::VisualStates
58
74
  include Enumerable
59
75
  def initialize(app_machine,visuals_el)
@@ -62,15 +78,15 @@ class UIC::Application::StateMachine < UIC::StateMachine
62
78
  @by_el = {}
63
79
  end
64
80
  def each
65
- @wrap.xpath('state').each{ |el| yield @by_el[el] ||= VisualState.new(el) }
81
+ @wrap.xpath('xmlns:state').each{ |el| yield @by_el[el] ||= VisualState.new(el,@machine) }
66
82
  end
67
83
  def [](id)
68
- if el=@wrap.at("state[@ref='#{id}']")
69
- @by_el[el] ||= VisualState.new(el)
84
+ if el=@wrap.at("xmlns:state[@ref='#{id}']")
85
+ @by_el[el] ||= VisualState.new(el,@machine)
70
86
  end
71
87
  end
72
88
  def length
73
- @wrap.xpath('count(state)').to_i
89
+ @wrap.xpath('count(xmlns:state)').to_i
74
90
  end
75
91
  alias_method :count, :length
76
92
  end
@@ -83,30 +99,131 @@ class UIC::Application::StateMachine < UIC::StateMachine
83
99
  @by_el = {}
84
100
  end
85
101
  def each
86
- @wrap.xpath('transition').each{ |el| yield @by_el[el] ||= VisualState.new(el) }
102
+ @wrap.xpath('xmlns:transition').each{ |el| yield @by_el[el] ||= VisualTransition.new(el,@machine) }
87
103
  end
88
104
  def [](id)
89
- if el=@wrap.at("transition[@ref='#{id}']")
90
- @by_el[el] ||= VisualTransition.new(el)
105
+ if el=@wrap.at("xmlns:transition[@ref='#{id}']")
106
+ @by_el[el] ||= VisualTransition.new(el,@machine)
91
107
  end
92
108
  end
93
109
  def length
94
- @wrap.xpath('count(transition)').to_i
110
+ @wrap.xpath('count(xmlns:transition)').to_i
95
111
  end
96
112
  alias_method :count, :length
97
113
  end
98
114
 
99
115
  class UIC::Application::StateMachine::VisualState
100
- def initialize(el)
116
+ include UIC::ElementBacked
117
+
118
+ # @!attribute ref
119
+ # @return [String] the `id` of the state in the state machine whose enter/exit will trigger the attached visual actions.
120
+ xmlattribute :ref
121
+
122
+ # @return [Nokogiri::XML::Element] the Nokogiri element in the `.uia` representing this visual state.
123
+ attr_reader :el
124
+
125
+ # @return [Application::StateMachine] the state machine containing the referenced state.
126
+ attr_reader :machine
127
+
128
+ def initialize(el,machine)
101
129
  @el = el
130
+ @machine = machine
131
+ end
132
+
133
+ def enter_actions
134
+ @el.xpath('xmlns:enter/*').to_a.map{ |el| VisualAction.create(el,self) }
135
+ end
136
+
137
+ def exit_actions
138
+ @el.xpath('xmlns:exit/*').to_a.map{ |el| VisualAction.create(el,self) }
102
139
  end
103
140
  end
104
141
 
105
142
  class UIC::Application::StateMachine::VisualTransition
106
- def initialize(el)
143
+ include UIC::ElementBacked
144
+
145
+ # @!attribute ref
146
+ # @return [String] the `uic:id` of the transition that will trigger the attached visual actions.
147
+ xmlattribute :ref
148
+
149
+ def initialize(el,machine)
107
150
  @el = el
151
+ @machine = machine
152
+ end
153
+ def actions
154
+ @el.xpath('./*').to_a.map{ |el| VisualAction.create(el,self) }
108
155
  end
109
156
  end
110
157
 
158
+ class UIC::Application::StateMachine::VisualAction
159
+ include UIC::ElementBacked
160
+
161
+ # @!attribute element
162
+ # @return [UIC::MetaData::AssetBase] the element in a UIC presentation affected by this action.
163
+ xmlattribute(:element, lambda{ |path,action| (action.machine.app / path) }){ |element| element.path }
164
+
165
+ # @return [Nokogiri::XML::Element] the Nokogiri element representing this action in the `.uia`.
166
+ attr_reader :el
111
167
 
168
+ # @return [VisualState,VisualTransition] the visual state or transition wrapping this action.
169
+ attr_reader :owner
170
+
171
+ # @return [Application::StateMachine] the state machine triggering this action.
172
+ attr_reader :machine
173
+
174
+ def self.create(el,owner)
175
+ klass = case el.name
176
+ when 'goto-slide' then GotoSlide
177
+ when 'call' then Call
178
+ when 'set-attribute' then SetAttribute
179
+ when 'fire-event' then FireEvent
180
+ else Generic
181
+ end
182
+ klass.new(el,owner)
183
+ end
184
+
185
+ def initialize(el,owner)
186
+ @el = el
187
+ @owner = owner
188
+ @machine = owner.machine
189
+ end
190
+
191
+ class UIC::Application::StateMachine::VisualAction::GotoSlide < self
192
+ # TODO: xmlattributes
193
+ end
194
+
195
+ class UIC::Application::StateMachine::VisualAction::Call < self
196
+ # TODO: xmlattributes
197
+ end
198
+
199
+ class UIC::Application::StateMachine::VisualAction::SetAttribute < self
200
+ # @!attribute attribute
201
+ # @return [String] the name of the attribute to set.
202
+ xmlattribute :attribute
203
+
204
+ # @!attribute value
205
+ # @return [String] the Lua expression to evaluate as the new value.
206
+ xmlattribute :value
207
+ end
208
+
209
+ class UIC::Application::StateMachine::VisualAction::FireEvent < self
210
+ # TODO: xmlattributes
211
+ end
212
+
213
+ class UIC::Application::StateMachine::VisualAction::Generic
214
+ # @return [Nokogiri::XML::Element] the Nokogiri element representing this action in the `.uia`.
215
+ attr_reader :el
216
+
217
+ # @return [VisualState,VisualTransition] the visual state or transition wrapping this action.
218
+ attr_reader :owner
219
+
220
+ # TODO: xmlattributes
221
+
222
+ def initialize(el,owner)
223
+ @el = el
224
+ @owner = owner
225
+ @machine = owner.machine
226
+ end
227
+ end
228
+ end
112
229
  end
@@ -1,3 +1,3 @@
1
1
  class RUIC
2
- VERSION = '0.5.0'
2
+ VERSION = '0.6.0'
3
3
  end
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruic
2
2
  metadata 'MetaData.xml' # optional; also may be set via -m flag
3
3
  uia 'projects/CustomClasses/CustomClasses.uia' # required before other commands
4
- show app.errors if app.errors?
5
4
 
6
5
  main = app.main_presentation
7
6
 
@@ -1,7 +1,6 @@
1
1
  metadata 'MetaData.xml'
2
2
 
3
3
  uia 'projects/SimpleScene/SimpleScene.uia'
4
- show app.errors if app.errors?
5
4
 
6
5
  main = app.main_presentation
7
6
 
@@ -1,6 +1,5 @@
1
1
  metadata 'MetaData-simple.xml'
2
2
  uia 'projects/CustomClasses/CustomClasses.uia' # required before other commands
3
- show app.errors if app.errors?
4
3
 
5
4
  future = app["#future"]
6
5
 
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruic
2
2
  metadata 'MetaData.xml' # optional; also may be set via -m flag
3
3
  uia 'projects/CustomClasses/CustomClasses.uia' # required before other commands
4
- show app.errors if app.errors?
5
4
 
6
5
  main = app.main_presentation
7
6
 
@@ -2,8 +2,6 @@
2
2
  metadata 'MetaData.xml' # optional; also may be set via -m flag
3
3
  uia 'projects/Paths/Paths.uia' # required before other commands
4
4
 
5
- show "Uh oh!", app.errors if app.errors?
6
-
7
5
  main = app.main
8
6
  paths = main.find _type:'Path'
9
7
  assert paths.length == 5
@@ -0,0 +1,87 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <UIP version="3" >
3
+ <Project >
4
+ <ProjectSettings author="" company="" presentationWidth="800" presentationHeight="480" maintainAspect="False" />
5
+ <Classes >
6
+ <Effect id="existing_001" name="existing" sourcepath=".\effects\existing.effect" />
7
+ <Effect id="missing_001" name="missing" sourcepath=".\effects\missing.effect" />
8
+ <CustomMaterial id="concrete" name="concrete" sourcepath=".\materials\concrete.material" />
9
+ <CustomMaterial id="missingcustom" name="missingcustom" sourcepath=".\materials\missing.material" />
10
+ <Behavior id="existing1" name="existing1" sourcepath=".\scripts\existing1.lua" />
11
+ <Behavior id="missing1" name="missing1" sourcepath=".\scripts\missing1.lua" />
12
+ </Classes>
13
+ <Graph >
14
+ <Scene id="Scene" >
15
+ <Behavior id="existing1_001" class="#existing1" />
16
+ <Behavior id="missing1_001" class="#missing1" />
17
+ <Layer id="Layer" >
18
+ <Camera id="Camera" />
19
+ <Light id="Light" />
20
+ <Text id="Text" />
21
+ <Text id="Text2" />
22
+ <Model id="existing" >
23
+ <Material id="Material" >
24
+ <Image id="Material_diffusemap" />
25
+ </Material>
26
+ </Model>
27
+ <Model id="missing" >
28
+ <Material id="Material_001" >
29
+ <Image id="Material_001_diffusemap" />
30
+ </Material>
31
+ </Model>
32
+ <Effect id="existing2" class="#existing_001" />
33
+ <Effect id="existing3" class="#existing_001" />
34
+ <Effect id="missing2" class="#missing_001" />
35
+ <Model id="Cube" >
36
+ <CustomMaterial id="Material_002" class="#concrete" />
37
+ </Model>
38
+ <Model id="Rectangle" >
39
+ <CustomMaterial id="concrete_001" class="#missingcustom" />
40
+ </Model>
41
+ <Group id="RoundedPlane-1" importid="__import__root__" >
42
+ <Model id="RoundedPlane" name="RoundedPlane" importid="RoundedPlane" orientation="Right Handed" position="0 0 -0" rotation="0 -0 0" rotationorder="XYZr" scale="1 1 1" sourcepath=".\RoundedPlane-1\meshes\RoundedPlane.mesh#1" >
43
+ <Material id="lambert2" name="lambert2" blendmode="Normal" diffuse="1 1 1" emissivepower="0" importid="lambert2" opacity="100" specularamount="0" />
44
+ </Model>
45
+ </Group>
46
+ <Group id="MissingImport" importid="__import__root__" >
47
+ <Model id="MissingMesh" name="RoundedPlane" importid="RoundedPlane" orientation="Right Handed" position="0 0 -0" rotation="0 -0 0" rotationorder="XYZr" scale="1 1 1" sourcepath=".\models\meshes\missing.mesh#1" >
48
+ <Material id="lambert2_001" name="lambert2" blendmode="Normal" diffuse="1 1 1" emissivepower="0" importid="lambert2" opacity="100" specularamount="0" />
49
+ </Model>
50
+ </Group>
51
+ </Layer>
52
+ </Scene>
53
+ </Graph>
54
+ <Logic >
55
+ <State name="Master Slide" component="#Scene" >
56
+ <Add ref="#Layer" />
57
+ <Add ref="#Camera" />
58
+ <Add ref="#Light" />
59
+ <State id="Scene-Intro" name="Intro" >
60
+ <Add ref="#existing1_001" name="existing1" />
61
+ <Add ref="#missing1_001" name="missing1" />
62
+ <Add ref="#Text" name="WelcomeMessage" font="Arimo-Regular" position="0 90 0" size="36" textstring="Hello World!&#10;This is the Intro Slide." />
63
+ <Add ref="#Text2" name="MissingText" font="Missing" />
64
+ <Add ref="#existing" name="existing" position="-353.627 -164.545 0" scale="2.7328 2.7328 0.53375" sourcepath="#Rectangle" />
65
+ <Add ref="#Material" diffusemap="#Material_diffusemap" />
66
+ <Add ref="#Material_diffusemap" sourcepath=".\maps\existing.png" />
67
+ <Add ref="#missing" name="missing" position="291.562 -170.318 0" scale="2.432 2.432 0.475" sourcepath="#Rectangle" />
68
+ <Add ref="#Material_001" diffusemap="#Material_001_diffusemap" />
69
+ <Add ref="#Material_001_diffusemap" sourcepath=".\maps\missing.png" />
70
+ <Add ref="#existing2" name="existing2" />
71
+ <Add ref="#existing3" name="existing3" NoiseSamp=".\maps\missing2.png" />
72
+ <Add ref="#missing2" name="missing2" />
73
+ <Add ref="#Cube" name="Cube" position="373.834 191.969 0" rotation="-41.4279 82.585 -28.6931" sourcepath="#Cube" />
74
+ <Add ref="#Material_002" name="Material" bump_texture=".\maps\missing.png" diffuse_texture=".\maps\existing.png" />
75
+ <Add ref="#Rectangle" name="Rectangle" position="-375.278 223.723 0" sourcepath="#Rectangle" />
76
+ <Add ref="#concrete_001" name="MissingCustom" />
77
+ <Add ref="#RoundedPlane-1" name="RoundedPlane-1" importfile=".\RoundedPlane-1\RoundedPlane-1.import" sourcepath=".\RoundedPlane-1\RoundedPlane-1.import" />
78
+ <Add ref="#RoundedPlane" importfile=".\RoundedPlane-1\RoundedPlane-1.import" />
79
+ <Add ref="#lambert2" importfile=".\RoundedPlane-1\RoundedPlane-1.import" />
80
+ <Add ref="#MissingImport" name="MissingImport" importfile=".\RoundedPlane-1\RoundedPlane-1.import" position="20 0 0" sourcepath=".\RoundedPlane-1\RoundedPlane-1.import" />
81
+ <Add ref="#MissingMesh" name="MissingMesh" importfile=".\RoundedPlane-1\RoundedPlane-1.import" />
82
+ <Add ref="#lambert2_001" importfile=".\RoundedPlane-1\RoundedPlane-1.import" />
83
+ </State>
84
+ </State>
85
+ </Logic>
86
+ </Project>
87
+ </UIP>
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <application scalemode="center" watermark-location="1.0 1.0" xmlns="http://nvidia.com/uicomposer">
3
+ <assets initial="main">
4
+ <behavior src="scripts/existing1.lua" />
5
+ <behavior src="scripts/missing1.lua" />
6
+ <statemachine id="s1" src="states/existing.scxml" />
7
+ <statemachine id="s2" src="states/missing.scxml" />
8
+ <presentation id="main" src="Existing.uip" />
9
+ <presentation id="sub" src="Missing.uip" />
10
+ <renderplugin id="rp" src="scripts/missing.so" />
11
+ </assets>
12
+ <statemachine ref="#s1">
13
+ <visual-states>
14
+ <state ref="Initial"><enter>
15
+ <set-attribute element="main:Scene.Layer.existing.Material.Image" attribute="sourcepath" value="'./maps/existing2.png'" />
16
+ </enter><exit>
17
+ <set-attribute element="main:Scene.Layer.existing.Material.Image" attribute="sourcepath" value="'.\\maps\\missing3.png'" />
18
+ </exit></state>
19
+ </visual-states>
20
+ </statemachine>
21
+ </application>