RUIC 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +3 -4
- data/README.md +278 -279
- data/bin/ruic +0 -0
- data/gui/TODO +2 -2
- data/gui/appattributesmodel.rb +51 -51
- data/gui/appelementsmodel.rb +126 -126
- data/gui/launch.rb +19 -19
- data/gui/makefile +14 -14
- data/gui/resources/style/dark.qss +459 -459
- data/gui/window.rb +90 -90
- data/gui/window.ui +753 -753
- data/lib/ruic.rb +175 -175
- data/lib/ruic/application.rb +5 -1
- data/lib/ruic/assets.rb +312 -289
- data/lib/ruic/attributes.rb +165 -165
- data/lib/ruic/behaviors.rb +1 -0
- data/lib/ruic/interfaces.rb +45 -7
- data/lib/ruic/presentation.rb +103 -39
- data/lib/ruic/ripl-after-result.rb +24 -11
- data/lib/ruic/statemachine.rb +3 -2
- data/lib/ruic/version.rb +2 -2
- data/ruic.gemspec +25 -25
- data/test/MetaData-simple.xml +28 -28
- data/test/MetaData.xml +435 -435
- data/test/customclasses.ruic +21 -21
- data/test/filtering.ruic +43 -43
- data/test/futureassets.ruic +8 -8
- data/test/nonmaster.ruic +0 -0
- data/test/paths.ruic +18 -18
- data/test/projects/CustomClasses/CustomClasses.uia +7 -7
- data/test/projects/CustomClasses/FutureAsset.uip +17 -17
- data/test/projects/Paths/Paths.uia +4 -0
- data/test/projects/Paths/Paths.uip +98 -0
- data/test/projects/SimpleScene/SimpleScene.uia +4 -4
- data/test/projects/SimpleScene/SimpleScene.uip +35 -35
- data/test/properties.ruic +82 -82
- data/test/referencematerials.ruic +52 -52
- data/test/usage.ruic +20 -20
- metadata +29 -3
data/lib/ruic/attributes.rb
CHANGED
@@ -1,165 +1,165 @@
|
|
1
|
-
#encoding: utf-8
|
2
|
-
class UIC::Property
|
3
|
-
class << self; attr_accessor :default; end
|
4
|
-
def initialize(el); @el = el; end
|
5
|
-
def name; @name||=@el['name']; end
|
6
|
-
def type; @type||=@el['type']; end
|
7
|
-
def formal; @formal||=@el['formalName'] || @el['name']; end
|
8
|
-
def min; @el['min']; end
|
9
|
-
def max; @el['max']; end
|
10
|
-
def description; @desc||=@el['description']; end
|
11
|
-
def default; @def ||= (@el['default'] || self.class.default); end
|
12
|
-
def get(asset,slide)
|
13
|
-
if asset.slide? || asset.has_slide?(slide)
|
14
|
-
asset.presentation.get_attribute(asset,name,slide) || default
|
15
|
-
end
|
16
|
-
end
|
17
|
-
def set(asset,new_value,slide_name_or_index)
|
18
|
-
asset.presentation.set_attribute(asset,name,slide_name_or_index,new_value)
|
19
|
-
end
|
20
|
-
def inspect
|
21
|
-
"<#{type} '#{name}'>"
|
22
|
-
end
|
23
|
-
|
24
|
-
class String < self
|
25
|
-
self.default = ''
|
26
|
-
end
|
27
|
-
MultiLineString = String
|
28
|
-
|
29
|
-
class Float < self
|
30
|
-
self.default = 0.0
|
31
|
-
def get(asset,slide); super.to_f; end
|
32
|
-
end
|
33
|
-
class Long < self
|
34
|
-
self.default = 0
|
35
|
-
def get(asset,slide); super.to_i; end
|
36
|
-
end
|
37
|
-
class Boolean < self
|
38
|
-
self.default = false
|
39
|
-
def get(asset,slide); super=='True'; end
|
40
|
-
def set(asset,new_value,slide_name_or_index)
|
41
|
-
super( asset, new_value ? 'True' : 'False', slide_name_or_index )
|
42
|
-
end
|
43
|
-
end
|
44
|
-
class Vector < self
|
45
|
-
self.default = '0 0 0'
|
46
|
-
def get(asset,slide)
|
47
|
-
VectorValue.new(asset,self,slide,super)
|
48
|
-
end
|
49
|
-
def set(asset,new_value,slide_name_or_index)
|
50
|
-
new_value = new_value.join(' ') if new_value.is_a?(Array)
|
51
|
-
super( asset, new_value, slide_name_or_index )
|
52
|
-
end
|
53
|
-
end
|
54
|
-
Rotation = Vector
|
55
|
-
Color = Vector
|
56
|
-
Float2 = Vector
|
57
|
-
class Image < self
|
58
|
-
self.default = nil
|
59
|
-
def get(asset,slide)
|
60
|
-
if idref = super
|
61
|
-
result = asset.presentation.asset_by_id( idref[1..-1] )
|
62
|
-
slide ? result.on_slide( slide ) : result
|
63
|
-
end
|
64
|
-
end
|
65
|
-
def set(asset,new_value,slide)
|
66
|
-
raise "Setting image attributes not yet supported"
|
67
|
-
end
|
68
|
-
end
|
69
|
-
class Texture < String
|
70
|
-
def get(asset,slide)
|
71
|
-
if path=super
|
72
|
-
path.empty? ? nil : path.gsub( '\\', '/' ).sub( /^.\// ,'' )
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
class ObjectRef < self
|
78
|
-
self.default = nil
|
79
|
-
def get(asset,slide)
|
80
|
-
ref = super
|
81
|
-
type = :absolute
|
82
|
-
obj = nil
|
83
|
-
unless ref=='' || ref.nil?
|
84
|
-
type = ref[0]=='#' ? :absolute : :path
|
85
|
-
ref = type==:absolute ? asset.presentation.asset_by_id( ref[1..-1] ) : asset.presentation.at( ref, asset )
|
86
|
-
end
|
87
|
-
ObjectReference.new(asset,self,slide,ref,type)
|
88
|
-
end
|
89
|
-
def set(asset,new_object,slide)
|
90
|
-
get(asset,slide).object = new_object
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
class ObjectReference
|
95
|
-
attr_reader :object, :type
|
96
|
-
def initialize(asset,property,slide,object=nil,type=nil)
|
97
|
-
@asset = asset
|
98
|
-
@name = property.name
|
99
|
-
@slide = slide
|
100
|
-
@object = object
|
101
|
-
@type = type
|
102
|
-
end
|
103
|
-
def object=(new_object)
|
104
|
-
raise "ObjectRef must be set to an asset (not a #{new_object.class.name})" unless new_object.is_a?(UIC::MetaData::
|
105
|
-
@object = new_object
|
106
|
-
write_value!
|
107
|
-
end
|
108
|
-
def type=(new_type)
|
109
|
-
raise "ObjectRef types must be either :absolute or :path (not #{new_type.inspect})" unless [:absolute,:path].include?(new_type)
|
110
|
-
@type = new_type
|
111
|
-
write_value!
|
112
|
-
end
|
113
|
-
private
|
114
|
-
def write_value!
|
115
|
-
path = case @object
|
116
|
-
when NilClass then ""
|
117
|
-
else case @type
|
118
|
-
when :absolute then "##{@object.el['id']}"
|
119
|
-
when :path then @asset.presentation.path_to( @object, @asset ).sub(/^[^:.]+:/,'')
|
120
|
-
# when :root then @asset.presentation.path_to( @object ).sub(/^[^:.]+:/,'')
|
121
|
-
end
|
122
|
-
end
|
123
|
-
@asset.presentation.set_attribute( @asset, @name, @slide, path )
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
Import = String #TODO: a real class
|
128
|
-
Mesh = String #TODO: a real class
|
129
|
-
Renderable = String #TODO: a real class
|
130
|
-
Font = String #TODO: a real class
|
131
|
-
FontSize = Long
|
132
|
-
|
133
|
-
StringListOrInt = String #TODO: a real class
|
134
|
-
|
135
|
-
class VectorValue
|
136
|
-
attr_reader :x, :y, :z
|
137
|
-
def initialize(asset,property,slide,str)
|
138
|
-
@asset = asset
|
139
|
-
@property = property
|
140
|
-
@slide = slide
|
141
|
-
@x, @y, @z = str.split(/\s+/).map(&:to_f)
|
142
|
-
end
|
143
|
-
def setall
|
144
|
-
@property.set( @asset, to_s, @slide )
|
145
|
-
end
|
146
|
-
def x=(n); @x=n; setall end
|
147
|
-
def y=(n); @y=n; setall end
|
148
|
-
def z=(n); @z=n; setall end
|
149
|
-
alias_method :r, :x
|
150
|
-
alias_method :g, :y
|
151
|
-
alias_method :b, :z
|
152
|
-
alias_method :r=, :x=
|
153
|
-
alias_method :g=, :y=
|
154
|
-
alias_method :b=, :z=
|
155
|
-
def inspect
|
156
|
-
"<#{@asset.path}.#{@property.name}: #{self}>"
|
157
|
-
end
|
158
|
-
def to_s
|
159
|
-
to_a.join(' ')
|
160
|
-
end
|
161
|
-
def to_a
|
162
|
-
[x,y,z]
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
1
|
+
#encoding: utf-8
|
2
|
+
class UIC::Property
|
3
|
+
class << self; attr_accessor :default; end
|
4
|
+
def initialize(el); @el = el; end
|
5
|
+
def name; @name||=@el['name']; end
|
6
|
+
def type; @type||=@el['type']; end
|
7
|
+
def formal; @formal||=@el['formalName'] || @el['name']; end
|
8
|
+
def min; @el['min']; end
|
9
|
+
def max; @el['max']; end
|
10
|
+
def description; @desc||=@el['description']; end
|
11
|
+
def default; @def ||= (@el['default'] || self.class.default); end
|
12
|
+
def get(asset,slide)
|
13
|
+
if asset.slide? || asset.has_slide?(slide)
|
14
|
+
asset.presentation.get_attribute(asset,name,slide) || default
|
15
|
+
end
|
16
|
+
end
|
17
|
+
def set(asset,new_value,slide_name_or_index)
|
18
|
+
asset.presentation.set_attribute(asset,name,slide_name_or_index,new_value)
|
19
|
+
end
|
20
|
+
def inspect
|
21
|
+
"<#{type} '#{name}'>"
|
22
|
+
end
|
23
|
+
|
24
|
+
class String < self
|
25
|
+
self.default = ''
|
26
|
+
end
|
27
|
+
MultiLineString = String
|
28
|
+
|
29
|
+
class Float < self
|
30
|
+
self.default = 0.0
|
31
|
+
def get(asset,slide); super.to_f; end
|
32
|
+
end
|
33
|
+
class Long < self
|
34
|
+
self.default = 0
|
35
|
+
def get(asset,slide); super.to_i; end
|
36
|
+
end
|
37
|
+
class Boolean < self
|
38
|
+
self.default = false
|
39
|
+
def get(asset,slide); super=='True'; end
|
40
|
+
def set(asset,new_value,slide_name_or_index)
|
41
|
+
super( asset, new_value ? 'True' : 'False', slide_name_or_index )
|
42
|
+
end
|
43
|
+
end
|
44
|
+
class Vector < self
|
45
|
+
self.default = '0 0 0'
|
46
|
+
def get(asset,slide)
|
47
|
+
VectorValue.new(asset,self,slide,super)
|
48
|
+
end
|
49
|
+
def set(asset,new_value,slide_name_or_index)
|
50
|
+
new_value = new_value.join(' ') if new_value.is_a?(Array)
|
51
|
+
super( asset, new_value, slide_name_or_index )
|
52
|
+
end
|
53
|
+
end
|
54
|
+
Rotation = Vector
|
55
|
+
Color = Vector
|
56
|
+
Float2 = Vector
|
57
|
+
class Image < self
|
58
|
+
self.default = nil
|
59
|
+
def get(asset,slide)
|
60
|
+
if idref = super
|
61
|
+
result = asset.presentation.asset_by_id( idref[1..-1] )
|
62
|
+
slide ? result.on_slide( slide ) : result
|
63
|
+
end
|
64
|
+
end
|
65
|
+
def set(asset,new_value,slide)
|
66
|
+
raise "Setting image attributes not yet supported"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
class Texture < String
|
70
|
+
def get(asset,slide)
|
71
|
+
if path=super
|
72
|
+
path.empty? ? nil : path.gsub( '\\', '/' ).sub( /^.\// ,'' )
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class ObjectRef < self
|
78
|
+
self.default = nil
|
79
|
+
def get(asset,slide)
|
80
|
+
ref = super
|
81
|
+
type = :absolute
|
82
|
+
obj = nil
|
83
|
+
unless ref=='' || ref.nil?
|
84
|
+
type = ref[0]=='#' ? :absolute : :path
|
85
|
+
ref = type==:absolute ? asset.presentation.asset_by_id( ref[1..-1] ) : asset.presentation.at( ref, asset )
|
86
|
+
end
|
87
|
+
ObjectReference.new(asset,self,slide,ref,type)
|
88
|
+
end
|
89
|
+
def set(asset,new_object,slide)
|
90
|
+
get(asset,slide).object = new_object
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class ObjectReference
|
95
|
+
attr_reader :object, :type
|
96
|
+
def initialize(asset,property,slide,object=nil,type=nil)
|
97
|
+
@asset = asset
|
98
|
+
@name = property.name
|
99
|
+
@slide = slide
|
100
|
+
@object = object
|
101
|
+
@type = type
|
102
|
+
end
|
103
|
+
def object=(new_object)
|
104
|
+
raise "ObjectRef must be set to an asset (not a #{new_object.class.name})" unless new_object.is_a?(UIC::MetaData::AssetBase)
|
105
|
+
@object = new_object
|
106
|
+
write_value!
|
107
|
+
end
|
108
|
+
def type=(new_type)
|
109
|
+
raise "ObjectRef types must be either :absolute or :path (not #{new_type.inspect})" unless [:absolute,:path].include?(new_type)
|
110
|
+
@type = new_type
|
111
|
+
write_value!
|
112
|
+
end
|
113
|
+
private
|
114
|
+
def write_value!
|
115
|
+
path = case @object
|
116
|
+
when NilClass then ""
|
117
|
+
else case @type
|
118
|
+
when :absolute then "##{@object.el['id']}"
|
119
|
+
when :path then @asset.presentation.path_to( @object, @asset ).sub(/^[^:.]+:/,'')
|
120
|
+
# when :root then @asset.presentation.path_to( @object ).sub(/^[^:.]+:/,'')
|
121
|
+
end
|
122
|
+
end
|
123
|
+
@asset.presentation.set_attribute( @asset, @name, @slide, path )
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
Import = String #TODO: a real class
|
128
|
+
Mesh = String #TODO: a real class
|
129
|
+
Renderable = String #TODO: a real class
|
130
|
+
Font = String #TODO: a real class
|
131
|
+
FontSize = Long
|
132
|
+
|
133
|
+
StringListOrInt = String #TODO: a real class
|
134
|
+
|
135
|
+
class VectorValue
|
136
|
+
attr_reader :x, :y, :z
|
137
|
+
def initialize(asset,property,slide,str)
|
138
|
+
@asset = asset
|
139
|
+
@property = property
|
140
|
+
@slide = slide
|
141
|
+
@x, @y, @z = str.split(/\s+/).map(&:to_f)
|
142
|
+
end
|
143
|
+
def setall
|
144
|
+
@property.set( @asset, to_s, @slide )
|
145
|
+
end
|
146
|
+
def x=(n); @x=n; setall end
|
147
|
+
def y=(n); @y=n; setall end
|
148
|
+
def z=(n); @z=n; setall end
|
149
|
+
alias_method :r, :x
|
150
|
+
alias_method :g, :y
|
151
|
+
alias_method :b, :z
|
152
|
+
alias_method :r=, :x=
|
153
|
+
alias_method :g=, :y=
|
154
|
+
alias_method :b=, :z=
|
155
|
+
def inspect
|
156
|
+
"<#{@asset.path}.#{@property.name}: #{self}>"
|
157
|
+
end
|
158
|
+
def to_s
|
159
|
+
to_a.join(' ')
|
160
|
+
end
|
161
|
+
def to_a
|
162
|
+
[x,y,z]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
data/lib/ruic/behaviors.rb
CHANGED
data/lib/ruic/interfaces.rb
CHANGED
@@ -1,35 +1,73 @@
|
|
1
|
+
# Supports classes that represent an XML file on disk (e.g. `.uia` and `.uip`).
|
1
2
|
module UIC::FileBacked
|
2
|
-
|
3
|
+
# @return [Nokogiri::XML::Document] the Nokogiri document representing the instance.
|
4
|
+
attr_accessor :doc
|
5
|
+
|
6
|
+
# @return [String] the absolute path to the underlying file.
|
7
|
+
attr_accessor :file
|
8
|
+
|
9
|
+
# @param relative [String] a file path relative to this file.
|
10
|
+
# @return [String] the full path resolved relative to this file.
|
3
11
|
def path_to( relative )
|
4
12
|
File.expand_path( relative.gsub('\\','/'), File.dirname(file) )
|
5
13
|
end
|
14
|
+
|
15
|
+
# @return [String] the name of the file (without any directories).
|
6
16
|
def filename
|
7
17
|
File.basename(file)
|
8
18
|
end
|
19
|
+
|
20
|
+
# @return [Boolean] `true` if the underlying file exists on disk.
|
9
21
|
def file_found?
|
10
|
-
|
22
|
+
@file && File.exist?(@file)
|
11
23
|
end
|
24
|
+
|
25
|
+
# Set the file for the class. Does **not** attempt to load the XML document.
|
26
|
+
# @param new_path [String] the file path for this class.
|
27
|
+
# @return [String]
|
12
28
|
def file=( new_path )
|
13
29
|
@file = File.expand_path(new_path)
|
14
|
-
@file_not_found = !File.exist?(new_path)
|
15
30
|
end
|
31
|
+
|
32
|
+
# @return [String] the XML representation of the document.
|
16
33
|
def to_xml
|
17
34
|
doc.to_xml( indent:1, indent_text:"\t" )
|
18
35
|
end
|
36
|
+
|
37
|
+
# Overwrite the associated file on disk with the {#to_xml} representation of this class.
|
38
|
+
# @return [true]
|
19
39
|
def save!
|
20
40
|
File.open(file,'w:utf-8'){ |f| f << to_xml }
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
# Save to the supplied file path. Subsequent calls to {#save!} will save to the new file, not the original file name.
|
45
|
+
def save_as(new_file)
|
46
|
+
File.open(new_file,'w:utf-8'){ |f| f << to_xml }
|
47
|
+
self.file = new_file
|
21
48
|
end
|
22
49
|
end
|
23
50
|
|
51
|
+
# Supports classes that represent an XML element (e.g. `<presentation id="main" src="foo.uip"/>`).
|
24
52
|
module UIC::ElementBacked
|
25
|
-
|
53
|
+
|
54
|
+
# @return [Object] the object in charge of this instance.
|
55
|
+
attr_accessor :owner
|
56
|
+
|
57
|
+
# @return [Nokogiri::XML::Element] the element backing this instance.
|
58
|
+
attr_accessor :el
|
59
|
+
|
60
|
+
# @private
|
26
61
|
def self.included(base)
|
27
62
|
base.extend(ClassMethods)
|
28
63
|
end
|
64
|
+
|
29
65
|
module ClassMethods
|
30
|
-
|
31
|
-
|
32
|
-
|
66
|
+
# @param name [String] the name of an XML attribute to expose.
|
67
|
+
# @param getblock [Proc] a proc to run
|
68
|
+
def xmlattribute(name,getblock=nil,&setblock)
|
69
|
+
define_method(name){ getblock ? getblock[@el[name]] : @el[name] }
|
70
|
+
define_method("#{name}="){ |new_value| @el[name] = (setblock ? setblock[new_value] : new_value).to_s }
|
33
71
|
end
|
34
72
|
end
|
35
73
|
end
|
data/lib/ruic/presentation.rb
CHANGED
@@ -52,16 +52,13 @@ class UIC::Presentation
|
|
52
52
|
@slides_by_el = {} # indexed by slide state element
|
53
53
|
end
|
54
54
|
|
55
|
+
# @return [String] the xml representation of this presentation. Formatted to match UI Composer Studio's formatting as closely as possible (for minimal diffs after update).
|
55
56
|
def to_xml
|
56
57
|
doc.to_xml( indent:1, indent_text:"\t" )
|
57
58
|
.gsub( %r{(<\w+(?: [\w:]+="[^"]*")*)(/?>)}i, '\1 \2' )
|
58
59
|
.sub('"?>','" ?>')
|
59
60
|
end
|
60
61
|
|
61
|
-
def save_as(new_file)
|
62
|
-
File.open(new_file,'w:utf-8'){ |f| f << to_xml }
|
63
|
-
end
|
64
|
-
|
65
62
|
# Update the presentation to be in-sync with the document.
|
66
63
|
# Must be called whenever the in-memory representation of the XML document is changed.
|
67
64
|
# Called automatically by all necessary methods; only necessary if script (dangerously)
|
@@ -90,12 +87,12 @@ class UIC::Presentation
|
|
90
87
|
|
91
88
|
# Find an asset in the presentation based on its internal XML identifier.
|
92
89
|
# @param id [String] the id of the asset (not an idref), e.g. `"Material_003"`.
|
93
|
-
# @return [MetaData::
|
90
|
+
# @return [MetaData::AssetBase] the found asset, or `nil` if could not be found.
|
94
91
|
def asset_by_id( id )
|
95
92
|
(@graph_by_id[id] && asset_for_el( @graph_by_id[id] ))
|
96
93
|
end
|
97
94
|
|
98
|
-
# @param asset [MetaData::
|
95
|
+
# @param asset [MetaData::AssetBase] an asset in the presentation
|
99
96
|
# @return [Integer] the index of the first slide where an asset is added (0 for master, non-zero for non-master).
|
100
97
|
def slide_index(asset)
|
101
98
|
# TODO: probably faster to .find the first @addsets_by_graph
|
@@ -104,8 +101,8 @@ class UIC::Presentation
|
|
104
101
|
(slide ? slide.xpath('count(ancestor::State) + count(preceding-sibling::State[ancestor::State])').to_i : 0) # the Scene is never added
|
105
102
|
end
|
106
103
|
|
107
|
-
# @param child_asset [MetaData::
|
108
|
-
# @return [MetaData::
|
104
|
+
# @param child_asset [MetaData::AssetBase] an asset in the presentation.
|
105
|
+
# @return [MetaData::AssetBase] the scene graph parent of the child asset, or `nil` for the Scene.
|
109
106
|
def parent_asset( child_asset )
|
110
107
|
child_graph_el = child_asset.el
|
111
108
|
unless child_graph_el==@scene || child_graph_el.parent.nil?
|
@@ -113,8 +110,8 @@ class UIC::Presentation
|
|
113
110
|
end
|
114
111
|
end
|
115
112
|
|
116
|
-
# @param parent_asset [MetaData::
|
117
|
-
# @return [Array<MetaData::
|
113
|
+
# @param parent_asset [MetaData::AssetBase] an asset in the presentation.
|
114
|
+
# @return [Array<MetaData::AssetBase>] array of scene graph children of the specified asset.
|
118
115
|
def child_assets( parent_asset )
|
119
116
|
parent_asset.el.element_children.map{ |child| asset_for_el(child) }
|
120
117
|
end
|
@@ -166,14 +163,13 @@ class UIC::Presentation
|
|
166
163
|
end
|
167
164
|
private :asset_for_el
|
168
165
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
end
|
166
|
+
# def referenced_files
|
167
|
+
# (
|
168
|
+
# (images + behaviors + effects + meshes + materials ).map(&:file)
|
169
|
+
# + effects.flat_map(&:images)
|
170
|
+
# + fonts
|
171
|
+
# ).sort_by{ |f| parts = f.split(/[\/\\]/); [parts.length,parts] }
|
172
|
+
# end
|
177
173
|
|
178
174
|
# @return [MetaData::Scene] the root scene asset for the presentation.
|
179
175
|
def scene
|
@@ -185,8 +181,8 @@ class UIC::Presentation
|
|
185
181
|
# * If `from_asset` is supplied the path will be relative to that asset (e.g. `"parent.parent.Group.Model"`).
|
186
182
|
# * If `from_asset` is omitted the path will be absolute (e.g. `"Scene.Layer.Group.Model"`).
|
187
183
|
#
|
188
|
-
# @param asset [MetaData::
|
189
|
-
# @param from_asset [MetaData::
|
184
|
+
# @param asset [MetaData::AssetBase] the asset to find the path to.
|
185
|
+
# @param from_asset [MetaData::AssetBase] the asset to find the path relative to.
|
190
186
|
# @return [String] the script path to the element.
|
191
187
|
def path_to( asset, from_asset=nil )
|
192
188
|
el = asset.el
|
@@ -244,7 +240,10 @@ class UIC::Presentation
|
|
244
240
|
#
|
245
241
|
# assert layer1==layer2 && layer2==layer3 && layer3==layer4
|
246
242
|
#
|
247
|
-
# @return [MetaData::
|
243
|
+
# @return [MetaData::AssetBase] The found asset, or `nil` if it cannot be found.
|
244
|
+
#
|
245
|
+
# @see Application#at
|
246
|
+
# @see MetaData::AssetBase#at
|
248
247
|
def at(path,root=@graph)
|
249
248
|
name,path = path.split('.',2)
|
250
249
|
root = root.el if root.respond_to?(:el)
|
@@ -267,7 +266,7 @@ class UIC::Presentation
|
|
267
266
|
#
|
268
267
|
# assert preso.get_attribute(camera,'position',0) == camera['position',0]
|
269
268
|
#
|
270
|
-
# @param asset [MetaData::
|
269
|
+
# @param asset [MetaData::AssetBase] the asset to fetch the attribute for.
|
271
270
|
# @param attr_name [String] the name of the attribute to get the value of.
|
272
271
|
# @param slide_name_or_index [String,Integer] the string name or integer index of the slide.
|
273
272
|
def get_attribute( asset, attr_name, slide_name_or_index )
|
@@ -293,7 +292,7 @@ class UIC::Presentation
|
|
293
292
|
# # …and the shorter way
|
294
293
|
# camera['endtime',0] = 1000
|
295
294
|
#
|
296
|
-
# @param asset [MetaData::
|
295
|
+
# @param asset [MetaData::AssetBase] the asset to fetch the attribute for.
|
297
296
|
# @param attr_name [String] the name of the attribute to get the value of.
|
298
297
|
# @param slide_name_or_index [String,Integer] the string name or integer index of the slide.
|
299
298
|
def set_attribute( asset, property_name, slide_name_or_index, str )
|
@@ -319,14 +318,14 @@ class UIC::Presentation
|
|
319
318
|
end
|
320
319
|
end
|
321
320
|
|
322
|
-
# @return [MetaData::
|
323
|
-
# @see MetaData::
|
321
|
+
# @return [MetaData::AssetBase] the component (or Scene) asset that owns the supplied asset.
|
322
|
+
# @see MetaData::AssetBase#component
|
324
323
|
def owning_component( asset )
|
325
324
|
asset_for_el( owning_component_element( asset.el ) )
|
326
325
|
end
|
327
326
|
|
328
|
-
# @return [MetaData::
|
329
|
-
# @see MetaData::
|
327
|
+
# @return [MetaData::AssetBase] the component asset that owns the supplied asset.
|
328
|
+
# @see MetaData::AssetBase#component
|
330
329
|
def owning_component_element( graph_element )
|
331
330
|
graph_element.at_xpath('(ancestor::Component[1] | ancestor::Scene[1])[last()]')
|
332
331
|
end
|
@@ -345,9 +344,9 @@ class UIC::Presentation
|
|
345
344
|
end
|
346
345
|
private :master_slide_for
|
347
346
|
|
348
|
-
# @param asset [MetaData::
|
347
|
+
# @param asset [MetaData::AssetBase] the asset to get the slides for.
|
349
348
|
# @return [SlideCollection] an array-like collection of all slides that the asset is available on.
|
350
|
-
# @see MetaData::
|
349
|
+
# @see MetaData::AssetBase#slides
|
351
350
|
def slides_for( asset )
|
352
351
|
graph_element = asset.el
|
353
352
|
@slides_for[graph_element] ||= begin
|
@@ -361,7 +360,7 @@ class UIC::Presentation
|
|
361
360
|
end
|
362
361
|
|
363
362
|
# @return [Boolean] true if the asset exists on the supplied slide.
|
364
|
-
# @see MetaData::
|
363
|
+
# @see MetaData::AssetBase#has_slide?
|
365
364
|
def has_slide?( asset, slide_name_or_index )
|
366
365
|
graph_element = asset.el
|
367
366
|
if graph_element == @scene
|
@@ -389,7 +388,7 @@ class UIC::Presentation
|
|
389
388
|
|
390
389
|
# Unlinks a master attribute, yielding distinct values on each slide. If the asset is not on the master slide, or the attribute is already unlinked, no change occurs.
|
391
390
|
#
|
392
|
-
# @param asset [MetaData::
|
391
|
+
# @param asset [MetaData::AssetBase] the master asset to unlink the attribute on.
|
393
392
|
# @param attribute_name [String] the name of the attribute to unlink.
|
394
393
|
# @return [Boolean] `true` if the attribute was previously linked; `false` otherwise.
|
395
394
|
def unlink_attribute(asset,attribute_name)
|
@@ -409,10 +408,10 @@ class UIC::Presentation
|
|
409
408
|
|
410
409
|
# Replace an existing asset with a new kind of asset.
|
411
410
|
#
|
412
|
-
# @param existing_asset [MetaData::
|
411
|
+
# @param existing_asset [MetaData::AssetBase] the existing asset to replace.
|
413
412
|
# @param new_type [String] the name of the asset type, e.g. `"ReferencedMaterial"` or `"Group"`.
|
414
413
|
# @param attributes [Hash] initial attribute values for the new asset.
|
415
|
-
# @return [MetaData::
|
414
|
+
# @return [MetaData::AssetBase] the newly-created asset.
|
416
415
|
def replace_asset( existing_asset, new_type, attributes={} )
|
417
416
|
old_el = existing_asset.el
|
418
417
|
new_el = old_el.replace( "<#{new_type}/>" ).first
|
@@ -431,13 +430,65 @@ class UIC::Presentation
|
|
431
430
|
(graph_element == @scene) || !!(@addsets_by_graph[graph_element] && @addsets_by_graph[graph_element][0])
|
432
431
|
end
|
433
432
|
|
434
|
-
|
433
|
+
# Find assets in this presentation matching criteria.
|
434
|
+
#
|
435
|
+
# @example 1) Searching for simple values
|
436
|
+
# every_asset = preso.find # Every asset in the presentation
|
437
|
+
# master_assets = preso.find _master:true # Test for master/nonmaster
|
438
|
+
# models = preso.find _type:'Model' # …or based on type
|
439
|
+
# slide2_assets = preso.find _slide:2 # …or presence on a specific slide
|
440
|
+
# rectangles = preso.find sourcepath:'#Rectangle' # …or attribute values
|
441
|
+
# gamecovers = preso.find name:'Game Cover' # …including the name
|
442
|
+
#
|
443
|
+
# @example 2) Combine tests to get more specific
|
444
|
+
# master_models = preso.find _type:'Model', _master:true
|
445
|
+
# slide2_rects = preso.find _type:'Model', _slide:2, sourcepath:'#Rectangle'
|
446
|
+
# nonmaster_s2 = preso.find _slide:2, _master:false
|
447
|
+
# red_materials = preso.find _type:'Material', diffuse:[1,0,0]
|
448
|
+
#
|
449
|
+
# @example 3) Matching values more loosely
|
450
|
+
# pistons = preso.find name:/^Piston/ # Regex for batch finding
|
451
|
+
# bottom_row = preso.find position:[nil,-200,nil] # nil for wildcards in vectors
|
452
|
+
#
|
453
|
+
# @example 4) Restrict the search to a sub-tree
|
454
|
+
# group = preso/"Scene.Layer.Group"
|
455
|
+
# group_models = preso.find _under:group, _type:'Model' # All models under the group
|
456
|
+
# group_models = group.find _type:'Model' # alternatively start from the asset
|
457
|
+
#
|
458
|
+
# @example 5) Iterate the results as they are found
|
459
|
+
# preso.find _type:'Model', name:/^Piston/ do |model, index|
|
460
|
+
# show "Model #{index} is named #{model.name}"
|
461
|
+
# end
|
462
|
+
#
|
463
|
+
# # You do not need to receive the index if you do not need its value
|
464
|
+
# group.find _type:'Model' do |model|
|
465
|
+
# scale = model['scale'].value
|
466
|
+
# scale.x = scale.y = scale.z = 1
|
467
|
+
# end
|
468
|
+
#
|
469
|
+
# @param criteria [Hash] Attribute names and values, along with a few special keys.
|
470
|
+
# @option criteria :_type [String] asset must be of specified type, e.g. `"Model"` or `"Material"` or `"PathAnchorPoint"`.
|
471
|
+
# @option criteria :_slide [Integer,String] slide number or name that the asset must be present on.
|
472
|
+
# @option criteria :_master [Boolean] `true` for only master assets, `false` for only non-master assets.
|
473
|
+
# @option criteria :_under [MetaData::AssetBase] a root asset to require as an ancestor; this asset will never be in the search results.
|
474
|
+
# @option criteria :attribute_name [Numeric] numeric attribute value must be within `0.001` of the supplied value.
|
475
|
+
# @option criteria :attribute_name [String] string attribute value must match the supplied string exactly.
|
476
|
+
# @option criteria :attribute_name [Regexp] supplied regex must match the string attribute value.
|
477
|
+
# @option criteria :attribute_name [Array] each component of the attribute's vector value must be within `0.001` of the supplied value in the array;
|
478
|
+
# if a value in the array is `nil` that component of the vector may have any value.
|
479
|
+
#
|
480
|
+
# @yield [asset,index] Yields each found asset (and its index in the results) to the block (if supplied) as they are found.
|
481
|
+
#
|
482
|
+
# @return [Array<MetaData::AssetBase>] array of all matching assets (may be empty).
|
483
|
+
#
|
484
|
+
# @see MetaData::AssetBase#find
|
485
|
+
def find(criteria={},&block)
|
435
486
|
index = -1
|
436
|
-
start =
|
487
|
+
start = criteria.key?(:_under) ? criteria.delete(:_under).el : @graph
|
437
488
|
[].tap do |result|
|
438
489
|
start.xpath('./descendant::*').each do |el|
|
439
490
|
asset = asset_for_el(el)
|
440
|
-
next unless
|
491
|
+
next unless criteria.all? do |att,val|
|
441
492
|
case att
|
442
493
|
when :_type then el.name == val
|
443
494
|
when :_slide then has_slide?(asset,val)
|
@@ -460,6 +511,7 @@ class UIC::Presentation
|
|
460
511
|
end
|
461
512
|
end
|
462
513
|
|
514
|
+
# @private
|
463
515
|
def inspect
|
464
516
|
"<#{self.class} #{File.basename(file)}>"
|
465
517
|
end
|
@@ -471,14 +523,25 @@ end
|
|
471
523
|
|
472
524
|
class UIC::Application::Presentation < UIC::Presentation
|
473
525
|
include UIC::ElementBacked
|
474
|
-
|
475
|
-
|
526
|
+
# @!parse extend UIC::ElementBacked::ClassMethods
|
527
|
+
|
528
|
+
# @!attribute [rw] id
|
529
|
+
# @return [String] the id of the presentation asset
|
476
530
|
xmlattribute :id do |new_id|
|
477
531
|
main_preso = app.main_presentation
|
478
532
|
super(new_id)
|
479
533
|
app.main_presentation=self if main_preso==self
|
480
534
|
end
|
481
|
-
|
535
|
+
|
536
|
+
# @!attribute src
|
537
|
+
# @return [String] the path to the presentation file
|
538
|
+
xmlattribute :src
|
539
|
+
|
540
|
+
# @!attribute active
|
541
|
+
# @return [Boolean] is the presentation initially active?
|
542
|
+
xmlattribute :active, ->(val){ val=="True" } do |new_val|
|
543
|
+
new_val ? 'True' : 'False'
|
544
|
+
end
|
482
545
|
|
483
546
|
def initialize(application,el)
|
484
547
|
self.owner = application
|
@@ -492,6 +555,7 @@ class UIC::Application::Presentation < UIC::Presentation
|
|
492
555
|
end
|
493
556
|
end
|
494
557
|
|
558
|
+
# @private no need to document this monkeypatch
|
495
559
|
class Nokogiri::XML::Element
|
496
560
|
def index(kind='*') # Find the index of this element amongs its siblings
|
497
561
|
xpath("count(./preceding-sibling::#{kind})").to_i
|