User:BenMcDonald
BenMcDonald (Talk | contribs) |
m (Reverted edits by Ebybymic (Talk); changed back to last version by BenMcDonald) |
||
(20 intermediate revisions by 2 users not shown) | |||
Line 2: | Line 2: | ||
− | I am doing my design study for my COSC426:Augmented Reality project. I need a design for a boxing skills trainer. No code | + | I am doing my design study for my COSC426:Augmented Reality project. I need a design for a boxing skills trainer. No code was written in the beginning or any design done. I have made a sketch of the requirements for the design of the software. The project is in C++ using the libraries open scene graph (osg), open scene graph augmented reality tool-kit(osgART) and the Wii controller library for windows |
Requirements: | Requirements: | ||
Line 10: | Line 10: | ||
** Bobbing and weaving | ** Bobbing and weaving | ||
** Timing of punches | ** Timing of punches | ||
− | * Each skill needs to be | + | * Each skill needs to be modeled and the evaluation method for each skill also needs to be modeled. |
* The performance on the skills needs to be evaluated and for additional activities to be recommended to the athlete. | * The performance on the skills needs to be evaluated and for additional activities to be recommended to the athlete. | ||
* An athlete’s evaluation history, progress and general details to be recorded and recoverable. | * An athlete’s evaluation history, progress and general details to be recorded and recoverable. | ||
+ | * Measurements from the augmented reality marker need to update the position of the athlete's head. | ||
+ | * WiiMote gestures need to be detected and reacted to. | ||
+ | |||
+ | My design is below. Any comments or suggestions are very welcome. | ||
+ | |||
+ | Src: - [[Image:BTM ARSport Src.zip]] | ||
+ | |||
+ | == Iteration Three == | ||
+ | === Style=== | ||
+ | {| | ||
+ | |- | ||
+ | | '''Standard'''|| '''Code''' || '''UML''' | ||
+ | |- | ||
+ | | Immutable attributes accessors are named after the attributes as is the C++ standard || ''Object foo_; virtual const Object& foo() const;'' || foo, foo() | ||
+ | |- | ||
+ | | Object queries end with the keyword 'const' || ''virtual const std::string& name() const; ''|| name():String const | ||
+ | |- | ||
+ | | Method inputs begin with the keyword 'const' || ''virtual void foo(const string &in, string *out); ''|| foo(const String in):String | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | [[Image:BenM's UMLI3.png|border|1000px]] | ||
+ | === Objects === | ||
+ | ==== UserBoxer ==== | ||
+ | The UserBoxer interacts with the WiiRemote library. Gestures are generated from the Wii remote data and these gestures change the UserBoxer's Pose. The UserBoxer is also updated from the TrainingSystem object about the user's height and head orientation from the ARtoolkit marker. Changes in UserBoxer's Pose are sent to the Trainer. | ||
+ | |||
+ | ==== Trainer==== | ||
+ | The Trainer observers the UserBoxer's Pose. On updates the Trainer reacts to the UserBoxer's Pose change depending on the state pattern, .train(const athleteHistory, TrainerPose). Changes in the Trainer's Pose get sent to the TrainerAnimation. | ||
+ | |||
+ | ==== TrainerAnimation ==== | ||
+ | The TrainerAnimation observes the Trainer's Pose and updates the animations according to the data in the Pose. The TrainerAnimation is added as a dynamic data structure to the osg node tree. TrainerAnimation acts independently to osg and changes in its animation are represented in the rendering. | ||
+ | |||
+ | ==== BoxingEvaluation ==== | ||
+ | The trainer has no knowlege about different TrainingActivities, only BoxingEvaluation does. BoxingEvaluation tells Trainer what TrainingActivity to use with the getRecommendedActivity() method. When a TrainingActivity .isComplete() then a Trainer asks the BoxingEvaluation for a new one. | ||
+ | ===Maxims=== | ||
+ | * [[Command query separation]] / [[Avoid side effects]] - The interface would be tidier if the getFeedback() method in the Trainer object also cleared the feedback but doing this would violate CQS. By have two methods, getFeedback() and clearFeedback(), there are no side effects and the status of the object can be queried without invoking any commands. Additionally, I could add a getAndClearFeedback() method for convenience. One plus of C++ is that you can mark queries, and enforce them, with the const keyword. | ||
+ | * [[Design by contract]] - Using assert's to enforce conditions at the moment. Design by contract has not been implemented wholeheartedly because of time constraints. Ideally, methods checking [[class invariants]] could be added. The UserBoxerPose class could have a method checkState() that tests for a correct head height range between public methods calls. | ||
+ | * [[Don't expose mutable attributes]] - In C++, objects returned from another object's method can be marked as const to make them immutable. | ||
+ | * [[Object Encapsulation]] - Methods are ether 'protected' or 'public' and are marked 'virtual' and attributes are 'protected'. Object encapsulation is more intuitive. Class encapsulation does not allow for effective unit testing through inheritance. | ||
+ | * [[Getters and setters]] - Tried to model behaviour in objects but could not avoid a long get/set list on my data objects when I seperated them out with the observer pattern -- Pose, BoxingPose and UserBoxerPose. | ||
+ | * [[Option-operand separation]] | ||
+ | |||
+ | ===Some C++ Language semantics avoided in this project design=== | ||
+ | * [[Object Encapsulation|Public and private attributes]] | ||
+ | * [[Don't expose mutable attributes|Exposing mutable attributes]] | ||
+ | * [[Object Encapsulation|Non virtual methods]] | ||
+ | * Calling methods in constructors - init() methods used instead | ||
+ | ** Hard to account for errors | ||
+ | ** Constructors cannot fail | ||
+ | ** Overloaded methods may be called | ||
+ | * Non-public inheritance | ||
+ | * Default Arguments, see [[Overloading| The problem with overloading]] | ||
+ | |||
+ | |||
+ | === Patterns === | ||
+ | [[Parallel hierarchies problem]] solution... | ||
+ | [[Intelligent children pattern]] | ||
+ | {| | ||
+ | |- | ||
+ | | Hierarchy One|| ''BoxerPose '' | ||
+ | |- | ||
+ | | Hierarchy Two || ''Boxer'' | ||
+ | |- | ||
+ | | Abstract accessor || ''Boxer.getPose() '' | ||
+ | |- | ||
+ | |} | ||
+ | I wanted to have some sort of contract in the Boxer object about having a BoxerPose so I could access a known Boxer object's BoxerPose. The solution to this was an abstract accessor in Boxer. | ||
+ | |||
+ | A problem with using an abstract accessor in C++ is that method returns cannot be overrided so any calls to getPose in derived objects had to be cast. | ||
+ | |||
+ | const TrainerPose *trainerPose = dynamic_cast< const TrainerPose *>(trainer.getPose()); | ||
+ | |||
+ | |||
+ | [[State|State Pattern]] | ||
+ | {| | ||
+ | |- | ||
+ | | Context || ''Trainer'' | ||
+ | |- | ||
+ | | Abstract State || ''TrainingActivity'' | ||
+ | |- | ||
+ | | Concrete State1 || ''PunchingDrill'' | ||
+ | |- | ||
+ | | Concrete State2 || ''BobbingAndWeaving'' | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | [[Visitor|Visitor Pattern]] | ||
+ | {| | ||
+ | |- | ||
+ | | ConcreteElement || Trainer, Boxer | ||
+ | |- | ||
+ | | Element || Boxer | ||
+ | |- | ||
+ | | Visitor || SerializerVisitor | ||
+ | |- | ||
+ | | ConcreteVisitor || XMLVisitor | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | [[Observer|Observer Pattern]] | ||
+ | {| | ||
+ | |- | ||
+ | | Subject || ''BoxerPose'' | ||
+ | |- | ||
+ | | ConcreteSubject || ''UserBoxerPose'' | ||
+ | |- | ||
+ | | Observer || ''Observer'' | ||
+ | |- | ||
+ | | ConcreteObserver || ''Trainer'' | ||
+ | |- | ||
+ | |} | ||
+ | A trainer can be made to observe a UserBoxerPose. The trainer will record the pose history and generate evaluations from that history, generate feedback from its observations and react to the pose's actions. It is behaviorally complete to have a boxer's pose Observable. | ||
+ | |||
+ | [[Observer|Observer Pattern]] | ||
+ | {| | ||
+ | |- | ||
+ | | Subject || ''BoxerPose'' | ||
+ | |- | ||
+ | | ConcreteSubject || ''TrainerPose'' | ||
+ | |- | ||
+ | | Observer || ''Observer'' | ||
+ | |- | ||
+ | | ConcreteObserver || ''TrainerAnimation'' | ||
+ | |- | ||
+ | |} | ||
+ | I wanted the Trainer to know about the timings of the animations and was tempted to couple the Trainer class to the TrainerAnimations class and vice versa. I instead decided to remove the Trainer's knowledge of its animations and required the animations to conform to the timings of the Trainer's movements. The TrainerAnimations class is likely to change and coupling the Trainer class to it would increase maintenance of the Trainer class. | ||
+ | |||
+ | [[Singleton]] | ||
+ | |||
+ | PunchingDrill, BobbingAndWeaving, TrainingSystem | ||
+ | |||
+ | Having the TrainingActivities as singletons in hindsight is probably not necessary. If the design was to be extended so that there could be multiple BoxingEvaluations, the states of the TrainingActivities would be corrupted from multiple BoxingEvaluations accesses. | ||
+ | |||
+ | ===MVC=== | ||
+ | Models - BoxerPose, UserBoxerPose and TrainerPose | ||
+ | * Respond to state queries | ||
+ | * Stores application state | ||
+ | * Notifies views of changes | ||
+ | |||
+ | View - TrainerAnimation, osgVideo | ||
+ | * Renders a model | ||
+ | * Requests updates from a model | ||
+ | * Allows controller to select view | ||
+ | |||
+ | Controller - BoxingTrainer | ||
+ | * Defines application behaviour | ||
+ | * Selects view for response | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
== Iteration Two == | == Iteration Two == | ||
− | [[Image:BenM's UMLI2.png|border| | + | [[Image:BenM's UMLI2.png|border|600px]] |
== Design: == | == Design: == | ||
[[State|State Pattern]]: | [[State|State Pattern]]: | ||
Line 31: | Line 170: | ||
[[Observer|Observer Pattern]]: | [[Observer|Observer Pattern]]: | ||
* Subject - Observable | * Subject - Observable | ||
− | * ConcreteSubject - | + | * ConcreteSubject - UserBoxer |
* Observer - Observer | * Observer - Observer | ||
* ConcreteObserver - Trainer | * ConcreteObserver - Trainer | ||
Line 39: | Line 178: | ||
* Observer - Observer | * Observer - Observer | ||
* ConcreteObserver - TrainerAnimation | * ConcreteObserver - TrainerAnimation | ||
− | |||
− | + | ||
− | + | == Iteration One == | |
− | + | [[Image:BenM's UMLI1.png|border|600px]] | |
+ | == Design: == | ||
+ | [[Observer|Observer Pattern]]: | ||
+ | * Subject - Observable | ||
+ | * ConcreteSubject - Athlete | ||
+ | * Observer - Observer | ||
+ | * ConcreteObserver - Trainer |
Latest revision as of 03:17, 25 November 2010
Contents |
Ben McDoanld's page
I am doing my design study for my COSC426:Augmented Reality project. I need a design for a boxing skills trainer. No code was written in the beginning or any design done. I have made a sketch of the requirements for the design of the software. The project is in C++ using the libraries open scene graph (osg), open scene graph augmented reality tool-kit(osgART) and the Wii controller library for windows
Requirements:
- A score needs to be generated from a set of tested skills:
- Consistent guard
- Eyes forward and on opponent
- Bobbing and weaving
- Timing of punches
- Each skill needs to be modeled and the evaluation method for each skill also needs to be modeled.
- The performance on the skills needs to be evaluated and for additional activities to be recommended to the athlete.
- An athlete’s evaluation history, progress and general details to be recorded and recoverable.
- Measurements from the augmented reality marker need to update the position of the athlete's head.
- WiiMote gestures need to be detected and reacted to.
My design is below. Any comments or suggestions are very welcome.
Src: - File:BTM ARSport Src.zip
Iteration Three
Style
Standard | Code | UML |
Immutable attributes accessors are named after the attributes as is the C++ standard | Object foo_; virtual const Object& foo() const; | foo, foo() |
Object queries end with the keyword 'const' | virtual const std::string& name() const; | name():String const |
Method inputs begin with the keyword 'const' | virtual void foo(const string &in, string *out); | foo(const String in):String |
Objects
UserBoxer
The UserBoxer interacts with the WiiRemote library. Gestures are generated from the Wii remote data and these gestures change the UserBoxer's Pose. The UserBoxer is also updated from the TrainingSystem object about the user's height and head orientation from the ARtoolkit marker. Changes in UserBoxer's Pose are sent to the Trainer.
Trainer
The Trainer observers the UserBoxer's Pose. On updates the Trainer reacts to the UserBoxer's Pose change depending on the state pattern, .train(const athleteHistory, TrainerPose). Changes in the Trainer's Pose get sent to the TrainerAnimation.
TrainerAnimation
The TrainerAnimation observes the Trainer's Pose and updates the animations according to the data in the Pose. The TrainerAnimation is added as a dynamic data structure to the osg node tree. TrainerAnimation acts independently to osg and changes in its animation are represented in the rendering.
BoxingEvaluation
The trainer has no knowlege about different TrainingActivities, only BoxingEvaluation does. BoxingEvaluation tells Trainer what TrainingActivity to use with the getRecommendedActivity() method. When a TrainingActivity .isComplete() then a Trainer asks the BoxingEvaluation for a new one.
Maxims
- Command query separation / Avoid side effects - The interface would be tidier if the getFeedback() method in the Trainer object also cleared the feedback but doing this would violate CQS. By have two methods, getFeedback() and clearFeedback(), there are no side effects and the status of the object can be queried without invoking any commands. Additionally, I could add a getAndClearFeedback() method for convenience. One plus of C++ is that you can mark queries, and enforce them, with the const keyword.
- Design by contract - Using assert's to enforce conditions at the moment. Design by contract has not been implemented wholeheartedly because of time constraints. Ideally, methods checking class invariants could be added. The UserBoxerPose class could have a method checkState() that tests for a correct head height range between public methods calls.
- Don't expose mutable attributes - In C++, objects returned from another object's method can be marked as const to make them immutable.
- Object Encapsulation - Methods are ether 'protected' or 'public' and are marked 'virtual' and attributes are 'protected'. Object encapsulation is more intuitive. Class encapsulation does not allow for effective unit testing through inheritance.
- Getters and setters - Tried to model behaviour in objects but could not avoid a long get/set list on my data objects when I seperated them out with the observer pattern -- Pose, BoxingPose and UserBoxerPose.
- Option-operand separation
Some C++ Language semantics avoided in this project design
- Public and private attributes
- Exposing mutable attributes
- Non virtual methods
- Calling methods in constructors - init() methods used instead
- Hard to account for errors
- Constructors cannot fail
- Overloaded methods may be called
- Non-public inheritance
- Default Arguments, see The problem with overloading
Patterns
Parallel hierarchies problem solution... Intelligent children pattern
Hierarchy One | BoxerPose |
Hierarchy Two | Boxer |
Abstract accessor | Boxer.getPose() |
I wanted to have some sort of contract in the Boxer object about having a BoxerPose so I could access a known Boxer object's BoxerPose. The solution to this was an abstract accessor in Boxer.
A problem with using an abstract accessor in C++ is that method returns cannot be overrided so any calls to getPose in derived objects had to be cast.
const TrainerPose *trainerPose = dynamic_cast< const TrainerPose *>(trainer.getPose());
Context | Trainer |
Abstract State | TrainingActivity |
Concrete State1 | PunchingDrill |
Concrete State2 | BobbingAndWeaving |
ConcreteElement | Trainer, Boxer |
Element | Boxer |
Visitor | SerializerVisitor |
ConcreteVisitor | XMLVisitor |
Subject | BoxerPose |
ConcreteSubject | UserBoxerPose |
Observer | Observer |
ConcreteObserver | Trainer |
A trainer can be made to observe a UserBoxerPose. The trainer will record the pose history and generate evaluations from that history, generate feedback from its observations and react to the pose's actions. It is behaviorally complete to have a boxer's pose Observable.
Subject | BoxerPose |
ConcreteSubject | TrainerPose |
Observer | Observer |
ConcreteObserver | TrainerAnimation |
I wanted the Trainer to know about the timings of the animations and was tempted to couple the Trainer class to the TrainerAnimations class and vice versa. I instead decided to remove the Trainer's knowledge of its animations and required the animations to conform to the timings of the Trainer's movements. The TrainerAnimations class is likely to change and coupling the Trainer class to it would increase maintenance of the Trainer class.
PunchingDrill, BobbingAndWeaving, TrainingSystem
Having the TrainingActivities as singletons in hindsight is probably not necessary. If the design was to be extended so that there could be multiple BoxingEvaluations, the states of the TrainingActivities would be corrupted from multiple BoxingEvaluations accesses.
MVC
Models - BoxerPose, UserBoxerPose and TrainerPose
- Respond to state queries
- Stores application state
- Notifies views of changes
View - TrainerAnimation, osgVideo
- Renders a model
- Requests updates from a model
- Allows controller to select view
Controller - BoxingTrainer
- Defines application behaviour
- Selects view for response
Iteration Two
Design:
- Context - Trainer
- State - TrainingState
- ConcreteStates - PunchingDrill, BobbingAndWeaving
- Subject - Observable
- ConcreteSubject - UserBoxer
- Observer - Observer
- ConcreteObserver - Trainer
- Subject - Observable
- ConcreteSubject - Trainer
- Observer - Observer
- ConcreteObserver - TrainerAnimation
Iteration One
Design:
- Subject - Observable
- ConcreteSubject - Athlete
- Observer - Observer
- ConcreteObserver - Trainer