From 4594ddde3aefb41fd543c2b2c9f0752f4a2a3865 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 1 Mar 2010 05:36:18 +0100 Subject: [PATCH] WIP: test driven brainstorming regarding sequence/timeline handling --- src/common/query/fake-configrules.cpp | 10 +- src/common/query/fake-configrules.hpp | 28 +++--- src/proc/asset/pipe.hpp | 24 ++--- src/proc/asset/sequence.cpp | 1 + src/proc/asset/timeline.cpp | 1 + .../session/session-structure-test.cpp | 19 ++-- .../timeline-sequence-handling-test.cpp | 91 ++++++++++++------- wiki/renderengine.html | 4 +- 8 files changed, 103 insertions(+), 75 deletions(-) diff --git a/src/common/query/fake-configrules.cpp b/src/common/query/fake-configrules.cpp index 5b6c9c23f..8a7ece449 100644 --- a/src/common/query/fake-configrules.cpp +++ b/src/common/query/fake-configrules.cpp @@ -101,6 +101,8 @@ namespace lumiera { answer_->insert (entry_Struct ("pipe(master), stream(video)")); item (answer_, "") = item(answer_,"pipe(master), stream(video)");// use as default + + answer_->insert (entry_Struct ("pipe(ambiance)")); } @@ -108,7 +110,7 @@ namespace lumiera { /* under some circumstances we need to emulate the behaviour * * of a real resolution engine in a more detailed manner. * - * These case are hard wired in code below */ + * The code below implements these cases hard wired. */ /** special case: create a new pipe with matching pipe and stream IDs on the fly when referred... */ bool @@ -130,14 +132,14 @@ namespace lumiera { answer_->insert (entry (q, newPipe)); return true; } - /** special case: create/retrieve new rocessing pattern for given stream ID... */ + /** special case: create/retrieve new processing pattern for given stream ID... */ bool - MockTable::fabricate_ProcPatt_on_demand (Query& q, string const& streamID) + MockTable::fabricate_ProcPatt_on_demand (Query& q) { typedef const ProcPatt cPP; typedef WrapReturn::Wrapper Ptr; - Ptr newPP (Struct::create (Query ("make(PP), "+q))); + Ptr newPP (Struct::create (Query ("make(PP), "+q))); // magic token: bail out and invoke factory for new object answer_->insert (entry (q, newPP)); return true; } diff --git a/src/common/query/fake-configrules.hpp b/src/common/query/fake-configrules.hpp index 7bba3430d..e5f55a27b 100644 --- a/src/common/query/fake-configrules.hpp +++ b/src/common/query/fake-configrules.hpp @@ -49,12 +49,9 @@ -namespace lumiera - { - - - namespace query - { +namespace lumiera { + namespace query { + using asset::Pipe; using asset::ProcPatt; using asset::PProcPatt; @@ -69,8 +66,7 @@ namespace lumiera - namespace // internal details - { + namespace { // internal details /** a traits-class to define the smart-ptr to wrap the result */ template @@ -81,16 +77,18 @@ namespace lumiera /** helper detecting if a query actually intended to retrieve a "default" object. - * This implementation is quite crude, of cours it would be necessary to actually + * This implementation is quite crude, of course it would be necessary actually to * parse and evaluate the query. @note query is modified if "default" ... */ inline bool is_defaults_query (string& query) { return !isnil (removeTerm ("default", query)); } + } // details (end) + /** * the actual table holding preconfigured answers * packaged as boost::any objects. @@ -112,7 +110,7 @@ namespace lumiera bool detect_case (typename WrapReturn::Wrapper&, Query& q); bool fabricate_matching_new_Pipe (Query& q, string const& pipeID, string const& streamID); bool fabricate_just_new_Pipe (Query& q); - bool fabricate_ProcPatt_on_demand (Query& q, string const& streamID); + bool fabricate_ProcPatt_on_demand (Query& q); template bool set_new_mock_solution (Query& q, typename WrapReturn::Wrapper& candidate); @@ -184,7 +182,7 @@ namespace lumiera if (!isnil (extractID("make", q))) return false; // let the query fail here, // so the invoking factory will go ahead - // and create a new object. + // and create a new object. (prevents infinite recursion) const string pipeID = extractID("pipe", q); const string streamID = extractID("stream", q); @@ -210,7 +208,7 @@ namespace lumiera const string streamID = extractID("stream", q); if (!candidate && !isnil(streamID)) - return fabricate_ProcPatt_on_demand (q, streamID); + return fabricate_ProcPatt_on_demand (q); q.clear(); return false; @@ -240,13 +238,11 @@ namespace lumiera public: - // TODO: implementation of any additional functions on the ConfigRules inteface goes here + // TODO: implementation of any additional functions on the ConfigRules interface goes here }; - } // namespace query - -} // namespace lumiera +}} // namespace lumiera::query #endif diff --git a/src/proc/asset/pipe.hpp b/src/proc/asset/pipe.hpp index 3fde3c44c..25098eb22 100644 --- a/src/proc/asset/pipe.hpp +++ b/src/proc/asset/pipe.hpp @@ -29,8 +29,8 @@ -namespace asset - { +namespace asset { + using lumiera::P; class Pipe; @@ -44,9 +44,9 @@ namespace asset ID (size_t id); ID (const Pipe&); }; - - - + + + /** * structural asset corresponding to some * processing pipe for generating media output @@ -64,13 +64,13 @@ namespace asset { return static_cast& > (Asset::getID()); } - + protected: Pipe (const Asset::Ident&, PProcPatt& wiring, const string& pipeID, wstring shortName =wstring(), wstring longName =wstring()) ; friend class StructFactory; friend class StructFactoryImpl; - + public: const string& getPipeID() const { return pipeID_; } const PProcPatt& getProcPatt() const { return wiringTemplate; } @@ -87,10 +87,10 @@ namespace asset // inline ID::ID(size_t id) : ID (id) {}; inline ID::ID(const Pipe& pipe) : ID (pipe.getID()) {}; - - - - - + + + + + } // namespace asset #endif diff --git a/src/proc/asset/sequence.cpp b/src/proc/asset/sequence.cpp index 537362df7..9b6cd8d9f 100644 --- a/src/proc/asset/sequence.cpp +++ b/src/proc/asset/sequence.cpp @@ -33,6 +33,7 @@ namespace asset { /** create an empty default configured Sequence */ Sequence::Sequence (const Asset::Ident& idi) + : Struct (idi) // : track (makeDefaultTrack ()) // , clips (0) { diff --git a/src/proc/asset/timeline.cpp b/src/proc/asset/timeline.cpp index 5ef5d6229..733d9502b 100644 --- a/src/proc/asset/timeline.cpp +++ b/src/proc/asset/timeline.cpp @@ -33,6 +33,7 @@ namespace asset { /** TODO??? */ Timeline::Timeline (const Asset::Ident& idi) + : Struct (idi) // : track (makeDefaultTrack ()) // , clips (0) { diff --git a/tests/components/proc/mobject/session/session-structure-test.cpp b/tests/components/proc/mobject/session/session-structure-test.cpp index abbb51087..89665c476 100644 --- a/tests/components/proc/mobject/session/session-structure-test.cpp +++ b/tests/components/proc/mobject/session/session-structure-test.cpp @@ -24,7 +24,9 @@ #include "lib/test/run.hpp" #include "proc/mobject/session.hpp" #include "proc/mobject/session/fixture.hpp" // TODO only temporarily needed -#include "proc/assetmanager.hpp" +#include "proc/assetmanager.hpp" //////?? +#include "proc/asset/timeline.hpp" +#include "proc/asset/sequence.hpp" #include "lib/lumitime.hpp" #include "lib/util.hpp" @@ -42,6 +44,9 @@ namespace test { using proc_interface::AssetManager; using proc_interface::PAsset; + using asset::PTimeline; + using asset::PSequence; + using lumiera::Time; @@ -88,23 +93,23 @@ namespace test { UNIMPLEMENTED ("how to refer to tracks..."); ASSERT (0 < sess->timelines.size()); - Timeline& til = sess->timelines[0]; + PTimeline til = sess->timelines[0]; ASSERT (0 < sess->sequences.size()); - Sequence& seq = sess->sequences[0]; + PSequence seq = sess->sequences[0]; - ASSERT (isSameObject (seq, til.getSequence())); + ASSERT (isSameObject (seq, til->getSequence())); //verify default timeline - Axis& axis = til.getAxis(); + Axis& axis = til->getAxis(); ASSERT (Time(0) == axis.origin()); - ASSERT (Time(0) == til.length()); ////////////////////////TICKET #177 + ASSERT (Time(0) == til->length()); ////////////////////////TICKET #177 //verify global pipes //TODO //verify default sequence - Track rootTrack = seq.rootTrack(); + Track rootTrack = seq->rootTrack(); ASSERT (rootTrack->isValid()); ASSERT (Time(0) == rootTrack->length()); ASSERT (0 == rootTrack->subTracks.size()); diff --git a/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp b/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp index c54b3e369..77f33379d 100644 --- a/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp +++ b/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp @@ -24,8 +24,12 @@ #include "lib/test/run.hpp" #include "proc/mobject/session.hpp" //#include "proc/mobject/session/fixture.hpp" // TODO only temporarily needed -#include "proc/assetmanager.hpp" +#include "proc/assetmanager.hpp" ///?? +#include "proc/asset/timeline.hpp" +#include "proc/asset/sequence.hpp" +#include "proc/asset/pipe.hpp" #include "lib/lumitime.hpp" +#include "lib/query.hpp" #include "lib/util.hpp" #include @@ -42,13 +46,25 @@ namespace test { using proc_interface::AssetManager; using proc_interface::PAsset; + using asset::PTimeline; + using asset::PSequence; + using asset::Pipe; + + using lumiera::Query; using lumiera::Time; /******************************************************************************** * @test verify retrieval and instance management of the top level facade objects - * as integrated with the session and high-level model. - * + * as integrated with the session and high-level model. Both sequences and + * timelines are at the same time structural assets and act as facades + * on the session API. Thus we can query specific instances from the + * struct factory or alternatively access them through the session. + * Moreover we can create new top level elements in the session + * just by querying the respective asset. + * + * @todo specify how deletion is handled + * * @see session-structure-test.cpp * @see Timeline * @see Sequence @@ -63,52 +79,57 @@ namespace test { ASSERT (Session::current.isUp()); verify_retrieval(); + verify_creation(); } void verify_retrieval() { + PSess sess = Session::current; + ASSERT (sess->isValid()); + ASSERT (0 < sess->timelines.size()); + PTimeline defaultTimeline = sess->defaults (Query ()); //////////////////////TICKET #549 + Query query1 = "id("+defaultTimeline->getNameID()+")."; + + PTimeline queriedTimeline = asset::Struct::create (query1); + ASSERT (queriedTimeline); + ASSERT (queriedTimeline == defaultTimeline); // retrieved the existing timeline asset again + ASSERT (queriedTimeline == sess->timelines[0]); + + Query query2 = "id("+defaultTimeline->getSequence()->getNameID()+")."; + PSequence queriedSequence = asset::Struct::create (query2); + ASSERT (queriedSequence); + ASSERT (queriedSequence == sess->sequences[0]); + ASSERT (queriedSequence == sess->timelines[0]->getSequence()); + ASSERT (queriedSequence == defaultTimeline->getSequence()); + } + + + void + verify_creation() + { PSess sess = Session::current; ASSERT (sess->isValid()); + uint num_timelines = sess->timelines.size(); + ASSERT (0 < num_timelines); - ASSERT (0 < sess->timelines.size()); - Timeline& til = sess->timelines[0]; + Query special = "id(aSillyName), sequence(" + + sess->sequences[0]->getNameID() + + "), pipe(ambiance)."; - ASSERT (0 < sess->sequences.size()); - Sequence& seq = sess->sequences[0]; + PTimeline specialTimeline (asset::Struct::create (special)); + ASSERT (specialTimeline); + ASSERT (num_timelines + 1 == sess->timelines.size()); + ASSERT (specialTimeline == session->timelines[num_timelines]); // new one got appended at the end - ASSERT (isSameObject (seq, til.getSequence())); + // verify the properties + ASSERT (specialTimeline->getSequence() == sess->sequences[0]); // the already existing sequence got bound into that timeline too + ASSERT (contains (specialTimeline->pipes, Pipe::query("pipe(ambiance)"))); - //verify default timeline - Axis& axis = til.getAxis(); - ASSERT (Time(0) == axis.origin()); - ASSERT (Time(0) == til.length()); ////////////////////////TICKET #177 - - //verify global pipes - //TODO - - //verify default sequence - Track rootTrack = seq.rootTrack(); - ASSERT (rootTrack->isValid()); - ASSERT (Time(0) == rootTrack->length()); - ASSERT (0 == rootTrack->subTracks.size()); - ASSERT (0 == rootTrack->clips.size()); - //TODO verify the output slots of the sequence - - //TODO now use the generic query API to discover the same structure. - ASSERT (til == *(sess->all())); - ASSERT (seq == *(sess->all())); - ASSERT (rootTrack == *(sess->all())); - ASSERT (! sess->all()); - - QueryFocus& focus = sess->focus(); - ASSERT (rootTrack == focus.getObject()); - focus.navigate (til); - ASSERT (til.getBinding() == focus.getObject()); - ASSERT (rootTrack == *(focus.children())); + ASSERT (specialTimeline.use_count() == 3); // we, the AssetManager and the session } }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index c3caf1a71..c34dce5d8 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -5794,7 +5794,7 @@ function addKeyDownHandlers(e)
http://tiddlywiki.com/
-
+
Timeline is the top level element within the [[Session (Project)|Session]]. It is visible within a //timeline view// in the GUI and represents the effective (resulting) arrangement of media objects, to be rendered for output or viewed in a Monitor (viewer window). A timeline is comprised of:
 * a time axis in abolute time ({{red{WIP 1/10}}}: not clear if this is an entity or just a conceptual definition) 
 * a PlayControler
@@ -5808,6 +5808,8 @@ Within the Project, there may be ''multiple timelines'', to be viewed and render
 
 !Façade and implementation
 Actually, Timeline is both an interface and acts as façade. Its an interface, because we'll need "timeline views" ({{red{really? is that a reason to create a hierarchy right here, or shouldn't that be rather conceptual?}}}. It is a façade to the raw structures in the model, in this case a {{{Placement<BindingMO>}}} attached immediately below the [[root scope|ModelRootMO]]. The implementation of the timeline(s) is maintained as StructAsset {{red{Really? or should this be a ~MetaAsset??}}} within the AssetManager, managed by shared-ptr.
+
+Besides building on the asset management, implementing Timeline (and Sequence) as StructAsset yields another benefit: ~StructAssets can be retrieved by query, allowing to specify more details of the configuration immediately on creation. //But on the short term, this approach causes problems:// there is no real inference engine integrated into Lumiera yet (as of 2/2010 the plan is to get an early alpha working end to end first). For now we're bound to use the {{{fake-configrules}}} and to rely on a hard wired simulation of the intended behaviour of a real query resolution. Just some special magic queries will work for now, but that's enough to get ahead.