From 8078357e3c874e39e18d42fc27b6db3e33c55e18 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 26 Sep 2010 06:01:40 +0200 Subject: [PATCH 01/36] revisiting the binding scope problem --- src/proc/mobject/session/scope-path.cpp | 2 +- src/proc/mobject/session/scope-path.hpp | 2 +- src/proc/mobject/session/scope.cpp | 2 +- src/proc/mobject/session/scope.hpp | 2 +- .../mobject/session/placement-scope-test.cpp | 4 +- wiki/renderengine.html | 49 ++++++++++++++++--- 6 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/proc/mobject/session/scope-path.cpp b/src/proc/mobject/session/scope-path.cpp index eb7d14319..ee4d4fa5d 100644 --- a/src/proc/mobject/session/scope-path.cpp +++ b/src/proc/mobject/session/scope-path.cpp @@ -128,7 +128,7 @@ namespace session { bool ScopePath::isValid() const { - return (0 < length()) + return (1 < length()) #ifndef NDEBUG && hasValidRoot() #endif diff --git a/src/proc/mobject/session/scope-path.hpp b/src/proc/mobject/session/scope-path.hpp index 8087b89d1..f40023926 100644 --- a/src/proc/mobject/session/scope-path.hpp +++ b/src/proc/mobject/session/scope-path.hpp @@ -24,7 +24,7 @@ ** An Object representing a sequence of nested scopes within the Session. ** MObjects are being attached to the model by Placements, and each Placement ** is added as belonging \em into another Placement, which defines the Scope - ** of the addition. There is one (abstract) root element, containing the timelines; + ** of the addition. There is one (formal) root element, containing the timelines; ** from there a nested sequence of scopes leads down to each Placement. ** Ascending this path yields all the scopes to search or query in proper order ** to be used when resolving some attribute of placement. Placements use visibility diff --git a/src/proc/mobject/session/scope.cpp b/src/proc/mobject/session/scope.cpp index 7a3a9091b..428329808 100644 --- a/src/proc/mobject/session/scope.cpp +++ b/src/proc/mobject/session/scope.cpp @@ -34,7 +34,7 @@ namespace mobject { namespace session { - LUMIERA_ERROR_DEFINE (INVALID_SCOPE, "Placement scope invalid an not locatable within model"); + LUMIERA_ERROR_DEFINE (INVALID_SCOPE, "Placement scope invalid and not locatable within model"); diff --git a/src/proc/mobject/session/scope.hpp b/src/proc/mobject/session/scope.hpp index 7dbc3ee00..6dbe72f88 100644 --- a/src/proc/mobject/session/scope.hpp +++ b/src/proc/mobject/session/scope.hpp @@ -46,7 +46,7 @@ namespace session { using lib::IterAdapter; - LUMIERA_ERROR_DECLARE (INVALID_SCOPE); ///< Placement scope invalid an not locatable within model + LUMIERA_ERROR_DECLARE (INVALID_SCOPE); ///< Placement scope invalid and not locatable within model diff --git a/tests/components/proc/mobject/session/placement-scope-test.cpp b/tests/components/proc/mobject/session/placement-scope-test.cpp index 936eb84ca..f0d1343c6 100644 --- a/tests/components/proc/mobject/session/placement-scope-test.cpp +++ b/tests/components/proc/mobject/session/placement-scope-test.cpp @@ -138,8 +138,8 @@ namespace test { ASSERT (scope1 != par); ASSERT (par != scope1); ASSERT (scope2 != par); ASSERT (par != scope2); - PlacementMO& plac2 (scope2.getTop()); - ASSERT (aPlac.getID() == plac2.getID()); + PlacementMO& placm2 (scope2.getTop()); + ASSERT (aPlac.getID() == placm2.getID()); PlacementMO& parPlac (par.getTop()); ASSERT (aPlac.getID() != parPlac.getID()); diff --git a/wiki/renderengine.html b/wiki/renderengine.html index be2a9b466..27bc8f7d5 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1045,7 +1045,7 @@ Meanwhile I've settled down on implementing the [[top-level entities|Timeline]] On the implementation side, we use a special kind of MObject, acting as an anchor and providing an unique identity. Like any ~MObject, actually a placement establishes the connection and the scope, and typically constitutes a nested scope (e.g. the scope of all objects //within// the sequence to be bound into a timeline) -
+
There is some flexibility in the HighLevelModel, allowing to attach the same [[Sequence]] onto multiple [[timelines|Timeline]] or even into a [[meta-clip|VirtualClip]]. Thus, while there is always an containment relation which can be used to define the current PlacementScope, we can't always establish an unique path from any given location up to the model root. In the most general case, we have to deal with a DAG, not a tree.
 
 !solution idea
@@ -1059,6 +1059,16 @@ We us a 2-layer approach: initially, containment is implemented through a regist
 each Placement (with the exception of the root) gets registered as contained in yet another Placement. This registration is entirely a tree, and thus differs from the real scope nesting at the Sequence level: The scopes constituting Sequences and Timelines are registered as siblings, immediately below the root. This has some consequences
 # Sequences as well as Timelines can be discovered as contents of the model root
 # ScopePath digresses at Sequence level from the basic containment tree
+
+!!!!locating a placement
+constituting the effective logical position of a placement poses sort-of a chicken or egg problem: We need alread a logical position to start with. In practice, this is done by recurring on the QueryFocus, which is a stack-like state and automatically follows the access or query operations on the session. //Locating a placement//&nbsp; is done by //navigating the current query focus.//
+
+!!!!navigating a scope path location
+As the current query focus stack top always holds a ScopePath, the ''navigating operation'' on ScopePath is the key for managing this logical view onto the "current" location.
+* first, try to locate the new scope in the same sequence as the current scope, resulting in a common path prefix
+* in case the new scope belongs to a different sequence, this sequence might be connected to the current one as a meta-clip, again resulting in a common prefix
+* otherwise use the first possible binding according to the ordering of timelines as a prefix
+* use the basic containment path as a fallback if no binding exists
 
@@ -1531,10 +1541,10 @@ As we don't have a Prolog interpreter on board yet, we utilize a mock store with {{{default(Obj)}}} is a predicate expressing that the object {{{Obj}}} can be considered the default setup under the given conditions. Using the //default// can be considered as a shortcut for actually finding a exact and unique solution. The latter would require to specify all sorts of detailed properties up to the point where only one single object can satisfy all conditions. On the other hand, leaving some properties unspecified would yield a set of solutions (and the user code issuing the query had to provide means for selecting one soltution from this set). Just falling back on the //default// means that the user code actually doesn't care for any additional properties (as long as the properties he //does// care for are satisfied). Nothing is said specifically on //how//&nbsp; this default gets configured; actually there can be rules //somewhere,// and, additionally, anything encountered once while asking for a default can be re-used as default under similar circumstances. &rarr; [[implementing defaults|DefaultsImplementation]]
-
+
Along the way of working out various [[implementation details|ImplementationDetails]], decisions need to be made on how to understand the different facilities and entities and how to tackle some of the problems. This page is mainly a collection of keywords, summaries and links to further the discussion. And the various decisions should allways be read as proposals to solve some problem at hand...
 
-''Everything is an object'' &mdash; of course, that's a //no-brainer,// todays. Rather, important is what is not "an object", meaning it can't be arranged arbitrarily
+''Everything is an object'' &mdash; yes of course, that's a //no-brainer,// todays. Rather, important is to note what is not "an object", meaning it can't be arranged arbitrarily
 * we have one and only one global [[Session]] which directly contains a collection of multiple [[Sequences|Sequence]] and is associated with a globally managed collection of [[assets|Asset]]. We don't utilise scoped variables here (no "mandantisation"); if e.g. a media has been //opened,// it is just plain //globally known//&nbsp; as asset.
 * the [[knowledge base|ConfigRules]] is just available globally. Obviously, the session gets a chance to install rules into this knowledge base, but we don't stress ownership here.
 * we have a [[Fixture]] which acts as isolation layer towards the render engine and is (re)built automatically.
@@ -1546,6 +1556,8 @@ An [[Sequence]] is just a collection of configured and placed objects (and has n
 
 Actual ''media data and handling'' is abstracted rigorously. Media is conceived as being stream-like data of distinct StreamType. When it comes to more low-level media handling, we build on the DataFrame abstraction. Media processing isn't the focus of Lumiera; we organise the processing but otherwise ''rely on media handling libraries.'' In a similar vein, multiplicity is understood as type variation. Consequently, we don't build an audio and video "section" and we don't even have audio tracks and video tracks. Lumiera uses tracks and clips, and clips build on media, but we're able to deal with multichannel mixed-typed media.
 
+Lumiera is not a connection manager, it is not an audio-visual real time instrument, and it doesn't aim at running presentations. It's an ''environment for assembling and building up'' something (an edit, a session, a piece of media work). This decision is visible at various levels and contexts, like a reserved attitude towards hardware acceleration (it //will// be supported, but reliable proxy editing has a higher priority), or the decision, not to incorporate system level ports and connections directly into the session model (they are mapped to [[output designations|OutputDesignation]] rather)
+
 ''State'' is rigorously ''externalized'' and operations are to be ''scheduled'', to simplify locking and error handling. State is either treated similar to media stream data (as addressable and cacheable data frame), or is represented as "parameter" to be served by some [[parameter provider|ParamProvider]]. Consequently, [[Automation]] is just another kind of parameter, i.e. a function &mdash; how this function is calculated is an encapsulated implementation detail (we don't have "bezier automation", and then maybe a "linear automation", a "mask automation" and yet another way to handle transitions)
 
 Deliberately there is an limitaion on the flexibility of what can be added to the system via Plugins. We allow configuration and parametrisation to be extended and we allow processing and data handling to be extended, but we disallow extensions to the fundamental structure of the system by plugins. They may provide new implementations for already known subsystems, but they can't introduce new subsystems not envisioned in the general design of the application.
@@ -2809,7 +2821,7 @@ The operation point is provided by the current BuilderMould and used by the [[pr
 This is possible because the operation point has been provided (by the mould) with informations about the media stream type to be wired, which, together with information accessible at the the [[render node interface|ProcNode]] and from the [[referred processing assets|ProcAsset]], with the help of the [[connection manager|ConManager]] allows to figure out what's possible and how to do the desired connections. Additionally, in the course of deciding about possible connections, the PathManager is consulted to guide strategic decisions regarding the [[render node configuration|NodeConfiguration]], possible type conversions and the rendering technology to employ.
 
-
+
__6/2010__: an ever recurring (and yet unsolved) problem in the design of Luimiera's ~Proc-Layer is how to refer to output destinations, and how to organise them.
 To get ahead with this problem, I'll start collecting observations, relations and drafts on this tiddler.
 
@@ -2838,12 +2850,24 @@ To get ahead with this problem, I'll start collecting observations, relations an
 ** there might be //master pipes//
 ** finally, there is the hardware output or the distinct channel within the rendered result &mdash; never to be referred explicitly
 !!!relation to Pipes
-in almost every case mentioned above, the output designation is identical with the starting point of a [[Pipe]]. This might be a global pipe or a ClipSourcePort. Thus it sounds reasonable to use pipe-~IDs directly as output designation. Pipes, as an accountable entity (=asset) just //leap into existence by being referred.// On the other hand, the //actual// pipe is a semantic concept, a specific structural arrangement of objects. Basically it means that some object acts as attachment point and thereby //claims//&nbsp; to be the entrance side of a pipe, while other processor objects chain up in sequence.
+in almost every case mentioned above, the output designation is identical with the starting point of a [[Pipe]]. This might be a global pipe or a ClipSourcePort. Thus it sounds reasonable to use pipe-~IDs directly as output designation. Pipes, as an accountable entity (=asset) just //leap into existence by being referred.// On the other hand, the //actual//&nbsp; pipe is a semantic concept, a specific structural arrangement of objects. Basically it means that some object acts as attachment point and thereby //claims//&nbsp; to be the entrance side of a pipe, while other processor objects chain up in sequence.
 !!!system outputs
-System level output connections are the exception to the above rule. There is no pipe at an external port, and these externally visible connection points can appear and disappear, driven by external conditions. So the question is if system outputs shall be directly addressable at all as output designation? Rather, there should be a separate OutputManagement to handle external outputs and displays, both in windows or full screen.
+System level output connections seem to be an exception to the above rule. There is no pipe at an external port, and these externally visible connection points can appear and disappear, driven by external conditions. But the question is if system outputs shall be directly addressable at all as output designation? Generally speaking, Lumiera is not intended to be a system wide connection manager or a real time performance software. Thus it's advisable to refrain from direct referrals to system level connections from within the model. Rather, there should be a separate OutputManagement to handle external outputs and displays, both in windows or full screen. So these external outputs become rather a matter of application configuration &mdash; and for all the other purposes we're free to ''use pipe-~IDs as output designation''.
 !!!consequences of mentioning an output designation
 The immediate consequence is that an connection to this designation is wired, using an appropriate [[processing pattern|ProcPatt]]. A further consequence is for the mentioned output designation to appear as exit node of the built render nodes network. (not sure if thats always the case, or only when the correponding pipe is an end point without further outgoing connections)
 
+!Mapping of output designations
+An entire sequence can be embedded within another sequence as a VirtualClip. While for a simple clip there is a Clip-~MObject placed into the model, holding an reference to a media asset, in case of a virtual clip an BindingMO takes on the role of the clip object. Note that BindingMO also acts as [[Timeline]] implementation &mdash; indeed the same sequence might be bound simultaneously as a timeline and into a virtual clip. This flexibility creates a special twist, as the contents of this sequence have no way of finding out if they're used as timeline or embedded virtual clip. So parts of this sequence might mention a OutputDesignation which, when using the sequence as virtual clip, needs to be translated into a virtual media channel, which can be treated in the same fashion as any channel (video, audio,...) found within real media. Especially, a new output designation has to be derived in a sensible way, which largely depends on how the original output designation has been specified
+* a direct output designation specicfiation from within the sequence is captured and translated into a summation pipe at a position corresponding to the global pipes when using the same sequence as timeline. The output of this summation pipe is treated like a media channel
+** now, if a distinct output designation can be determined at this placement of the virtual clip, it will be used for the further routing
+** otherwise, the routing will be done as if the original output designation was given at this placement of the virtual clip.
+* a relative output designation (the N^^th^^ channel of this kind) is just carried over to the enclosing scope.
+
+!Using output designations
+While actually data frames are //pulled,// on a conceptual level data is assumed to "flow" through the pipes from source to output. This conceptual ("high level") model of data flow is comprised of the pipes (which are rather rigid), and flexible interconnections. The purpose of an output designation is to specify where the data should go, especially through these flexible interconnections. Thus, when reaching the exit point of a pipe, an output designation will be //queried.// Finding a suitable output designation involves two parts:
+* first of all, we need to know //what to route// &mdash; kind of the traits of the data. This is given by the //current pipe.//
+* then we'll need to determine an output designation //suitable for this data.// This is given by a "Plug" (WiringRequest) in the placement, and may be derived.
+As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type (e.g. and audio output when the pipe currently in question deals with video) is irrelevant.
 
@@ -4394,11 +4418,14 @@ In the general case, this user visible high-level-model of the [[objects|MObject While there //is// a "current state" involved, the effect of concurrent access deliberately remains unspecified, because access is expected to be serialised on a higher level. If this assumption were to break, then probably the ScopeLocator would involve some thread local state.
-
+
The sequence of nested [[placement scopes|PlacementScope]] leading from the root (global) scope down to a specific [[Placement]] is called ''scope path''. Ascending this path yields all the scopes to search or query in proper order to be used when resolving some attribute of placement. Placements use visibility rules comparable to visibility of scoped definitions in common programming languages or in cascading style sheets, where a local definition can shadow a global one. In a similar way, properties not defined locally may be resolved by querying up the sequence of nested scopes.
 
 A scope path is a sequence of scopes, where each scope is implemented by a PlacementRef pointing to the &raquo;scope top&laquo;, i.e. the placement in the session //constituting this scope.// Each Placement is registered with the session as belonging to a scope, and each placement can contain other placements and thus form a scope. Thus, the ''leaf'' of this path can be considered the current scope. In addition to some search and query functions, a scope path has the ability to ''navigate'' to a given target scope, which must be reachable by ascending and descending into the branches of the overall tree or DAG. Navigating changes the current path. ({{red{WIP 11/09}}} navigation to scopes outside the current path and the immediate children of the current leaf is left out for now. We'll need it later, when actually implementing [[meta-clips|VirtualClip]].)
 
+!access path and session structure
+ScopePath represents an ''effective scoping location'' within the model &mdash; it is not necessarily identical to the storage structure (&rarr; PlacementIndex) used to organise the session. While similar in most cases, binding a sequence into multiple timelines or meta-clips will cause the internal and the logical (effective) structure to digress (&rarr; BindingScopeProblem). An internal singleton service, the ScopeLocator is used to constitute the logical (effective) position for a given placement (which in itself defines a position in the session datastructure). This translation involves a [[current focus|QueryFocus]] remembering the access path used to reach the placement in question. Actually, this translation is built on top of the //navigation//-Operation of ScopePath, which thus forms the foundation to provide such a logical view on the "current" location.
+
 !Operations
 * the default scope path contains just the root (of the implicit PlacementIndex, i.e. usually the root of the model in the session)
 * a scope path can be created starting from a given scope. This convenience shortcut uses the ScopeLocator to establish the position of the given start scope. This way, effectively the PlacementIndex within the current session is queried for parentship relations until reaching the root of the HighLevelModel.
@@ -6525,6 +6552,14 @@ In case it's not already clear: we don't have "the" Render Engine, rat
 The &raquo;current setup&laquo; of the objects in the session is sort of a global state. Same holds true for the Controller, as the Engine can be at playback, it can run a background render or scrub single frames. But the whole complicated subsystem of the Builder and one given Render Engine configuration can be made ''stateless''. As a benefit of this we can run this subsystems multi-threaded without the need of any precautions (locking, synchronizing). Because all state information is just passed in as function parameters and lives in local variables on the stack, or is contained in the StateProxy which represents the given render //process// and is passed down as function parameter as well. (note: I use the term "stateless" in the usual, slightly relaxed manner; of course there are some configuration values contained in instance variables of the objects carrying out the calculations, but this values are considered to be constant over the course of the object usage).
 
+
+
within Lumiera's Proc-Layer, on the conceptual level there are two kinds of connections: data streams and control connections. The wiring details on how these connections are defined, how they are to be detected by the builder and finally implemented by links in the RenderEngine.
+
+!Stream connections
+
+!Control connections
+
+
Each [[processing node|ProcNode]] contains a stateless ({{{const}}}) descriptor detailing the inputs, outputs and predecessors. Moreover, this descriptor contains the configuration of the call sequence yielding the &raquo;data pulled from predecessor(s)&laquo;. The actual type of this object is composed out of several building blocks (policy classes) and placed by the builder as a template parameter on the WiringDescriptor of the individual ProcNode. This happens in the WiringFactory in file {{{nodewiring.cpp}}}, which consequently contains all the possible combinations (pre)generated at compile time.
 

From d1d7f3bc586f0163d2036dbfb447e9f3e2309980 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 1 Oct 2010 18:22:38 +0200
Subject: [PATCH 02/36] decided on an extension point to add virtual/effective
 paths we need that later to get full meta-clip functionality

---
 src/proc/mobject/session/query-focus.hpp   | 12 ++++++++---
 src/proc/mobject/session/scope-locator.hpp |  3 +++
 src/proc/mobject/session/scope-path.cpp    |  5 +++--
 src/proc/mobject/session/scope.cpp         |  2 +-
 wiki/renderengine.html                     | 25 ++++++++++++++++------
 5 files changed, 35 insertions(+), 12 deletions(-)

diff --git a/src/proc/mobject/session/query-focus.hpp b/src/proc/mobject/session/query-focus.hpp
index 29249ba58..de27f33fd 100644
--- a/src/proc/mobject/session/query-focus.hpp
+++ b/src/proc/mobject/session/query-focus.hpp
@@ -53,10 +53,15 @@ namespace session {
    * Alternatively, through the static factory function #push(), a new
    * focus location may be opened, thereby pushing the currently used
    * focus location aside. This new focus location will remain the
-   * current focus, while any handles referring to it is still in use.
+   * current focus, until all handles referring to it go out of scope.
    * 
    * Using an existing QueryFocus (handle), the current focus may be 
-   * shifted to another scope within the current session.
+   * shifted to another scope within the current session. This
+   * »navigating« operation will use the current focus position as
+   * point of departure, thus retaining a similar access path to any
+   * nested sequences. (These might be attached multiple times within
+   * the same session, each attachement constituing a different
+   * context scope. Navigating tries to retain the current context)
    * 
    * The templated query functions allow to issue specifically typed
    * queries to retrieve all children (immediately contained in a
@@ -73,7 +78,8 @@ namespace session {
    * are delivered without any defined order (implementation is
    * hashtable based)
    * 
-   * @see query-focus-test.cpp
+   * @see QueryFocus_test
+   * @see scope-path.hpp (concept: path of scopes)
    */
   class QueryFocus
     {
diff --git a/src/proc/mobject/session/scope-locator.hpp b/src/proc/mobject/session/scope-locator.hpp
index d936ae119..bd6e3fffd 100644
--- a/src/proc/mobject/session/scope-locator.hpp
+++ b/src/proc/mobject/session/scope-locator.hpp
@@ -116,6 +116,9 @@ namespace session {
   
   /** use the contents-resolving facility exposed by the session
    *  to discover the path up from the given scope to model root
+   *  @todo this seems to be the prime candidate to add the support for virtual paths,
+   *        as required to implement meta-clips ///////////////////////////////////////////TICKET #663
+   *        only used by the ScopePath implementation thus far
    */
   template
   inline typename ScopeQuery::iterator
diff --git a/src/proc/mobject/session/scope-path.cpp b/src/proc/mobject/session/scope-path.cpp
index ee4d4fa5d..7fdb2428e 100644
--- a/src/proc/mobject/session/scope-path.cpp
+++ b/src/proc/mobject/session/scope-path.cpp
@@ -59,7 +59,7 @@ namespace session {
     inline ScopeQuery::iterator
     discoverScopePath (Scope const& leaf)
     {
-      return ScopeLocator::instance().locate (leaf);
+      return ScopeLocator::instance().locate (leaf);   /////////////////////////////TICKET #663   create a single extension point to add meta-clip support later
     }
     
     
@@ -107,7 +107,7 @@ namespace session {
   {
     if (!leaf.isValid()) return; // invalid leaf defines invalid path....
     
-    append_all (discoverScopePath(leaf), path_);
+    append_all (discoverScopePath(leaf), path_);   /////////////////////////////TICKET #663   extension point for meta-clip support
     reverse (path_.begin(), path_.end());
   }
   
@@ -272,6 +272,7 @@ namespace session {
   {
     ___check_notBottom (this, "Navigating");
     *this = ScopePath(target);             //////////////////////////////TICKET #424
+                                          ///////////////////////////////TICKET #663   extension point for meta-clip support
   }
   
   
diff --git a/src/proc/mobject/session/scope.cpp b/src/proc/mobject/session/scope.cpp
index 428329808..871c386cc 100644
--- a/src/proc/mobject/session/scope.cpp
+++ b/src/proc/mobject/session/scope.cpp
@@ -72,7 +72,7 @@ namespace session {
   ScopeLocator::ScopeLocator()
     : focusStack_(new QueryFocusStack)
   {
-    TODO ("anything in initialise here?");
+    TODO ("anything to initialise here?");
   }
   
   ScopeLocator::~ScopeLocator() { }
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 27bc8f7d5..ef5524b84 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1045,12 +1045,17 @@ Meanwhile I've settled down on implementing the [[top-level entities|Timeline]]
 On the implementation side, we use a special kind of MObject, acting as an anchor and providing an unique identity. Like any ~MObject, actually a placement establishes the connection and the scope, and typically constitutes a nested scope (e.g. the scope of all objects //within// the sequence to be bound into a timeline)
 
-
+
There is some flexibility in the HighLevelModel, allowing to attach the same [[Sequence]] onto multiple [[timelines|Timeline]] or even into a [[meta-clip|VirtualClip]]. Thus, while there is always an containment relation which can be used to define the current PlacementScope, we can't always establish an unique path from any given location up to the model root. In the most general case, we have to deal with a DAG, not a tree.
 
 !solution idea
 Transform the DAG into a tree by //focussing//&nbsp; on the current situation and context. Have a state containing the //current path.//  &rarr; QueryFocus
 
+Incidentally, this problem is quite similar to file system navigation involving ''symlinks''.
+* under which circumstances shall discovery follow symlinks?
+* where do you get by {{{cd ..}}} &mdash; especially when you went down following a symlink?
+This leads us to building our solution here to match a similar behaviour pattern, according to the principle of least surprise. That is, disovery shall follow the special [[bindings|BindingMO]] only when requested explicitly, but the current "shell" (QueryFocus) should maintain a virtual/effective path to root scope.
+
 !!detail decisions
 !!!!how to represent scoping
 We us a 2-layer approach: initially, containment is implemented through a registration in the PlacementIndex. Building on that, scope is layered on top as an abstraction, which uses the registered containment relation, but takes the current access path into account at the  critical points (where a [[binding|BindingMO]] comes into play)
@@ -1061,7 +1066,7 @@ each Placement (with the exception of the root) gets registered as contained in
 # ScopePath digresses at Sequence level from the basic containment tree
 
 !!!!locating a placement
-constituting the effective logical position of a placement poses sort-of a chicken or egg problem: We need alread a logical position to start with. In practice, this is done by recurring on the QueryFocus, which is a stack-like state and automatically follows the access or query operations on the session. //Locating a placement//&nbsp; is done by //navigating the current query focus.//
+constituting the effective logical position of a placement poses sort-of a chicken or egg problem: We need already a logical position to start with. In practice, this is done by recurring on the QueryFocus, which is a stack-like state and automatically follows the access or query operations on the session. //Locating a placement//&nbsp; is done by //navigating the current query focus.//
 
 !!!!navigating a scope path location
 As the current query focus stack top always holds a ScopePath, the ''navigating operation'' on ScopePath is the key for managing this logical view onto the "current" location.
@@ -1069,6 +1074,14 @@ As the current query focus stack top always holds a ScopePath, the ''navigating
 * in case the new scope belongs to a different sequence, this sequence might be connected to the current one as a meta-clip, again resulting in a common prefix
 * otherwise use the first possible binding according to the ordering of timelines as a prefix
 * use the basic containment path as a fallback if no binding exists
+
+!!{{red{WIP 9/10}}}Deficiencies
+To buy us some time, analysing and implementing the gory details of scope path navigation and meta-clips was skipped for now.
+Please note the shortcomings and logical contradictions in the solution currently in code:
+* it is assumed that the translation basic tree &rarr; logical position happens "somehow" in the discovery query; but this query is implemented by the standard query resolver, which is the PlacementIndex. Thus, the facility providing the basic view should also be in charge for providing the logical view &mdash; not a very clever move
+* QueryFocus, with the help of ScopeLocator exposes the query services of the PlacementIndex. This yields the basic containment tree, while actually the logical view should be exposed. Thus we need an augmenting and translating layer on top
+* the {{{ScopePath::navigate()}}}-function actually creates a new path on-the-fly, just to get this translation. Rather, //exactly this function//&nbsp; should be the sole location to //provide//&nbsp; this service.
+* so we need a way to share the implementation logic, so the query/discovery functions can participate
 
@@ -3963,7 +3976,7 @@ The stack of scopes must not be confused with the ScopePath. Each single frame o The full implementation of this scope navigation is tricky, especially when it comes to determining the relation of two positions. It should be ''postponed'' and replaced by a ''dummy'' (no-op) implementation for the first integration round.
-
+
The ScopeLocator uses a special stack of ScopePath &raquo;frames&laquo; to maintain the //current focus.//
 What is the ''current'' QueryFocus and why is it necessary? There is a state-dependent part involved, inasmuch the effective ScopePath depends on how the invoking client has navigated the //current location// down into the HighLevelModel structures. Especially, when a VirtualClip is involved, there can be discrepancies between the paths resulting when descending down through different paths. (See &rarr; BindingScopeProblem).
 
@@ -3973,7 +3986,7 @@ Thus, doing something with the current location, and especially descending or qu
 As long as client code is just interested to use the current query location, we can provide a handle referring to it. But when a query needs to be run without side effect on the current location, we //push//&nbsp; it aside and start using a new QueryFocus on top, which starts out at a new initial location. Client code again gets a handle (smart-ptr) to this location, and additionally may access the new //current location.// When all references are out of scope and gone, we'll drop back to the focus put aside previously.
 
 !implementation of ref-counting and clean-up
-Actually, client code should use QueryFocus instances as frontend to access this &raquo;current focus&laquo;. Each ~QueryFocus instance incorporates a smart-ptr. But as in this case we're not managing objects allocated somewhere, we use an {{{boost::intrusive_ptr}}} and maintain the ref-count immediately within the target objects to be managed. These target objects are ScopePath instances and are living within the QueryFocusStack, which in turn in managed by the ScopeLocator singleton (see the UML diagram &rarr;[[here|QueryFocus]]). We use an (hand-written) stack implementation to ensure the memory locations of these ScopePath &raquo;frames&laquo; remain valid (and also to help with providing strong exception guarantees). The stack is aware of these ref-count and takes it into account on performing the {{{pop_unused()}}} operation: any unused frame on top will be evicted, stopping at the first frame still in use (which may be even just the old top). This cleanup also happens automatically when accessing the current top, re-initialising an potentially empty stack with a default-constructed new frame if necessary. This way, just accessing the stack top always yields the ''current focus location'', which thereby is //defined as the most recently used focus location still referred.//
+Actually, client code should use QueryFocus instances as frontend to access this &raquo;current focus&laquo;. Each ~QueryFocus instance incorporates a smart-ptr. But as in this case we're not managing objects allocated somewhere, we use an {{{boost::intrusive_ptr}}} and maintain the ref-count immediately within the target objects to be managed. These target objects are ScopePath instances and are living within the QueryFocusStack, which in turn is managed by the ScopeLocator singleton (see the UML diagram &rarr;[[here|QueryFocus]]). We use an (hand-written) stack implementation to ensure the memory locations of these ScopePath &raquo;frames&laquo; remain valid (and also to help with providing strong exception guarantees). The stack is aware of these ref-count and takes it into account on performing the {{{pop_unused()}}} operation: any unused frame on top will be evicted, stopping at the first frame still in use (which may be even just the old top). This cleanup also happens automatically when accessing the current top, re-initialising an potentially empty stack with a default-constructed new frame if necessary. This way, just accessing the stack top always yields the ''current focus location'', which thereby is //defined as the most recently used focus location still referred.//
 
 !concurrency
 This concept deliberately ignores parallelism. But, as the current path state is already encapsulated (and ref-counting is in place), the only central access point is to reach the current scope. Instead of using a plain-flat singleton here, this access can easily be routed through thread local storage.
@@ -4418,10 +4431,10 @@ In the general case, this user visible high-level-model of the [[objects|MObject
 While there //is// a "current state" involved, the effect of concurrent access deliberately remains unspecified, because access is expected to be serialised on a higher level. If this assumption were to break, then probably the ScopeLocator would involve some thread local state.
 
-
+
The sequence of nested [[placement scopes|PlacementScope]] leading from the root (global) scope down to a specific [[Placement]] is called ''scope path''. Ascending this path yields all the scopes to search or query in proper order to be used when resolving some attribute of placement. Placements use visibility rules comparable to visibility of scoped definitions in common programming languages or in cascading style sheets, where a local definition can shadow a global one. In a similar way, properties not defined locally may be resolved by querying up the sequence of nested scopes.
 
-A scope path is a sequence of scopes, where each scope is implemented by a PlacementRef pointing to the &raquo;scope top&laquo;, i.e. the placement in the session //constituting this scope.// Each Placement is registered with the session as belonging to a scope, and each placement can contain other placements and thus form a scope. Thus, the ''leaf'' of this path can be considered the current scope. In addition to some search and query functions, a scope path has the ability to ''navigate'' to a given target scope, which must be reachable by ascending and descending into the branches of the overall tree or DAG. Navigating changes the current path. ({{red{WIP 11/09}}} navigation to scopes outside the current path and the immediate children of the current leaf is left out for now. We'll need it later, when actually implementing [[meta-clips|VirtualClip]].)
+A scope path is a sequence of scopes, where each scope is implemented by a PlacementRef pointing to the &raquo;scope top&laquo;, i.e. the placement in the session //constituting this scope.// Each Placement is registered with the session as belonging to a scope, and each placement can contain other placements and thus form a scope. Thus, the ''leaf'' of this path can be considered the current scope. In addition to some search and query functions, a scope path has the ability to ''navigate'' to a given target scope, which must be reachable by ascending and descending into the branches of the overall tree or DAG. Navigating changes the current path. ({{red{WIP 11/09}}} navigation to scopes outside the current path and the immediate children of the current leaf is left out for now. We'll need it later, when actually implementing meta-clips. &rarr; see BindingScopeProblem)
 
 !access path and session structure
 ScopePath represents an ''effective scoping location'' within the model &mdash; it is not necessarily identical to the storage structure (&rarr; PlacementIndex) used to organise the session. While similar in most cases, binding a sequence into multiple timelines or meta-clips will cause the internal and the logical (effective) structure to digress (&rarr; BindingScopeProblem). An internal singleton service, the ScopeLocator is used to constitute the logical (effective) position for a given placement (which in itself defines a position in the session datastructure). This translation involves a [[current focus|QueryFocus]] remembering the access path used to reach the placement in question. Actually, this translation is built on top of the //navigation//-Operation of ScopePath, which thus forms the foundation to provide such a logical view on the "current" location.

From 1fe76e33a2e85b2ee90e69b06287f6fc78b9e4ad Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 2 Oct 2010 05:27:51 +0200
Subject: [PATCH 03/36] WIP: move the (planned) logic for virtual paths to
 ScopePath::navigate()

---
 src/proc/mobject/session/query-focus.hpp |  4 ++--
 src/proc/mobject/session/scope-path.cpp  | 17 +++++++++++++----
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/src/proc/mobject/session/query-focus.hpp b/src/proc/mobject/session/query-focus.hpp
index de27f33fd..05a426277 100644
--- a/src/proc/mobject/session/query-focus.hpp
+++ b/src/proc/mobject/session/query-focus.hpp
@@ -122,14 +122,14 @@ namespace session {
    */
   inline QueryFocus::operator Scope() const
   {
-    return currPath().getLeaf();
+    return focus_->getLeaf();
   }
   
   /**@note returning a copy */
   inline ScopePath
   QueryFocus::currentPath()  const
   {
-    return currPath();
+    return *focus_;
   }
   
   
diff --git a/src/proc/mobject/session/scope-path.cpp b/src/proc/mobject/session/scope-path.cpp
index 7fdb2428e..cb937e403 100644
--- a/src/proc/mobject/session/scope-path.cpp
+++ b/src/proc/mobject/session/scope-path.cpp
@@ -107,8 +107,8 @@ namespace session {
   {
     if (!leaf.isValid()) return; // invalid leaf defines invalid path....
     
-    append_all (discoverScopePath(leaf), path_);   /////////////////////////////TICKET #663   extension point for meta-clip support
-    reverse (path_.begin(), path_.end());
+    clear();
+    navigate (leaf);
   }
   
   
@@ -271,8 +271,17 @@ namespace session {
   ScopePath::navigate (Scope const& target)
   {
     ___check_notBottom (this, "Navigating");
-    *this = ScopePath(target);             //////////////////////////////TICKET #424
-                                          ///////////////////////////////TICKET #663   extension point for meta-clip support
+    std::vector otherPath;
+    append_all (discoverScopePath(target), otherPath);
+    reverse (otherPath.begin(), otherPath.end());
+                                        ////////////////////////////TICKET #663   extension point for meta-clip support
+    ASSERT (path_[0] == otherPath[0]); // sharing the root element
+    this->path_ = otherPath;          //  TODO really relate the two paths, including a treatment for meta-clips
+                                     //   - if both are in the same sequence (same head element): just attach the tail of the other
+                                    //    - if the other path points into a sequence which is attached as meta-clip to the current sequence,
+                                   //          then attach the other path below that meta-clip
+                                  //      - otherwise use the first timeline, to which the other path's sequence is attached
+                                 //       - otherwise, if all else fails, use the raw otherPath
   }
   
   

From 58757281b0211edf0fbedef6814698748efd9b88 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 2 Oct 2010 18:01:59 +0200
Subject: [PATCH 04/36] rearrange the query functions to suit the changed
 ScopePath internals

---
 src/proc/mobject/session/query-focus.cpp   |  1 +
 src/proc/mobject/session/scope-locator.hpp | 40 ++++++++++++++++++----
 src/proc/mobject/session/scope-path.cpp    |  6 ++--
 wiki/renderengine.html                     | 11 +++---
 4 files changed, 43 insertions(+), 15 deletions(-)

diff --git a/src/proc/mobject/session/query-focus.cpp b/src/proc/mobject/session/query-focus.cpp
index 965d50b66..f5680db65 100644
--- a/src/proc/mobject/session/query-focus.cpp
+++ b/src/proc/mobject/session/query-focus.cpp
@@ -22,6 +22,7 @@
 
 
 #include "proc/mobject/session/query-focus.hpp"
+#include "proc/mobject/mobject.hpp"
 
 
 namespace mobject {
diff --git a/src/proc/mobject/session/scope-locator.hpp b/src/proc/mobject/session/scope-locator.hpp
index bd6e3fffd..d49312d4b 100644
--- a/src/proc/mobject/session/scope-locator.hpp
+++ b/src/proc/mobject/session/scope-locator.hpp
@@ -78,6 +78,10 @@ namespace session {
       typename ScopeQuery::iterator
       locate (Scope scope);
       
+      ScopeQuery::iterator
+      getRawPath (Scope);
+
+
      ~ScopeLocator();
      
     protected:
@@ -113,20 +117,42 @@ namespace session {
     return ScopeQuery (scope.getTop(), CONTENTS).resolveBy (theResolver());
   }
   
-  
-  /** use the contents-resolving facility exposed by the session
-   *  to discover the path up from the given scope to model root
-   *  @todo this seems to be the prime candidate to add the support for virtual paths,
-   *        as required to implement meta-clips ///////////////////////////////////////////TICKET #663
-   *        only used by the ScopePath implementation thus far
+
+  /** navigate the \em current QueryFocus scope location. The resulting
+   *  access path to the new location is chosen such as to be most closely related
+   *  to the original location; this includes picking a timeline or meta-clip
+   *  attachment most similar to the one used in the original path. So effectively
+   *  you'll see things through the same "scoping perspective" as given by the
+   *  original path, if possible to the new location
+   *  given as parameter. use the contents-resolving facility exposed by the session
+   * @note changes the \em current QueryFocus as a sideeffect
+   * @param scope the new target location to navigate
+   * @return an iterator yielding the nested scopes from the new location
+   *         up to root, in a way likely to be similar to the original location
    */
   template
   inline typename ScopeQuery::iterator
   ScopeLocator::locate (Scope scope)
   {
-    return ScopeQuery (scope.getTop(), PATH).resolveBy (theResolver());
+    UNIMPLEMENTED ("virtual navigation");
   }
   
   
+  /** use the contents-resolving facility exposed by the session
+   *  to discover the path up from the given scope to model root.
+   *  @note this yields the \em raw path (basic containment hierarchy),
+   *        as opposed to an effective or virtual path, which should reflect
+   *        the attachment of Sequences to Timelines or meta-clips. That is,
+   *        you'll always get the top-level track of any sequence as direct
+   *        child of the root node and timelines (BindingMO) just appear
+   *        to be "dead ends"
+   */
+  inline ScopeQuery::iterator
+  ScopeLocator::getRawPath (Scope scope)
+  {
+    return ScopeQuery (scope.getTop(), PATH).resolveBy (theResolver());
+  }
+
+
 }} // namespace mobject::session
 #endif
diff --git a/src/proc/mobject/session/scope-path.cpp b/src/proc/mobject/session/scope-path.cpp
index cb937e403..208ce1231 100644
--- a/src/proc/mobject/session/scope-path.cpp
+++ b/src/proc/mobject/session/scope-path.cpp
@@ -54,12 +54,12 @@ namespace session {
   
   namespace { // Helpers and shortcuts....
     
-    /** issue a query to discover the path to root,
+    /** issue a query to discover the (raw) path to root,
      *  starting with the given scope */
     inline ScopeQuery::iterator
     discoverScopePath (Scope const& leaf)
     {
-      return ScopeLocator::instance().locate (leaf);   /////////////////////////////TICKET #663   create a single extension point to add meta-clip support later
+      return ScopeLocator::instance().getRawPath (leaf);
     }
     
     
@@ -279,7 +279,7 @@ namespace session {
     this->path_ = otherPath;          //  TODO really relate the two paths, including a treatment for meta-clips
                                      //   - if both are in the same sequence (same head element): just attach the tail of the other
                                     //    - if the other path points into a sequence which is attached as meta-clip to the current sequence,
-                                   //          then attach the other path below that meta-clip
+                                   //          then attach the other path below that meta-clip (problem: resolve multiple attachments)
                                   //      - otherwise use the first timeline, to which the other path's sequence is attached
                                  //       - otherwise, if all else fails, use the raw otherPath
   }
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index ef5524b84..e3b5a932a 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1045,7 +1045,7 @@ Meanwhile I've settled down on implementing the [[top-level entities|Timeline]]
 On the implementation side, we use a special kind of MObject, acting as an anchor and providing an unique identity. Like any ~MObject, actually a placement establishes the connection and the scope, and typically constitutes a nested scope (e.g. the scope of all objects //within// the sequence to be bound into a timeline)
 
-
+
There is some flexibility in the HighLevelModel, allowing to attach the same [[Sequence]] onto multiple [[timelines|Timeline]] or even into a [[meta-clip|VirtualClip]]. Thus, while there is always an containment relation which can be used to define the current PlacementScope, we can't always establish an unique path from any given location up to the model root. In the most general case, we have to deal with a DAG, not a tree.
 
 !solution idea
@@ -1078,10 +1078,11 @@ As the current query focus stack top always holds a ScopePath, the ''navigating
 !!{{red{WIP 9/10}}}Deficiencies
 To buy us some time, analysing and implementing the gory details of scope path navigation and meta-clips was skipped for now.
 Please note the shortcomings and logical contradictions in the solution currently in code:
-* it is assumed that the translation basic tree &rarr; logical position happens "somehow" in the discovery query; but this query is implemented by the standard query resolver, which is the PlacementIndex. Thus, the facility providing the basic view should also be in charge for providing the logical view &mdash; not a very clever move
-* QueryFocus, with the help of ScopeLocator exposes the query services of the PlacementIndex. This yields the basic containment tree, while actually the logical view should be exposed. Thus we need an augmenting and translating layer on top
-* the {{{ScopePath::navigate()}}}-function actually creates a new path on-the-fly, just to get this translation. Rather, //exactly this function//&nbsp; should be the sole location to //provide//&nbsp; this service.
-* so we need a way to share the implementation logic, so the query/discovery functions can participate
+* the {{{ScopePath::navigate()}}}-function was chosen as the location to implement the translation logic. //But actually this translation logic is missing.//
+* so basically we're just using the raw paths of the basic containment tree; more specifically, the BindingMO (=Timeline) isn't part of the derived ScopePath
+* this will result in problems even way before we implement meta-clips (because the Timeline is assumed to provide output routing information) to the Placements
+* QueryFocus, with the help of ScopeLocator exposes the query services of the PlacementIndex. So actually it's up to the client code to pick the right functions. This might get confusing
+* The current design rather places the implementation according to the roles of the involved entities, which causes some ping-pong on the implementation level. Especially the ScopeLocator singleton can be accessed multiple times. This is the usual clarity vs. performance tradeoff. Scope resolution is assumed rather to be //not performance critical.//
 
From 088922a79035ae259921a9f576283de343f2ff99 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 2 Oct 2010 18:37:40 +0200 Subject: [PATCH 05/36] remove obsolete helper seems to be a leftover, obsoleted by the simplifications done this june in c80b1894e6d Ticket #641 --- src/proc/mobject/session/scope-query.hpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/proc/mobject/session/scope-query.hpp b/src/proc/mobject/session/scope-query.hpp index 27d3e4f1b..b0b113535 100644 --- a/src/proc/mobject/session/scope-query.hpp +++ b/src/proc/mobject/session/scope-query.hpp @@ -184,12 +184,6 @@ namespace session { { return bind (&PlacementMO::isCompatible, _1 ); } - - void - resetResultIteration (iterator const& newQueryResults) - { - static_cast (*this) = newQueryResults; - } }; From d5cdd39f524dff779d8d1a62fd8922a4964eb51e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 2 Oct 2010 19:22:28 +0200 Subject: [PATCH 06/36] WIP trying to code up the public query function for scope paths problem is how to get an iterator compatible with ScopeQuery... --- src/lib/iter-adapter.hpp | 6 ++--- src/proc/mobject/session/scope-locator.hpp | 3 +++ src/proc/mobject/session/scope-path.cpp | 2 +- src/proc/mobject/session/scope-query.hpp | 27 ++++++++++++++++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index 26062f458..fb0e98de4 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -145,11 +145,11 @@ namespace lib { * -# it should be copy constructible * -# when IterAdapter is supposed to be assignable, then POS should be * -# it should provide embedded typedefs for pointer, reference and value_type, - * or alternatively resolve these types through a specialisation if IterTraits. + * or alternatively resolve these types through a specialisation of IterTraits. * -# it should be convertible to the pointer type it declares - * -# dereferencing it should yield type that is convertible to the reference type + * -# dereferencing should yield a type that is convertible to the reference type * - CON points to the data source of this iterator (typically a data container type) - * We store a pointer-like backlink to invoke a special iteration control API: + * We store a pointer-like backlink to invoke a special iteration control API: //////////////TICKET #410 * -# \c hasNext yields true iff the source has yet more result values to yield * -# \c iterNext advances the POS to the next element * diff --git a/src/proc/mobject/session/scope-locator.hpp b/src/proc/mobject/session/scope-locator.hpp index d49312d4b..37884608e 100644 --- a/src/proc/mobject/session/scope-locator.hpp +++ b/src/proc/mobject/session/scope-locator.hpp @@ -135,6 +135,9 @@ namespace session { ScopeLocator::locate (Scope scope) { UNIMPLEMENTED ("virtual navigation"); + ///////////////////////////////////////////TODO: see scope-query.hpp + ///////////////////////////////////////////TODO: its hard to come up with a generic implementation which yields a compatible iterator + ///////////////////////////////////////////TODO: *alternatively* just expose an Iterator of Scopes? } diff --git a/src/proc/mobject/session/scope-path.cpp b/src/proc/mobject/session/scope-path.cpp index 208ce1231..aaffc2780 100644 --- a/src/proc/mobject/session/scope-path.cpp +++ b/src/proc/mobject/session/scope-path.cpp @@ -282,7 +282,7 @@ namespace session { // then attach the other path below that meta-clip (problem: resolve multiple attachments) // - otherwise use the first timeline, to which the other path's sequence is attached // - otherwise, if all else fails, use the raw otherPath - } + } ////////////////////////////////////TICKET #672 void diff --git a/src/proc/mobject/session/scope-query.hpp b/src/proc/mobject/session/scope-query.hpp index b0b113535..8046bc289 100644 --- a/src/proc/mobject/session/scope-query.hpp +++ b/src/proc/mobject/session/scope-query.hpp @@ -209,5 +209,32 @@ namespace session { }; + /** + * Exposes an opaque MObject result set + * similar to a DiscoveryQuery, including the + * ability to filter/downcast to a specific + * kind of MObject + */ + template + class WrappedResults + { + + class Wrapper + { +//////////////TODO: it would be *nice* if we could build something similar to the scope-query Resolution +//////////////TODO: *requirement* is that it must not pull in anything beyond and iter-adapters.hpp +//////////////TODO: but if that's too expensive to implement, just ditch the ability of subclassing and just yield MObjects, or even just Scopes + }; + + + template + WrappedResults (IT const& results) + { + UNIMPLEMENTED ("build a suitable wrapper"); + } + + }; + + }} // namespace mobject::session #endif From e14aba833e7784732b9688dca59dd15102997e65 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 3 Oct 2010 23:49:38 +0200 Subject: [PATCH 07/36] remove the parent-iteration capability from Scope its not needed yet, and would be surprisingly tricky to implement, so lets get rid of it! Use QueryFocus or ScopeLocator instead --- src/proc/mobject/session/query-focus.hpp | 1 + src/proc/mobject/session/scope-locator.hpp | 26 +---------------- src/proc/mobject/session/scope.cpp | 33 ++++++++++++++-------- src/proc/mobject/session/scope.hpp | 9 +----- 4 files changed, 25 insertions(+), 44 deletions(-) diff --git a/src/proc/mobject/session/query-focus.hpp b/src/proc/mobject/session/query-focus.hpp index 05a426277..2ffa30902 100644 --- a/src/proc/mobject/session/query-focus.hpp +++ b/src/proc/mobject/session/query-focus.hpp @@ -25,6 +25,7 @@ #define MOBJECT_SESSION_QUERY_FOCUS_H #include "proc/mobject/session/scope-path.hpp" +#include "proc/mobject/session/scope-query.hpp" #include "proc/mobject/session/scope-locator.hpp" #include diff --git a/src/proc/mobject/session/scope-locator.hpp b/src/proc/mobject/session/scope-locator.hpp index 37884608e..a1520ffca 100644 --- a/src/proc/mobject/session/scope-locator.hpp +++ b/src/proc/mobject/session/scope-locator.hpp @@ -74,8 +74,7 @@ namespace session { typename ScopeQuery::iterator query (Scope); - template - typename ScopeQuery::iterator + ScopeQuery::iterator locate (Scope scope); ScopeQuery::iterator @@ -117,29 +116,6 @@ namespace session { return ScopeQuery (scope.getTop(), CONTENTS).resolveBy (theResolver()); } - - /** navigate the \em current QueryFocus scope location. The resulting - * access path to the new location is chosen such as to be most closely related - * to the original location; this includes picking a timeline or meta-clip - * attachment most similar to the one used in the original path. So effectively - * you'll see things through the same "scoping perspective" as given by the - * original path, if possible to the new location - * given as parameter. use the contents-resolving facility exposed by the session - * @note changes the \em current QueryFocus as a sideeffect - * @param scope the new target location to navigate - * @return an iterator yielding the nested scopes from the new location - * up to root, in a way likely to be similar to the original location - */ - template - inline typename ScopeQuery::iterator - ScopeLocator::locate (Scope scope) - { - UNIMPLEMENTED ("virtual navigation"); - ///////////////////////////////////////////TODO: see scope-query.hpp - ///////////////////////////////////////////TODO: its hard to come up with a generic implementation which yields a compatible iterator - ///////////////////////////////////////////TODO: *alternatively* just expose an Iterator of Scopes? - } - /** use the contents-resolving facility exposed by the session * to discover the path up from the given scope to model root. diff --git a/src/proc/mobject/session/scope.cpp b/src/proc/mobject/session/scope.cpp index 871c386cc..be3e05790 100644 --- a/src/proc/mobject/session/scope.cpp +++ b/src/proc/mobject/session/scope.cpp @@ -122,6 +122,28 @@ namespace session { return focusStack_->push (SessionServiceExploreScope::getScopeRoot()); } + + /** navigate the \em current QueryFocus scope location. The resulting + * access path to the new location is chosen such as to be most closely related + * to the original location; this includes picking a timeline or meta-clip + * attachment most similar to the one used in the original path. So effectively + * you'll see things through the same "scoping perspective" as given by the + * original path, if possible to the new location + * given as parameter. use the contents-resolving facility exposed by the session + * @note changes the \em current QueryFocus as a sideeffect + * @param scope the new target location to navigate + * @return an iterator yielding the nested scopes from the new location + * up to root, in a way likely to be similar to the original location + */ + ScopeQuery::iterator + ScopeLocator::locate (Scope scope) + { + UNIMPLEMENTED ("virtual navigation"); + ///////////////////////////////////////////TODO: see scope-query.hpp + ///////////////////////////////////////////TODO: its hard to come up with a generic implementation which yields a compatible iterator + ///////////////////////////////////////////TODO: *alternatively* just expose an Iterator of Scopes? + } + /** discover the enclosing scope of a given Placement */ @@ -175,17 +197,6 @@ namespace session { } - /** enumerate the path of nested scopes up to root scope. - * @return an iterator which starts with this scope and - * successively yields outer scopes, stopping at root. - */ - Scope::IterType_ - Scope::ascend() const - { - UNIMPLEMENTED ("ascend scope hierarchy up to root"); - } - - diff --git a/src/proc/mobject/session/scope.hpp b/src/proc/mobject/session/scope.hpp index 6dbe72f88..934ee87de 100644 --- a/src/proc/mobject/session/scope.hpp +++ b/src/proc/mobject/session/scope.hpp @@ -28,7 +28,7 @@ #include "proc/mobject/placement.hpp" #include "proc/mobject/placement-ref.hpp" //#include "proc/mobject/session/query-resolver.hpp" ///////////TODO: really? -#include "lib/iter-adapter.hpp" +//#include "lib/iter-adapter.hpp" #include "lib/error.hpp" //#include "lib/singleton.hpp" @@ -44,8 +44,6 @@ namespace mobject { namespace session { - using lib::IterAdapter; - LUMIERA_ERROR_DECLARE (INVALID_SCOPE); ///< Placement scope invalid and not locatable within model @@ -61,8 +59,6 @@ namespace session { { RefPlacement anchor_; - typedef IterAdapter IterType_; - public: Scope (PlacementMO const& constitutingPlacement); Scope (); ///< unlocated NIL scope @@ -78,9 +74,6 @@ namespace session { bool isValid() const; bool isRoot() const; - typedef IterType_ iterator; - iterator ascend() const; - friend bool operator== (Scope const&, Scope const&); friend bool operator!= (Scope const&, Scope const&); }; From 001a45f64a90ca7baeacf37f3098f93243e16ed1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 4 Oct 2010 02:30:44 +0200 Subject: [PATCH 08/36] WIP implementation idea how to get both iterations unified --- src/proc/mobject/session/scope-query.hpp | 49 ++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/proc/mobject/session/scope-query.hpp b/src/proc/mobject/session/scope-query.hpp index 8046bc289..d167e6554 100644 --- a/src/proc/mobject/session/scope-query.hpp +++ b/src/proc/mobject/session/scope-query.hpp @@ -216,21 +216,62 @@ namespace session { * kind of MObject */ template - class WrappedResults + class CachedQuery + : Query > { + typedef Placement PMO; + typedef Query _Query; + typedef typename _Query::Result Result; + typedef typename _Query::Cursor Cursor; - class Wrapper + + public: + typedef typename _Query::iterator iterator; + + class Snapshot + : public Resolution { //////////////TODO: it would be *nice* if we could build something similar to the scope-query Resolution //////////////TODO: *requirement* is that it must not pull in anything beyond and iter-adapters.hpp //////////////TODO: but if that's too expensive to implement, just ditch the ability of subclassing and just yield MObjects, or even just Scopes + + vector results_; + + Result + prepareResolution() + { + UNIMPLEMENTED ("hook up first result"); + } + + void + nextResult(Result& pos) + { + UNIMPLEMENTED ("how to advance embedded pointer"); + } + + public: + template + Snapshot(IT const& iter) + { + for(IT ii(iter); ii; ++ii) + results_.push_back(*ii); + } }; + Snapshot resultset_; template - WrappedResults (IT const& results) + CachedQuery (IT const& results) + : resultset_(results) + { } + + iterator + resolve () const { - UNIMPLEMENTED ("build a suitable wrapper"); + PReso resultSet(new Snapshot(resultset_)); + Result first = resultSet->prepareResolution(); + Cursor& start = static_cast (first); // note: type RES must be compatible! + return iterator (resultSet, start); } }; From 9593d388ccff29a460f786b8c295b09534dbff1e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 5 Oct 2010 01:11:32 +0200 Subject: [PATCH 09/36] back out that caching query, it's nonsense defeating the whole purpose of that interface --- src/proc/mobject/session/scope-query.hpp | 66 ------------------------ 1 file changed, 66 deletions(-) diff --git a/src/proc/mobject/session/scope-query.hpp b/src/proc/mobject/session/scope-query.hpp index d167e6554..f8d7e600a 100644 --- a/src/proc/mobject/session/scope-query.hpp +++ b/src/proc/mobject/session/scope-query.hpp @@ -209,72 +209,6 @@ namespace session { }; - /** - * Exposes an opaque MObject result set - * similar to a DiscoveryQuery, including the - * ability to filter/downcast to a specific - * kind of MObject - */ - template - class CachedQuery - : Query > - { - typedef Placement PMO; - typedef Query _Query; - typedef typename _Query::Result Result; - typedef typename _Query::Cursor Cursor; - - - public: - typedef typename _Query::iterator iterator; - - class Snapshot - : public Resolution - { -//////////////TODO: it would be *nice* if we could build something similar to the scope-query Resolution -//////////////TODO: *requirement* is that it must not pull in anything beyond and iter-adapters.hpp -//////////////TODO: but if that's too expensive to implement, just ditch the ability of subclassing and just yield MObjects, or even just Scopes - - vector results_; - - Result - prepareResolution() - { - UNIMPLEMENTED ("hook up first result"); - } - - void - nextResult(Result& pos) - { - UNIMPLEMENTED ("how to advance embedded pointer"); - } - - public: - template - Snapshot(IT const& iter) - { - for(IT ii(iter); ii; ++ii) - results_.push_back(*ii); - } - }; - - Snapshot resultset_; - - template - CachedQuery (IT const& results) - : resultset_(results) - { } - - iterator - resolve () const - { - PReso resultSet(new Snapshot(resultset_)); - Result first = resultSet->prepareResolution(); - Cursor& start = static_cast (first); // note: type RES must be compatible! - return iterator (resultSet, start); - } - - }; }} // namespace mobject::session From 2c58e595c04d239c9c27e1f4ca6e1e7d54bad6af Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 5 Oct 2010 01:40:50 +0200 Subject: [PATCH 10/36] augment IterSource adapters by definition through classical iterator range --- src/lib/iter-source.hpp | 22 ++++++++++++++++++++++ tests/lib/iter-source-test.cpp | 5 +++++ 2 files changed, 27 insertions(+) diff --git a/src/lib/iter-source.hpp b/src/lib/iter-source.hpp index 57936d481..ee3ec8228 100644 --- a/src/lib/iter-source.hpp +++ b/src/lib/iter-source.hpp @@ -255,6 +255,13 @@ namespace lib { typedef typename IterSource::iterator Iter; }; + template + struct _RangeT + { + typedef typename IT::value_type Val; + typedef typename IterSource::iterator Iter; + }; + template struct _MapT { @@ -400,6 +407,21 @@ namespace lib { return IterSource::build (new WrappedLumieraIterator(contents)); } + + /** @return a Lumiera Forward Iterator yielding all values + * defined by a classical Iterator range. + */ + template + typename _RangeT::Iter + eachEntry (IT const& begin, IT const& end) + { + typedef typename _RangeT::Val ValType; + typedef RangeIter Range; + + Range contents (begin, end); + return IterSource::build (new WrappedLumieraIterator(contents)); + } + } using iter_source::wrapIter; using iter_source::eachMapKey; diff --git a/tests/lib/iter-source-test.cpp b/tests/lib/iter-source-test.cpp index 3fc03664f..0021d80f3 100644 --- a/tests/lib/iter-source-test.cpp +++ b/tests/lib/iter-source-test.cpp @@ -183,17 +183,22 @@ namespace test{ // build the test data sources WrappedList customList(NUM_ELMS); TestSource dedicatedSource(NUM_ELMS); + list& rawList(customList.data_); IntIter iii (eachEntry (customList)); + IntIter isi (eachEntry (rawList.begin(), rawList.end())); StrIter cii (IterSource::build(dedicatedSource)); ASSERT (!isnil (iii)); + ASSERT (!isnil (isi)); ASSERT (!isnil (cii)); pullOut (iii); + pullOut (isi); pullOut (cii); ASSERT (!iii); + ASSERT (!isi); ASSERT (!cii); } From e5de12fc7af047674bfa70bedb2ca1b81900195d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 5 Oct 2010 04:12:17 +0200 Subject: [PATCH 11/36] (draft) maybe solved the problem defining a scope iterator actually that would require to extract the IterTraits from iter-adapter.hpp to allow for such dedicate specialisations --- src/lib/iter-adapter.hpp | 39 ++++++++++++++++------ src/proc/mobject/session/scope-locator.hpp | 18 ++++++++-- src/proc/mobject/session/scope.cpp | 31 ++++++++++++++--- 3 files changed, 70 insertions(+), 18 deletions(-) diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index fb0e98de4..cbbe7ac57 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -81,14 +81,21 @@ #include - +namespace mobject { +namespace session { + class Scope; +}} +#include +using std::vector; namespace lib { - namespace { + namespace iter { /** * Helper for creating nested typedefs * within the iterator adaptor, similar to what the STL does. + * @note client code might define specialisations + * to handle tricky situations (like const_reverse_iter) */ template struct IterTraits @@ -113,8 +120,20 @@ namespace lib { typedef const TY& reference; typedef const TY* pointer; }; - - + + using mobject::session::Scope; + + template<> + struct IterTraits::const_reverse_iterator> + { + typedef const Scope value_type; + typedef Scope const& reference; + typedef const Scope* pointer; + }; + + } + + namespace { // internal helpers void _throwIterExhausted() { @@ -164,9 +183,9 @@ namespace lib { mutable POS pos_; public: - typedef typename IterTraits::pointer pointer; - typedef typename IterTraits::reference reference; - typedef typename IterTraits::value_type value_type; + typedef typename iter::IterTraits::pointer pointer; + typedef typename iter::IterTraits::reference reference; + typedef typename iter::IterTraits::value_type value_type; IterAdapter (CON src, POS const& startpos) : source_(src) @@ -299,9 +318,9 @@ namespace lib { IT e_; public: - typedef typename IterTraits::pointer pointer; - typedef typename IterTraits::reference reference; - typedef typename IterTraits::value_type value_type; + typedef typename iter::IterTraits::pointer pointer; + typedef typename iter::IterTraits::reference reference; + typedef typename iter::IterTraits::value_type value_type; RangeIter (IT const& start, IT const& end) : p_(start) diff --git a/src/proc/mobject/session/scope-locator.hpp b/src/proc/mobject/session/scope-locator.hpp index a1520ffca..56a0b8606 100644 --- a/src/proc/mobject/session/scope-locator.hpp +++ b/src/proc/mobject/session/scope-locator.hpp @@ -27,6 +27,7 @@ #include "proc/mobject/session/scope.hpp" #include "proc/mobject/session/scope-query.hpp" #include "proc/mobject/placement.hpp" +#include "lib/iter-source.hpp" ////////////////////TICKET #493 : the bare interface would be sufficient here #include "lib/singleton.hpp" #include @@ -74,12 +75,16 @@ namespace session { typename ScopeQuery::iterator query (Scope); - ScopeQuery::iterator - locate (Scope scope); - + template + typename ScopeQuery::iterator + getRawPath (Scope); + ScopeQuery::iterator getRawPath (Scope); + lib::IterSource::iterator + locate (Scope scope); + ~ScopeLocator(); @@ -126,6 +131,13 @@ namespace session { * child of the root node and timelines (BindingMO) just appear * to be "dead ends" */ + template + inline typename ScopeQuery::iterator + ScopeLocator::getRawPath (Scope scope) + { + return ScopeQuery (scope.getTop(), PATH).resolveBy (theResolver()); + } + inline ScopeQuery::iterator ScopeLocator::getRawPath (Scope scope) { diff --git a/src/proc/mobject/session/scope.cpp b/src/proc/mobject/session/scope.cpp index be3e05790..b10df0982 100644 --- a/src/proc/mobject/session/scope.cpp +++ b/src/proc/mobject/session/scope.cpp @@ -26,9 +26,31 @@ #include "proc/mobject/session/query-focus-stack.hpp" #include "proc/mobject/session/session-service-explore-scope.hpp" #include "proc/mobject/mobject.hpp" +#include "lib/iter-source.hpp" ////////////////////TICKET #493 : using the IterSource adapters here //#include "proc/mobject/session/track.hpp" + //#include "proc/mobject/placement.hpp" //#include "proc/mobject/session/mobjectfactory.hpp" +#include + +using std::vector; +using lib::IterSource; +using lib::WrappedLumieraIterator; +using lib::iter_source::wrapIter; + +namespace lib { +namespace iter{ + +// using mobject::session::Scope; +// +// template<> +// struct IterTraits::const_reverse_iterator> +// { +// typedef const Scope value_type; +// typedef Scope const& reference; +// typedef const Scope* pointer; +// }; +}} namespace mobject { namespace session { @@ -135,13 +157,12 @@ namespace session { * @return an iterator yielding the nested scopes from the new location * up to root, in a way likely to be similar to the original location */ - ScopeQuery::iterator + IterSource::iterator ScopeLocator::locate (Scope scope) { - UNIMPLEMENTED ("virtual navigation"); - ///////////////////////////////////////////TODO: see scope-query.hpp - ///////////////////////////////////////////TODO: its hard to come up with a generic implementation which yields a compatible iterator - ///////////////////////////////////////////TODO: *alternatively* just expose an Iterator of Scopes? + ScopePath& currentPath = focusStack_->top(); + currentPath.navigate (scope); + return wrapIter (currentPath.begin()); } From fdfb946d63bcc3e6bd51992033a305b9526e92a8 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 6 Oct 2010 03:22:58 +0200 Subject: [PATCH 12/36] preparation to allow explicit type bindings for iter-adapter --- src/lib/iter-adapter.hpp | 22 +++++++++++----------- src/proc/mobject/session/scope.cpp | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index cbbe7ac57..93944288f 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -98,7 +98,7 @@ namespace lib { * to handle tricky situations (like const_reverse_iter) */ template - struct IterTraits + struct TypeBinding { typedef typename TY::pointer pointer; typedef typename TY::reference reference; @@ -106,7 +106,7 @@ namespace lib { }; template - struct IterTraits + struct TypeBinding { typedef TY value_type; typedef TY& reference; @@ -114,7 +114,7 @@ namespace lib { }; template - struct IterTraits + struct TypeBinding { typedef TY value_type; typedef const TY& reference; @@ -124,7 +124,7 @@ namespace lib { using mobject::session::Scope; template<> - struct IterTraits::const_reverse_iterator> + struct TypeBinding::const_reverse_iterator> { typedef const Scope value_type; typedef Scope const& reference; @@ -164,7 +164,7 @@ namespace lib { * -# it should be copy constructible * -# when IterAdapter is supposed to be assignable, then POS should be * -# it should provide embedded typedefs for pointer, reference and value_type, - * or alternatively resolve these types through a specialisation of IterTraits. + * or alternatively resolve these types through specialisation of iter::TypeBinding. * -# it should be convertible to the pointer type it declares * -# dereferencing should yield a type that is convertible to the reference type * - CON points to the data source of this iterator (typically a data container type) @@ -183,9 +183,9 @@ namespace lib { mutable POS pos_; public: - typedef typename iter::IterTraits::pointer pointer; - typedef typename iter::IterTraits::reference reference; - typedef typename iter::IterTraits::value_type value_type; + typedef typename iter::TypeBinding::pointer pointer; + typedef typename iter::TypeBinding::reference reference; + typedef typename iter::TypeBinding::value_type value_type; IterAdapter (CON src, POS const& startpos) : source_(src) @@ -318,9 +318,9 @@ namespace lib { IT e_; public: - typedef typename iter::IterTraits::pointer pointer; - typedef typename iter::IterTraits::reference reference; - typedef typename iter::IterTraits::value_type value_type; + typedef typename iter::TypeBinding::pointer pointer; + typedef typename iter::TypeBinding::reference reference; + typedef typename iter::TypeBinding::value_type value_type; RangeIter (IT const& start, IT const& end) : p_(start) diff --git a/src/proc/mobject/session/scope.cpp b/src/proc/mobject/session/scope.cpp index b10df0982..d53dd153d 100644 --- a/src/proc/mobject/session/scope.cpp +++ b/src/proc/mobject/session/scope.cpp @@ -44,7 +44,7 @@ namespace iter{ // using mobject::session::Scope; // // template<> -// struct IterTraits::const_reverse_iterator> +// struct TypeBinding::const_reverse_iterator> // { // typedef const Scope value_type; // typedef Scope const& reference; From 02d6d6a65c3aed861b197b66ddb1e6c09d43b48e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 6 Oct 2010 03:37:53 +0200 Subject: [PATCH 13/36] extract iter::TypeBinding into separate header to allow explicit specialisation --- src/lib/iter-adapter.hpp | 53 ++----------------- src/lib/iter-type-binding.hpp | 98 +++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 50 deletions(-) create mode 100644 src/lib/iter-type-binding.hpp diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index 93944288f..28d69f74b 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -77,62 +77,14 @@ #include "lib/error.hpp" #include "lib/bool-checkable.hpp" +#include "lib/iter-type-binding.hpp" #include -namespace mobject { -namespace session { - class Scope; -}} -#include -using std::vector; namespace lib { - namespace iter { - /** - * Helper for creating nested typedefs - * within the iterator adaptor, similar to what the STL does. - * @note client code might define specialisations - * to handle tricky situations (like const_reverse_iter) - */ - template - struct TypeBinding - { - typedef typename TY::pointer pointer; - typedef typename TY::reference reference; - typedef typename TY::value_type value_type; - }; - - template - struct TypeBinding - { - typedef TY value_type; - typedef TY& reference; - typedef TY* pointer; - }; - - template - struct TypeBinding - { - typedef TY value_type; - typedef const TY& reference; - typedef const TY* pointer; - }; - - using mobject::session::Scope; - - template<> - struct TypeBinding::const_reverse_iterator> - { - typedef const Scope value_type; - typedef Scope const& reference; - typedef const Scope* pointer; - }; - - } - namespace { // internal helpers void _throwIterExhausted() @@ -140,10 +92,10 @@ namespace lib { throw lumiera::error::Invalid ("Can't iterate further", lumiera::error::LUMIERA_ERROR_ITER_EXHAUST); } - } + /** * Adapter for building an implementation of the lumiera forward iterator concept. * The "current position" is represented as an opaque element (usually an nested iterator), @@ -173,6 +125,7 @@ namespace lib { * -# \c iterNext advances the POS to the next element * * @see scoped-ptrvect.hpp usage example + * @see iter-type-binding.hpp * @see iter-adaptor-test.cpp */ template diff --git a/src/lib/iter-type-binding.hpp b/src/lib/iter-type-binding.hpp new file mode 100644 index 000000000..68f6f21a7 --- /dev/null +++ b/src/lib/iter-type-binding.hpp @@ -0,0 +1,98 @@ +/* + ITER-TYPE-BINDING.hpp - control type variations for IterAdapter + + Copyright (C) Lumiera.org + 2010, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file iter-type-binding.hpp + ** Type re-binding helper template for IterAdapter and friends. + ** This header defines a trait template which is used by the Iterator + ** adapters to figure out the value-, pointer- and reference types + ** when wrapping iterators or containers. For extending the use of + ** Iterator adapters and Iter-tools to situations involving const + ** iterators or custom containers, explicit specialisations + ** might be injected prior to instantiating the Iterator adapter + ** template. + ** + */ + + +#ifndef LIB_ITER_TYPE_BINDING_H +#define LIB_ITER_TYPE_BINDING_H + + +#include "lib/error.hpp" + + +namespace mobject { +namespace session { + class Scope; +}} +#include +using std::vector; + + +namespace lib { +namespace iter { + + /** + * Type re-binding helper template for creating nested typedefs + * for use by IterAdapter or similar "Lumiera Forward Iterators". + * This trait provides a value-, reference- and pointer type, + * similar to what the STL does. + * @note client code might define specialisations + * to handle tricky situations (like const_reverse_iter) + */ + template + struct TypeBinding + { + typedef typename TY::pointer pointer; + typedef typename TY::reference reference; + typedef typename TY::value_type value_type; + }; + + template + struct TypeBinding + { + typedef TY value_type; + typedef TY& reference; + typedef TY* pointer; + }; + + template + struct TypeBinding + { + typedef TY value_type; + typedef const TY& reference; + typedef const TY* pointer; + }; + + using mobject::session::Scope; + + template<> + struct TypeBinding::const_reverse_iterator> + { + typedef const Scope value_type; + typedef Scope const& reference; + typedef const Scope* pointer; + }; + + +}} // namespace lib +#endif From 99f29f979542062445b609e46556ba08598fd66e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 6 Oct 2010 03:58:46 +0200 Subject: [PATCH 14/36] now able to build a IterSource based opaque const Scope iterator for this to work, scope-path.hpp injects an explicit specialisation, causing the RangeIter defined in ScopePath to yield const Scope --- src/lib/iter-adapter.hpp | 8 +++++--- src/lib/iter-type-binding.hpp | 18 ++---------------- src/proc/mobject/session/scope-path.hpp | 22 ++++++++++++++++++++++ src/proc/mobject/session/scope.cpp | 19 ++----------------- 4 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index 28d69f74b..88968cca5 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -63,11 +63,13 @@ ** \em dereferenced to yield the "current" value. ** - moreover, iterators may be incremented until exhaustion. ** - ** @todo WIP WIP WIP - ** @todo see Ticket #182 ** @todo naming of the iteration control function: TICKET #410 ** - ** @see scoped-ptrvect.hpp + ** @see iter-adapter-test.cpp + ** @see itertools.hpp + ** @see IterSource (completely opaque iterator) + ** @see iter-type-binding.hpp + ** */ diff --git a/src/lib/iter-type-binding.hpp b/src/lib/iter-type-binding.hpp index 68f6f21a7..a370e99fb 100644 --- a/src/lib/iter-type-binding.hpp +++ b/src/lib/iter-type-binding.hpp @@ -30,6 +30,8 @@ ** might be injected prior to instantiating the Iterator adapter ** template. ** + ** @see iter-adapter.hpp + ** @see scope-path.hpp usage example (explicit specialisation) */ @@ -40,13 +42,6 @@ #include "lib/error.hpp" -namespace mobject { -namespace session { - class Scope; -}} -#include -using std::vector; - namespace lib { namespace iter { @@ -83,15 +78,6 @@ namespace iter { typedef const TY* pointer; }; - using mobject::session::Scope; - - template<> - struct TypeBinding::const_reverse_iterator> - { - typedef const Scope value_type; - typedef Scope const& reference; - typedef const Scope* pointer; - }; }} // namespace lib diff --git a/src/proc/mobject/session/scope-path.hpp b/src/proc/mobject/session/scope-path.hpp index f40023926..ba774bffb 100644 --- a/src/proc/mobject/session/scope-path.hpp +++ b/src/proc/mobject/session/scope-path.hpp @@ -91,6 +91,28 @@ #include +namespace lib { +namespace iter{ + + using mobject::session::Scope; + + /** + * this explicit specialisation allows to build a RangeIter + * to yield const Scope elements, based on the const_reverse_iterator + * used internally within ScopePath. This specialisation needs to be + * injected prior to actually building the iterator type of ScopePath + * @see iter-type-binding.hpp + * @see iter-adapter.hpp + */ + template<> + struct TypeBinding::const_reverse_iterator> + { + typedef const Scope value_type; + typedef Scope const& reference; + typedef const Scope* pointer; + }; +}} + namespace mobject { namespace session { diff --git a/src/proc/mobject/session/scope.cpp b/src/proc/mobject/session/scope.cpp index d53dd153d..ab12be8c7 100644 --- a/src/proc/mobject/session/scope.cpp +++ b/src/proc/mobject/session/scope.cpp @@ -35,31 +35,16 @@ using std::vector; using lib::IterSource; -using lib::WrappedLumieraIterator; using lib::iter_source::wrapIter; -namespace lib { -namespace iter{ - -// using mobject::session::Scope; -// -// template<> -// struct TypeBinding::const_reverse_iterator> -// { -// typedef const Scope value_type; -// typedef Scope const& reference; -// typedef const Scope* pointer; -// }; -}} - namespace mobject { namespace session { LUMIERA_ERROR_DEFINE (INVALID_SCOPE, "Placement scope invalid and not locatable within model"); + + - - /** conversion of a scope top (placement) into a Scope. * only allowed if the given Placement is actually attached * to the session, which will be checked by index access */ From 6dd18f96c6056f9aabd14a27736ae51e0029debd Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 6 Oct 2010 04:26:04 +0200 Subject: [PATCH 15/36] extend the session services API to support scope discovery This allows the implementation of the Scope class to access the current Session / PlacementIndex behind the scenes --- src/proc/mobject/session/session-impl.hpp | 6 ++++++ .../mobject/session/session-service-explore-scope.hpp | 1 + src/proc/mobject/session/session-services.cpp | 8 ++++++++ 3 files changed, 15 insertions(+) diff --git a/src/proc/mobject/session/session-impl.hpp b/src/proc/mobject/session/session-impl.hpp index e9618bede..247450bba 100644 --- a/src/proc/mobject/session/session-impl.hpp +++ b/src/proc/mobject/session/session-impl.hpp @@ -179,6 +179,12 @@ namespace session { { return resolvingWrapper_; } + + PlacementMO& + getScope (PlacementMO const& placement2locate) + { + return IMPL::getPlacementIndex().getScope(placement2locate); + } PlacementMO& getScopeRoot() diff --git a/src/proc/mobject/session/session-service-explore-scope.hpp b/src/proc/mobject/session/session-service-explore-scope.hpp index a93b08ba3..580389d63 100644 --- a/src/proc/mobject/session/session-service-explore-scope.hpp +++ b/src/proc/mobject/session/session-service-explore-scope.hpp @@ -69,6 +69,7 @@ namespace session { { static QueryResolver const& getResolver(); + static PlacementMO& getScope (PlacementMO const&); static PlacementMO& getScopeRoot(); }; diff --git a/src/proc/mobject/session/session-services.cpp b/src/proc/mobject/session/session-services.cpp index 674f92e11..f3b0a5d81 100644 --- a/src/proc/mobject/session/session-services.cpp +++ b/src/proc/mobject/session/session-services.cpp @@ -156,6 +156,14 @@ namespace session { /** @return root scope of the current model (session datastructure) */ PlacementMO& + SessionServiceExploreScope::getScope (PlacementMO const& placementToLocate) + { + return SessionImplAPI::current->getScope(placementToLocate); + } + + + /** @return root scope of the current model (session datastructure) */ + PlacementMO& SessionServiceExploreScope::getScopeRoot() { return SessionImplAPI::current->getScopeRoot(); From bba9ce2570f28f0bfdf52d20c3a13432f2ea63fb Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 6 Oct 2010 04:59:57 +0200 Subject: [PATCH 16/36] code up the remaining Scope operations. Closes #430 --- src/proc/mobject/session/scope-path.hpp | 5 +++- src/proc/mobject/session/scope.cpp | 38 ++++++++++++------------- src/proc/mobject/session/scope.hpp | 31 +++++++------------- 3 files changed, 33 insertions(+), 41 deletions(-) diff --git a/src/proc/mobject/session/scope-path.hpp b/src/proc/mobject/session/scope-path.hpp index ba774bffb..4eaa6db1f 100644 --- a/src/proc/mobject/session/scope-path.hpp +++ b/src/proc/mobject/session/scope-path.hpp @@ -259,7 +259,10 @@ namespace session { } - inline ScopePath::iterator + /** @note actually this is an Lumiera Forward Iterator, + * yielding the path up to root as a sequence of + * const Scope elements */ + inline ScopePath::iterator ScopePath::begin() const { return iterator (path_.rbegin(), path_.rend()); diff --git a/src/proc/mobject/session/scope.cpp b/src/proc/mobject/session/scope.cpp index ab12be8c7..92db84948 100644 --- a/src/proc/mobject/session/scope.cpp +++ b/src/proc/mobject/session/scope.cpp @@ -27,10 +27,7 @@ #include "proc/mobject/session/session-service-explore-scope.hpp" #include "proc/mobject/mobject.hpp" #include "lib/iter-source.hpp" ////////////////////TICKET #493 : using the IterSource adapters here -//#include "proc/mobject/session/track.hpp" -//#include "proc/mobject/placement.hpp" -//#include "proc/mobject/session/mobjectfactory.hpp" #include using std::vector; @@ -42,17 +39,16 @@ namespace session { LUMIERA_ERROR_DEFINE (INVALID_SCOPE, "Placement scope invalid and not locatable within model"); + LUMIERA_ERROR_DEFINE (NO_PARENT_SCOPE, "Parent scope of root not accessible"); + - /** conversion of a scope top (placement) into a Scope. * only allowed if the given Placement is actually attached * to the session, which will be checked by index access */ Scope::Scope (PlacementMO const& constitutingPlacement) : anchor_(constitutingPlacement) - { - - } + { } Scope::Scope () @@ -64,7 +60,9 @@ namespace session { Scope::Scope (Scope const& o) : anchor_(o.anchor_) - { } + { + ENSURE (anchor_.isValid()); + } Scope& @@ -78,9 +76,7 @@ namespace session { ScopeLocator::ScopeLocator() : focusStack_(new QueryFocusStack) - { - TODO ("anything to initialise here?"); - } + { } ScopeLocator::~ScopeLocator() { } @@ -129,7 +125,7 @@ namespace session { return focusStack_->push (SessionServiceExploreScope::getScopeRoot()); } - + /** navigate the \em current QueryFocus scope location. The resulting * access path to the new location is chosen such as to be most closely related * to the original location; this includes picking a timeline or meta-clip @@ -149,18 +145,18 @@ namespace session { currentPath.navigate (scope); return wrapIter (currentPath.begin()); } - + /** discover the enclosing scope of a given Placement */ - Scope const& + Scope Scope::containing (PlacementMO const& aPlacement) { - UNIMPLEMENTED ("scope discovery"); + return SessionServiceExploreScope::getScope (aPlacement); } - Scope const& + Scope Scope::containing (RefPlacement const& refPlacement) { return containing (*refPlacement); @@ -178,10 +174,14 @@ namespace session { /** retrieve the parent scope which encloses this scope. * @throw error::Invalid if this is the root scope */ - Scope const& + Scope Scope::getParent() const { - UNIMPLEMENTED ("retrieve the enclosing parent scope"); + if (isRoot()) + throw lumiera::error::Invalid ("can't get parent of root scope" + , LUMIERA_ERROR_NO_PARENT_SCOPE); + + return SessionServiceExploreScope::getScope (*anchor_); } @@ -189,7 +189,7 @@ namespace session { bool Scope::isRoot() const { - UNIMPLEMENTED ("detection of root scope"); + return *anchor_ == SessionServiceExploreScope::getScopeRoot(); } diff --git a/src/proc/mobject/session/scope.hpp b/src/proc/mobject/session/scope.hpp index 934ee87de..fbdee81a3 100644 --- a/src/proc/mobject/session/scope.hpp +++ b/src/proc/mobject/session/scope.hpp @@ -24,30 +24,19 @@ #ifndef MOBJECT_SESSION_SCOPE_H #define MOBJECT_SESSION_SCOPE_H -//#include "proc/mobject/mobject.hpp" #include "proc/mobject/placement.hpp" #include "proc/mobject/placement-ref.hpp" -//#include "proc/mobject/session/query-resolver.hpp" ///////////TODO: really? -//#include "lib/iter-adapter.hpp" #include "lib/error.hpp" -//#include "lib/singleton.hpp" -//#include -//#include -//#include -//#include -//#include - -//using std::vector; -//using std::string; namespace mobject { namespace session { - LUMIERA_ERROR_DECLARE (INVALID_SCOPE); ///< Placement scope invalid and not locatable within model + LUMIERA_ERROR_DECLARE (NO_PARENT_SCOPE); ///< Parent scope of root not accessible + LUMIERA_ERROR_DECLARE (INVALID_SCOPE); ///< Placement scope invalid and not locatable within model + - /** * TODO type comment * @note Scope is a passive entity, @@ -66,10 +55,10 @@ namespace session { Scope (Scope const&); Scope& operator= (Scope const&); - static Scope const& containing (PlacementMO const& aPlacement); //////////////TODO really returning a const& here?? - static Scope const& containing (RefPlacement const& refPlacement); + static Scope containing (PlacementMO const& aPlacement); + static Scope containing (RefPlacement const& refPlacement); - Scope const& getParent() const; + Scope getParent() const; PlacementMO& getTop() const; bool isValid() const; bool isRoot() const; @@ -79,9 +68,9 @@ namespace session { }; -///////////////////////////TODO currently just fleshing the API - + + /** as scopes are constituted by a "scope top" element (placement) * registered within the PlacementIndex of the current session, * equality is defined in terms of this defining placement. @@ -89,13 +78,13 @@ namespace session { inline bool operator== (Scope const& scope1, Scope const& scope2) { - return scope1.anchor_ == scope2.anchor_; + return scope1.anchor_ == scope2.anchor_; } inline bool operator!= (Scope const& scope1, Scope const& scope2) { - return scope1.anchor_ != scope2.anchor_; + return scope1.anchor_ != scope2.anchor_; } From 6bb7886b5414de5ec672a2b5497d69c6a1ca95b7 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 8 Oct 2010 00:04:46 +0200 Subject: [PATCH 17/36] define the proper behaviour expected when copying ScopePath (#662) --- .../proc/mobject/session/scope-path-test.cpp | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/tests/components/proc/mobject/session/scope-path-test.cpp b/tests/components/proc/mobject/session/scope-path-test.cpp index a2bcfb206..f6d5507da 100644 --- a/tests/components/proc/mobject/session/scope-path-test.cpp +++ b/tests/components/proc/mobject/session/scope-path-test.cpp @@ -75,6 +75,7 @@ namespace test { invalidPath (testPath,startPlacement); rootPath (testPath); check_Identity_and_Copy (startPlacement); + check_RefcountProtection (startPlacement); navigate (testPath, index); clear (testPath, index); ////////////////////////////////////////TICKET #429 : verify diagnostic output (to be added later) @@ -181,7 +182,7 @@ namespace test { ASSERT (!invalidP.contains (refScope)); ASSERT (!invalidP.endsAt (refScope)); - ASSERT (refPath.contains (invalidP)); // If the moon consists of green cheese, I'll eat my hat! + ASSERT (refPath.contains (invalidP)); // If the moon is made of green cheese, I'll eat my hat! ASSERT (!invalidP.contains (refPath)); ASSERT (invalidP == commonPrefix(refPath,invalidP)); ASSERT (invalidP == commonPrefix(invalidP,refPath)); @@ -240,6 +241,43 @@ namespace test { } + /** @test the embedded refcount is handled sensibly + * when it comes to copying. (This refcount + * is used by QueryFocusStack) */ + void + check_RefcountProtection (PMO& refPlacement) + { + Scope startScope (refPlacement); + ScopePath path1 (startScope); + ScopePath path2 (path1); + + path1 = path2; + CHECK (!isSameObject (path1,path2)); + CHECK (0 == path1.ref_count()); + CHECK (0 == path2.ref_count()); + + intrusive_ptr_add_ref (&path2); + CHECK (0 == path1.ref_count()); + CHECK (0 < path2.ref_count()); + + ScopePath path3 (path2); + CHECK (0 == path3.ref_count()); // refcount not copied + + path3.moveUp(); + + VERIFY_ERROR (LOGIC, path2 = path3 ); // overwriting of path with refcount is prohibited + CHECK (path1 != path3); + path1 = path2; // but path without refcount may be overwritten + path1 = path3; + CHECK (path1 == path3); + + intrusive_ptr_release (&path2); // refcount drops to zero... + CHECK (0 == path1.ref_count()); + CHECK (0 == path2.ref_count()); + } + + + /** @test modify a path by \em navigating it. * - move one step above the current leaf * - move up to the root element @@ -333,7 +371,7 @@ namespace test { ASSERT (!isnil (path)); ASSERT (path.getLeaf() == rootNode); } - + }; From 119f7be36caaa56d40c8601fd40e55658978b7af Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 8 Oct 2010 00:55:52 +0200 Subject: [PATCH 18/36] define copy operations explicitly (closes #662) ScopePath and Scope implementation should be roughly complete --- src/proc/mobject/session/scope-path.cpp | 35 +++++++++++++++++++------ src/proc/mobject/session/scope-path.hpp | 17 +++++++++--- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/proc/mobject/session/scope-path.cpp b/src/proc/mobject/session/scope-path.cpp index aaffc2780..40c19cd2d 100644 --- a/src/proc/mobject/session/scope-path.cpp +++ b/src/proc/mobject/session/scope-path.cpp @@ -26,24 +26,19 @@ #include "proc/mobject/session/scope-locator.hpp" #include "proc/mobject/session/session-service-explore-scope.hpp" #include "proc/mobject/mobject.hpp" -#include "lib/util-foreach.hpp" #include "lib/itertools.hpp" #include "lib/symbol.hpp" #include "lib/error.hpp" +#include "lib/util.hpp" -#include #include namespace mobject { namespace session { using std::reverse; - - using std::tr1::bind; - using std::tr1::function; - using std::tr1::placeholders::_1; using lib::append_all; - using util::and_all; + using util::isSameObject; using namespace lumiera; @@ -112,6 +107,30 @@ namespace session { } + ScopePath::ScopePath (ScopePath const& o) + : refcount_(0) + , path_(o.path_) + { } + + /** + * Copy from existing path + * @throw error::Logic when current path has nonzero refcount + */ + ScopePath& + ScopePath::operator= (ScopePath const& ref) + { + if (0 < refcount_) + throw error::Logic ("Attempt to overwrite a ScopePath with nonzero refcount"); + + if (!isSameObject (*this, ref)) + { + path_ = ref.path_; + ENSURE (0 == refcount_); + } + return *this; + } + + ScopePath::~ScopePath() { WARN_IF (refcount_, session, "Destroying a scope path frame with ref-count=%lu", refcount_); @@ -129,7 +148,7 @@ namespace session { ScopePath::isValid() const { return (1 < length()) -#ifndef NDEBUG +#if NOBUG_MODE_ALPHA && hasValidRoot() #endif ; diff --git a/src/proc/mobject/session/scope-path.hpp b/src/proc/mobject/session/scope-path.hpp index 4eaa6db1f..e0d3792f0 100644 --- a/src/proc/mobject/session/scope-path.hpp +++ b/src/proc/mobject/session/scope-path.hpp @@ -36,15 +36,15 @@ ** A scope path is represented as sequence of scopes, where each Scope is implemented ** by a PlacementRef pointing to the »scope top«, i.e. the placement in the session ** constituting this scope. The leaf of this path can be considered the current scope. - ** ScopePath is intended to remember a \em current location within the model, to be - ** used for resolving queries and discovering contents. + ** ScopePath is intended to be used for remembering a \em current location within the + ** model, usable for resolving queries and discovering contents. ** ** \par operations and behaviour ** ** In addition to some search and query functions, a scope path has the ability to ** \em navigate to a given target scope, which must be reachable by ascending and ** descending into the branches of the overall tree or DAG (in the general case). - ** Navigating changes the current path, which usually happens when the current + ** Navigating is a mutating operation which usually happens when the current ** "focus" shifts while operating on the model. ** ** - ScopePath can be default constructed, yielding an \em invalid path. @@ -55,6 +55,8 @@ ** - ScopePaths are intended to be handled by value (as are Scopes and ** PlacementRefs). They are equality comparable and provide several specialised ** relation predicates. + ** - while generally copying is permitted, you may not overwrite an ScopePath + ** which is attached (referred by a QueryFocus, see below) ** - all implementations are focused on clarity, not uttermost performance, as ** the assumption is for paths to be relatively short and path operations to ** be executed rather in a GUI action triggered context. @@ -71,7 +73,9 @@ ** Each of these stack frames represents the current location for some evaluation ** context; it is organised as stack to allow intermediate evaluations. Management ** of these stack frames is automated, with the assistance of ScopePath by - ** incorporating a ref-count. + ** incorporating a ref-count. Client code usually accesses this mechanism + ** through QueryFocus objects as frontend, which is reflected in the + ** mentioned embedded refcount ** ** @see scope-path-test.cpp ** @see Scope @@ -145,8 +149,13 @@ namespace session { ScopePath (); ScopePath (Scope const& leaf); + ScopePath (ScopePath const&); + ScopePath& + operator= (ScopePath const&); + static const ScopePath INVALID; + /* == state diagnostics == */ bool isValid() const; bool empty() const; From 5c28b5d46eac1d6a963cbd2449071f8bd9b270c7 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 8 Oct 2010 01:54:58 +0200 Subject: [PATCH 19/36] re-read, verify and finish off the Scope, ScopeLocator and QueryFocus implementation --- src/proc/control/command.cpp | 4 ++-- src/proc/mobject/session/query-focus.hpp | 24 ++++++++++++++++--- src/proc/mobject/session/scope-locator.hpp | 28 +++++++++++----------- src/proc/mobject/session/scope.cpp | 22 +++++++++++++++-- src/proc/mobject/session/scope.hpp | 22 +++++++++++++++-- 5 files changed, 77 insertions(+), 23 deletions(-) diff --git a/src/proc/control/command.cpp b/src/proc/control/command.cpp index 55d9a1ef0..43906c704 100644 --- a/src/proc/control/command.cpp +++ b/src/proc/control/command.cpp @@ -21,8 +21,8 @@ * *****************************************************/ -/** @file command.cpp - ** Implementation of the command frontend. +/** @file command.cpp + ** Implementation of the command frontend. ** Within this file, the implementation level of the command frontend ** is linked to the implementation of the command registry. Client code ** is shielded from those implementation classes and need only include diff --git a/src/proc/mobject/session/query-focus.hpp b/src/proc/mobject/session/query-focus.hpp index 2ffa30902..7b35f8054 100644 --- a/src/proc/mobject/session/query-focus.hpp +++ b/src/proc/mobject/session/query-focus.hpp @@ -61,7 +61,7 @@ namespace session { * »navigating« operation will use the current focus position as * point of departure, thus retaining a similar access path to any * nested sequences. (These might be attached multiple times within - * the same session, each attachement constituing a different + * the same session, each attachment constituting a different * context scope. Navigating tries to retain the current context) * * The templated query functions allow to issue specifically typed @@ -106,6 +106,9 @@ namespace session { typename ScopeQuery::iterator explore() const; + lib::IterSource::iterator + locate (Scope const& toTarget); + private: QueryFocus (ScopePath&); @@ -141,7 +144,7 @@ namespace session { inline typename ScopeQuery::iterator QueryFocus::query() const { - ScopeLocator::instance().query (*this); + return ScopeLocator::instance().query (*this); } @@ -152,7 +155,22 @@ namespace session { inline typename ScopeQuery::iterator QueryFocus::explore() const { - ScopeLocator::instance().explore (*this); + return ScopeLocator::instance().explore (*this); + } + + + /** shift or navigate the current focus to point at + * the given target scope. In case of multiple possible + * access paths, the \em current location is taken into + * account, trying to reach the new location in a + * \em similar fashion as much as possible. + * @note moves the current focus as side-effect + * @return the effective / virtual new access path + * leading to the new target focus scope */ + inline lib::IterSource::iterator + QueryFocus::locate (Scope const& toTarget) + { + return ScopeLocator::instance().locate (toTarget); } diff --git a/src/proc/mobject/session/scope-locator.hpp b/src/proc/mobject/session/scope-locator.hpp index 56a0b8606..4d6753967 100644 --- a/src/proc/mobject/session/scope-locator.hpp +++ b/src/proc/mobject/session/scope-locator.hpp @@ -69,25 +69,25 @@ namespace session { template typename ScopeQuery::iterator - explore (Scope); + explore (Scope const&); template typename ScopeQuery::iterator - query (Scope); + query (Scope const&); template typename ScopeQuery::iterator - getRawPath (Scope); - + getRawPath (Scope const&); + ScopeQuery::iterator - getRawPath (Scope); - + getRawPath (Scope const&); + lib::IterSource::iterator - locate (Scope scope); - - + locate (Scope const& target); + + ~ScopeLocator(); - + protected: ScopeLocator(); @@ -105,7 +105,7 @@ namespace session { */ template inline typename ScopeQuery::iterator - ScopeLocator::explore (Scope scope) + ScopeLocator::explore (Scope const& scope) { return ScopeQuery (scope.getTop(), CHILDREN).resolveBy (theResolver()); } @@ -116,7 +116,7 @@ namespace session { */ template inline typename ScopeQuery::iterator - ScopeLocator::query (Scope scope) + ScopeLocator::query (Scope const& scope) { return ScopeQuery (scope.getTop(), CONTENTS).resolveBy (theResolver()); } @@ -133,13 +133,13 @@ namespace session { */ template inline typename ScopeQuery::iterator - ScopeLocator::getRawPath (Scope scope) + ScopeLocator::getRawPath (Scope const& scope) { return ScopeQuery (scope.getTop(), PATH).resolveBy (theResolver()); } inline ScopeQuery::iterator - ScopeLocator::getRawPath (Scope scope) + ScopeLocator::getRawPath (Scope const& scope) { return ScopeQuery (scope.getTop(), PATH).resolveBy (theResolver()); } diff --git a/src/proc/mobject/session/scope.cpp b/src/proc/mobject/session/scope.cpp index 92db84948..ee1c9218b 100644 --- a/src/proc/mobject/session/scope.cpp +++ b/src/proc/mobject/session/scope.cpp @@ -21,6 +21,23 @@ * *****************************************************/ +/** @file scope.cpp + ** Implementation of placement scopes and scope locator. + ** This translation unit embeds the (hidden) link to the session implementation + ** used to establish the position of a given placement within the hierarchy + ** of nested scopes. The rest of the model implementation code mostly builds + ** on top of this access point, when it comes to discovering contents or + ** navigating within the model. Especially the ScopeLocator singleton + ** defined here plays the role of linking together the system of nested scopes, + ** the current QueryFocus and the actual session implementation and storage + ** (PlacementIndex) + ** + ** @see command.hpp + ** @see command-registry.hpp + ** + */ + + #include "proc/mobject/session/scope.hpp" #include "proc/mobject/session/scope-locator.hpp" #include "proc/mobject/session/query-focus-stack.hpp" @@ -68,7 +85,8 @@ namespace session { Scope& Scope::operator= (Scope const& o) { - anchor_ = o.anchor_; ////////////////////////////TODO verify correctness + anchor_ = o.anchor_; // note: actually we're just assigning an hash value + ENSURE (o.isValid()); return *this; } @@ -139,7 +157,7 @@ namespace session { * up to root, in a way likely to be similar to the original location */ IterSource::iterator - ScopeLocator::locate (Scope scope) + ScopeLocator::locate (Scope const& scope) { ScopePath& currentPath = focusStack_->top(); currentPath.navigate (scope); diff --git a/src/proc/mobject/session/scope.hpp b/src/proc/mobject/session/scope.hpp index fbdee81a3..e7593fc0b 100644 --- a/src/proc/mobject/session/scope.hpp +++ b/src/proc/mobject/session/scope.hpp @@ -38,11 +38,29 @@ namespace session { /** - * TODO type comment + * A Placement scope within the high-level-model. + * Within the Session/Model, Placements are used to attach + * MObjects; but beyond that, each Placement can \em contain + * other Placements, effectively forming a scope. Thus Scope + * is basically another view on Placements which are attached + * to the session. This (hidden) link to the session is utilised + * to establish the nesting of scopes and allow querying and navigating. + * + * Actually, Scope is implemented through a PlacementRef pointing to + * the Placement which \em constitutes this Scope. We call this Placement + * the "scope top". A track e.g. can \em contain several clips, but also + * nested sub tracks, all of which would be within the scope of this track. + * This scoping relation plays an important role when it comes to \em resolving + * properties of placement, like e.g. the output designation, overlay mode, + * sound pan position etc -- properties from enclosing scopes will be + * inherited unless \em shaded by local definitions, similar to the + * behaviour known from most programming languages when referring + * to local variables. * @note Scope is a passive entity, * basically just wrapping up a Scope-top Placement. * Contrast this to QueryFocus, which actively - * maintains the current focus location. + * maintains the current focus location + * and exposes query facilities. */ class Scope { From 46597009f9ab57ce4a0436fb1ad8b31c2812ca7b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 8 Oct 2010 06:24:25 +0200 Subject: [PATCH 20/36] get the basic ScopePath_test to pass --- src/proc/mobject/session/scope-path.cpp | 21 ++-- src/proc/mobject/session/scope-path.hpp | 4 +- src/proc/mobject/session/scope.cpp | 4 + src/proc/mobject/session/scope.hpp | 4 +- tests/43session.tests | 3 +- .../proc/mobject/session/scope-path-test.cpp | 107 ++++++++++++------ 6 files changed, 98 insertions(+), 45 deletions(-) diff --git a/src/proc/mobject/session/scope-path.cpp b/src/proc/mobject/session/scope-path.cpp index 40c19cd2d..49ec87b0c 100644 --- a/src/proc/mobject/session/scope-path.cpp +++ b/src/proc/mobject/session/scope-path.cpp @@ -63,8 +63,8 @@ namespace session { { REQUIRE (path); if (path->empty()) - throw error::Invalid (operation_descr+" an empty placement scope path" - , LUMIERA_ERROR_EMPTY_SCOPE_PATH); + throw error::Logic (operation_descr+" an empty placement scope path" + , LUMIERA_ERROR_EMPTY_SCOPE_PATH); } }//(End) helpers @@ -100,7 +100,7 @@ namespace session { : refcount_(0) , path_() { - if (!leaf.isValid()) return; // invalid leaf defines invalid path.... + if (leaf == Scope::INVALID) return; // invalid leaf defines invalid path.... clear(); navigate (leaf); @@ -196,6 +196,8 @@ namespace session { bool ScopePath::contains (Scope const& aScope) const { + if (aScope == Scope::INVALID) return true; // bottom is contained everywhere + for (iterator ii = this->begin(); ii; ++ii) if (aScope == *ii) return true; @@ -207,8 +209,8 @@ namespace session { bool ScopePath::contains (ScopePath const& otherPath) const { - if ( empty()) return false; if (!otherPath.isValid()) return true; + if ( empty()) return false; if (!isValid()) return false; ASSERT (1 < length()); @@ -262,20 +264,19 @@ namespace session { } - Scope& + Scope const& ScopePath::moveUp() { ___check_notBottom (this, "Navigating"); - static Scope invalidScope; path_.resize (length()-1); - if (empty()) return invalidScope; + if (empty()) return Scope::INVALID; else return path_.back(); } - Scope& + Scope const& ScopePath::goRoot() { ___check_notBottom (this, "Navigating"); @@ -290,6 +291,10 @@ namespace session { ScopePath::navigate (Scope const& target) { ___check_notBottom (this, "Navigating"); + if (!target.isValid()) + throw error::Invalid ("can't navigate to a target scope outside the model" + , LUMIERA_ERROR_INVALID_SCOPE); + std::vector otherPath; append_all (discoverScopePath(target), otherPath); reverse (otherPath.begin(), otherPath.end()); diff --git a/src/proc/mobject/session/scope-path.hpp b/src/proc/mobject/session/scope-path.hpp index e0d3792f0..b807e7ea0 100644 --- a/src/proc/mobject/session/scope-path.hpp +++ b/src/proc/mobject/session/scope-path.hpp @@ -187,8 +187,8 @@ namespace session { /* == mutations == */ void clear(); - Scope& moveUp(); - Scope& goRoot(); + Scope const& moveUp(); + Scope const& goRoot(); void navigate (Scope const&); diff --git a/src/proc/mobject/session/scope.cpp b/src/proc/mobject/session/scope.cpp index ee1c9218b..dc86bc2ed 100644 --- a/src/proc/mobject/session/scope.cpp +++ b/src/proc/mobject/session/scope.cpp @@ -91,6 +91,10 @@ namespace session { } + /** constant \em invalid scope token. */ + const Scope Scope::INVALID = Scope(); + + ScopeLocator::ScopeLocator() : focusStack_(new QueryFocusStack) diff --git a/src/proc/mobject/session/scope.hpp b/src/proc/mobject/session/scope.hpp index e7593fc0b..a6e266e6f 100644 --- a/src/proc/mobject/session/scope.hpp +++ b/src/proc/mobject/session/scope.hpp @@ -72,7 +72,9 @@ namespace session { Scope (Scope const&); Scope& operator= (Scope const&); - + + static const Scope INVALID; + static Scope containing (PlacementMO const& aPlacement); static Scope containing (RefPlacement const& refPlacement); diff --git a/tests/43session.tests b/tests/43session.tests index dec155f1d..e70ae0af6 100644 --- a/tests/43session.tests +++ b/tests/43session.tests @@ -164,7 +164,8 @@ PLANNED "Placement search scope" PlacementScope_test < -//#include - namespace mobject { @@ -42,19 +36,44 @@ namespace test { using util::isnil; using util::isSameObject; -//using lumiera::Time; -//using std::string; -//using std::cout; -//using std::endl; -// using namespace mobject::test; + using lumiera::error::LUMIERA_ERROR_LOGIC; using lumiera::error::LUMIERA_ERROR_INVALID; + namespace { // subversive test helper... + + Scope const& + fabricate_invalidScope() + { /** + * assumed to have identical memory layout + * to a Scope object, as the latter is implemented + * by a PlacementRef, which in turn is just an + * encapsulated Placement-ID + */ + struct Ambush + { + /** random ID assumed to be + * nowhere in the model */ + PlacementMO::ID derailed_; + }; + + static Ambush _kinky_; + return *reinterpret_cast (&_kinky_); + } + } + + + + + /*************************************************************************** * @test properties and behaviour of the path of nested scopes. - * Using a pseudo-session (actually just a PlacementIndex), this test - * creates some nested scopes and executes navigation moves on them. + * Using a pseudo-session (actually just a PlacementIndex), + * this test creates some nested scopes, builds scope paths + * and executes various comparisons navigation moves on them. + * Especially detection of invalid scopes and paths and the + * special handling of empty and root paths is covered. * @see mobject::Placement * @see mobject::session::ScopePath * @see mobject::session::QueryFocus @@ -70,6 +89,7 @@ namespace test { PMO& startPlacement = retrieve_startElm(); ASSERT (startPlacement.isValid()); + checkInvalidScopeDetection(); ScopePath testPath = buildPath (startPlacement); checkRelations (testPath,startPlacement); invalidPath (testPath,startPlacement); @@ -95,13 +115,30 @@ namespace test { ASSERT ( path.getLeaf() == path2.getLeaf()); ASSERT (path2.getLeaf() == path3.getLeaf()); - Scope unrelatedScope (TestPlacement<> (*new DummyMO)); - VERIFY_ERROR (INVALID, ScopePath(unrelatedScope) ); - return path; } + void + checkInvalidScopeDetection() + { + // verify detection of illegal scopes and paths... + TestPlacement<> notRelated2anything (*new DummyMO); + VERIFY_ERROR (NOT_IN_SESSION, Scope invalid (notRelated2anything) ); + + Scope const& scopeOfEvil = fabricate_invalidScope(); + REQUIRE (!scopeOfEvil.isValid()); + + VERIFY_ERROR (INVALID_SCOPE, ScopePath outsideCurrentModel (scopeOfEvil) ); + + // but there is one exception to this rule... + ScopePath theInvalidToken (Scope::INVALID); + CHECK (!theInvalidToken.isValid()); + CHECK (theInvalidToken.empty()); + } + + + void checkIteration (ScopePath path, PMO& refPlacement) { @@ -180,28 +217,28 @@ namespace test { Scope refScope (refPlacement); ASSERT (!invalidP.contains (refScope)); - ASSERT (!invalidP.endsAt (refScope)); + VERIFY_ERROR (EMPTY_SCOPE_PATH, invalidP.endsAt (refScope) ); // Logic: can't inspect the end of nothing - ASSERT (refPath.contains (invalidP)); // If the moon is made of green cheese, I'll eat my hat! + ASSERT (refPath.contains (invalidP)); // If the moon is made of green cheese, I'll eat my hat! ASSERT (!invalidP.contains (refPath)); ASSERT (invalidP == commonPrefix(refPath,invalidP)); ASSERT (invalidP == commonPrefix(invalidP,refPath)); - VERIFY_ERROR (LOGIC, invalidP.moveUp()); + VERIFY_ERROR (EMPTY_SCOPE_PATH, invalidP.moveUp() ); Scope root = refPath.goRoot(); ASSERT (1 == refPath.length()); - Scope nil = refPath.moveUp(); + Scope const& nil = refPath.moveUp(); ASSERT (refPath.empty()); ASSERT (!nil.isValid()); ASSERT (refPath == invalidP); ASSERT (invalidP.contains (nil)); + ASSERT (invalidP.contains (refPath)); + ASSERT (!invalidP.contains (refScope)); - refPath.navigate(root); - ASSERT (refPath != invalidP); - ASSERT (!isnil (refPath)); + VERIFY_ERROR (EMPTY_SCOPE_PATH, refPath.navigate(root) ); - //ScopePath::INVALID.navigate(root); // doesn't compile + //ScopePath::INVALID.navigate(root); // doesn't compile: INVALID is immutable } @@ -234,7 +271,7 @@ namespace test { ASSERT (path2 == path3); ASSERT (path1 != path3); - path1 = ScopePath::INVALID; + path2 = ScopePath::INVALID; ASSERT (path1 != path2); ASSERT (path2 != path3); ASSERT (path1 != path3); @@ -285,7 +322,7 @@ namespace test { * - attach a new sibling node and move the path down to there * - extract the common prefix, which should again point to the parent * - find a placement in a completely separate branch (only sharing the - * root node). Navigate to there and verify root is the common prefix. + * root node). Navigate to there and verify root is the common prefix. */ void navigate (const ScopePath refPath, PPIdx index) @@ -316,9 +353,12 @@ namespace test { TestPlacement<> newNode (*new DummyMO); PMO& parentRefPoint = parent.getTop(); - index->insert (newNode, parentRefPoint); // place as sibling of "leaf" - path.navigate (newNode); + Scope newLocation = + index->find( // place newNode as sibling of "leaf" + index->insert (newNode, parentRefPoint)); + path.navigate (newLocation); Scope sibling = path.getLeaf(); + ASSERT (sibling == newLocation); ASSERT (parent == sibling.getParent()); ASSERT (path.endsAt (sibling)); ASSERT (path.contains (parent)); @@ -338,8 +378,8 @@ namespace test { // try to navigate to an unconnected location... ScopePath beforeInvalidNavigation = path; - Scope unrelatedScope (TestPlacement<> (*new DummyMO)); - VERIFY_ERROR (INVALID, path.navigate (unrelatedScope) ); + Scope const& unrelatedScope (fabricate_invalidScope()); + VERIFY_ERROR (INVALID_SCOPE, path.navigate (unrelatedScope) ); ASSERT (path == beforeInvalidNavigation); // not messed up by the incident // now explore a completely separate branch.... @@ -359,11 +399,12 @@ namespace test { } + void clear (ScopePath& path, PPIdx index) { ASSERT (path); - PMO rootNode = index->getRoot(); + PMO& rootNode = index->getRoot(); ASSERT (path.getLeaf() != rootNode); path.clear(); @@ -371,10 +412,10 @@ namespace test { ASSERT (!isnil (path)); ASSERT (path.getLeaf() == rootNode); } - }; + /** Register this test class... */ LAUNCHER (ScopePath_test, "unit session"); From 64f017da433ea4b3833d62558c47b79b8b387851 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 9 Oct 2010 06:18:40 +0200 Subject: [PATCH 21/36] QueryFocusStack_test pass --- .../mobject/session/query-focus-stack.hpp | 1 + src/proc/mobject/session/scope-path.hpp | 12 ++++ tests/43session.tests | 3 +- .../session/query-focus-stack-test.cpp | 19 +++--- .../proc/mobject/session/scope-path-test.cpp | 25 +------- .../mobject/session/test-scope-invalid.hpp | 63 +++++++++++++++++++ 6 files changed, 91 insertions(+), 32 deletions(-) create mode 100644 tests/components/proc/mobject/session/test-scope-invalid.hpp diff --git a/src/proc/mobject/session/query-focus-stack.hpp b/src/proc/mobject/session/query-focus-stack.hpp index 7bac96683..d58b02481 100644 --- a/src/proc/mobject/session/query-focus-stack.hpp +++ b/src/proc/mobject/session/query-focus-stack.hpp @@ -112,6 +112,7 @@ namespace session { QueryFocusStack::clear () { paths_.clear(); + openDefaultFrame(); } diff --git a/src/proc/mobject/session/scope-path.hpp b/src/proc/mobject/session/scope-path.hpp index b807e7ea0..d23d286cf 100644 --- a/src/proc/mobject/session/scope-path.hpp +++ b/src/proc/mobject/session/scope-path.hpp @@ -159,6 +159,7 @@ namespace session { /* == state diagnostics == */ bool isValid() const; bool empty() const; + bool isRoot() const; size_t size() const; size_t length() const; size_t ref_count()const; @@ -267,6 +268,17 @@ namespace session { return path_.empty(); } + inline bool + ScopePath::isRoot() const + { + return (1 == size()) +#if NOBUG_MODE_ALPHA + && path_[0].isRoot() +#endif + ; + } + + /** @note actually this is an Lumiera Forward Iterator, * yielding the path up to root as a sequence of diff --git a/tests/43session.tests b/tests/43session.tests index e70ae0af6..6f69b10b2 100644 --- a/tests/43session.tests +++ b/tests/43session.tests @@ -193,7 +193,8 @@ PLANNED "Query focus management" QueryFocus_test < (*new DummyMO)); + Scope const& unrelatedScope = fabricate_invalidScope(); // try to navigate to an invalid place - VERIFY_ERROR (INVALID, stack.top().navigate (unrelatedScope) ); + VERIFY_ERROR (INVALID_SCOPE, stack.top().navigate (unrelatedScope) ); ASSERT (1 == stack.size()); ASSERT (1 == firstFrame.ref_count()); ASSERT (stack.top().getLeaf() == startPoint); // try to push an invalid place - VERIFY_ERROR (INVALID, stack.push (unrelatedScope) ); + VERIFY_ERROR (INVALID_SCOPE, stack.push (unrelatedScope) ); ASSERT (1 == stack.size()); ASSERT (1 == firstFrame.ref_count()); ASSERT (stack.top().getLeaf() == startPoint); diff --git a/tests/components/proc/mobject/session/scope-path-test.cpp b/tests/components/proc/mobject/session/scope-path-test.cpp index 30cc7fc92..d425e394c 100644 --- a/tests/components/proc/mobject/session/scope-path-test.cpp +++ b/tests/components/proc/mobject/session/scope-path-test.cpp @@ -26,6 +26,7 @@ #include "proc/mobject/session/test-scopes.hpp" #include "proc/mobject/session/placement-index.hpp" #include "proc/mobject/session/scope-path.hpp" +#include "proc/mobject/session/test-scope-invalid.hpp" #include "lib/util.hpp" @@ -41,30 +42,6 @@ namespace test { using lumiera::error::LUMIERA_ERROR_INVALID; - namespace { // subversive test helper... - - Scope const& - fabricate_invalidScope() - { /** - * assumed to have identical memory layout - * to a Scope object, as the latter is implemented - * by a PlacementRef, which in turn is just an - * encapsulated Placement-ID - */ - struct Ambush - { - /** random ID assumed to be - * nowhere in the model */ - PlacementMO::ID derailed_; - }; - - static Ambush _kinky_; - return *reinterpret_cast (&_kinky_); - } - } - - - /*************************************************************************** diff --git a/tests/components/proc/mobject/session/test-scope-invalid.hpp b/tests/components/proc/mobject/session/test-scope-invalid.hpp new file mode 100644 index 000000000..ecfa18f04 --- /dev/null +++ b/tests/components/proc/mobject/session/test-scope-invalid.hpp @@ -0,0 +1,63 @@ +/* + TEST-SCOPE-INVALID.hpp - helper for placement scope and scope stack tests + + Copyright (C) Lumiera.org + 2010, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef MOBJECT_SESSION_TEST_SCOPE_INVALID_H +#define MOBJECT_SESSION_TEST_SCOPE_INVALID_H + + +#include "proc/mobject/placement.hpp" +#include "proc/mobject/session/scope.hpp" + + + +namespace mobject { +namespace session { +namespace test { + + + namespace { // nifty subversive test helper... + + Scope const& + fabricate_invalidScope() + { /** + * assumed to have identical memory layout + * to a Scope object, as the latter is implemented + * by a PlacementRef, which in turn is just an + * encapsulated Placement-ID + */ + struct Ambush + { + /** random ID assumed to be + * nowhere in the model */ + PlacementMO::ID derailed_; + }; + + static Ambush _kinky_; + return *reinterpret_cast (&_kinky_); + } + } + + + +}}} // namespace mobject::session::test +#endif From 735af19891448997a76d16791ebbe435fb9e49a1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 10 Oct 2010 03:43:35 +0200 Subject: [PATCH 22/36] getting PacementScope_test from last year to compile ...now using the real implementation! --- .../mobject/session/placement-scope-test.cpp | 95 +++++++++++-------- .../proc/mobject/session/test-scopes.hpp | 3 +- 2 files changed, 59 insertions(+), 39 deletions(-) diff --git a/tests/components/proc/mobject/session/placement-scope-test.cpp b/tests/components/proc/mobject/session/placement-scope-test.cpp index f0d1343c6..0c8e9af7f 100644 --- a/tests/components/proc/mobject/session/placement-scope-test.cpp +++ b/tests/components/proc/mobject/session/placement-scope-test.cpp @@ -23,8 +23,10 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" +#include "proc/mobject/mobject.hpp" #include "proc/mobject/session/scope.hpp" #include "proc/mobject/session/test-scopes.hpp" +#include "proc/mobject/session/scope-locator.hpp" //#include "lib/lumitime.hpp" //#include "proc/mobject/placement-ref.hpp" //#include "proc/mobject/session/placement-index.hpp" @@ -40,6 +42,26 @@ namespace mobject { namespace session { namespace test { + namespace { // Helper to enumerate all Contents + // of the test-dummy session + typedef _ScopeIterMO _Iter; + + _Iter + contents_of_testSession() + { + Scope root_of_testSession (retrieve_startElm()); + return ScopeLocator::instance().query (root_of_testSession); + } + + _Iter + pathToRoot(PlacementMO& elm) + { + Scope startScope(elm); + return ScopeLocator::instance().getRawPath (startScope); + } + } + + // using namespace mobject::test; using lumiera::error::LUMIERA_ERROR_INVALID; @@ -73,21 +95,12 @@ namespace test { PPIdx index = build_testScopes(); verifyEquality(); - UNIMPLEMENTED ("function test of placement scope interface"); -#if false //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! verifyLookup (index); verifyNavigation (index); } - typedef Query >::iterator _Iter; - _Iter - getContents(PPIdx index) - { - return index->query(index->getRoot()); -#endif //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! - } /** @test for each Placement in our test "session", * find the scope and verify it's in line with the index @@ -95,24 +108,25 @@ namespace test { void verifyLookup (PPIdx ref_index) { -#if false //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! - for (_Iter elm = getContents(ref_index); elm; ++elm) + for (_Iter ii = contents_of_testSession(); ii; ++ii) { - ASSERT (elm->isValid()); - cout << string(*elm) << endl; - Scope const& scope1 = Scope::containing(*elm); + PlacementMO& elm = *ii; + ASSERT (elm.isValid()); + cout << string(elm) << endl; + Scope const& scope1 = Scope::containing(elm); - RefPlacement ref (*elm); + RefPlacement ref (elm); Scope const& scope2 = Scope::containing(ref); // verify this with the scope registered within the index... - PlacementMO& scopeTop = ref_index->getScope(*elm); + PlacementMO& scopeTop = ref_index->getScope(elm); ASSERT (scope1 == scopeTop); ASSERT (scope2 == scopeTop); ASSERT (scope1 == scope2); ASSERT (isSameObject (scope1,scope2)); } +#if false //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! } @@ -150,29 +164,34 @@ namespace test { void verifyNavigation (PPIdx ref_index) { -#if false //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! - for (_Iter elm = getContents(ref_index); elm; ++elm) + for (_Iter elm = contents_of_testSession(); elm; ++elm) { - Scope const& scope = Scope::containing(*elm); - ASSERT (scope == *scope.ascend()); - for (Scope::iterator sco = scope.ascend(); sco; ++sco) - if (sco->isRoot()) - { - VERIFY_ERROR (INVALID, sco->getParent() ); - RefPlacement top = sco->getTop(); - RefPlacement root = ref_index->getRoot(); - - ASSERT (isSameObject (top,root)); - } - else - { - Scope& parent = sco->getParent(); - RefPlacement top = sco->getTop(); - Scope& parentsScope = Scope::containing(top); - RefPlacement topsTop = ref_index->getScope(top); ///////////////////TODO impact of Binding a Sequence? see Ticket #311 - ASSERT (topsTop == parentsScope); - ASSERT (isSameObject (topsTop, parentsScope.getTop())); - } } + _Iter pathIter = pathToRoot(*elm); + Scope const& enclosing = Scope::containing(*elm); + ASSERT (enclosing == Scope(*elm).getParent()); + ASSERT (*pathIter == enclosing); + + for ( ; pathIter; ++pathIter) + { + Scope sco(*pathIter); + if (sco.isRoot()) + { + VERIFY_ERROR (INVALID, sco.getParent() ); + PlacementMO& top = sco.getTop(); + PlacementMO& root = ref_index->getRoot(); + + ASSERT (isSameObject (top,root)); + } + else + { + Scope parent = sco.getParent(); + PlacementMO& top = sco.getTop(); + Scope parentsScope = Scope::containing(top); + PlacementMO& topsTop = ref_index->getScope(top); ///////////////////TODO impact of Binding a Sequence? see Ticket #311 + ASSERT (topsTop == parentsScope); + ASSERT (isSameObject (topsTop, parentsScope.getTop())); + } } } +#if false //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! } diff --git a/tests/components/proc/mobject/session/test-scopes.hpp b/tests/components/proc/mobject/session/test-scopes.hpp index 29da6ba88..c83856791 100644 --- a/tests/components/proc/mobject/session/test-scopes.hpp +++ b/tests/components/proc/mobject/session/test-scopes.hpp @@ -41,6 +41,7 @@ namespace test { typedef TestPlacement PDum; typedef std::tr1::shared_ptr PPIdx; + typedef ScopeQuery::iterator _ScopeIterMO; @@ -67,7 +68,7 @@ namespace test { * Usually, clients would use QueryFocus or ScopeLocator to perform this task, * but for the purpose of testing we're better off to invoke the query directly */ - ScopeQuery::iterator explore_testScope (PlacementMO const& scopeTop); + _ScopeIterMO explore_testScope (PlacementMO const& scopeTop); From 9b5a24f6cc33ad9188c675a008369f1d570475bb Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 10 Oct 2010 04:42:06 +0200 Subject: [PATCH 23/36] Integration: Placement Scope test pass --- tests/43session.tests | 8 ++- .../mobject/session/placement-scope-test.cpp | 69 +++++++++---------- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/tests/43session.tests b/tests/43session.tests index 6f69b10b2..4bfab8f7f 100644 --- a/tests/43session.tests +++ b/tests/43session.tests @@ -157,10 +157,16 @@ out: --------------------------------Test-10: same path, but filtered to TestSub out: Placement<.+TestSubMO21.> out: --------------------------------Test-11: continue exploring partially used TestSubMO2 iterator out: Placement<.+TestSubMO21.> +return: 0 END -PLANNED "Placement search scope" PlacementScope_test < +out: Placement<.+TestSubMO2.> +out: Placement<.+TestSubMO1.> +out: Placement<.+DummyMO.> +return: 0 END diff --git a/tests/components/proc/mobject/session/placement-scope-test.cpp b/tests/components/proc/mobject/session/placement-scope-test.cpp index 0c8e9af7f..374246c71 100644 --- a/tests/components/proc/mobject/session/placement-scope-test.cpp +++ b/tests/components/proc/mobject/session/placement-scope-test.cpp @@ -27,34 +27,29 @@ #include "proc/mobject/session/scope.hpp" #include "proc/mobject/session/test-scopes.hpp" #include "proc/mobject/session/scope-locator.hpp" -//#include "lib/lumitime.hpp" -//#include "proc/mobject/placement-ref.hpp" -//#include "proc/mobject/session/placement-index.hpp" -//#include "proc/mobject/test-dummy-mobject.hpp" #include "lib/util.hpp" #include -//#include +using util::isSameObject; namespace mobject { namespace session { namespace test { - namespace { // Helper to enumerate all Contents + namespace { // Helper to enumerate Contents // of the test-dummy session typedef _ScopeIterMO _Iter; _Iter - contents_of_testSession() + contents_of_testSession (PPIdx testSession) { - Scope root_of_testSession (retrieve_startElm()); - return ScopeLocator::instance().query (root_of_testSession); + return ScopeLocator::instance().query (testSession->getRoot()); } _Iter - pathToRoot(PlacementMO& elm) + pathToRoot (PlacementMO& elm) { Scope startScope(elm); return ScopeLocator::instance().getRawPath (startScope); @@ -62,14 +57,6 @@ namespace test { } -// using namespace mobject::test; - using lumiera::error::LUMIERA_ERROR_INVALID; - - using util::isSameObject; - //using lumiera::Time; - //using std::string; - using std::cout; - using std::endl; @@ -91,7 +78,8 @@ namespace test { virtual void run (Arg) { - // Prepare an (test)Index backing the PlacementRefs + // Prepare an (test)Session + // with some dummy contents PPIdx index = build_testScopes(); verifyEquality(); @@ -106,31 +94,30 @@ namespace test { * find the scope and verify it's in line with the index */ void - verifyLookup (PPIdx ref_index) + verifyLookup (PPIdx sess) { - for (_Iter ii = contents_of_testSession(); ii; ++ii) + for (_Iter ii = contents_of_testSession(sess); ii; ++ii) { PlacementMO& elm = *ii; ASSERT (elm.isValid()); - cout << string(elm) << endl; + std::cout << string(elm) << std::endl; Scope const& scope1 = Scope::containing(elm); RefPlacement ref (elm); Scope const& scope2 = Scope::containing(ref); // verify this with the scope registered within the index... - PlacementMO& scopeTop = ref_index->getScope(elm); + PlacementMO& scopeTop = sess->getScope(elm); ASSERT (scope1 == scopeTop); ASSERT (scope2 == scopeTop); ASSERT (scope1 == scope2); - ASSERT (isSameObject (scope1,scope2)); + ASSERT (!isSameObject (scope1,scope2)); } -#if false //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! -#endif //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! } + /** @test equality of scopes is based on the ID of the scope top (Placement) */ void verifyEquality () @@ -144,8 +131,8 @@ namespace test { ASSERT (scope1 != nil); ASSERT (nil != scope1); ASSERT (scope2 != nil); ASSERT (nil != scope2); - ASSERT (aPlac == scope1); ASSERT (scope1 == aPlac); - ASSERT (aPlac == scope2); ASSERT (scope2 == aPlac); + ASSERT (aPlac == scope1); ASSERT (scope1 == aPlac); + ASSERT (aPlac == scope2); ASSERT (scope2 == aPlac); ASSERT (aPlac != nil); ASSERT (nil != aPlac); Scope par (scope1.getParent()); @@ -160,25 +147,32 @@ namespace test { } - /** @test navigate to root, starting from each Placement */ + + /** @test for each element in our test session, + * establish the scope and retrieve the path to root, + * verifying the parent relationships as we go up. + * @note this is the "raw" path, i.e as stored in the + * PlacementIndex, as opposed to the effective + * path, which might digress for meta-clips + */ void - verifyNavigation (PPIdx ref_index) + verifyNavigation (PPIdx sess) { - for (_Iter elm = contents_of_testSession(); elm; ++elm) + for (_Iter elm = contents_of_testSession(sess); elm; ++elm) { _Iter pathIter = pathToRoot(*elm); Scope const& enclosing = Scope::containing(*elm); ASSERT (enclosing == Scope(*elm).getParent()); - ASSERT (*pathIter == enclosing); + ASSERT (*pathIter == Scope(*elm)); for ( ; pathIter; ++pathIter) { Scope sco(*pathIter); if (sco.isRoot()) { - VERIFY_ERROR (INVALID, sco.getParent() ); + VERIFY_ERROR (NO_PARENT_SCOPE, sco.getParent() ); PlacementMO& top = sco.getTop(); - PlacementMO& root = ref_index->getRoot(); + PlacementMO& root = sess->getRoot(); ASSERT (isSameObject (top,root)); } @@ -187,17 +181,16 @@ namespace test { Scope parent = sco.getParent(); PlacementMO& top = sco.getTop(); Scope parentsScope = Scope::containing(top); - PlacementMO& topsTop = ref_index->getScope(top); ///////////////////TODO impact of Binding a Sequence? see Ticket #311 + PlacementMO& topsTop = sess->getScope(top); ///////////////////TODO impact of Binding a Sequence? see Ticket #311 ASSERT (topsTop == parentsScope); ASSERT (isSameObject (topsTop, parentsScope.getTop())); - } } } -#if false //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! -#endif //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! + }}} } }; + /** Register this test class... */ LAUNCHER (PlacementScope_test, "function session"); From a0234ecc38dd89d3b30b914dea3e8767ab218717 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 10 Oct 2010 05:36:12 +0200 Subject: [PATCH 24/36] get QueryFocus_test to compile with the (now existing) implementation --- src/proc/mobject/session/query-focus.cpp | 15 ++++ src/proc/mobject/session/query-focus.hpp | 1 + .../proc/mobject/session/query-focus-test.cpp | 69 ++++++++++++------- 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/src/proc/mobject/session/query-focus.cpp b/src/proc/mobject/session/query-focus.cpp index f5680db65..d79eec26a 100644 --- a/src/proc/mobject/session/query-focus.cpp +++ b/src/proc/mobject/session/query-focus.cpp @@ -123,6 +123,21 @@ namespace session { } + /** push the "current QueryFocus" aside and open a new focus frame, + * which starts out at the same location as the original */ + QueryFocus + QueryFocus::push () + { + Scope currentLocation (ScopeLocator::instance().currPath().getLeaf()); + ENSURE (currentLocation.isValid()); + + QueryFocus newFocus (ScopeLocator::instance().pushPath()); + newFocus.attach (currentLocation); + return newFocus; + } + + + /** cease to use \em this specific reference to the current frame. * This operation immediately tries to re-attach to what is "current" * and readjusts the internal handle. But when the previously released diff --git a/src/proc/mobject/session/query-focus.hpp b/src/proc/mobject/session/query-focus.hpp index 7b35f8054..466d5c2bc 100644 --- a/src/proc/mobject/session/query-focus.hpp +++ b/src/proc/mobject/session/query-focus.hpp @@ -94,6 +94,7 @@ namespace session { QueryFocus& attach (Scope const&); static QueryFocus push (Scope const&); + static QueryFocus push (); QueryFocus& reset (); QueryFocus& pop (); diff --git a/tests/components/proc/mobject/session/query-focus-test.cpp b/tests/components/proc/mobject/session/query-focus-test.cpp index 93b3f0a04..0637e06ac 100644 --- a/tests/components/proc/mobject/session/query-focus-test.cpp +++ b/tests/components/proc/mobject/session/query-focus-test.cpp @@ -39,6 +39,18 @@ namespace mobject { namespace session { namespace test { + namespace { + /** Helper: extract the refcount + * of the current path referred by the given focus + */ + inline size_t + refs (QueryFocus const& focus) + { + return focus.currentPath().ref_count(); + } + } + + //using util::isSameObject; //using lumiera::Time; using std::string; @@ -49,7 +61,9 @@ namespace test { /********************************************************************************** * @test handling of current query focus when navigating a system of nested scopes. * Using a pseudo-session (actually just a PlacementIndex), this test - * creates some nested scopes and then checks moving the "current scope". + * accesses some nested scopes and then checks moving the "current scope". + * Moreover a (stack-like) sub-focus is created, temporarily moving aside + * the current focus and returning later on * * @see mobject::PlacementIndex * @see mobject::session::ScopePath @@ -89,8 +103,7 @@ namespace test { focus.reset(); ASSERT (Scope(focus).isRoot()); -#ifdef false ////////////////////////////////////////////////////////////////////////////////TICKET 384 - PMO& someObj = focus.query(); + PMO& someObj = *focus.query(); // by construction of the test fixture, // we know this object is root -> ps2 -> ps3 @@ -99,60 +112,66 @@ namespace test { ASSERT (!Scope(focus).isRoot()); ScopePath path = focus.currentPath(); ASSERT (someObj == path.getLeaf()); - ASSERT (path.getParent().getParent().isRoot()); + ASSERT (Scope(focus).getParent().getParent().isRoot()); - focus.attach (path.getParent()); - ASSERT (Scope(focus) == path.getParent()); + focus.attach (path.getLeaf().getParent()); + ASSERT (Scope(focus) == path.getLeaf().getParent()); ASSERT (someObj != Scope(focus)); ASSERT (path.contains (focus.currentPath())); - ASSERT (focus.currentPath().getParent().isRoot()); + ASSERT (focus.currentPath().getLeaf().getParent().isRoot()); + + // as the focus now has been moved up one level, + // we'll re-discover the original starting point as immediate child + CHECK (someObj == *focus.explore()); +#ifdef false ////////////////////////////////////////////////////////////////////////////////TICKET 384 #endif } - /** @test side-effect free manipulation of a sub-focus */ + /** @test side-effect free manipulation of a sub-focus, + * while the original focus is pushed on a stack */ void manipulate_subFocus() { -#ifdef false ////////////////////////////////////////////////////////////////////////////////TICKET 384 - QueryFocus original; // automatically attaches to current stack top - uint num_refs = original.ref_count(); - ASSERT (num_refs > 1); + QueryFocus original; // automatically attaches to current stack top + uint num_refs = refs (original); + ASSERT (num_refs > 1); // because the run() function also holds a ref QueryFocus subF = QueryFocus::push(); - cout << string(subF) << endl; +// cout << string(subF) << endl; ASSERT (subF == original); - ASSERT ( 1 == subF.ref_count()); - ASSERT (num_refs == original.ref_count()); + ASSERT ( 1 == refs(subF) ); + ASSERT (num_refs == refs(original)); { // temporarily creating an independent focus attached differently QueryFocus subF2 = QueryFocus::push(Scope(subF).getParent()); ASSERT (subF2 != subF); ASSERT (subF == original); - cout << string(subF2) << endl; +// cout << string(subF2) << endl; - Iterator ii = subF2.query(); + ScopeQuery::iterator ii = subF2.query(); while (ii) { subF2.attach(*ii); - cout << string(subF2) << endl; +// cout << string(subF2) << endl; ii = subF2.query(); } - cout << string(subF2) << "<<<--discovery exhausted" << endl; +// cout << string(subF2) << "<<<--discovery exhausted" << endl; subF2.pop(); // releasing this focus and re-attaching to what's on stack top - cout << string(subF2) << "<<<--after pop()" << endl; +// cout << string(subF2) << "<<<--after pop()" << endl; ASSERT (subF2 == subF); - ASSERT (2 == subF2.ref_count()); // both are now attached to the same path - ASSERT (2 == subF.ref_count()); + ASSERT (2 == refs(subF2)); // both are now attached to the same path + ASSERT (2 == refs(subF)); } // subF2 went out of scope, but no auto-pop happens (because subF is still there) - cout << string(subF) << endl; +// cout << string(subF) << endl; - ASSERT ( 1 == subF.ref_count()); - ASSERT (num_refs == original.ref_count()); + ASSERT ( 1 == refs(subF)); + ASSERT (num_refs == refs(original)); // when subF goes out of scope now, auto-pop will happen... +#ifdef false ////////////////////////////////////////////////////////////////////////////////TICKET 384 #endif } From 73a1adcdf5099deb33170ca322b369f300d03581 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 11 Oct 2010 04:42:48 +0200 Subject: [PATCH 25/36] QueryFocus_test working now (still without diagnostic output) --- src/proc/mobject/session/query-focus.hpp | 13 +++-- src/proc/mobject/session/query-resolver.cpp | 2 +- tests/43session.tests | 3 +- .../proc/mobject/session/query-focus-test.cpp | 52 +++++++++---------- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/proc/mobject/session/query-focus.hpp b/src/proc/mobject/session/query-focus.hpp index 466d5c2bc..20f8a67d4 100644 --- a/src/proc/mobject/session/query-focus.hpp +++ b/src/proc/mobject/session/query-focus.hpp @@ -89,8 +89,8 @@ namespace session { public: QueryFocus(); - ScopePath currentPath() const; - operator Scope() const; + ScopePath const& currentPath() const; + operator Scope() const; QueryFocus& attach (Scope const&); static QueryFocus push (Scope const&); @@ -130,8 +130,13 @@ namespace session { return focus_->getLeaf(); } - /**@note returning a copy */ - inline ScopePath + /** @return ref to internal datastructure + * @warning don't store it directly. Rather + * copy it or store a QueryFocus instance. + * @note use #attach if the intention is + * to \em manipulate the current + * focus location */ + inline ScopePath const& QueryFocus::currentPath() const { return *focus_; diff --git a/src/proc/mobject/session/query-resolver.cpp b/src/proc/mobject/session/query-resolver.cpp index 5b01ef3c5..1461cf7f5 100644 --- a/src/proc/mobject/session/query-resolver.cpp +++ b/src/proc/mobject/session/query-resolver.cpp @@ -101,7 +101,7 @@ namespace session { PReso QueryResolver::issue (Goal const& query) const { - TODO ("ensure proper initialisation"); + REQUIRE (!dispatcher_->empty(), "attempt to issue a query without having installed any resolver (yet)"); if (!canHandle (query)) throw lumiera::error::Invalid ("unable to resolve this kind of query"); ////TICKET #197 diff --git a/tests/43session.tests b/tests/43session.tests index 4bfab8f7f..7f0cb4233 100644 --- a/tests/43session.tests +++ b/tests/43session.tests @@ -195,7 +195,8 @@ out: ^Lumiera END -PLANNED "Query focus management" QueryFocus_test < #include @@ -46,37 +43,37 @@ namespace test { inline size_t refs (QueryFocus const& focus) { - return focus.currentPath().ref_count(); + return focus.currentPath().ref_count(); } } - //using util::isSameObject; - //using lumiera::Time; + using std::string; using std::cout; using std::endl; - /********************************************************************************** + /*********************************************************************************** * @test handling of current query focus when navigating a system of nested scopes. * Using a pseudo-session (actually just a PlacementIndex), this test * accesses some nested scopes and then checks moving the "current scope". * Moreover a (stack-like) sub-focus is created, temporarily moving aside - * the current focus and returning later on + * the current focus and returning later on. * * @see mobject::PlacementIndex * @see mobject::session::ScopePath * @see mobject::session::QueryFocus */ - class QueryFocus_test : public Test + class QueryFocus_test + : public Test { virtual void run (Arg) { - - // Prepare an (test)Index backing the PlacementRefs + // Prepare a (test)Session with + // some nested dummy placements PPIdx index = build_testScopes(); PMO& root = index->getRoot(); @@ -92,10 +89,13 @@ namespace test { QueryFocus currentFocus; ASSERT (scopePosition == Scope(currentFocus)); ASSERT (currentFocus == theFocus); + ASSERT (2 == refs(currentFocus)); + ASSERT (2 == refs(theFocus)); } - /** @test move the current focus to various locations + + /** @test move the current focus to different locations * and discover contents there. */ void checkNavigation (QueryFocus& focus) @@ -123,13 +123,12 @@ namespace test { // as the focus now has been moved up one level, // we'll re-discover the original starting point as immediate child CHECK (someObj == *focus.explore()); -#ifdef false ////////////////////////////////////////////////////////////////////////////////TICKET 384 -#endif } + /** @test side-effect free manipulation of a sub-focus, - * while the original focus is pushed on a stack */ + * while the original focus is pushed aside (stack) */ void manipulate_subFocus() { @@ -138,7 +137,7 @@ namespace test { ASSERT (num_refs > 1); // because the run() function also holds a ref QueryFocus subF = QueryFocus::push(); -// cout << string(subF) << endl; +// cout << string(subF) << endl; /////////////////////////////////////TICKET #679 display diagnostic output ASSERT (subF == original); ASSERT ( 1 == refs(subF) ); @@ -148,36 +147,35 @@ namespace test { QueryFocus subF2 = QueryFocus::push(Scope(subF).getParent()); ASSERT (subF2 != subF); ASSERT (subF == original); -// cout << string(subF2) << endl; +// cout << string(subF2) << endl; /////////////////////////////////////TICKET #679 display diagnostic output - ScopeQuery::iterator ii = subF2.query(); - while (ii) + ScopeQuery::iterator ii = subF2.explore(); + while (ii) // drill down depth first { subF2.attach(*ii); -// cout << string(subF2) << endl; - ii = subF2.query(); +// cout << string(subF2) << endl; /////////////////////////////////////TICKET #679 display diagnostic output + ii = subF2.explore(); } -// cout << string(subF2) << "<<<--discovery exhausted" << endl; +// cout << string(subF2) << "<<<--discovery exhausted" << endl; /////////////////////////////////////TICKET #679 display diagnostic output subF2.pop(); // releasing this focus and re-attaching to what's on stack top -// cout << string(subF2) << "<<<--after pop()" << endl; +// cout << string(subF2) << "<<<--after pop()" << endl; /////////////////////////////////////TICKET #679 display diagnostic output ASSERT (subF2 == subF); ASSERT (2 == refs(subF2)); // both are now attached to the same path ASSERT (2 == refs(subF)); } // subF2 went out of scope, but no auto-pop happens (because subF is still there) -// cout << string(subF) << endl; +// cout << string(subF) << endl; /////////////////////////////////////TICKET #679 display diagnostic output ASSERT ( 1 == refs(subF)); ASSERT (num_refs == refs(original)); // when subF goes out of scope now, auto-pop will happen... -#ifdef false ////////////////////////////////////////////////////////////////////////////////TICKET 384 -#endif } - + }; + /** Register this test class... */ LAUNCHER (QueryFocus_test, "unit session"); From 722ab4e5831cb0ca4fe065e2d5d246ac28f9d816 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 11 Oct 2010 05:02:51 +0200 Subject: [PATCH 26/36] corr. wrong ticket number --- .../proc/mobject/session/query-focus-test.cpp | 12 ++++++------ .../proc/mobject/session/scope-path-test.cpp | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/components/proc/mobject/session/query-focus-test.cpp b/tests/components/proc/mobject/session/query-focus-test.cpp index fa633287e..769662d95 100644 --- a/tests/components/proc/mobject/session/query-focus-test.cpp +++ b/tests/components/proc/mobject/session/query-focus-test.cpp @@ -137,7 +137,7 @@ namespace test { ASSERT (num_refs > 1); // because the run() function also holds a ref QueryFocus subF = QueryFocus::push(); -// cout << string(subF) << endl; /////////////////////////////////////TICKET #679 display diagnostic output +// cout << string(subF) << endl; /////////////////////////////////////TICKET #429 display diagnostic output ASSERT (subF == original); ASSERT ( 1 == refs(subF) ); @@ -147,25 +147,25 @@ namespace test { QueryFocus subF2 = QueryFocus::push(Scope(subF).getParent()); ASSERT (subF2 != subF); ASSERT (subF == original); -// cout << string(subF2) << endl; /////////////////////////////////////TICKET #679 display diagnostic output +// cout << string(subF2) << endl; /////////////////////////////////////TICKET #429 display diagnostic output ScopeQuery::iterator ii = subF2.explore(); while (ii) // drill down depth first { subF2.attach(*ii); -// cout << string(subF2) << endl; /////////////////////////////////////TICKET #679 display diagnostic output +// cout << string(subF2) << endl; /////////////////////////////////////TICKET #429 display diagnostic output ii = subF2.explore(); } -// cout << string(subF2) << "<<<--discovery exhausted" << endl; /////////////////////////////////////TICKET #679 display diagnostic output +// cout << string(subF2) << "<<<--discovery exhausted" << endl; /////////////////////////////////////TICKET #429 display diagnostic output subF2.pop(); // releasing this focus and re-attaching to what's on stack top -// cout << string(subF2) << "<<<--after pop()" << endl; /////////////////////////////////////TICKET #679 display diagnostic output +// cout << string(subF2) << "<<<--after pop()" << endl; /////////////////////////////////////TICKET #429 display diagnostic output ASSERT (subF2 == subF); ASSERT (2 == refs(subF2)); // both are now attached to the same path ASSERT (2 == refs(subF)); } // subF2 went out of scope, but no auto-pop happens (because subF is still there) -// cout << string(subF) << endl; /////////////////////////////////////TICKET #679 display diagnostic output +// cout << string(subF) << endl; /////////////////////////////////////TICKET #429 display diagnostic output ASSERT ( 1 == refs(subF)); ASSERT (num_refs == refs(original)); diff --git a/tests/components/proc/mobject/session/scope-path-test.cpp b/tests/components/proc/mobject/session/scope-path-test.cpp index d425e394c..927349451 100644 --- a/tests/components/proc/mobject/session/scope-path-test.cpp +++ b/tests/components/proc/mobject/session/scope-path-test.cpp @@ -152,7 +152,7 @@ namespace test { ASSERT (parent == refScope.getParent()); ASSERT (path1 != path2); ASSERT (path2 != path1); - ASSERT (path1.contains (path2)); ////////////////////TODO: not clear if we really need to implement those relations + ASSERT (path1.contains (path2)); ASSERT (!disjoint(path1,path2)); ASSERT (path2 == commonPrefix(path1,path2)); ASSERT (path2 == commonPrefix(path2,path1)); From b15a1c2d3ce61d13dc38c0d026cd0f82613ed967 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 12 Oct 2010 02:38:18 +0200 Subject: [PATCH 27/36] changed wording of the error message --- src/lib/test/test-helper.hpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/lib/test/test-helper.hpp b/src/lib/test/test-helper.hpp index a73a47e50..5ad305977 100644 --- a/src/lib/test/test-helper.hpp +++ b/src/lib/test/test-helper.hpp @@ -129,15 +129,16 @@ namespace test{ * an assertion failure. In case of an exception, the #lumiera_error * state is checked, cleared and verified. */ -#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT) \ - try \ - { \ - ERRONEOUS_STATEMENT ; \ - NOTREACHED("'%s' resisted to fail", #ERRONEOUS_STATEMENT); \ - } \ - catch (...) \ - { \ - CHECK (lumiera_error_expect (LUMIERA_ERROR_##ERROR_ID)); \ +#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT) \ + try \ + { \ + ERRONEOUS_STATEMENT ; \ + NOTREACHED("expected '%s' failure in: %s", \ + #ERROR_ID, #ERRONEOUS_STATEMENT); \ + } \ + catch (...) \ + { \ + CHECK (lumiera_error_expect (LUMIERA_ERROR_##ERROR_ID));\ } #endif From b9e99a2be2644a1d3abf9a5ebfbb2ec6a837f9de Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 12 Oct 2010 03:34:42 +0200 Subject: [PATCH 28/36] add ability to generate a short-ID to MObject hierarchy --- src/proc/mobject/mobject.cpp | 25 +++++++++++- src/proc/mobject/mobject.hpp | 31 ++++++++------- src/proc/mobject/session/abstractmo.cpp | 51 ++++++++++++++++--------- src/proc/mobject/session/abstractmo.hpp | 28 ++++++++++---- src/proc/mobject/session/auto.hpp | 6 +++ src/proc/mobject/session/binding.hpp | 6 +++ src/proc/mobject/session/clip.cpp | 4 +- src/proc/mobject/session/clip.hpp | 17 +++++++-- src/proc/mobject/session/effect.hpp | 6 +++ src/proc/mobject/session/label.hpp | 12 ++++-- src/proc/mobject/session/meta.hpp | 6 +++ src/proc/mobject/session/root.hpp | 6 +++ src/proc/mobject/session/track.hpp | 10 ++++- src/proc/mobject/test-dummy-mobject.hpp | 5 ++- 14 files changed, 162 insertions(+), 51 deletions(-) diff --git a/src/proc/mobject/mobject.cpp b/src/proc/mobject/mobject.cpp index 022c3f9ff..8ac73a1d2 100644 --- a/src/proc/mobject/mobject.cpp +++ b/src/proc/mobject/mobject.cpp @@ -23,9 +23,11 @@ #include "proc/mobject/mobject.hpp" #include "proc/mobject/session/mobjectfactory.hpp" +#include "lib/util.hpp" -namespace mobject - { +using util::isnil; + +namespace mobject { using ::NOBUG_FLAG(memory); NOBUG_CPP_DEFINE_FLAG_PARENT(mobjectmem, memory); @@ -35,6 +37,25 @@ namespace mobject */ session::MObjectFactory MObject::create; + + MObject::MObject() + : length_() + , shortID_() + { } + + + MObject::~MObject() { }; + + + + string const& + MObject::shortID() const + { + if (isnil (shortID_)) + shortID_ = initShortID(); + return shortID_; + } + } // namespace mobject diff --git a/src/proc/mobject/mobject.hpp b/src/proc/mobject/mobject.hpp index c8f831bf2..3944c06b5 100644 --- a/src/proc/mobject/mobject.hpp +++ b/src/proc/mobject/mobject.hpp @@ -34,10 +34,9 @@ #include #include -#include +#include -using std::list; #include "proc/assetmanager.hpp" using proc_interface::IDA; //TODO finally not needed? @@ -46,6 +45,7 @@ using proc_interface::AssetManager; namespace mobject { + using std::string; using lumiera::P; //NOBUG_DECLARE_FLAG (mobjectmem); @@ -71,10 +71,12 @@ namespace mobject { typedef lumiera::Time Time; // TODO: how to represent time intervals best? - Time length; + Time length_; - MObject() {} - virtual ~MObject() {}; + mutable string shortID_; + + MObject() ; + virtual ~MObject() ; friend class session::MObjectFactory; @@ -82,13 +84,23 @@ namespace mobject { public: static session::MObjectFactory create; + /** a short readable ID as a single name-token, + * denoting both the kind of MObject and some sort of + * instance identity. Not necessarily unique but should + * be reasonable unique in most cases */ + string const& shortID() const; + /** MObject self-test (usable for asserting) */ virtual bool isValid() const =0; virtual Time& getLength() =0; ///< @todo how to deal with the time/length field?? ////TICKET #448 - + virtual bool operator== (const MObject& oo) const =0; + protected: + + virtual string initShortID() const =0; + }; @@ -98,10 +110,3 @@ namespace mobject { } // namespace mobject #endif -/* -// Local Variables: -// mode: C++ -// c-file-style: "gnu" -// indent-tabs-mode: nil -// End: -*/ diff --git a/src/proc/mobject/session/abstractmo.cpp b/src/proc/mobject/session/abstractmo.cpp index e3ebffd4f..89cb0cf8b 100644 --- a/src/proc/mobject/session/abstractmo.cpp +++ b/src/proc/mobject/session/abstractmo.cpp @@ -22,22 +22,39 @@ #include "proc/mobject/session/abstractmo.hpp" +#include "lib/util.hpp" -namespace mobject +#include + +using boost::format; +using util::isnil; + +namespace mobject { +namespace session { + + /** default/fallback implementation of equality + * using literal object identity (same address) + */ + bool + AbstractMO::operator== (const MObject& oo) const { - namespace session - { - - /** default/fallback implementation of equality - * using literal object identity (same address) - */ - bool - AbstractMO::operator== (const MObject& oo) const - { - return (this == &oo); - } - - - } // namespace mobject::session - -} // namespace mobject + return (this == &oo); + } + + + + string + AbstractMO::buildShortID (lib::Literal typeID, string suffix) const + { + static uint i=0; + static format namePattern ("%s.%03d"); + + REQUIRE (!isnil (typeID)); + if (!isnil (suffix)) + return typeID+"."+suffix; + else + return str(namePattern % typeID % (++i) ); + } + + +}} // namespace mobject::session diff --git a/src/proc/mobject/session/abstractmo.hpp b/src/proc/mobject/session/abstractmo.hpp index 69e1c409f..eff1d5d0c 100644 --- a/src/proc/mobject/session/abstractmo.hpp +++ b/src/proc/mobject/session/abstractmo.hpp @@ -25,6 +25,7 @@ #define MOBJECT_SESSION_ABSTRACTMO_H #include "proc/mobject/mobject.hpp" +#include "lib/symbol.hpp" @@ -36,18 +37,30 @@ namespace session { * abstract base class of all MObjects for providing common services. * @todo seems that we don't need this intermediate class... */ - class AbstractMO : public MObject + class AbstractMO + : public MObject { - public: - - /* some dummy implementations used to make the code compile... */ - - virtual Time& getLength() { return length; } + + /* === some default implementations === */ DEFINE_PROCESSABLE_BY (builder::BuilderTool); - virtual bool operator== (const MObject& oo) const; + string + initShortID() const + { + return buildShortID("MObject"); + } + + public: + + Time& + getLength() + { + return length_; + } + + bool operator== (const MObject& oo) const; protected: void @@ -58,6 +71,7 @@ namespace session { "or similarly broken internal assumptions."); } + string buildShortID (lib::Literal typeID, string suffix ="") const; }; diff --git a/src/proc/mobject/session/auto.hpp b/src/proc/mobject/session/auto.hpp index 68320fd45..0ec133bc0 100644 --- a/src/proc/mobject/session/auto.hpp +++ b/src/proc/mobject/session/auto.hpp @@ -42,6 +42,12 @@ namespace mobject template class Auto : public Meta, public ParamProvider { + string + initShortID() const + { + return buildShortID("Auto"); + } + public: //////////////////////////////TICKET #566 diff --git a/src/proc/mobject/session/binding.hpp b/src/proc/mobject/session/binding.hpp index 135482e57..05c51fd74 100644 --- a/src/proc/mobject/session/binding.hpp +++ b/src/proc/mobject/session/binding.hpp @@ -50,6 +50,12 @@ namespace session { { PSequence boundSequence_; + string + initShortID() const + { + return buildShortID("Binding"); + } + bool isValid() const; public: diff --git a/src/proc/mobject/session/clip.cpp b/src/proc/mobject/session/clip.cpp index 09b271b4c..a99f7c5df 100644 --- a/src/proc/mobject/session/clip.cpp +++ b/src/proc/mobject/session/clip.cpp @@ -50,7 +50,7 @@ namespace session { Clip::isValid () const { TODO ("check consistency of clip length def, implies accessing the underlying media def"); - return length > Time(0); + return length_ > Time(0); } @@ -58,7 +58,7 @@ namespace session { Clip::setupLength() { TODO ("really calculate the length of a clip and set length field"); - this->length = mediaDef_.getLength(); + this->length_ = mediaDef_.getLength(); } diff --git a/src/proc/mobject/session/clip.hpp b/src/proc/mobject/session/clip.hpp index 4674e0003..09aeda4c7 100644 --- a/src/proc/mobject/session/clip.hpp +++ b/src/proc/mobject/session/clip.hpp @@ -52,8 +52,19 @@ namespace session { * because it depends on the actual media type, and we want to encapsulate * all these details as much as possible. */ - class Clip : public AbstractMO + class Clip + : public AbstractMO { + string + initShortID() const + { + return buildShortID("Clip"); + } + + void setupLength(); + + + protected: /** start position in source */ Time start_; @@ -72,10 +83,8 @@ namespace session { friend class MObjectFactory; - virtual void setupLength(); - public: - virtual bool isValid() const; + bool isValid() const; /** access the underlying media asset */ PMedia getMedia () const; diff --git a/src/proc/mobject/session/effect.hpp b/src/proc/mobject/session/effect.hpp index 23250b3bc..ba0f08e8c 100644 --- a/src/proc/mobject/session/effect.hpp +++ b/src/proc/mobject/session/effect.hpp @@ -42,6 +42,12 @@ namespace session { class Effect : public AbstractMO { + string + initShortID() const + { + return buildShortID("Effect"); + } + protected: /** Identifier of the Plug-in to be used */ string pluginID; diff --git a/src/proc/mobject/session/label.hpp b/src/proc/mobject/session/label.hpp index e83221f29..d2b6c3616 100644 --- a/src/proc/mobject/session/label.hpp +++ b/src/proc/mobject/session/label.hpp @@ -46,10 +46,16 @@ namespace session { class Label : public Meta { ///////////TODO: timespan fields here or already in class Meta?? - + Symbol typeID_; - - virtual bool isValid() const; + + string + initShortID() const + { + return buildShortID("Label"); + } + + bool isValid() const; public: Label (Symbol type) diff --git a/src/proc/mobject/session/meta.hpp b/src/proc/mobject/session/meta.hpp index 3f3f02ec9..bea5bdb25 100644 --- a/src/proc/mobject/session/meta.hpp +++ b/src/proc/mobject/session/meta.hpp @@ -43,6 +43,12 @@ namespace session { { /////////// //////////////////////////////TICKET #448 what to do with the length here?? + + string + initShortID() const + { + return buildShortID("MetaMO"); + } }; diff --git a/src/proc/mobject/session/root.hpp b/src/proc/mobject/session/root.hpp index b6457bd77..8d5b744ad 100644 --- a/src/proc/mobject/session/root.hpp +++ b/src/proc/mobject/session/root.hpp @@ -54,6 +54,12 @@ namespace session { ///////////TODO: timespan fields here or already in class Meta?? ///////////TODO: any idea about the purpose of root's "timespan"?? ///////TICKET #448 + string + initShortID() const + { + return buildShortID("Root","(✼)"); + } + virtual bool isValid() const; public: diff --git a/src/proc/mobject/session/track.hpp b/src/proc/mobject/session/track.hpp index 3b40922c0..0bff48744 100644 --- a/src/proc/mobject/session/track.hpp +++ b/src/proc/mobject/session/track.hpp @@ -76,6 +76,15 @@ namespace session { Time start_; TrackID id_; + + string + initShortID() const + { + return buildShortID("Fork"); + } + + bool isValid() const; + protected: Track (TrackID const&); friend class MObjectFactory; @@ -87,7 +96,6 @@ namespace session { bool isSameID (string const&); - virtual bool isValid() const; }; diff --git a/src/proc/mobject/test-dummy-mobject.hpp b/src/proc/mobject/test-dummy-mobject.hpp index 9f18cc7e8..adbeeb313 100644 --- a/src/proc/mobject/test-dummy-mobject.hpp +++ b/src/proc/mobject/test-dummy-mobject.hpp @@ -81,8 +81,9 @@ namespace test { DEFINE_PROCESSABLE_BY (BuilderTool); - virtual bool isValid() const { return true;} - virtual operator string() const { return display("DummyMO"); } + virtual bool isValid() const { return true;} + virtual string initShortID() const { return buildShortID("DummyMO"); } + virtual operator string() const { return display("DummyMO"); } static void killDummy (MObject* dum) { delete (DummyMO*)dum; } protected: From 48605827a68464c9b4b0fdb50a12ea15bd472f82 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 15 Oct 2010 02:34:33 +0200 Subject: [PATCH 29/36] Add test to cover the very basic MObject interface --- src/proc/mobject/mobject.hpp | 4 +- src/proc/mobject/session/abstractmo.cpp | 3 +- tests/43session.tests | 7 + tests/components/backend/mediaaccessmock.cpp | 135 +++++++++--------- tests/components/backend/mediaaccessmock.hpp | 33 ++--- .../proc/mobject/mobject-interface-test.cpp | 120 ++++++++++++++++ wiki/renderengine.html | 8 +- 7 files changed, 216 insertions(+), 94 deletions(-) create mode 100644 tests/components/proc/mobject/mobject-interface-test.cpp diff --git a/src/proc/mobject/mobject.hpp b/src/proc/mobject/mobject.hpp index 3944c06b5..a1a7c8eab 100644 --- a/src/proc/mobject/mobject.hpp +++ b/src/proc/mobject/mobject.hpp @@ -95,12 +95,12 @@ namespace mobject { virtual Time& getLength() =0; ///< @todo how to deal with the time/length field?? ////TICKET #448 - virtual bool operator== (const MObject& oo) const =0; + virtual bool operator== (const MObject& oo) const =0; ///< needed for handling by lumiera::P protected: virtual string initShortID() const =0; - + }; diff --git a/src/proc/mobject/session/abstractmo.cpp b/src/proc/mobject/session/abstractmo.cpp index 89cb0cf8b..bdebb727d 100644 --- a/src/proc/mobject/session/abstractmo.cpp +++ b/src/proc/mobject/session/abstractmo.cpp @@ -33,7 +33,8 @@ namespace mobject { namespace session { /** default/fallback implementation of equality - * using literal object identity (same address) + * using literal object identity (same address). + * Required to enable handling by lumiera::P */ bool AbstractMO::operator== (const MObject& oo) const diff --git a/tests/43session.tests b/tests/43session.tests index 7f0cb4233..5dda9c56d 100644 --- a/tests/43session.tests +++ b/tests/43session.tests @@ -25,6 +25,13 @@ PLANNED "DeleteClip_test" DeleteClip_test < Response; - const ChanDesc NULLResponse; + + + namespace { // implementation details - struct TestCases : map - { - TestCases () - { - // ------------------------------------------------------------------TESTCASES - (*this)["test-1"].push_back (ChanDesc ("video","ID", genH())); - - (*this)["test-2"].push_back (ChanDesc ("video","H264", genH())); - (*this)["test-2"].push_back (ChanDesc ("audio-L","PCM", genH())); - (*this)["test-2"].push_back (ChanDesc ("audio-R","PCM", genH())); - // ------------------------------------------------------------------TESTCASES - } - - bool known (string key) - { - const_iterator i = find (key); - return (i != end()); - } - private: - int _i_; - ChanHandle genH() - { - return reinterpret_cast (++_i_); - } - }; - - // instantiate TestCasses table - TestCases testCases; - - } // (end) implementation namespace - - - FileHandle - MediaAccessMock::queryFile (const char* name) throw(Invalid) - { - if (isnil (name)) - throw Invalid ("empty filename passed to MediaAccessFacade."); + typedef vector Response; + const ChanDesc NULLResponse; - if (!testCases.known(name)) - return 0; - else - return reinterpret_cast (&testCases[name]); - } - - ChanDesc - MediaAccessMock::queryChannel (FileHandle h, uint chanNo) throw() - { - const Response* res (reinterpret_cast (h)); + struct TestCases : map + { + TestCases () + { + // ------------------------------------------------------------------TESTCASES + (*this)["test-1"].push_back (ChanDesc ("video","ID", genH())); + + (*this)["test-2"].push_back (ChanDesc ("video","H264", genH())); + (*this)["test-2"].push_back (ChanDesc ("audio-L","PCM", genH())); + (*this)["test-2"].push_back (ChanDesc ("audio-R","PCM", genH())); + // ------------------------------------------------------------------TESTCASES + } + + bool known (string key) + { + const_iterator i = find (key); + return (i != end()); + } + private: + int _i_; + ChanHandle genH() + { + return reinterpret_cast (++_i_); + } + }; - if (!res || res->size() <= chanNo) - return NULLResponse; - else - return (*res)[chanNo]; - } - - - } // namespace test - -} // namespace backend_interface + // instantiate TestCasses table + TestCases testCases; + + } // (end) implementation namespace + + + FileHandle + MediaAccessMock::queryFile (const char* name) throw(Invalid) + { + if (isnil (name)) + throw Invalid ("empty filename passed to MediaAccessFacade."); + + if (!testCases.known(name)) + return 0; + else + return reinterpret_cast (&testCases[name]); + } + + ChanDesc + MediaAccessMock::queryChannel (FileHandle h, uint chanNo) throw() + { + const Response* res (reinterpret_cast (h)); + + if (!res || res->size() <= chanNo) + return NULLResponse; + else + return (*res)[chanNo]; + } + + +}} // namespace backend_interface::test diff --git a/tests/components/backend/mediaaccessmock.hpp b/tests/components/backend/mediaaccessmock.hpp index 06eedad5c..4199b29f8 100644 --- a/tests/components/backend/mediaaccessmock.hpp +++ b/tests/components/backend/mediaaccessmock.hpp @@ -30,23 +30,20 @@ -namespace backend_interface - { - namespace test +namespace backend_interface { +namespace test { + + /** + * Mock implementation of the MediaAccessFacade. + * Provides preconfigured responses for some Test-Filenames. + */ + class MediaAccessMock : public MediaAccessFacade { - /** - * Mock implementation of the MediaAccessFacade. - * Provides preconfigured responses for some Test-Filenames. - */ - class MediaAccessMock : public MediaAccessFacade - { - public: - FileHandle queryFile (const char* name) throw(lumiera::error::Invalid); - ChanDesc queryChannel (FileHandle, uint chanNo) throw(); - }; - - - } // namespace test - -} // namespace backend_interface + public: + FileHandle queryFile (const char* name) throw(lumiera::error::Invalid); + ChanDesc queryChannel (FileHandle, uint chanNo) throw(); + }; + + +}} // namespace backend_interface::test #endif diff --git a/tests/components/proc/mobject/mobject-interface-test.cpp b/tests/components/proc/mobject/mobject-interface-test.cpp new file mode 100644 index 000000000..49717737f --- /dev/null +++ b/tests/components/proc/mobject/mobject-interface-test.cpp @@ -0,0 +1,120 @@ +/* + MObjectInterface(Test) - covers behaviour common to all MObjects + + Copyright (C) Lumiera.org + 2010, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/lumitime.hpp" +#include "lib/symbol.hpp" + +#include "proc/asset/media.hpp" +#include "proc/mobject/mobject.hpp" +#include "proc/mobject/session/mobjectfactory.hpp" +//#include "proc/mobject/mobject-ref.hpp" +#include "proc/mobject/placement.hpp" +//#include "proc/mobject/placement-ref.hpp" +//#include "proc/mobject/session/placement-index.hpp" +//#include "proc/mobject/session/session-service-mock-index.hpp" +//#include "proc/mobject/session/clip.hpp" +//#include "proc/mobject/explicitplacement.hpp" +#include "proc/mobject/test-dummy-mobject.hpp" +//#include "lib/test/test-helper.hpp" + +#include + + + +namespace mobject { +namespace test { + +// using lib::test::showSizeof; + using std::string; + using std::cout; + using std::endl; + + using lib::Symbol; + + + using lumiera::Time; +// using session::Clip; +// using session::PMedia; + + + using namespace mobject::test; + typedef TestPlacement PDummy; + + + /********************************************************************************* + * @test cover the common behaviour of all MObjects. + * @note the MObject interface is still very preliminary (as of 10/10). + * It is expected to support some kind of metadata and object serialisation + * + * @see mobject::MObject + * @see mobject::Placement + */ + class MObjectInterface_test : public Test + { + + + + virtual void + run (Arg) + { + PMO testClip1 = asset::Media::create("test-1", asset::VIDEO)->createClip(); + PMO testClip2 = asset::Media::create("test-2", asset::VIDEO)->createClip(); + + // set up a tie to fixed start positions (i.e. "properties of placement") + testClip1.chain(Time(10)); + testClip2.chain(Time(20)); + + Symbol labelType ("dummyLabel"); + PMO testLabel1 = MObject::create (labelType); + + testLabel1.chain(Time(30)); + + PDummy testDummy1(*new DummyMO); + PDummy testDummy2(*new TestSubMO1); + + ASSERT (testClip1->isValid()); + ASSERT (testClip2->isValid()); + ASSERT (testLabel1->isValid()); + ASSERT (testDummy1->isValid()); + ASSERT (testDummy2->isValid()); + + Time lenC1 = testClip1->getLength(); + Time lenC2 = testClip2->getLength(); + Time lenL1 = testLabel1->getLength(); + + cout << testClip1->shortID() << endl; + cout << testClip2->shortID() << endl; + cout << testLabel1->shortID() << endl; + cout << testDummy1->shortID() << endl; + cout << testDummy2->shortID() << endl; + } + }; + + + + /** Register this test class... */ + LAUNCHER (MObjectInterface_test, "unit session"); + + +}} // namespace mobject::test diff --git a/wiki/renderengine.html b/wiki/renderengine.html index e3b5a932a..116b8bf63 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1437,7 +1437,7 @@ More often than not, these emerge from immediate solutions, being percieved as e &rarr; [[accessing and integrating configuration queries|ConfigQueryIntegration]]
-
+
* planning to embed a YAP Prolog engine
 * currently just integrated by a table driven mock
 * the baseline is a bit more clear by now (4/08)
@@ -1465,7 +1465,7 @@ At various places, instead of requiring a fixed set of capabilities, it is possi
 <br/>
 <br/>
 
-Access point is the interface {{{ConfigRules}}}, which allowes to resolve a ConfigQuery resulting in an object with properties configured such as to fulfill the query. This whole subsystem employes quite some generic programming, because actually we don't deal with "objects", but rather with similar instantiations of the same functionality for a collection of different object types. For the purpose of resolving these queries, the actual kind of object is not so much of importance, but on the caller sinde, of course we want to deal with the result of the queries in a typesafe manner.
+Access point is the interface {{{ConfigRules}}}, which allowes to resolve a ConfigQuery resulting in an object with properties configured such as to fulfill the query. This whole subsystem employes quite some generic programming, because actually we don't deal with "objects", but rather with similar instantiations of the same functionality for a collection of different object types. For the purpose of resolving these queries, the actual kind of object is not so much of importance, but on the caller side, of course we want to deal with the result of the queries in a typesafe manner.
 Examples for //participating object kinds// are [[pipes|Pipe]], [[processing patterns|ProcPatt]], effect instances, [[tags|Tag]], [[labels|Label]], [[automation data sets|AutomationData]],...
 @@clear(right):display(block):@@
 For this to work, we need each of the //participating object types// to provide the implementation of a generic interface {{{TypeHandler}}}, which allows to access actual C/C++ implementations for the predicates usable on objects of this type within the Prolog rules. The implementation has to make sure that, alongside with each config query, there are additional //type constraints// to be regarded. For example, if the client code runs a {{{Query<Pipe>}}}, an additional //type guard// (implemented by a predicate {{{type(pipe)}}} has to be inserted, so only rules and facts in accordance with this type will be used for resolution.
@@ -6419,7 +6419,7 @@ Just an ''registration scheme'' should be implemented right now, working complet
 see [[implementation planning|TypedLookup]]
 
-
+
TypedID is a registration service to associate object identities, symbolic identifiers and types. It acts as frontend to the TypedLookup service within Proc-Layer, at the implementation level. While TypedID works within a strictly typed context, this type information is translated into an internal index on passing over to the implementation, which manages a set of tables containing base entries with an combined symbolic+hash ID, plus an opaque buffer. Thus, the strictly typed context is required to re-access the stored data. But the type information wasn't erased entirely, so this typed context can be re-gained with the help of an internal type index. All of this is considered implementation detail and may be subject to change without further notice; any access is assumed to happen through the TypedID frontend. Besides, there are two more specialised frontends.
 
 !Front-ends
@@ -6440,7 +6440,7 @@ In most cases, the //actually usable instance// of an entity isn't identical to
 !basic usage patterns
 * ''Assets'' are maintained by the AssetManager, which always holds a smart-ptr to the managed asset. Assets include explicit links to dependent assets. Thus, there is no point in interfering with lifecylce management, so we store just a ''weak reference'' here, which the access functor turns back into a smart-ptr, sharing ownership.
 * Plain ''~MObjects'' are somewhat similar, but there is no active lifecycle management &mdash; they are always tied either to a placement of linked from within the assets or render processes. When done, they just go out of scope. Thus we too use a ''weak reference'' here, thereby expecting the respective entity to mix in {{{TypedID::Link}}}
-* Active ''Placements'' of an MObject behave like //object instances// within the model/session. They live within the PlacementIndex and cary an unique {{{LUID}}}-ID. Thus, it's sufficient to store this ''Placement-ID'', which can be used by the access functor to fetch the corresponding Placement from the session.
+* Active ''Placements'' of an MObject behave like //object instances// within the model/session. They live within the PlacementIndex and cary an unique {{{LUID}}}-ID. Thus, it's sufficient to store this ''~Placement-ID'', which can be used by the access functor to fetch the corresponding Placement from the session.
 Obviously, the ~TypedLookup system is open for addition of completely separate and different types.
 [>img[TypedLookup implementation sketch|uml/fig140293.png]]
 |>| !Entity |!pattern |

From e48a9fb8111c065b062dcd983a745d9e733dcb2f Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 15 Oct 2010 03:49:19 +0200
Subject: [PATCH 30/36] add diagnostic self display to placement-Scope

---
 src/proc/mobject/session/scope.cpp                 | 14 ++++++++++++++
 src/proc/mobject/session/scope.hpp                 | 11 +++++++----
 tests/43session.tests                              | 12 ++++++++++++
 .../proc/mobject/session/placement-scope-test.cpp  |  3 ++-
 4 files changed, 35 insertions(+), 5 deletions(-)

diff --git a/src/proc/mobject/session/scope.cpp b/src/proc/mobject/session/scope.cpp
index dc86bc2ed..bc2912085 100644
--- a/src/proc/mobject/session/scope.cpp
+++ b/src/proc/mobject/session/scope.cpp
@@ -47,6 +47,7 @@
 
 #include 
 
+using std::string;
 using std::vector;
 using lib::IterSource;
 using lib::iter_source::wrapIter;
@@ -225,6 +226,19 @@ namespace session {
   }
   
   
+  /** Scope diagnostic self display.
+   *  Implemented based on the self-display of the MObject
+   *  attached through the scope top placement. Usually this
+   *  should yield a reasonably unique, descriptive string. */
+  Scope::operator string()  const
+  {
+    string res("[");
+    res += anchor_->shortID();
+    res += "]";
+    return res;
+  }
+  
+  
   
   
   
diff --git a/src/proc/mobject/session/scope.hpp b/src/proc/mobject/session/scope.hpp
index a6e266e6f..c9806ddce 100644
--- a/src/proc/mobject/session/scope.hpp
+++ b/src/proc/mobject/session/scope.hpp
@@ -28,6 +28,8 @@
 #include "proc/mobject/placement-ref.hpp"
 #include "lib/error.hpp"
 
+#include 
+
 
 namespace mobject {
 namespace session {
@@ -72,16 +74,17 @@ namespace session {
       
       Scope (Scope const&);
       Scope& operator= (Scope const&);
-            
+      
       static const Scope INVALID;
-
+      
       static Scope containing (PlacementMO const& aPlacement);
       static Scope containing (RefPlacement const& refPlacement);
       
+      operator std::string() const;
       Scope getParent()      const;
       PlacementMO& getTop()  const;
-      bool isValid() const;
-      bool isRoot()  const;
+      bool isValid()         const;
+      bool isRoot()          const;
       
       friend bool operator== (Scope const&, Scope const&);
       friend bool operator!= (Scope const&, Scope const&);
diff --git a/tests/43session.tests b/tests/43session.tests
index 5dda9c56d..32c12d434 100644
--- a/tests/43session.tests
+++ b/tests/43session.tests
@@ -169,9 +169,21 @@ END
 
 
 TEST "Placement search scope" PlacementScope_test <
+out: Scope: \[DummyMO\.[0-9]{3}\]
+out: Placement<.+TestSubMO21.>
+out: Scope: \[DummyMO\.[0-9]{3}\]
+out: Placement<.+TestSubMO21.>
+out: Scope: \[DummyMO\.[0-9]{3}\]
+out: Placement<.+TestSubMO21.>
+out: Scope: \[DummyMO\.[0-9]{3}\]
+out: Placement<.+TestSubMO21.>
+out: Scope: \[Label\.[0-9]{3}\]
 out: Placement<.+TestSubMO2.>
+out: Scope: \[DummyMO\.[0-9]{3}\]
 out: Placement<.+TestSubMO1.>
+out: Scope: \[Label\.[0-9]{3}\]
 out: Placement<.+DummyMO.>
 return: 0
 END
diff --git a/tests/components/proc/mobject/session/placement-scope-test.cpp b/tests/components/proc/mobject/session/placement-scope-test.cpp
index 374246c71..f3efca615 100644
--- a/tests/components/proc/mobject/session/placement-scope-test.cpp
+++ b/tests/components/proc/mobject/session/placement-scope-test.cpp
@@ -100,8 +100,9 @@ namespace test    {
             {
               PlacementMO& elm = *ii;
               ASSERT (elm.isValid());
-              std::cout << string(elm) << std::endl;
               Scope const& scope1 = Scope::containing(elm);
+              std::cout << "Scope: " << string(scope1) << std::endl;
+              std::cout << string(elm) << std::endl;
               
               RefPlacement ref (elm);
               Scope const& scope2 = Scope::containing(ref);

From c7723594405103cbb717a14d372b4549b33bc58e Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 15 Oct 2010 04:33:10 +0200
Subject: [PATCH 31/36] add diagnostic self display to ScopePath

---
 src/proc/mobject/session/scope-path.cpp       | 21 +++++++++++++++++++
 src/proc/mobject/session/scope-path.hpp       |  3 ++-
 tests/43session.tests                         |  6 ++++++
 .../proc/mobject/session/scope-path-test.cpp  | 21 ++++++++++++-------
 4 files changed, 43 insertions(+), 8 deletions(-)

diff --git a/src/proc/mobject/session/scope-path.cpp b/src/proc/mobject/session/scope-path.cpp
index 49ec87b0c..c713e2fc0 100644
--- a/src/proc/mobject/session/scope-path.cpp
+++ b/src/proc/mobject/session/scope-path.cpp
@@ -39,6 +39,7 @@ namespace session {
   using std::reverse;
   using lib::append_all;
   using util::isSameObject;
+  using util::isnil;
   
   using namespace lumiera;
   
@@ -141,6 +142,26 @@ namespace session {
   const ScopePath ScopePath::INVALID = ScopePath(Scope());
   
   
+  /** ScopePath diagnostic self display.
+   *  Implemented similar to a filesystem path, where the
+   *  path elements are based on the self-display of the MObject
+   *  attached through the respective scope top placement. */
+  ScopePath::operator string()  const
+  {
+    if (isnil (path_)) return "!";
+    if (1 == length()) return "/";
+    
+    string res;
+    vector::const_iterator node (path_.begin());
+    while (++node != path_.end())
+      {
+        res += "/";
+        res += string (*node);
+      }
+    return res;
+  }
+  
+  
   /** a \em valid path consists of more than just the root element.
    *  @note contrary to this, an \em empty path doesn't even contain a root element
    */
diff --git a/src/proc/mobject/session/scope-path.hpp b/src/proc/mobject/session/scope-path.hpp
index d23d286cf..61799d95d 100644
--- a/src/proc/mobject/session/scope-path.hpp
+++ b/src/proc/mobject/session/scope-path.hpp
@@ -93,6 +93,7 @@
 #include "lib/error.hpp"
 
 #include 
+#include 
 
 
 namespace lib {
@@ -163,7 +164,7 @@ namespace session {
       size_t size()     const;
       size_t length()   const;
       size_t ref_count()const;
-                        ////////////////////////////////////////TICKET #429 : diagnostic output to be added later
+      operator string() const;
       
       /// Iteration is always ascending from leaf to root
       typedef _IterType iterator;
diff --git a/tests/43session.tests b/tests/43session.tests
index 32c12d434..5f6f8994b 100644
--- a/tests/43session.tests
+++ b/tests/43session.tests
@@ -190,6 +190,12 @@ END
 
 
 TEST "Path of nested scopes" ScopePath_test <
+#include 
 
 
 namespace mobject {
 namespace session {
 namespace test    {
   
+  using std::cout;
+  using std::endl;
+  using std::string;
   using util::isnil;
   using util::isSameObject;
   
@@ -75,7 +80,6 @@ namespace test    {
           check_RefcountProtection (startPlacement);
           navigate (testPath, index);
           clear (testPath, index);
-                                  ////////////////////////////////////////TICKET #429 : verify diagnostic output (to be added later)
         }
       
       
@@ -183,6 +187,7 @@ namespace test    {
           ASSERT (refPath);
           ASSERT (!ScopePath::INVALID);
           ASSERT (isnil (ScopePath::INVALID));
+          ASSERT ("!" == string(ScopePath::INVALID));
           
           ScopePath invalidP (ScopePath::INVALID);
           ASSERT (isnil (invalidP));
@@ -304,11 +309,13 @@ namespace test    {
       void
       navigate (const ScopePath refPath, PPIdx index)
         {
-          ScopePath path (refPath);
+          #define __SHOWPATH(N) cout << "Step("<find(      // place newNode as sibling of "leaf"
                   index->insert (newNode, parentRefPoint));
-          path.navigate (newLocation);
+          path.navigate (newLocation);            __SHOWPATH(5)
           Scope sibling = path.getLeaf();
           ASSERT (sibling == newLocation);
           ASSERT (parent == sibling.getParent());
@@ -350,7 +357,7 @@ namespace test    {
           ASSERT (prefix.endsAt (parent));
           ASSERT (!prefix.contains (leaf));
           ASSERT (!prefix.contains (sibling));
-          path.navigate (prefix.getLeaf());
+          path.navigate (prefix.getLeaf());       __SHOWPATH(6)
           ASSERT (path == prefix);
           
           // try to navigate to an unconnected location...

From d8c06fac1ee1a02b068df9c4889f4145b2c7f10e Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 15 Oct 2010 05:05:05 +0200
Subject: [PATCH 32/36] add diagnostic self display to QueryFocus

---
 src/proc/mobject/session/query-focus.cpp          | 15 +++++++++++++++
 src/proc/mobject/session/query-focus.hpp          |  2 ++
 src/proc/mobject/session/scope-locator.hpp        |  6 ++++--
 src/proc/mobject/session/scope.cpp                |  7 +++++++
 tests/43session.tests                             | 10 ++++++++++
 .../proc/mobject/session/query-focus-test.cpp     | 12 ++++++------
 6 files changed, 44 insertions(+), 8 deletions(-)

diff --git a/src/proc/mobject/session/query-focus.cpp b/src/proc/mobject/session/query-focus.cpp
index d79eec26a..b6bcf3bd0 100644
--- a/src/proc/mobject/session/query-focus.cpp
+++ b/src/proc/mobject/session/query-focus.cpp
@@ -24,6 +24,11 @@
 #include "proc/mobject/session/query-focus.hpp"
 #include "proc/mobject/mobject.hpp"
 
+#include 
+
+using boost::format;
+using boost::str;
+
 
 namespace mobject {
 namespace session {
@@ -155,4 +160,14 @@ namespace session {
   
   
   
+  /** diagnostic self-display based on the ScopePath */
+  QueryFocus::operator string()  const 
+  {
+    static format display("Focus(%d)--->%s");
+    return str ( display % ScopeLocator::instance().stackSize() 
+                         % string (*focus_));
+  }
+  
+  
+  
 }} // namespace mobject::session
diff --git a/src/proc/mobject/session/query-focus.hpp b/src/proc/mobject/session/query-focus.hpp
index 20f8a67d4..1fed6da2a 100644
--- a/src/proc/mobject/session/query-focus.hpp
+++ b/src/proc/mobject/session/query-focus.hpp
@@ -29,6 +29,7 @@
 #include "proc/mobject/session/scope-locator.hpp"
 
 #include 
+#include 
 
 namespace mobject {
 namespace session {
@@ -91,6 +92,7 @@ namespace session {
       
       ScopePath const& currentPath()  const;
       operator Scope()                const;
+      operator string()               const;
       
       QueryFocus&     attach (Scope const&);
       static QueryFocus push (Scope const&);
diff --git a/src/proc/mobject/session/scope-locator.hpp b/src/proc/mobject/session/scope-locator.hpp
index 4d6753967..5512f3917 100644
--- a/src/proc/mobject/session/scope-locator.hpp
+++ b/src/proc/mobject/session/scope-locator.hpp
@@ -85,6 +85,8 @@ namespace session {
       lib::IterSource::iterator
       locate (Scope const& target);
       
+      size_t stackSize()  const;
+      
       
      ~ScopeLocator();
       
@@ -143,7 +145,7 @@ namespace session {
   {
     return ScopeQuery (scope.getTop(), PATH).resolveBy (theResolver());
   }
-
-
+  
+  
 }} // namespace mobject::session
 #endif
diff --git a/src/proc/mobject/session/scope.cpp b/src/proc/mobject/session/scope.cpp
index bc2912085..30fab781c 100644
--- a/src/proc/mobject/session/scope.cpp
+++ b/src/proc/mobject/session/scope.cpp
@@ -121,6 +121,13 @@ namespace session {
   }
   
   
+  size_t
+  ScopeLocator::stackSize()  const
+  {
+    return focusStack_->size();
+  }
+  
+  
   /** establishes the \em current query focus location.
    *  Relies on the state of the QueryFocusStack.
    *  If there is no current focus location, a new
diff --git a/tests/43session.tests b/tests/43session.tests
index 5f6f8994b..ebe930800 100644
--- a/tests/43session.tests
+++ b/tests/43session.tests
@@ -221,6 +221,16 @@ END
 
 
 TEST "Query focus management" QueryFocus_test </\[DummyMO....\]
+out: Focus\(3\)--->/$
+out: Focus\(3\)--->/\[DummyMO....\]$
+out: Focus\(3\)--->/\[DummyMO....\]/\[DummyMO....\]$
+out: Focus\(3\)--->/\[DummyMO....\]/\[DummyMO....\]/\[DummyMO....\]$
+out: Focus\(3\)--->/\[DummyMO....\]/\[DummyMO....\]/\[DummyMO....\]/\[DummyMO....\]$
+out: Focus\(3\)--->/\[DummyMO....\]/\[DummyMO....\]/\[DummyMO....\]/\[DummyMO....\]/\[DummyMO....\]$
+out: Focus\(3\)--->/\[DummyMO....\]/\[DummyMO....\]/\[DummyMO....\]/\[DummyMO....\]/\[DummyMO....\]<<<--discovery exhausted
+out: Focus\(2\)--->/\[DummyMO....\]<<<--after pop
+out: Focus\(2\)--->/\[DummyMO....\]
 return: 0
 END
 
diff --git a/tests/components/proc/mobject/session/query-focus-test.cpp b/tests/components/proc/mobject/session/query-focus-test.cpp
index 769662d95..946250c37 100644
--- a/tests/components/proc/mobject/session/query-focus-test.cpp
+++ b/tests/components/proc/mobject/session/query-focus-test.cpp
@@ -137,7 +137,7 @@ namespace test    {
           ASSERT (num_refs > 1);  // because the run() function also holds a ref
           
           QueryFocus subF = QueryFocus::push();
-//        cout << string(subF) << endl;                                          /////////////////////////////////////TICKET #429  display diagnostic output
+          cout << string(subF) << endl;
           ASSERT (subF == original);
           
           ASSERT (       1 == refs(subF) );
@@ -147,25 +147,25 @@ namespace test    {
             QueryFocus subF2 = QueryFocus::push(Scope(subF).getParent());
             ASSERT (subF2 != subF);
             ASSERT (subF == original);
-//          cout << string(subF2) << endl;                                       /////////////////////////////////////TICKET #429  display diagnostic output
+            cout << string(subF2) << endl;
             
             ScopeQuery::iterator ii = subF2.explore();
             while (ii) // drill down depth first
               {
                 subF2.attach(*ii);
-//              cout << string(subF2) << endl;                                   /////////////////////////////////////TICKET #429  display diagnostic output
+                cout << string(subF2) << endl;
                 ii = subF2.explore();
               }
-//          cout << string(subF2) << "<<<--discovery exhausted" << endl;         /////////////////////////////////////TICKET #429  display diagnostic output
+            cout << string(subF2) << "<<<--discovery exhausted" << endl;
             
             subF2.pop(); // releasing this focus and re-attaching to what's on stack top
-//          cout << string(subF2) << "<<<--after pop()" << endl;                 /////////////////////////////////////TICKET #429  display diagnostic output
+            cout << string(subF2) << "<<<--after pop()" << endl;
             ASSERT (subF2 == subF);
             ASSERT (2 == refs(subF2));  // both are now attached to the same path
             ASSERT (2 == refs(subF));
           }
           // subF2 went out of scope, but no auto-pop happens (because subF is still there)
-//        cout << string(subF) << endl;                                          /////////////////////////////////////TICKET #429  display diagnostic output
+          cout << string(subF) << endl;
           
           ASSERT (       1 == refs(subF));
           ASSERT (num_refs == refs(original));

From 5f2e749ab7070443eaa1669b95cc85eea485f203 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 16 Jul 2010 03:07:05 +0200
Subject: [PATCH 33/36] (cont) output handling

---
 wiki/renderengine.html | 27 ++++++++++++++++++---------
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index be2a9b466..dfd285f32 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1731,15 +1731,20 @@ Some further details
 * a special case of this factory use is the [[Singleton]] factory, which is used a lot within the Proy-Layer code
 
-
+
a specially configured sequence list
 * all MObjects have their position, length and configuration set up ready for rendering.
 * any nested sequences (or other kinds of indirections) have been resolved.
 * every MObject is associated with an ExplicitPlacement, which declares a fixed position (Time, [[Pipe-ID|OutputDesignation]])
 * these ~ExplicitPlacements are contained in a ordered List, sometimes denoted as the //effective timeline.//
-* besides, there is a collection of all effective, possibly externally visible [[model ports|ModelPorts]]
+* besides, there is a collection of all effective, possibly externally visible [[model ports|ModelPort]]
 
-As the builder and thus render engine //only consults the fixture,// while all editing operations finally propagate to the fixture as well, we get an isolation layer between the high level part of the Proc layer (editing, object manipulation) and the render engine. Creating the Fixture can be seen as a preprocessing step to simplify the build process. For this reason, the process of [[(re)building the fixture|PlanningBuildFixture]] has been designed together with the [[Builder]]
+As the builder and thus render engine //only consults the fixture,// while all editing operations finally propagate to the fixture as well, we get an isolation layer between the high level part of the Proc layer (editing, object manipulation) and the render engine. Creating the Fixture can be seen as a preprocessing step to simplify the build process. For this reason, the process of [[(re)building the fixture|PlanningBuildFixture]] has been designed together with the [[Builder]] + +!{{red{WIP}}} Structure of the fixture +The fixture is like a grid, where one dimension is given by the [[model ports|ModelPort]], and the other dimension extends in time. The time axis is grouped in segments of constant structure. +A problem yet to be solved is how the fixture relates to the mulitude of top-level timelines, without generating a too fine grained segmentation. +
The situation focussed by this concept is when an API needs to expose a sequence of results, values or objects, instead of just yielding a function result value. As the naive solution of passing an pointer or array creates coupling to internals, it was superseded by the ~GoF [[Iterator pattern|http://en.wikipedia.org/wiki/Iterator]]. Iteration can be implemented by convention, polymorphically or by generic programming; we use the latter approach.
@@ -1863,13 +1868,13 @@ For this Lumiera design, we could consider making GOP just another raw media dat
 &rarr;see in [[Wikipedia|http://en.wikipedia.org/wiki/Group_of_pictures]]
 
-
+
Each [[Timeline]] has an associated set of global [[pipes|Pipe]] (global busses), similar to the subgroups of a sound mixing desk.
 In the typical standard configuration, there is (at least) a video master and a sound master pipe. Like any pipe, ingoing connections attach to the input side, attached effects form a chain, where the last node acts as exit node. The ~Pipe-ID of such a global bus can be used to route media streams, allowing the global pipe to act as a summation bus bar.
 
 !{{red{WIP}}} Design problem with global Pipes
 actually building up the implementation of global pipes seems to pose a rather subtle design problem: it is difficult to determine how to do it //right.//
-To start with, we need the ability to attach effects to global pipes. There is already an obvious way how to attach effects to clips (=local pipes), and thus it's desirable to do handle it the same way for global pipes. At least there should be a really good reason //not//&nbsp; to do it the same way. Thus, we're going to attach these effects by placement into the scope of another placed MObject. And, moreover, this other object should be part of the HighLevelModel's tree, to allow using the PlacementIndex as implementation. So this reasoning brings us to inventing or postulating some kind of object, without having any existing concept to justify the existence of the corresponding class or shaping its properties on itself. Which means &mdash; from a design view angle &mdash; we're entering slippery ground.
+To start with, we need the ability to attach effects to global pipes. There is already an obvious way how to attach effects to clips (=local pipes), and thus it's desirable to handle it the same way for global pipes. At least there should be a really good reason //not//&nbsp; to do it the same way. Thus, we're going to attach these effects by placement into the scope of another placed MObject. And, moreover, this other object should be part of the HighLevelModel's tree, to allow using the PlacementIndex as implementation. So this reasoning brings us to inventing or postulating some kind of object, without having any existing concept to justify the existence of the corresponding class or shaping its properties on itself. Which means &mdash; from a design view angle &mdash; we're entering slippery ground.
 !!~Model-A: dedicated object per pipe
 Just for the sake of symmetry, for each global pipe we'd attach some ~MObject as child of the BindingMO representing the timeline. If no further specific properties or functionality is required, we could use Track objects, which are generally used as containers within the model. Individual effects would then be attached as children, while output routing would be done within the attaching placement, the same way as it's done with clips or tracks in general. As the pipe asset itself already stores a StreamType reference, all we'd need is some kind of link to the pipe asset or pipe-ID, and maybe some façade functions within the binding to support the handling.
 !!~Model-B: attaching to the container
@@ -2809,7 +2814,7 @@ The operation point is provided by the current BuilderMould and used by the [[pr
 This is possible because the operation point has been provided (by the mould) with informations about the media stream type to be wired, which, together with information accessible at the the [[render node interface|ProcNode]] and from the [[referred processing assets|ProcAsset]], with the help of the [[connection manager|ConManager]] allows to figure out what's possible and how to do the desired connections. Additionally, in the course of deciding about possible connections, the PathManager is consulted to guide strategic decisions regarding the [[render node configuration|NodeConfiguration]], possible type conversions and the rendering technology to employ.
 
-
+
__6/2010__: an ever recurring (and yet unsolved) problem in the design of Luimiera's ~Proc-Layer is how to refer to output destinations, and how to organise them.
 To get ahead with this problem, I'll start collecting observations, relations and drafts on this tiddler.
 
@@ -2830,6 +2835,7 @@ To get ahead with this problem, I'll start collecting observations, relations an
 
 !Conclusions
 * there are //direct, indirect//&nbsp; and //relative//&nbsp; referrals to an output designation
+* //indirect// means to derive the destination transitively (looking at the destination's output designation and so on)
 * //relative// in this context means that we refer to "the N^^th^^ of this kind" (e.g. the second video out)
 * we need to attach some metadata with an output; especially we need an associated StreamType
 * output designation is structured into several //levels://
@@ -2842,11 +2848,11 @@ in almost every case mentioned above, the output designation is identical with t
 !!!system outputs
 System level output connections are the exception to the above rule. There is no pipe at an external port, and these externally visible connection points can appear and disappear, driven by external conditions. So the question is if system outputs shall be directly addressable at all as output designation? Rather, there should be a separate OutputManagement to handle external outputs and displays, both in windows or full screen.
 !!!consequences of mentioning an output designation
-The immediate consequence is that an connection to this designation is wired, using an appropriate [[processing pattern|ProcPatt]]. A further consequence is for the mentioned output designation to appear as exit node of the built render nodes network. (not sure if thats always the case, or only when the correponding pipe is an end point without further outgoing connections)
-
+The immediate consequence is that a [[Pipe]] with the given ID exists as an accountable entity. Only if &mdash; additionally &mdash; a suitable object within the model //claims to root this pipe,//  a connection to this designation is wired, using an appropriate [[processing pattern|ProcPatt]]. A further consequence then is for the mentioned output designation to become a possible candidate for a ModelPort, an exit node of the built render nodes network. By default, only those designations without further outgoing connections actually become active model ports (but an existing and wired pipe exit node can be promoted to a model port explicitly).
+&rarr; OutputManagement
 
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
@@ -2861,6 +2867,9 @@ From the implementation side, the only interesting exit nodes are the ones to be
 * __rendering__ happens immediately at the output of a GlobalPipe (i.e. at a [[Timeline]], which is top-level)
 * __playback__ always happens at a viewer element
 
+!Attaching and mapping of exit nodes
+[[Output designations|OutputDesignation]] are created by using a [[Pipe]]-ID and by some object //claiming to root this pipe.// All of this is figured out dynamically, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when such a timeline is connected to a viewer, an output network is built to hook up exit points to the viewer component. Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting connections at the "first of each kind".
+
 !Connection to external outputs
 External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
 

From 9cb028a2615988293a8832af2d44541cd71984ad Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 17 Jul 2010 05:34:05 +0200
Subject: [PATCH 34/36] (cont) output handling

---
 wiki/renderengine.html | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index dfd285f32..6a21f209a 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -2852,7 +2852,7 @@ The immediate consequence is that a [[Pipe]] with the given ID exists as an acco
 &rarr; OutputManagement
 
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
@@ -2868,11 +2868,13 @@ From the implementation side, the only interesting exit nodes are the ones to be
 * __playback__ always happens at a viewer element
 
 !Attaching and mapping of exit nodes
-[[Output designations|OutputDesignation]] are created by using a [[Pipe]]-ID and by some object //claiming to root this pipe.// All of this is figured out dynamically, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when such a timeline is connected to a viewer, an output network is built to hook up exit points to the viewer component. Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting connections at the "first of each kind".
+[[Output designations|OutputDesignation]] are created by using a [[Pipe]]-ID and by some object //claiming to root this pipe.// All of this is figured out dynamically while building the render network, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an //output network is built,// to allow hooking exit points to the viewer component. Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting connections at the "first of each kind".
 
 !Connection to external outputs
 External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
 
+We should note that in both cases, this mapping operation is controlled and driven by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format, again with fixed and pre-settled channel configuration (when configurting a render process, it might be necessary to account for //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
+
 Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &mdash; any slot implementation provides a way to handle timeouts gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
 &rarr; see also the PlayerDummy
 
@@ -4362,8 +4364,8 @@ __see also__ &rarr; the protocol [[how to operate the nodes|NodeOperationProtocol]]
-
-
For each segment (of the effective timeline), there is a Processor holding the exit node(s) of a processing network, which is a "Directed Acyclic Graph" of small, preconfigured, stateless [[processing nodes|ProcNode]]. This network is operated according to the ''pull principle'', meaning that the rendering is just initiated by "pulling" output from the exit node, causing a cascade of recursive downcalls. Each node knows its predecessor(s) an can pull the necessary input from there. Consequently, there is no centralized "engine object" which may invoke nodes iteratively or table driven &mdash; rather, the rendering can be seen as a passive service provided for the backend, which may pull from the exit nodes at any time, in any order (?), and possibly multithreaded.
+
+
For each segment (of the effective timeline), there is a Processor holding the exit node(s) of a processing network, which is a "Directed Acyclic Graph" of small, preconfigured, stateless [[processing nodes|ProcNode]]. This network is operated according to the ''pull principle'', meaning that the rendering is just initiated by "pulling" output from the exit node, causing a cascade of recursive downcalls or prerequisite calculations to be scheduled as separate jobs. Each node knows its predecessor(s), thus the necessary input can be pulled from there. Consequently, there is no centralized "engine object" which may invoke nodes iteratively or table driven &mdash; rather, the rendering can be seen as a passive service provided for the backend, which may pull from the exit nodes at any time, in any order (?), and possibly multithreaded.
 All State necessary for a given calculation process is encapsulated and accessible by a StateProxy object, which can be seen as the representation of "the process". At the same time, this proxy provides the buffers holding data to be processed and acts as a gateway to the backend to handle the communication with the Cache. In addition to this //top-level State,// each calculation step includes a small [[state adapter object|StateAdapter]] (stack allocated), which is pre-configured by the builder and serves the purpose to isolate the processing function from the detals of buffer management.
 
 

From 02c383d1ddc808e913915a4ae805f79ff1a6ac68 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 19 Jul 2010 05:21:05 +0200
Subject: [PATCH 35/36] Deciding on the implementation of global pipes. Channel
 mapping for virtual clips

---
 wiki/renderengine.html | 41 ++++++++++++++++++++++++++++++-----------
 1 file changed, 30 insertions(+), 11 deletions(-)

diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 6a21f209a..52ea102d9 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1868,17 +1868,29 @@ For this Lumiera design, we could consider making GOP just another raw media dat
 &rarr;see in [[Wikipedia|http://en.wikipedia.org/wiki/Group_of_pictures]]
 
-
+
Each [[Timeline]] has an associated set of global [[pipes|Pipe]] (global busses), similar to the subgroups of a sound mixing desk.
 In the typical standard configuration, there is (at least) a video master and a sound master pipe. Like any pipe, ingoing connections attach to the input side, attached effects form a chain, where the last node acts as exit node. The ~Pipe-ID of such a global bus can be used to route media streams, allowing the global pipe to act as a summation bus bar.
 
 !{{red{WIP}}} Design problem with global Pipes
 actually building up the implementation of global pipes seems to pose a rather subtle design problem: it is difficult to determine how to do it //right.//
-To start with, we need the ability to attach effects to global pipes. There is already an obvious way how to attach effects to clips (=local pipes), and thus it's desirable to handle it the same way for global pipes. At least there should be a really good reason //not//&nbsp; to do it the same way. Thus, we're going to attach these effects by placement into the scope of another placed MObject. And, moreover, this other object should be part of the HighLevelModel's tree, to allow using the PlacementIndex as implementation. So this reasoning brings us to inventing or postulating some kind of object, without having any existing concept to justify the existence of the corresponding class or shaping its properties on itself. Which means &mdash; from a design view angle &mdash; we're entering slippery ground.
-!!~Model-A: dedicated object per pipe
-Just for the sake of symmetry, for each global pipe we'd attach some ~MObject as child of the BindingMO representing the timeline. If no further specific properties or functionality is required, we could use Track objects, which are generally used as containers within the model. Individual effects would then be attached as children, while output routing would be done within the attaching placement, the same way as it's done with clips or tracks in general. As the pipe asset itself already stores a StreamType reference, all we'd need is some kind of link to the pipe asset or pipe-ID, and maybe some façade functions within the binding to support the handling.
-!!~Model-B: attaching to the container
-Acknowledging the missing justification, we could instead use //something to attach// &mdash; and actually handle the real association elsewhere. The obvious "something" in this case would be the BindingMO, which already acts as implementation of the timeline (which is a façade asset). Thus, for this approach, the bus-level effects would be attached as direct children of the {{{Placement<BindingMO>}}}, just for the sake of beeing attached and stored within the session, with an additional convention for the actual ordering and association to a specific pipe. The Builder then would rather query the ~BindingMO to discover and build up the implementation of the global pipes in terms of the render nodes.
+To start with, we need the ability to attach effects to global pipes. There is already an obvious way how to attach effects to clips (=local pipes), and thus it's desirable to handle it the same way for global pipes. At least there should be a really good reason //not//&nbsp; to do it the same way. Thus, we're going to attach these effects by placement into the scope of another placed MObject. And, moreover, this other object should be part of the HighLevelModel's tree, to allow using the PlacementIndex as implementation. So this reasoning brings us to re-using or postulating some kind of object, while lacking a point or reference //outside this design considerations//&nbsp; to justify the existence of the corresponding class or shaping its properties on itself. Which means &mdash; from a design view angle &mdash; we're entering slippery ground.
+!!!~Model-A: dedicated object per pipe
+Just for the sake of symmetry, for each global pipe we'd attach some suitable ~MObject as child of the BindingMO representing the timeline. If no further specific properties or functionality is required, we could use Track objects, which are generally used as containers within the model. Individual effects would then be attached as children, while output routing could be specified within the attaching placement, the same way as it's done with clips or tracks in general. As the pipe asset itself already stores a StreamType reference, all we'd need is some kind of link to the pipe asset or pipe-ID, and maybe façade functions within the binding to support the handling.
+!!!~Model-B: attaching to the container
+Acknowledging the missing justification, we could instead use //just something to attach// &mdash; and actually handle the real association elsewhere. The obvious "something" in this case would be the BindingMO, which already acts as implementation of the timeline (which is a façade asset). Thus, for this approach, the bus-level effects would be attached as direct children of the {{{Placement<BindingMO>}}}, just for the sake of beeing attached and stored within the session, with an additional convention for the actual ordering and association to a specific pipe. The Builder then would rather query the ~BindingMO to discover and build up the implementation of the global pipes in terms of the render nodes.
+
+!!!Comparision
+While there might still be some compromises or combined solutions &mdash; to support the decision, the following table detailes the handling in each case
+|>|   !~Model-B|!~Model-A |
+|Association: | by plug in placement|by scope |
+|Output:      | entry in routing table|by plug in placement |
+|Ordering:    |>| stored in placement |
+|Building:    | sort children by plug+order<br/>query output from routing|build like a clip |
+|Extras:      | ? |tree-like bus arrangement,<br/> multi-stream bus |
+
+So through this detailed comparison ''~Model-A looks favourable'': while the other model requires us to invent a good deal of the handling specifically for the global pipes, the former can be combined from patterns and solutions already used in other parts of the model, plus it allows some interesting extensions. 
+On a second thought, the fact that the Bus-~MObject is rather void of any specific meaning doesn't weight so much: As the Builder is based on the visitor pattern, the individual objects can be seen as //algebraic data types.// Besides, there is at least one little bit of specific functionality: a Bus object actually needs to //claim//&nbsp; to be the OutputDesignation, by referring to the same ~Pipe-ID used in other parts of the model to request output routing to this Bus. Without this match on both ends, an ~OutputDesignation may be mentioned at will, but no connection whatsoever will happen.
 
@@ -2814,7 +2826,7 @@ The operation point is provided by the current BuilderMould and used by the [[pr This is possible because the operation point has been provided (by the mould) with informations about the media stream type to be wired, which, together with information accessible at the the [[render node interface|ProcNode]] and from the [[referred processing assets|ProcAsset]], with the help of the [[connection manager|ConManager]] allows to figure out what's possible and how to do the desired connections. Additionally, in the course of deciding about possible connections, the PathManager is consulted to guide strategic decisions regarding the [[render node configuration|NodeConfiguration]], possible type conversions and the rendering technology to employ.
-
+
__6/2010__: an ever recurring (and yet unsolved) problem in the design of Luimiera's ~Proc-Layer is how to refer to output destinations, and how to organise them.
 To get ahead with this problem, I'll start collecting observations, relations and drafts on this tiddler.
 
@@ -2850,9 +2862,16 @@ System level output connections are the exception to the above rule. There is no
 !!!consequences of mentioning an output designation
 The immediate consequence is that a [[Pipe]] with the given ID exists as an accountable entity. Only if &mdash; additionally &mdash; a suitable object within the model //claims to root this pipe,//  a connection to this designation is wired, using an appropriate [[processing pattern|ProcPatt]]. A further consequence then is for the mentioned output designation to become a possible candidate for a ModelPort, an exit node of the built render nodes network. By default, only those designations without further outgoing connections actually become active model ports (but an existing and wired pipe exit node can be promoted to a model port explicitly).
 &rarr; OutputManagement
+
+!Mapping of output designations
+An entire sequence can be embedded within another sequence as a VirtualClip. While for a simple clip there is a Clip-~MObject placed into the model, holding an reference to a media asset, in case of a virtual clip an BindingMO takes on the role of the clip object. Note that BindingMO also acts as [[Timeline]] implementation &mdash; indeed the same sequence might be bound simultaneously as a timeline and into a virtual clip. This flexibility creates a special twist, as the contents of this sequence have no way of finding out if they're used as timeline or embedded virtual clip. So parts of this sequence might mention a OutputDesignation which, when using the sequence as virtual clip, needs to be translated into a virtual media channel, which can be treated the same way as any channel (video, audio,...) from a real media. Especially, a new output designation has to be derived in a sensible way, which largely depends on how the original output designation has been specified
+* a direct output designation specicfiation from within the sequence is captured and translated into a summation pipe at a position corresponding to the global pipes when using the same sequence as timeline. The output of this summation pipe is treated like a media channel
+** now, if a distinct output designation can be determined at this placement of the virtual clip, it will be used for the further routing
+** otherwise, the routing will be done as if the original output designation was given at this placement of the virtual clip.
+* a relative output designation (the N^^th^^ channel of this kind) is just carried over to the enclosing scope.
 
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
@@ -2868,13 +2887,13 @@ From the implementation side, the only interesting exit nodes are the ones to be
 * __playback__ always happens at a viewer element
 
 !Attaching and mapping of exit nodes
-[[Output designations|OutputDesignation]] are created by using a [[Pipe]]-ID and by some object //claiming to root this pipe.// All of this is figured out dynamically while building the render network, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an //output network is built,// to allow hooking exit points to the viewer component. Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting connections at the "first of each kind".
+[[Output designations|OutputDesignation]] are created by using a [[Pipe]]-ID and &mdash; at the same time &mdash; by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an //output network is built,// to allow hooking exit points to the viewer component. Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting connections at the "first of each kind".
+
+We should note that in both cases this mapping operation is controlled and driven by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format, again with fixed and pre-settled channel configuration (when configurting a render process, it might be necessary to account for //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
 
 !Connection to external outputs
 External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
 
-We should note that in both cases, this mapping operation is controlled and driven by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format, again with fixed and pre-settled channel configuration (when configurting a render process, it might be necessary to account for //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
-
 Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &mdash; any slot implementation provides a way to handle timeouts gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
 &rarr; see also the PlayerDummy
 
From b7b9a9e79dc2092c604269581168c03f926c28a0 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 21 Jul 2010 05:30:02 +0200 Subject: [PATCH 36/36] (cont) expanding on how to use output designations --- wiki/renderengine.html | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 52ea102d9..d016c787a 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -2826,7 +2826,7 @@ The operation point is provided by the current BuilderMould and used by the [[pr This is possible because the operation point has been provided (by the mould) with informations about the media stream type to be wired, which, together with information accessible at the the [[render node interface|ProcNode]] and from the [[referred processing assets|ProcAsset]], with the help of the [[connection manager|ConManager]] allows to figure out what's possible and how to do the desired connections. Additionally, in the course of deciding about possible connections, the PathManager is consulted to guide strategic decisions regarding the [[render node configuration|NodeConfiguration]], possible type conversions and the rendering technology to employ.
-
+
__6/2010__: an ever recurring (and yet unsolved) problem in the design of Luimiera's ~Proc-Layer is how to refer to output destinations, and how to organise them.
 To get ahead with this problem, I'll start collecting observations, relations and drafts on this tiddler.
 
@@ -2869,6 +2869,12 @@ An entire sequence can be embedded within another sequence as a VirtualClip. Whi
 ** now, if a distinct output designation can be determined at this placement of the virtual clip, it will be used for the further routing
 ** otherwise, the routing will be done as if the original output designation was given at this placement of the virtual clip.
 * a relative output designation (the N^^th^^ channel of this kind) is just carried over to the enclosing scope.
+
+!Using output designations
+While actually data frames are //pulled,// on a conceptual level data is assumed to "flow" through the pipes from source to output. This conceptual ("high level") model of data flow is comprised of the pipes, which are rather rigid, and flexible interconnections. The purpose of an output designation is to specify where the data should go, especially through these flexible interconnections. Thus, when reaching the exit point of a pipe, an output designation will be //queried.// Finding a suitable output designation involves two parts:
+* first of all, we need to know //what to route// &mdash; kind of the traits of the data. This is given by the //current pipe.//
+* then we'll need to fine an output designation //suitable for this data.// This is given by a "Plug" (WiringRequest) in the placement, and may be derived.
+As both of these specifications are given by [[Pipe]]-IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type (e.g. and audio output when the pipe currently in question deals with video) is irrelevant.
 
@@ -3486,7 +3492,7 @@ The GUI can connect the viewer(s) to some pipe (and moreover can use [[probe poi
-
+
!Identification
 Pipes are distinct objects and can be identified by their asset ~IDs. Besides, as for all [[structural assets|StructAsset]] there are extended query capabilities, including a symbolic pipe-id and a media (stream) type id. Any pipe can accept and deliver exactly one media stream kind (which may be inherently structured though, e.g. spatial sound systems or stereoscopic video)
 
@@ -3497,7 +3503,7 @@ Pipe assets are created automatically by being used and referred. Each [[Timelin
 Deleting a Pipe is an advanced operation, because it includes finding and "detaching" all references, otherwise the pipe will leap back into existence immediately. Thus, global pipe entries in the Session and pipe references in [[locating pins|LocatingPin]] within any placement have to be removed, while clips using a given source port will be disabled. {{red{todo: implementation deferred}}}
 
 !using Pipes
-there is not much you can do directly with a pipe asset. It is an point of reference, after all. Any connection to some pipe is only temporarily done by a placement in some part of the timeline, so it isn't stored with the pipe. You can edit the (user visible) description an you can globally disable a pipe asset. The pipe's ID and media stream type of course are fixed, because any connection and referral (via the asset ID) is based on them. Later on, we should provide a {{{rewire(oldPipe, newPipe)}}} to search any ref to the {{{oldPipe}}} and try to rewrite it to use the {{{newPipe}}}, possibly with a new media stream type.
+there is not much you can do directly with a pipe asset. It is an point of reference, after all. Any connection to some pipe is only temporarily done by a placement in some part of the timeline (&rarr; OutputDesignation), so it isn't stored with the pipe. You can edit the (user visible) description an you can globally disable a pipe asset. The pipe's ID and media stream type of course are fixed, because any connection and referral (via the asset ID) is based on them. Later on, we should provide a {{{rewire(oldPipe, newPipe)}}} to search any ref to the {{{oldPipe}}} and try to rewrite it to use the {{{newPipe}}}, possibly with a new media stream type.
 Pipes are integrated with the [[management of defaults|DefaultsManagement]]. For example, any pipe implicitly uses some [[processing pattern|ProcPatt]] &mdash; it may default to the empty pattern. This way, any kind of standard wiring might be applied to the pipes (e.g a fader for audio, similar to the classic mixing consoles). This //is // a global property of the pipe, but &mdash; contrary to the stream type &mdash; this pattern may be switched {{red{really?}}}