Class: UIC::Presentation
- Inherits:
-
Object
- Object
- UIC::Presentation
- Includes:
- FileBacked
- Defined in:
- lib/ruic/presentation.rb
Direct Known Subclasses
Instance Attribute Summary
Attributes included from FileBacked
Instance Method Summary collapse
- #asset_by_id(id) ⇒ Object
- #asset_for_el(el) ⇒ Object
-
#assets ⇒ Object
Get an array of all assets in the scene graph, in document order.
- #at(path, root = @graph) ⇒ Object (also: #/)
- #attribute_linked?(graph_element, attribute_name) ⇒ Boolean
- #child_assets(parent_graph_el) ⇒ Object
- #errors ⇒ Object
- #errors? ⇒ Boolean
- #find(options = {}) ⇒ Object
- #get_attribute(graph_element, property_name, slide_name_or_index) ⇒ Object
- #has_slide?(graph_element, slide_name_or_index) ⇒ Boolean
- #image_paths ⇒ Object
-
#image_usage ⇒ Object
Returns a hash mapping image paths to arrays of the assets referencing them.
-
#initialize(uip_path) ⇒ Presentation
constructor
A new instance of Presentation.
- #inspect ⇒ Object
- #load_from_file ⇒ Object
-
#master?(graph_element) ⇒ Boolean
Is this element added on the master slide?.
- #master_slide_for(graph_element) ⇒ Object
- #owning_component(graph_element) ⇒ Object
- #owning_component_element(graph_element) ⇒ Object
- #owning_or_self_component_element(graph_element) ⇒ Object
- #parent_asset(child_graph_el) ⇒ Object
- #path_to(el, from_el = nil) ⇒ Object
- #rebuild_caches_from_document ⇒ Object
- #referenced_files ⇒ Object
- #replace_asset(existing_asset, new_type, attributes = {}) ⇒ Object
- #save! ⇒ Object
- #save_as(new_file) ⇒ Object
- #scene ⇒ Object
- #set_attribute(graph_element, property_name, slide_name_or_index, str) ⇒ Object
-
#slide_index(graph_element) ⇒ Object
Find the index of the slide where an element is added.
- #slides_for(graph_element) ⇒ Object
- #to_xml ⇒ Object
- #unlink_attribute(graph_element, attribute_name) ⇒ Object
Methods included from FileBacked
Constructor Details
#initialize(uip_path) ⇒ Presentation
Returns a new instance of Presentation.
3 4 5 6 |
# File 'lib/ruic/presentation.rb', line 3 def initialize( uip_path ) self.file = uip_path load_from_file if file_found? end |
Instance Method Details
#asset_by_id(id) ⇒ Object
77 78 79 |
# File 'lib/ruic/presentation.rb', line 77 def asset_by_id( id ) (@graph_by_id[id] && asset_for_el( @graph_by_id[id] )) end |
#asset_for_el(el) ⇒ Object
136 137 138 |
# File 'lib/ruic/presentation.rb', line 136 def asset_for_el(el) (@asset_by_el[el] ||= el['class'] ? @class_by_ref[el['class']].new(self,el) : app..new_instance(self,el)) end |
#assets ⇒ Object
Get an array of all assets in the scene graph, in document order
99 100 101 |
# File 'lib/ruic/presentation.rb', line 99 def assets @graph_by_id.map{ |id,graph_element| asset_for_el(graph_element) } end |
#at(path, root = @graph) ⇒ Object Also known as: /
188 189 190 191 192 193 194 195 196 |
# File 'lib/ruic/presentation.rb', line 188 def at(path,root=@graph) name,path = path.split('.',2) el = case name when 'parent' then root==@scene ? nil : root.parent when 'Scene' then @scene else root.element_children.find{ |el| asset_for_el(el).name==name } end path ? at(path,el) : asset_for_el(el) if el end |
#attribute_linked?(graph_element, attribute_name) ⇒ Boolean
266 267 268 |
# File 'lib/ruic/presentation.rb', line 266 def attribute_linked?(graph_element,attribute_name) !(@addsets_by_graph[graph_element] && @addsets_by_graph[graph_element][1] && @addsets_by_graph[graph_element][1].key?(attribute_name)) end |
#child_assets(parent_graph_el) ⇒ Object
94 95 96 |
# File 'lib/ruic/presentation.rb', line 94 def child_assets( parent_graph_el ) parent_graph_el.element_children.map{ |child| asset_for_el(child) } end |
#errors ⇒ Object
184 185 186 |
# File 'lib/ruic/presentation.rb', line 184 def errors (file_found? ? [] : ["File not found: '#{file}'"]) end |
#errors? ⇒ Boolean
180 181 182 |
# File 'lib/ruic/presentation.rb', line 180 def errors? (!errors.empty?) end |
#find(options = {}) ⇒ Object
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/ruic/presentation.rb', line 299 def find(={}) index = -1 start = [:under] ? [:under].el : @graph ([:attributes]||={})[:name]=[:name] if [:name] [].tap do |result| start.xpath('./descendant::*').each do |el| next if .key?(:type) && el.name != [:type] next if .key?(:slide) && !(el,[:slide]) next if .key?(:master) && master?(el)!= [:master] asset = asset_for_el(el) next if .key?(:attributes) && [:attributes].any?{ |att,val| if asset.properties[att.to_s] value = asset[att.to_s].value case val when Regexp then val !~ value.to_s when Numeric then (val-value).abs >= 0.001 when Array then value.to_a.zip(val).map{ |a,b| b && (a-b).abs>=0.001 }.any? else value != val end end } yield asset, index+=1 if block_given? result << asset end end end |
#get_attribute(graph_element, property_name, slide_name_or_index) ⇒ Object
199 200 201 202 203 204 205 |
# File 'lib/ruic/presentation.rb', line 199 def get_attribute( graph_element, property_name, ) ((addsets=@addsets_by_graph[graph_element]) && ( # State (slide) don't have any addsets ( addsets[] && addsets[][property_name] ) || # Try for a Set on the specific slide ( addsets[0] && addsets[0][property_name] ) # …else try the master slide ) || graph_element[property_name]) # …else try the graph # TODO: handle animation (child of addset) end |
#has_slide?(graph_element, slide_name_or_index) ⇒ Boolean
257 258 259 260 261 262 263 264 |
# File 'lib/ruic/presentation.rb', line 257 def ( graph_element, ) if graph_element == @scene # The scene is never actually added, so we'll treat it just like the first add, which is on the master slide of the scene ( @addsets_by_graph.first.first, ) else @addsets_by_graph[graph_element][] || @addsets_by_graph[graph_element][0] end end |
#image_paths ⇒ Object
132 133 134 |
# File 'lib/ruic/presentation.rb', line 132 def image_paths image_usage.keys end |
#image_usage ⇒ Object
Returns a hash mapping image paths to arrays of the assets referencing them
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/ruic/presentation.rb', line 104 def image_usage asset_types = app..by_name.values + @class_by_ref.values image_properties_by_type = asset_types.flat_map do |type| type.properties.values .select{ |property| property.type=='Image' || property.type == 'Texture' } .map{ |property| [type,property] } end.group_by(&:first).tap{ |x| x.each{ |t,a| a.map!(&:last) } } Hash[ assets.each_with_object({}) do |asset,usage| if properties = image_properties_by_type[asset.class] properties.each do |property| asset[property.name].values.compact.each do |value| value = value['sourcepath'] if property.type=='Image' unless value.nil? || value.empty? value = value.gsub('\\','/').sub(/^.\//,'') usage[value] ||= [] usage[value] << asset end end end end end.sort_by do |path,assets| parts = path.downcase.split '/' [ parts.length, parts ] end ].tap{ |h| h.extend(UIC::PresentableHash) } end |
#inspect ⇒ Object
326 327 328 |
# File 'lib/ruic/presentation.rb', line 326 def inspect "<#{self.class} #{File.basename(file)}>" end |
#load_from_file ⇒ Object
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 42 |
# File 'lib/ruic/presentation.rb', line 8 def load_from_file @doc = Nokogiri.XML( File.read( file, encoding:'utf-8' ), &:noblanks ) @graph = @doc.at('Graph') @scene = @graph.at('Scene') @logic = @doc.at('Logic') @class_by_ref = {} @doc.xpath('/UIP/Project/Classes/*').each do |reference| path = app.path_to(reference['sourcepath']) raise "Cannot find file '#{path}' referenced by #{self.inspect}" unless File.exist?( path ) = case reference.name when 'CustomMaterial' = Nokogiri.XML(File.read(path,encoding:'utf-8')).at('/*/MetaData') from = app..by_name[ 'MaterialBase' ] app..create_class( , from, reference.name ) when 'Effect' = Nokogiri.XML(File.read(path,encoding:'utf-8')).at('/*/MetaData') from = app..by_name[ 'Effect' ] app..create_class( , from, reference.name ) when 'Behavior' lua = File.read(path,encoding:'utf-8') = lua[ /--\[\[(.+?)(?:--)?\]\]/m, 1 ] = Nokogiri.XML("<MetaData>#{}</MetaData>").root from = app..by_name[ 'Behavior' ] app..create_class( , from, reference.name ) end @class_by_ref[ "##{reference['id']}" ] = end rebuild_caches_from_document @asset_by_el = {} # indexed by asset graph element @slides_for = {} # indexed by asset graph element @slides_by_el = {} # indexed by slide state element end |
#master?(graph_element) ⇒ Boolean
Is this element added on the master slide?
295 296 297 |
# File 'lib/ruic/presentation.rb', line 295 def master?(graph_element) (graph_element == @scene) || !!(@addsets_by_graph[graph_element] && @addsets_by_graph[graph_element][0]) end |
#master_slide_for(graph_element) ⇒ Object
241 242 243 244 |
# File 'lib/ruic/presentation.rb', line 241 def ( graph_element ) comp = owning_or_self_component_element( graph_element ) @logic.at("./State[@component='##{comp['id']}']") end |
#owning_component(graph_element) ⇒ Object
229 230 231 |
# File 'lib/ruic/presentation.rb', line 229 def owning_component( graph_element ) asset_for_el( owning_component_element( graph_element ) ) end |
#owning_component_element(graph_element) ⇒ Object
233 234 235 |
# File 'lib/ruic/presentation.rb', line 233 def owning_component_element( graph_element ) graph_element.at_xpath('(ancestor::Component[1] | ancestor::Scene[1])[last()]') end |
#owning_or_self_component_element(graph_element) ⇒ Object
237 238 239 |
# File 'lib/ruic/presentation.rb', line 237 def owning_or_self_component_element( graph_element ) graph_element.at_xpath('(ancestor-or-self::Component[1] | ancestor-or-self::Scene[1])[last()]') end |
#parent_asset(child_graph_el) ⇒ Object
88 89 90 91 92 |
# File 'lib/ruic/presentation.rb', line 88 def parent_asset( child_graph_el ) unless child_graph_el==@scene || child_graph_el.parent.nil? asset_for_el( child_graph_el.parent ) end end |
#path_to(el, from_el = nil) ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/ruic/presentation.rb', line 155 def path_to( el, from_el=nil ) to_parts = if el.ancestors('Graph') [].tap{ |parts| until el==@graph parts.unshift asset_for_el(el).name el = el.parent end } end if from_el && from_el.ancestors('Graph') from_parts = [].tap{ |parts| until from_el==@graph parts.unshift asset_for_el(from_el).name from_el = from_el.parent end } until to_parts.empty? || from_parts.empty? || to_parts.first!=from_parts.first to_parts.shift from_parts.shift end to_parts.unshift *(['parent']*from_parts.length) end to_parts.join('.') end |
#rebuild_caches_from_document ⇒ Object
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/ruic/presentation.rb', line 58 def rebuild_caches_from_document @graph_by_id = {} @scene.traverse{ |x| @graph_by_id[x['id']]=x if x.is_a?(Nokogiri::XML::Element) } @graph_by_addset = {} @addsets_by_graph = {} = {} @logic.xpath('.//Add|.//Set').each do |addset| graph = @graph_by_id[addset['ref'][1..-1]] @graph_by_addset[addset] = graph @addsets_by_graph[graph] ||= {} = addset.parent name = ['name'] index = name == 'Master Slide' ? 0 : ([] ||= (.index('State') + 1)) @addsets_by_graph[graph][name] = addset @addsets_by_graph[graph][index] = addset end end |
#referenced_files ⇒ Object
143 144 145 146 147 148 149 |
# File 'lib/ruic/presentation.rb', line 143 def referenced_files ( (images + behaviors + effects + meshes + materials ).map(&:file) + effects.flat_map(&:images) + fonts ).sort_by{ |f| parts = f.split(/[\/\\]/); [parts.length,parts] } end |
#replace_asset(existing_asset, new_type, attributes = {}) ⇒ Object
282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/ruic/presentation.rb', line 282 def replace_asset( existing_asset, new_type, attributes={} ) old_el = existing_asset.el new_el = old_el.replace( "<#{new_type}/>" ).first attributes['id'] = old_el['id'] attributes.each{ |att,val| new_el[att.to_s] = val } asset_for_el( new_el ).tap do |new_asset| unsupported_attributes = ".//*[name()='Add' or name()='Set'][@ref='##{old_el['id']}']/@*[name()!='ref' and #{new_asset.properties.keys.map{|p| "name()!='#{p}'"}.join(' and ')}]" @logic.xpath(unsupported_attributes).remove rebuild_caches_from_document end end |
#save! ⇒ Object
50 51 52 |
# File 'lib/ruic/presentation.rb', line 50 def save! File.open(file,'w:utf-8'){ |f| f << to_xml } end |
#save_as(new_file) ⇒ Object
54 55 56 |
# File 'lib/ruic/presentation.rb', line 54 def save_as(new_file) File.open(new_file,'w:utf-8'){ |f| f << to_xml } end |
#scene ⇒ Object
151 152 153 |
# File 'lib/ruic/presentation.rb', line 151 def scene asset_for_el( @scene ) end |
#set_attribute(graph_element, property_name, slide_name_or_index, str) ⇒ Object
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/ruic/presentation.rb', line 207 def set_attribute( graph_element, property_name, , str ) if attribute_linked?( graph_element, property_name ) if @addsets_by_graph[graph_element] @addsets_by_graph[graph_element][0][property_name] = str else raise "TODO" end else if @addsets_by_graph[graph_element] if @addsets_by_graph[graph_element][][property_name] = str else master = ( graph_element ) = master.xpath('count(./State)').to_i 0.upto().each{ |idx| set_attribute(graph_element,property_name,idx,str) } end else raise "TODO" end end end |
#slide_index(graph_element) ⇒ Object
Find the index of the slide where an element is added
82 83 84 85 86 |
# File 'lib/ruic/presentation.rb', line 82 def (graph_element) # TODO: probably faster to .find the first @addsets_by_graph = @logic.at(".//Add[@ref='##{graph_element['id']}']/..") ( ? .xpath('count(ancestor::State) + count(preceding-sibling::State[ancestor::State])').to_i : 0) # the Scene is never added end |
#slides_for(graph_element) ⇒ Object
246 247 248 249 250 251 252 253 254 255 |
# File 'lib/ruic/presentation.rb', line 246 def ( graph_element ) @slides_for[graph_element] ||= begin = [] master = ( graph_element ) << [master,0] if graph_element==@scene || (@addsets_by_graph[graph_element] && @addsets_by_graph[graph_element][0]) .concat( master.xpath('./State').map.with_index{ |el,i| [el,i+1] } ) .map!{ |el,idx| @slides_by_el[el] ||= app..new_instance(self,el).tap{ |s| s.index=idx; s.name=el['name'] } } UIC::SlideCollection.new( ) end end |
#to_xml ⇒ Object
44 45 46 47 48 |
# File 'lib/ruic/presentation.rb', line 44 def to_xml @doc.to_xml( indent:1, indent_text:"\t" ) .gsub( %r{(<\w+(?: [\w:]+="[^"]*")*)(/?>)}i, '\1 \2' ) .sub('"?>','" ?>') end |
#unlink_attribute(graph_element, attribute_name) ⇒ Object
270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/ruic/presentation.rb', line 270 def unlink_attribute(graph_element,attribute_name) if master?(graph_element) && attribute_linked?(graph_element,attribute_name) master_value = get_attribute( graph_element, attribute_name, 0 ) ( graph_element ).to_ary[1..-1].each do || addset = .el.at_xpath( ".//*[@ref='##{graph_element['id']}']" ) || .el.add_child("<Set ref='##{graph_element['id']}'/>").first addset[attribute_name] = master_value end rebuild_caches_from_document true end end |