Class: OpenUSD::Stage

Inherits:
Object
  • Object
show all
Defined in:
lib/openusd/stage.rb

Overview

Composed, editable view of one root layer and its composition arcs.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root_layer, resolver: AssetResolver.new) ⇒ Stage

Returns a new instance of Stage.



30
31
32
33
34
35
36
37
38
# File 'lib/openusd/stage.rb', line 30

def initialize(root_layer, resolver: AssetResolver.new)
  @root_layer = root_layer
  @resolver = resolver
  @edit_target = root_layer
  @pseudo_root = PseudoRoot.new(self)
  @composition = nil
  @index = nil
  @layer_cache = {}
end

Instance Attribute Details

#edit_targetObject

Returns the value of attribute edit_target.



6
7
8
# File 'lib/openusd/stage.rb', line 6

def edit_target
  @edit_target
end

#pseudo_rootObject (readonly)

Returns the value of attribute pseudo_root.



6
7
8
# File 'lib/openusd/stage.rb', line 6

def pseudo_root
  @pseudo_root
end

#resolverObject (readonly)

Returns the value of attribute resolver.



6
7
8
# File 'lib/openusd/stage.rb', line 6

def resolver
  @resolver
end

#root_layerObject (readonly)

Returns the value of attribute root_layer.



6
7
8
# File 'lib/openusd/stage.rb', line 6

def root_layer
  @root_layer
end

Class Method Details

.create(identifier) ⇒ Stage

Create an empty file-backed stage.

Returns:



19
20
21
# File 'lib/openusd/stage.rb', line 19

def create(identifier)
  new(Layer.create(identifier))
end

.create_in_memoryStage

Create an anonymous in-memory stage.

Returns:



25
26
27
# File 'lib/openusd/stage.rb', line 25

def create_in_memory
  new(Layer.create("anonymous:#{object_id}:#{Process.clock_gettime(Process::CLOCK_MONOTONIC)}"))
end

.open(path, missing_assets: :error) ⇒ Stage

Open a composed stage.

Returns:



11
12
13
14
15
# File 'lib/openusd/stage.rb', line 11

def open(path, missing_assets: :error)
  expanded = File.expand_path(path)
  resolver = AssetResolver.new(missing_assets: missing_assets)
  new(Layer.open(expanded), resolver: resolver)
end

Instance Method Details

#author_attribute(path, name, type_name) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Find or author an attribute spec in the edit target.



159
160
161
162
163
164
165
166
167
# File 'lib/openusd/stage.rb', line 159

def author_attribute(path, name, type_name)
  spec = ensure_spec(edit_target, Path.parse(path))
  property = spec.property_named(name)
  validate_property_kind!(property, AttributeSpec, "#{name} is authored as a relationship")
  property ||= spec.add_property(AttributeSpec.new(name, type_name || "token"))
  property.type_name = type_name if type_name
  invalidate!
  property
end

#author_prim_metadata(path, operation, key, value) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Apply one metadata mutation to an edit-target prim spec.



191
192
193
194
195
# File 'lib/openusd/stage.rb', line 191

def (path, operation, key, value)
   = ensure_spec(edit_target, Path.parse(path)).
  operation == :set ? [key] = value : .delete(key)
  invalidate!
end

#author_relationship(path, name) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Find or author a relationship spec in the edit target.



171
172
173
174
175
176
177
178
# File 'lib/openusd/stage.rb', line 171

def author_relationship(path, name)
  spec = ensure_spec(edit_target, Path.parse(path))
  property = spec.property_named(name)
  validate_property_kind!(property, RelationshipSpec, "#{name} is authored as an attribute")
  property ||= spec.add_property(RelationshipSpec.new(name))
  invalidate!
  property
end

#child_paths(path) ⇒ Array<Path>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns direct composed child paths.

Returns:

  • (Array<Path>)

    direct composed child paths



143
144
145
146
147
148
149
# File 'lib/openusd/stage.rb', line 143

def child_paths(path)
  prefix = "#{Path.parse(path)}/"
  paths = composed_index.keys.select do |candidate|
    candidate.start_with?(prefix) && !candidate.delete_prefix(prefix).include?("/")
  end
  paths.sort.map { |candidate| Path.parse(candidate) }
end

#default_primPrim?

Returns composed default prim.

Returns:

  • (Prim, nil)

    composed default prim



60
61
62
63
# File 'lib/openusd/stage.rb', line 60

def default_prim
  name = root_layer.["defaultPrim"]
  prim_at("/#{name}") if name
end

#define_prim(path, type_name = nil) ⇒ Prim

Define a prim and any missing ancestors in the edit target.

Returns:



67
68
69
70
71
72
73
74
# File 'lib/openusd/stage.rb', line 67

def define_prim(path, type_name = nil)
  parsed = validate_prim_path(path)
  spec = ensure_spec(edit_target, parsed)
  spec.specifier = :def
  spec.type_name = type_name.to_s if type_name
  invalidate!
  Prim.new(self, parsed)
end

#export(path) ⇒ Stage

Export the root layer by destination extension.

Returns:



113
114
115
116
# File 'lib/openusd/stage.rb', line 113

def export(path)
  root_layer.export(path)
  self
end

#invalidate!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Invalidate the cached composition index after an edit.



205
206
207
208
209
# File 'lib/openusd/stage.rb', line 205

def invalidate!
  @index = nil
  @composition = nil
  self
end

#layer_stackArray<Layer>

Returns layers participating in composition.

Returns:

  • (Array<Layer>)

    layers participating in composition



198
199
200
201
# File 'lib/openusd/stage.rb', line 198

def layer_stack
  composed_index
  @composition.layers
end

#opinions_for(path) ⇒ Array<PrimSpec>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns strongest-to-weakest opinions.

Returns:

  • (Array<PrimSpec>)

    strongest-to-weakest opinions



137
138
139
# File 'lib/openusd/stage.rb', line 137

def opinions_for(path)
  composed_index.fetch(Path.parse(path).to_s, [])
end

#prim_at(path) ⇒ Prim, ...

Find an active composed prim.

Returns:



49
50
51
52
53
54
55
56
57
# File 'lib/openusd/stage.rb', line 49

def prim_at(path)
  parsed = Path.parse(path)
  return pseudo_root if parsed.to_s == "/"
  return nil if parsed.property?
  return nil unless composed_index.key?(parsed.to_s)

  prim = Prim.new(self, parsed)
  prim.active? ? prim : nil
end

#remove_prim(path) ⇒ Stage

Remove or deactivate a prim in the edit target.

Returns:



78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/openusd/stage.rb', line 78

def remove_prim(path)
  parsed = validate_prim_path(path)
  remove_spec(edit_target, parsed)
  invalidate!
  if composed_index.key?(parsed.to_s)
    blocker = ensure_spec(edit_target, parsed)
    blocker.specifier = :over
    blocker.["active"] = false
  end
  invalidate!
  self
end

#root_pathsArray<Path>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns composed root paths.

Returns:

  • (Array<Path>)

    composed root paths



153
154
155
# File 'lib/openusd/stage.rb', line 153

def root_paths
  composed_index.keys.select { |path| path.count("/") == 1 }.sort.map { |path| Path.parse(path) }
end

#saveStage

Save the root layer to its identifier.

Returns:



106
107
108
109
# File 'lib/openusd/stage.rb', line 106

def save
  root_layer.save
  self
end

#set_prim_type(path, type_name) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Author a prim type in the edit target.



182
183
184
185
186
187
# File 'lib/openusd/stage.rb', line 182

def set_prim_type(path, type_name)
  spec = ensure_spec(edit_target, Path.parse(path))
  spec.type_name = type_name&.to_s
  invalidate!
  type_name
end

#set_variant_selection(path, set_name, choice) ⇒ Stage

Author a variant choice for a prim.

Returns:

Raises:



120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/openusd/stage.rb', line 120

def set_variant_selection(path, set_name, choice)
  prim = prim_at(path)
  raise CompositionError, "prim not found: #{path}" unless prim

  choices = prim.variant_sets[set_name.to_s]
  raise CompositionError, "variant set not found: #{set_name}" unless choices
  raise CompositionError, "variant not found: #{choice}" unless choices.key?(choice.to_s)

  spec = ensure_spec(edit_target, Path.parse(path))
  spec.["variants"] ||= {}
  spec.["variants"][set_name.to_s] = choice.to_s
  invalidate!
  self
end

#traverseEnumerator, Stage

Traverse active prims depth-first.

Returns:



93
94
95
96
97
98
99
100
101
102
# File 'lib/openusd/stage.rb', line 93

def traverse
  return enum_for(__method__) unless block_given?

  walk = lambda do |prim|
    yield prim
    prim.children.each { |child| walk.call(child) }
  end
  pseudo_root.children.each { |prim| walk.call(prim) }
  self
end