Browse Source

Merge branch 'magSlideShowSource'

master
Krisjanis Rijnieks 8 years ago
parent
commit
b6a0f695c1
  1. 14
      .gitignore
  2. 51
      README.md
  3. 23
      example/CMakeLists.txt
  4. 2
      example/addons.make
  5. 15
      example/bin/data/magslideshow_settings.xml
  6. 2
      example/bin/data/ofxpimapper.xml
  7. 990
      example/example.xcodeproj/project.pbxproj
  8. 4
      example/openFrameworks-Info.plist
  9. 2
      example/src/main.cpp
  10. 11
      example/src/ofApp.cpp
  11. 38
      example/src/ofApp.h
  12. 6
      src/Application/Application.cpp
  13. 42
      src/Application/Modes/ProjectionMappingMode.cpp
  14. 51
      src/MediaServer/DirectoryWatcher.cpp
  15. 24
      src/MediaServer/DirectoryWatcher.h
  16. 19
      src/MediaServer/MediaServer.cpp
  17. 204
      src/Sources/magSlide.cpp
  18. 263
      src/Sources/magSlide.h
  19. 533
      src/Sources/magSlideShowSource.cpp
  20. 168
      src/Sources/magSlideShowSource.h
  21. 60
      src/Sources/magSlideTransition.cpp
  22. 129
      src/Sources/magSlideTransition.h
  23. 76
      src/Sources/magSlideTransitionFactory.cpp
  24. 41
      src/Sources/magSlideTransitionFactory.h

14
.gitignore

@ -105,3 +105,17 @@ Desktop.ini
*.log
*.sql
*.sqlite
example/cmake-build-debug/
example/\.idea/
example/cmake-build-release/
example/example\.xcodeproj/xcshareddata/xcschemes/
.idea/
cmake-build-debug/
CMakeLists\.txt

51
README.md

@ -25,15 +25,6 @@ You can use the example application of the addon for projection mapping projects
```
## Compiling on Linux
There might be issues with compiling because `ofxOMXPlayer` and `ofxRPiCameraVideoGrabber` addons do not exist on Linux and also other non-RPi platforms. To avoid the issues, do the following.
1. Open `example/addons.make` and remove ofxOMXPlayer line.
2. Open `example-camera/addons.make` and remove ofxRPiCameraVideoGrabber line.
3. Compile one or both examples by using make.
## Problems with Audio
If you are having problems with audio playback, here are two steps for you. Before you do these, make sure audio of your video file works.
@ -162,33 +153,41 @@ Key | Function
2 | Texture editing mode
3 | Projection mapping mode, use this to select a surface first
4 | Source selection mode
i | Show info
c | Show info
t | Add triangle surface
q | Add quad surface
x | Add hexagon surface
g | Add grid warp surface
d | duplicate selected surface
\+ | Scale surface up
\- | Scale surface down
a | duplicate selected surface
o | Scale surface up
i | Scale surface down
p | toggle perspective warping (quad surfaces only)
] | add columns to grid surface (grid warp surfaces only)
[ | remove columns from grid surface (grid warp surfaces only)
} | add rows to grid surface (grid warp surfaces only)
{ | remove rows from grid surface (grid warp surfaces only)
v | add columns to grid surface (grid warp surfaces only)
b | remove columns from grid surface (grid warp surfaces only)
n | add rows to grid surface (grid warp surfaces only)
m | remove rows from grid surface (grid warp surfaces only)
. | select next surface (projection mapping mode only)
, | select previous surface (projection mapping mode only)
\> | select next vertex
\< | select previous vertex
0 | Move selected surface one layer up
9 | Move selected surface one layer down
k | select next vertex
l | select previous vertex
h | Move selected surface one layer up
j | Move selected surface one layer down
s | Save composition
l | Hide/show layer panel
y | Hide/show layer panel
z | Undo
rbt | Reboot (Raspberry Pi only)
sdn | Shutdown (Raspberry Pi only)
new | Clear composition (remove all surfaces)
ext | Exit application and return to command line
BACKSPACE ('\' via SSH) | Delete surface.
SPACE | Toggle pause for video sources (texture and projection mapping modes)
TAB | Select next source (no need to use the source selection interface)
Arrow keys | Move selection. If no surface is selected in the projection mapping mode, all surfaces are moved.
d | Delete surface.
w | Toggle pause for video sources (texture and projection mapping modes)
5 | Select next source (no need to use the source selection interface)
8 | Move selection Up
9 | Move selection Down
7 | Move selection Left
0 | Move selection Right
If no surface is selected in the projection mapping mode, all surfaces are moved.
Dont know if this works already:
\/ | Toggle 1px/10px steps for keyboard moves on Raspberry Pi

23
example/CMakeLists.txt

@ -4,6 +4,7 @@
# ===================== CMake Settings ===================
# ========================================================
cmake_minimum_required( VERSION 3.3 )
project( openframeworks )
# ========================================================
@ -17,21 +18,23 @@ set( APP_NAME example_ofxPiMapper )
set( OF_DIRECTORY_BY_USER "../../.." )
# --------------------- Source Files ---------------------
file( GLOB_RECURSE
APP_SRC
"src/*.cpp"
)
set( ${APP_NAME}_SOURCE_FILES
src/main.cpp
src/ofApp.cpp
src/CrossSource.cpp
src/CustomSource.cpp
src/Settings.cpp)
${APP_SRC} )
# ------------------------ AddOns -----------------------
set( OFX_ADDONS_ACTIVE
ofxXmlSettings
ofxGui
ofxPiMapper
ofxIO
)
ofxXmlSettings
ofxGui
ofxPiMapper
# ofxIO
)
# =========================================================================
# ============================== OpenFrameworks ===========================

2
example/addons.make

@ -1,4 +1,4 @@
ofxGui
ofxPiMapper
ofxXmlSettings
ofxGui
ofxOMXPlayer

15
example/bin/data/magslideshow_settings.xml

@ -0,0 +1,15 @@
<magSlideShow>
<Width>1920</Width>
<Height>1080</Height>
<SlideDuration>2</SlideDuration> <!-- Optional default duration for each slide-->
<Loop>
<Type>PING-PONG</Type> <!-- NONE | NORMAL | PING-PONG -->
<Count>0</Count> <!-- 0 = forever -->
</Loop>
<Transition>
<Type>Dissolve</Type>
<Duration>2</Duration>
</Transition>
<!-- NoResize | Native | Fit | FitProportionally | FillProportionally -->
<ResizeOption>FitProportionally</ResizeOption>
</magSlideShow>

2
example/bin/data/ofxpimapper.xml

@ -72,7 +72,7 @@
</texCoords>
<source>
<source-type>fbo</source-type>
<source-name>Custom FBO Source</source-name>
<source-name>Slide Show Source</source-name>
</source>
<properties>
<perspectiveWarping>1</perspectiveWarping>

990
example/example.xcodeproj/project.pbxproj

File diff suppressed because it is too large

4
example/openFrameworks-Info.plist

@ -6,8 +6,6 @@
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>${ICON}</string>
<key>CFBundleIdentifier</key>
<string>cc.openFrameworks.ofapp</string>
<key>CFBundleInfoDictionaryVersion</key>
@ -18,5 +16,7 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>CFBundleIconFile</key>
<string>${ICON}</string>
</dict>
</plist>

2
example/src/main.cpp

@ -17,6 +17,6 @@ int main(int argc, char * argv[]){
Settings::instance()->setFullscreen(fullscreen);
ofSetupOpenGL(800, 450, OF_WINDOW);
ofSetupOpenGL(1024, 768, OF_WINDOW);
ofRunApp(new ofApp());
}

11
example/src/ofApp.cpp

@ -14,15 +14,24 @@ void ofApp::setup(){
// a surface in XML settings.
crossSource = new CrossSource();
customSource = new CustomSource();
// Create the slide show source.
slideShowSource = new magSlideShowSource();
// Register our sources:
piMapper.registerFboSource(crossSource);
piMapper.registerFboSource(customSource);
piMapper.registerFboSource(slideShowSource);
piMapper.setup();
// The info layer is hidden by default, press <i> to toggle
// piMapper.showInfo();
ofSetFullscreen(Settings::instance()->getFullscreen());
ofSetEscapeQuitsApp(false);
ofSetLogLevel(OF_LOG_VERBOSE);
slideShowSource->play();
}
void ofApp::update(){

38
example/src/ofApp.h

@ -6,24 +6,28 @@
#include "CustomSource.h"
#include "CrossSource.h"
#include "VideoSource.h"
#include "magSlideShowSource.h"
class ofApp : public ofBaseApp {
public:
void setup();
void update();
void draw();
void keyPressed(int key);
void keyReleased(int key);
void mousePressed(int x, int y, int button);
void mouseReleased(int x, int y, int button);
void mouseDragged(int x, int y, int button);
class ofApp : public ofBaseApp
{
public:
void setup();
void update();
void draw();
ofxPiMapper piMapper;
void keyPressed(int key);
void keyReleased(int key);
void mousePressed(int x, int y, int button);
void mouseReleased(int x, int y, int button);
void mouseDragged(int x, int y, int button);
ofxPiMapper piMapper;
// By using a custom source that is derived from FboSource
// you will be able to see the source listed in sources editor
CustomSource *customSource;
CrossSource *crossSource;
magSlideShowSource *slideShowSource;
// By using a custom source that is derived from FboSource
// you will be able to see the source listed in sources editor
CustomSource * customSource;
CrossSource * crossSource;
};

6
src/Application/Application.cpp

@ -121,7 +121,7 @@ void Application::onKeyPressed(ofKeyEventArgs & args){
setSourceMode();
break;
case 'i':
case 'c':
toggleInfo();
break;
@ -132,8 +132,8 @@ void Application::onKeyPressed(ofKeyEventArgs & args){
case 'z':
undo();
break;
case 'n':
case 'f':
setNextPreset();
break;

42
src/Application/Modes/ProjectionMappingMode.cpp

@ -64,11 +64,11 @@ void ProjectionMappingMode::onKeyPressed(Application * app, ofKeyEventArgs & arg
app->createSurface(SurfaceType::GRID_WARP_SURFACE);
break;
case 'h':
case 'x':
app->createSurface(SurfaceType::HEXAGON_SURFACE);
break;
case OF_KEY_BACKSPACE:
case 'd':
app->eraseSurface(app->getSurfaceManager()->getSelectedSurfaceIndex());
break;
@ -76,19 +76,19 @@ void ProjectionMappingMode::onKeyPressed(Application * app, ofKeyEventArgs & arg
app->togglePerspective();
break;
case '}':
case 'n':
app->addGridRow();
break;
case '{':
case 'm':
app->removeGridRow();
break;
case ']':
case 'v':
app->addGridColumn();
break;
case '[':
case 'b':
app->removeGridColumn();
break;
@ -100,15 +100,15 @@ void ProjectionMappingMode::onKeyPressed(Application * app, ofKeyEventArgs & arg
app->selectPrevSurface();
break;
case '>':
case 'k':
app->selectNextVertex();
break;
case '<':
case 'l':
app->selectPrevVertex();
break;
case OF_KEY_UP:
case '8':
if(app->isShiftKeyDown()){
app->moveSelection(ofVec2f(0.0f, -10.0f));
}else{
@ -116,7 +116,7 @@ void ProjectionMappingMode::onKeyPressed(Application * app, ofKeyEventArgs & arg
}
break;
case OF_KEY_DOWN:
case '9':
if(app->isShiftKeyDown()){
app->moveSelection(ofVec2f(0.0f, 10.0f));
}else{
@ -124,7 +124,7 @@ void ProjectionMappingMode::onKeyPressed(Application * app, ofKeyEventArgs & arg
}
break;
case OF_KEY_LEFT:
case '7':
if(app->isShiftKeyDown()){
app->moveSelection(ofVec2f(-10.0f, 0.0f));
}else{
@ -132,7 +132,7 @@ void ProjectionMappingMode::onKeyPressed(Application * app, ofKeyEventArgs & arg
}
break;
case OF_KEY_RIGHT:
case '0':
if(app->isShiftKeyDown()){
app->moveSelection(ofVec2f(10.0f, 0.0f));
}else{
@ -140,35 +140,35 @@ void ProjectionMappingMode::onKeyPressed(Application * app, ofKeyEventArgs & arg
}
break;
case ' ':
case 'w':
app->togglePause();
break;
case OF_KEY_TAB:
case '5':
app->setNextSource();
break;
case 'd':
case 'a':
app->duplicateSurface();
break;
case '0': // Move selected surface up the layer stack
case 'h': // Move selected surface up the layer stack
app->moveLayerUp();
break;
case '9': // Move selected surface down the layer stack
case 'j': // Move selected surface down the layer stack
app->moveLayerDown();
break;
case '+': // Scale surface up
case 'o': // Scale surface up
app->scaleUp();
break;
case '-': // Scale surface down
case 'i': // Scale surface down
app->scaleDown();
break;
case 'l':
case 'y':
_bDrawLayerPanel = !_bDrawLayerPanel;
break;
@ -295,4 +295,4 @@ void ProjectionMappingMode::onGuiEvent(Application * app, GuiEvent & e){
}
} // namespace piMapper
} // namespace ofx
} // namespace ofx

51
src/MediaServer/DirectoryWatcher.cpp

@ -4,8 +4,9 @@ namespace ofx {
namespace piMapper {
DirectoryWatcher::DirectoryWatcher(string path, int watcherMediaType){
directoryPath = path;
_mediaType = watcherMediaType;
if(_mediaType == SourceType::SOURCE_TYPE_VIDEO){
_directory.allowExt("mp4");
_directory.allowExt("h264");
@ -21,20 +22,27 @@ DirectoryWatcher::DirectoryWatcher(string path, int watcherMediaType){
ofLogFatalError("DirectoryWatcher::DirectoryWatcher", "Unkonwn media type");
exit(EXIT_FAILURE);
}
_directory.listDir(path);
_directory.listDir(directoryPath);
_directory.sort();
for(int i = 0; i < _directory.size(); ++i){
_filePaths.push_back(path + _directory.getName(i));
ofFile file(path + _directory.getName(i));
_filePaths.push_back(directoryPath + _directory.getName(i));
ofFile file(directoryPath + _directory.getName(i));
if(_mediaType == SourceType::SOURCE_TYPE_VIDEO){
file.copyTo("sources/videos/" + _directory.getName(i));
}else if(_mediaType == SourceType::SOURCE_TYPE_IMAGE){
file.copyTo("sources/images/" + _directory.getName(i));
}
}
dirSize = _directory.size();
}
DirectoryWatcher::~DirectoryWatcher() {
endWatch();
// waitForThread(false);
}
vector <string> & DirectoryWatcher::getFilePaths(){
@ -45,5 +53,34 @@ int DirectoryWatcher::getMediaType(){
return _mediaType;
}
void DirectoryWatcher::beginWatch(int intervalInMillis) {
watchInterval = intervalInMillis;
startThread();
}
void DirectoryWatcher::endWatch() {
stopThread();
}
void DirectoryWatcher::threadedFunction() {
while (isThreadRunning()) {
int newSize = _directory.listDir();
if (newSize > dirSize) {
ofLogVerbose("DirectoryWatcher") << "Directory changed";
dirSize = newSize;
ofNotifyEvent(directoryFileAddedEvent, this);
} else if (newSize < dirSize) {
dirSize = newSize;
ofNotifyEvent(directoryFileRemovedEvent, this);
}
sleep(watchInterval);
}
}
} // namespace piMapper
} // namespace ofx

24
src/MediaServer/DirectoryWatcher.h

@ -6,16 +6,36 @@
namespace ofx {
namespace piMapper {
class DirectoryWatcher {
class DirectoryWatcher : public ofThread {
public:
DirectoryWatcher(string path, int watcherMediaType);
vector<string> & getFilePaths();
virtual ~DirectoryWatcher();
vector<string> &getFilePaths();
int getMediaType();
void beginWatch(int intervalInMillis = 5000);
void endWatch();
void threadedFunction();
/**
* Notifies when files are added to this directory.
* Sender is a pointer to this DirectoryWatcher
*/
ofEvent<void> directoryFileAddedEvent;
/**
* Notifies when files are removed from this directory.
* Sender is a pointer to this DirectoryWatcher
*/
ofEvent<void> directoryFileRemovedEvent;
private:
ofDirectory _directory;
vector<string> _filePaths;
std::string directoryPath;
int _mediaType;
int dirSize;
long watchInterval; // in millis.
};
} // namespace piMapper

19
src/MediaServer/MediaServer.cpp

@ -19,14 +19,17 @@ MediaServer::MediaServer():
{
// By initialising all above we also copy files from external media
// to the ofxPiMapper storage.
imageWatcher = DirectoryWatcher(
ofToDataPath(DEFAULT_IMAGES_DIR, true),
SourceType::SOURCE_TYPE_IMAGE);
videoWatcher = DirectoryWatcher(
ofToDataPath(DEFAULT_VIDEOS_DIR, true),
SourceType::SOURCE_TYPE_VIDEO);
imageWatcher.beginWatch();
// imageWatcher = DirectoryWatcher(
// ofToDataPath(DEFAULT_IMAGES_DIR, true),
// SourceType::SOURCE_TYPE_IMAGE);
// imageWatcher.beginWatch();
//
// videoWatcher = DirectoryWatcher(
// ofToDataPath(DEFAULT_VIDEOS_DIR, true),
// SourceType::SOURCE_TYPE_VIDEO);
}
void MediaServer::setup(){

204
src/Sources/magSlide.cpp

@ -0,0 +1,204 @@
//
// magSlide.cpp
// Copyright (c) 2017 Cristobal Mendoza
// http://cuppetellimendoza.com
//
#include "magSlide.h"
#include "magSlideTransition.h"
#pragma mark magSlide
int magSlide::idCount = 0;
magSlide::magSlide(std::string type)
{
this->type = type;
position = ofPoint(0, 0);
id = magSlide::idCount;
magSlide::idCount++;
}
void magSlide::update(u_int64_t deltaTime)
{
transition->update(deltaTime);
runningTime += deltaTime;
switch (slideState)
{
// case SlideState::BuildIn:
// if (runningTime >= buildInDuration)
// {
// setState(Normal);
// activeTransition = nullptr;
// }
// break;
case SlideState::Normal:
if (runningTime >= buildOutStartTime)
{
setState(BuildOut);
}
break;
case SlideState::BuildOut:
if (runningTime >= endTime)
{
setState(Complete);
}
break;
}
}
void magSlide::setSize(float w, float h)
{
width = w;
height = h;
}
void magSlide::setResizeOption(magSlide::ResizeOptions resizeOption)
{
this->resizeOption = resizeOption;
}
magSlide::ResizeOptions magSlide::getResizeOption() const
{
return resizeOption;
}
void magSlide::setDuration(u_int64_t duration)
{
this->duration = duration;
}
void magSlide::setState(SlideState state)
{
// Don't do anything if the new state is the same
// as the current one:
if (slideState == state) return;
slideState = state;
ofEventArgs args;
ofNotifyEvent(slideStateChangedEvent, args, this);
if (slideState == Complete)
{
ofNotifyEvent(slideCompleteEvent, args, this);
}
}
void magSlide::setTransitionDuration(u_int64_t tDuration)
{
buildOutDuration = tDuration;
}
const std::string magSlide::getSlideStateName()
{
switch (slideState)
{
// case SlideState::BuildIn:
// return "BuildIn";
case SlideState::BuildOut:
return "BuildOut";
case Normal:
return "Normal";
case SlideState::Complete:
return "Complete";
case SlideState::Off:
return "Off";
}
return "unknown";
}
void magSlide::start(u_int64_t startTime)
{
this->startTime = startTime;
runningTime = 0;
endTime = duration + (buildOutDuration*2); // *2 because we take into account transition in and out
buildOutStartTime = duration + buildOutDuration;
slideState = magSlide::SlideState::Normal;
position.set(0, 0);
opacity = 255;
isComplete = false;
}
void magSlide::draw()
{
ofSetColor(255, opacity);
if(transition->isActive())
{
transition->draw();
}
}
////////////////////////////////////////////////////////
#pragma mark MAG_IMAGE_SLIDE
////////////////////////////////////////////////////////
magImageSlide::magImageSlide() : magSlide("magImageSlide") {}
magImageSlide::~magImageSlide() {}
void magImageSlide::setup(ofImage &image)
{
// Make a copy of the image:
this->image = ofImage(image);
image.setAnchorPercent(0.5, 0.5);
width = image.getWidth();
height = image.getHeight();
}
void magImageSlide::draw()
{
magSlide::draw();
image.draw(position, width, height);
}
////////////////////////////////////////////////////////
#pragma mark MAG_VIDEO_SLIDE
////////////////////////////////////////////////////////
magVideoSlide::magVideoSlide() : magSlide("magVideoSlide")
{}
magVideoSlide::~magVideoSlide()
{
videoPlayer.stop();
}
bool magVideoSlide::setup(ofFile &file, bool useVideoDuration)
{
bool success = videoPlayer.load(file.getAbsolutePath());
if (success)
{
videoPlayer.setAnchorPercent(0.5, 0.5);
if (useVideoDuration)
{
useVideoForDuration();
}
}
return success;
}
void magVideoSlide::update()
{
videoPlayer.update();
}
void magVideoSlide::draw()
{
magSlide::draw();
videoPlayer.draw(position.x, position.y, width, height);
}
void magVideoSlide::useVideoForDuration()
{
duration = u_int64_t((videoPlayer.getDuration()*1000)) - buildOutDuration;
}

263
src/Sources/magSlide.h

@ -0,0 +1,263 @@
//
// Created by Cristobal Mendoza on 11/9/17.
//
#ifndef MAGSLIDE_H
#define MAGSLIDE_H
#include "ofMain.h"
class magSlideTransition;
class magSlide
{
public:
magSlide(std::string type);
// ~magSlide();
virtual void update(u_int64_t deltaTime);
virtual void draw();
/**
* Sets the slide up for playback. This method must be
* called before displaying the slide.
* @param startTime
*/
void start(u_int64_t startTime);
enum ResizeOptions : short
{
/**
* No resizing applied, displays the slide in its native pixel dimensions.
* This is the default behavior.
*/
NoResize = 0,
/**
* Explicitly set a slide to display in its native dimension.
* None and NoResize result in the same output, but if you specify
* a default resizing option in your slideshow and you want a particular
* slide not to resize, you must specify this option. Otherwise the
* slide show option will apply.
*/
Native,
/**
* Sets width and height to match the source's.
* This will distort the slide if the aspect ratios
* don't match.
*/
Fit,
/**
* Resizes the largest dimension to the source's max,
* the other dimension is resized proportionally.
*/
FitProportionally,
/**
* Resizes the shortest dimensions to the source's max,
*/
FillProportionally,
};
enum SlideState : short
{
Off = 0,
// BuildIn,
Normal,
BuildOut,
Complete
};
const std::string &getType()
{ return type; }
float getWidth()
{ return width; }
float getHeight()
{ return height; }
float getOpacity() const
{
return opacity;
}
void setOpacity(float opacity)
{
magSlide::opacity = opacity;
}
/**
* Change the display size of a slide. This will implicitly
* set resizeOptions to ResizeOption.NoResize.
* This method does not resize the pixel source of the slide.
* @param w The new width
* @param h The new height
*/
virtual void setSize(float w, float h);
u_int64_t getDuration()
{
return duration;
}
/**
* Sets the display time of the slide, excluding
* builds (in or out)
* @param duration in milliseconds.
*/
void setDuration(u_int64_t duration);
/**
* Sets the duration of the buildIn and buildOut
* transitions. Transition times are added to the
* duration of the slide.
* @param tDuration in milliseconds.
*/
void setTransitionDuration(u_int64_t tDuration);
ResizeOptions getResizeOption() const;
void setResizeOption(ResizeOptions resizeOption);
SlideState getSlideState() const
{
return slideState;
}
bool isSlideComplete()
{ return isComplete; }
int getId()
{ return id; }
const std::string getSlideStateName();
//////////////////////////////
/// Events
//////////////////////////////
ofEvent<ofEventArgs> slideCompleteEvent;
ofEvent<ofEventArgs> slideStateChangedEvent;
friend class magSlideShowSource;
protected:
int id;
std::string type;
float width;
float height;
ofPoint position;
public:
const ofPoint &getPosition() const
{
return position;
}
void setPosition(const ofPoint &position)
{
magSlide::position = position;
}
void setPosition(float x, float y)
{
position.set(x, y);
}
protected:
float opacity = 255;
ResizeOptions resizeOption = NoResize;
SlideState slideState = Off;
bool isComplete = false;
// std::shared_ptr<magSlideTransition> buildIn = nullptr;
// std::shared_ptr<magSlideTransition> buildOut = nullptr;
std::shared_ptr<magSlideTransition> transition = nullptr;
public:
const shared_ptr<magSlideTransition> &getTransition() const
{
return transition;
}
protected:
/**
* The duration of the slide in millis, not counting builds
*/
u_int64_t duration;
/**
* The start time of the slide in milliseconds, in "local time".
* In practice, this means that the start time is always 0.
*/
u_int64_t startTime;
/**
* The end time of the slide in milliseconds, in "local time".
* The endTime is startTime + buildInDuration + duration + buildOutDuration.
*/
u_int64_t endTime;
/**
* The duration of the build in transition or effect, in milliseconds.
*/
// u_int64_t buildInDuration;
/**
* The duration of the build out transition or effect, in milliseconds.
*/
u_int64_t buildOutDuration;
/**
* The "local time" start of the build out transition. This time should also
* signal the enqueuing of the next slide, if applicable.
*/
u_int64_t buildOutStartTime;
/**
* The amount of time the slide has been displayed, in milliseconds.
* This constitutes the "local time" of the slide.
*/
u_int64_t runningTime;
void setState(SlideState state);
static int idCount;
};
class magImageSlide : public magSlide
{
public:
magImageSlide();
~ magImageSlide();
/**
* Sets up an image slide.
* @param image is copied into the member magImageSlide::image and is
* not modified in any way.
*/
void setup(ofImage &image);
void draw();
protected:
ofImage image;
};
class magVideoSlide : public magSlide
{
public:
magVideoSlide();
~magVideoSlide();
bool setup(ofFile &file, bool useVideoDuration = false);
void update();
void draw();
/**
* Sets the slide duration to the duration of the video.
* magSlide::duration will be changed by this call, so if you later
* change your mind you'll have to use magSlide::setDuration.
*/
void useVideoForDuration();
protected:
ofVideoPlayer videoPlayer;
};
#endif

533
src/Sources/magSlideShowSource.cpp

@ -0,0 +1,533 @@
//
// magSlideShowSource.cpp
// Copyright (c) 2017 Cristobal Mendoza
// http://cuppetellimendoza.com
//
#include "magSlideShowSource.h"
#include "magSlideTransition.h"
#include "SettingsLoader.h"
#include "magSlideTransitionFactory.h"
const std::string magSlideShowSource::SettingsFileName = "magslideshow_settings.xml";
magSlideShowSource::magSlideShowSource() {
name = "Slide Show Source";
currentSlideIndex = 0;
isPlaying = false;
directoryWatcher = 0;
doInit = false;
if (!loadFromXml(SettingsFileName))
{
ofLogError("magSlideShowSource") << "Could not find slide show settings file " << SettingsFileName;
Settings sets;
initialize(sets);
}
}
magSlideShowSource::~magSlideShowSource() {
directoryWatcher->endWatch();
delete directoryWatcher;
}
bool magSlideShowSource::initialize(magSlideShowSource::Settings settings) {
this->settings = settings;
bool success = true;
if (settings.width <= 0 || settings.height <= 0)
{
ofLogError("magSlideShowSource::initialize") << "Invalid value for width or height. Width and height "
"must be assigned in your Settings struct!";
return false;
}
// Allocate the FBO:
allocate(settings.width, settings.height);
// If there is a path in slidesFolderPath, attempt
// to load the folder and any files in it:
if (!settings.slidesFolderPath.empty())
{
// ofDirectory dir = ofDirectory(settings.slidesFolderPath);
success = createFromFolderContents(settings.slidesFolderPath);
if (!success)
{
ofLogError("magSlideShowSource::initialize") << "Failed to create slide show from folder "
<< settings.slidesFolderPath;
return success;
}
{
if (directoryWatcher == 0)
{
using namespace ofx::piMapper;
directoryWatcher = new DirectoryWatcher(settings.slidesFolderPath,
SourceTypeHelper::GetSourceTypeHelperEnum(
SOURCE_TYPE_NAME_IMAGE));
ofAddListener(directoryWatcher->directoryFileAddedEvent, this, &magSlideShowSource::fileAddedListener);
ofAddListener(directoryWatcher->directoryFileRemovedEvent, this,
&magSlideShowSource::fileRemovedListener);
directoryWatcher->beginWatch();
}
}
}
else if (!settings.slideshowFilePath.empty())
{
success = false;
}
return success;
}
void magSlideShowSource::setup() {
ofx::piMapper::FboSource::setup();
}
void magSlideShowSource::update() {
// Perform re-initialization if the DirectoryWatcher
// detects file changes:
if (doInit)
{
initialize(settings);
doInit = false;
}
if (!isPlaying) return;
auto nowTime = ofGetElapsedTimeMillis();
deltaTime = nowTime-lastTime;
runningTime += deltaTime;
lastTime = nowTime;
for (auto &slide : activeSlides)
{
slide->update(deltaTime);
}
// Queue the next slide if it is time
if (doPlayNextSlide)
{
playNextSlide();
if (activeSlides.size() > 1)
{
activeSlides[1]->transition->start(activeSlides[0]);
}
doPlayNextSlide = false;
}
// Erase any complete slides:
auto iter = activeSlides.begin();
for (; iter < activeSlides.end(); iter++)
{
if ((*iter)->isSlideComplete())
{
// ofLogVerbose() << "Removing from active slides id: " << (*iter)->getId();
activeSlides.erase(iter);
--iter;
}
}
if (activeSlides.size() == 0 && isPlaying)
{
ofEventArgs args;
isPlaying = false;
ofNotifyEvent(slideshowCompleteEvent, args, this);
}
}
void magSlideShowSource::draw() {
ofBackground(0, 0);
ofPushMatrix();
ofPushStyle();
ofTranslate(getWidth()/2.0f, getHeight()/2.0f);
ofEnableAlphaBlending();
ofSetRectMode(OF_RECTMODE_CENTER);
ofFill();
ofSetColor(255, 255);
for (auto &slide : activeSlides)
{
slide->draw();
}
ofPopStyle();
ofPopMatrix();
ofDisableAlphaBlending();
}
bool magSlideShowSource::createFromFolderContents(std::string path) {
ofDirectory dir = ofDirectory(path);
slides.clear();
if (!dir.isDirectory())
{
ofLogError("magSlideShowSource::createFromFolderContents") << "Folder path " << dir.getAbsolutePath()
<< " is not a directory";
return false;
}
auto sortedDir = dir.getSorted();
auto files = sortedDir.getFiles();
if (files.size() < 1)
{
ofLogError("magSlideShowSource::createFromFolderContents") << "Folder " << dir.getAbsolutePath() << " is empty";
return false;
}
ofImage tempImage;
for (auto &file : files)
{
if (tempImage.load(file))
{
// make a new image slide
auto slide = std::make_shared<magImageSlide>();
slide->setup(tempImage);
slide->setDuration(static_cast<u_int64_t>(settings.slideDuration*1000));
slide->setTransitionDuration(static_cast<u_int64_t>(settings.transitionDuration*1000));
// if (settings.transitionName == "")
addSlide(slide);
}
else
{
auto ext = ofToLower(file.getExtension());
static std::vector<std::string> movieExtensions = {
"mov", "qt", // Mac
"mp4", "m4p", "m4v", // MPEG
"mpg", "mp2", "mpeg", "mpe", "mpv", "m2v", // MPEG
"3gp", // Phones
"avi", "wmv", "asf", // Windows
"webm", "mkv", "flv", "vob", // Other containers
"ogv", "ogg",
"drc", "mxf"
};
// Check if the extension matches known movie formats:
if (ofContains(movieExtensions, ext))
{
// Make a new video slide
auto slide = std::make_shared<magVideoSlide>();
if (slide->setup(file))
{
slide->setDuration(settings.slideDuration*1000.0);
slide->setTransitionDuration(settings.transitionDuration*1000.0);
addSlide(slide);
}
else
{
ofLogError("magSlideShowSource") << "Failed loading video: " << file.getAbsolutePath();
}
}
}
}
if (slides.size() > 0)
{
return true;
}
else
{
return false;
}
}
bool magSlideShowSource::loadFromXml(std::string path) {
auto xml = ofxXmlSettings();
Settings settings;
if (!xml.load(path))
{
ofLogError("magSlideShowSource") << "Could not load settings file " << path;
return false;
}
// xml.pushTag("surfaces");
if (!xml.pushTag("magSlideShow"))
{
ofLogError("magSlideShowSource") << "Slide show settings not found in " << path;
return false;
}
settings.width = xml.getValue("Width", settings.width);
settings.height = xml.getValue("Height", settings.height);
// Default slide duration:
settings.slideDuration = xml.getValue("SlideDuration", settings.slideDuration);
// Default loop:
if (xml.pushTag("Loop"))
{
auto type = xml.getValue("Type", "");
if (type == "NONE")
{
settings.loopType = LoopType::NONE;
}
else if (type == "NORMAL")
{
settings.loopType = LoopType::NORMAL;
}
else if (type == "PING-PONG")
{
settings.loopType = LoopType::PING_PONG;
}
settings.numLoops = xml.getValue("Count", settings.numLoops);
xml.popTag();
}
if (xml.pushTag("Transition"))
{
settings.transitionName = xml.getValue("Type", settings.transitionName);
settings.transitionDuration = xml.getValue("Duration", settings.transitionDuration);
xml.popTag();
}
// Default resize options:
auto ropts = xml.getValue("ResizeOption", "");
if (ropts == "NoResize")
{
settings.resizeOption = magSlide::NoResize;
}
else if (ropts == "Native")
{
settings.resizeOption = magSlide::Native;
}
else if (ropts == "Fit")
{
settings.resizeOption = magSlide::Fit;
}
else if (ropts == "FitProportionally")
{
settings.resizeOption = magSlide::FitProportionally;
}
else if (ropts == "FillProportionally")
{
settings.resizeOption = magSlide::FillProportionally;
}
initialize(settings);
return true;
}
void magSlideShowSource::addSlide(std::shared_ptr<magSlide> slide) {
// ofLogVerbose("addSlide") << slide->getId();
slides.insert(slides.begin(), slide);
auto rOption = slide->getResizeOption();
// If the slide does not have a resize option assign
// the slide show's option
if (rOption == magSlide::ResizeOptions::NoResize)
{
rOption = settings.resizeOption;
}
// Resize the slide according to the resize option:
switch (rOption)
{
float sw, sh, ratio;
case magSlide::ResizeOptions::FitProportionally:
sw = slide->getWidth();
sh = slide->getHeight();
if (sw > sh)
{
ratio = (float) getWidth()/sw;
}
else
{
ratio = (float) getHeight()/sh;
}
slide->setSize(sw*ratio, sh*ratio);
break;
case magSlide::ResizeOptions::FillProportionally:
sw = slide->getWidth();
sh = slide->getHeight();
if (sw > sh)
{
ratio = (float) getHeight()/sh;
}
else
{
ratio = (float) getWidth()/sw;
}
slide->setSize(sw*ratio, sh*ratio);
break;
case magSlide::Fit:
slide->setSize(getWidth(), getHeight());
break;
}
// Add transitions:
static ofParameterGroup bogusParamGroup; // This is temporary so that things compile
auto tf = magSlideTransitionFactory::instance();
// slide->buildIn = tf->createTransition(settings.transitionName,
// slide,
// bogusParamGroup,
// slide->buildInDuration);
slide->transition = tf->createTransition(settings.transitionName,
slide,
bogusParamGroup,
slide->buildOutDuration);
//// void method(const void * sender, ArgumentsType &args)
ofAddListener(slide->slideStateChangedEvent, this, &magSlideShowSource::slideStateChanged);
ofAddListener(slide->slideCompleteEvent, this, &magSlideShowSource::slideComplete);
}
void magSlideShowSource::play() {
if (!isPlaying)
{
runningTime = 0;
lastTime = ofGetElapsedTimeMillis();
isPlaying = true;
auto currentSlide = slides[currentSlideIndex];
enqueueSlide(currentSlide, ofGetElapsedTimeMillis());
}
}
void magSlideShowSource::pause() {
isPlaying = false;
}
void magSlideShowSource::playNextSlide() {
//TODO
// I should check here to see if there are less than two slides.
// If so, we should probably return
currentSlideIndex += direction;
ofEventArgs args;
// This makes sure that we are doing a signed integer comparison,
// otherwise things get weird
int num = slides.size();
switch (settings.loopType)
{
case LoopType::NONE:
if (currentSlideIndex >= slides.size() || currentSlideIndex < 0)
{
// If we are not looping and we are out of bounds, return
// without enqueueing a slide. This will cause the slide show
// to end once the last slide builds out.
return;
}
break;
case LoopType::NORMAL:
if (currentSlideIndex >= num)
{
loopCount++;
if (loopCount == settings.numLoops)
{
// Return without enqueueing a new slide if we have
// reached the max number of loops.
return;
}
currentSlideIndex = 0;
ofNotifyEvent(slideshowWillLoopEvent, args, this);
}
else if (currentSlideIndex < 0)
{
loopCount++;
if (loopCount == settings.numLoops)
{
// Return without enqueueing a new slide if we have
// reached the max number of loops.
return;
}
currentSlideIndex = slides.size()-1;
ofNotifyEvent(slideshowWillLoopEvent, args, this);
}
break;
case LoopType::PING_PONG:
int num = slides.size();
if (currentSlideIndex >= num)
{
loopCount++;
if (loopCount == settings.numLoops)
{
// Return without enqueueing a new slide if we have
// reached the max number of loops.
return;
}
direction = -1;
currentSlideIndex = slides.size()-2;
ofNotifyEvent(slideshowWillLoopEvent, args, this);
}
else if (currentSlideIndex < 0)
{
loopCount++;
if (loopCount == settings.numLoops)
{
// Return without enqueueing a new slide if we have
// reached the max number of loops.
return;
}
direction = 1;
currentSlideIndex = 1;
ofNotifyEvent(slideshowWillLoopEvent, args, this);
}
break;
}
enqueueSlide(slides[currentSlideIndex], ofGetElapsedTimeMillis());
}
void magSlideShowSource::playPrevSlide() {
currentSlideIndex -= (direction*2);
playNextSlide();
}
void magSlideShowSource::playSlide(int slideIndex) {
currentSlideIndex = slideIndex-direction;
playNextSlide();
}
void magSlideShowSource::enqueueSlide(std::shared_ptr<magSlide> slide, u_int64_t startTime) {
// ofLogVerbose() << "Enqueuing slide " << currentSlideIndex << " slide id: " << slide->getId();
slide->start(startTime);
activeSlides.insert(activeSlides.begin(), slide);
}
void magSlideShowSource::slideStateChanged(const void *sender, ofEventArgs &args) {
magSlide *slide = (magSlide *) sender;
// ofLogVerbose("slideStateChanged") << "Slide id: " << slide->getId() << " Slide state: "
// << slide->getSlideStateName();
if (slide->getSlideState() == magSlide::SlideState::BuildOut)
{
// Flag that we need to load the next slide:
doPlayNextSlide = true;
}
}
void magSlideShowSource::slideComplete(const void *sender, ofEventArgs &args) {
magSlide *slide = (magSlide *) sender;
// ofLogVerbose() << "Slide Complete. id: " << slide->getId();
slide->isComplete = true;
}
void magSlideShowSource::fileAddedListener(const void *sender) {
doInit = true;
}
void magSlideShowSource::fileRemovedListener(const void *sender) {
doInit = true;
}

168
src/Sources/magSlideShowSource.h

@ -0,0 +1,168 @@
//
// magSlideShowSource.h
// Copyright (c) 2017 Cristobal Mendoza
// http://cuppetellimendoza.com
//
#ifndef MAGSLIDESHOWSOURCE_H
#define MAGSLIDESHOWSOURCE_H
#include "FboSource.h"
#include "magSlide.h"
#include "DirectoryWatcher.h"
class magSlide;
class magSlideShowSource : public ofx::piMapper::FboSource {
public:
magSlideShowSource();
/**
* Default settings file name.
*/
static const std::string SettingsFileName;
struct Settings; // forward declaration
bool initialize(magSlideShowSource::Settings settings);
void setup() override;
void update() override;
void draw() override;
/**
* Removes all slides and then attempts to create a new slide show
* based on the contents of the ofDirectory specified. The files may
* be images or videos, which will be transformed into the appropriate slide type.
* Any other file type in the directory is ignored (including other directories).
* The slide order is alphabetical according to filename.
*
* @param dir The ofDirectory to use as a source for a slide show.
* @return true if at least one slide was created. false is returned
* otherwise. Check the console for the specific error.
*/
bool createFromFolderContents(std::string path);
bool loadFromXml(std::string path);
void addSlide(std::shared_ptr<magSlide> slide);
void play();
void pause();
void playNextSlide();
void playPrevSlide();
void playSlide(int slideIndex);
enum LoopType : int {
NONE = 0,
NORMAL,
PING_PONG
};
struct Settings {
/**
* The pixel width of the FBO.
*/
float width = 1280;
/**
* The pixel height of the FBO.
*/
float height = 720;
/**
* An optional default slide duration, in seconds.
* If a slide specifies a duration this value is ignored.
*/
float slideDuration = 5;
/**
* The default transition for the slide show.
*/
std::string transitionName = "Dissolve";
/**
* Default transition duration.
*/
float transitionDuration = 1;
/**
* If specified, all applicable files in the folder will
* be used as slides in the slide show. They will be ordered
* alphabetically according to their file names.
*
* If path is relative, the root will likely be the Data folder.
*/
std::string slidesFolderPath = "sources/images";
/**
* If specified,
*/
std::string slideshowFilePath;
/**
* Loop type for the slide show. See @code LoopType for options.
* The default is @code LoopType:None.
*/
LoopType loopType = LoopType::NONE;
/**
* The number of loops to perform, if the loopType is not NONE.
* If the value is 0 or less than 0, the slide show loops forever.
*/
int numLoops = 0;
/**
* The resizing option for the slide show. The default is FitProportionally.
* If a slide already has a resizing option applied, that option will be
* respected and this resizeOption will not be used.
*/
magSlide::ResizeOptions resizeOption = magSlide::ResizeOptions::FitProportionally;
};
////////////////////////////////////////////
//// Event Listeners
////////////////////////////////////////////
void slideStateChanged(const void *sender, ofEventArgs &args);
void slideComplete(const void *sender, ofEventArgs &args);
virtual ~magSlideShowSource();
/**
* Fires when the slide show is done, which happens when
* the loop count is equal to Settings::numLoops, or when
* the last slide is played when @code LoopType::NONE is specified.
* Sender is this slide show.
*/
ofEvent<ofEventArgs> slideshowCompleteEvent;
/**
* Fires when the slide show reaches the last slide
* and will perform a loop in the next call.
* Sender is this slide show.
*/
ofEvent<ofEventArgs> slideshowWillLoopEvent;
protected:
Settings settings;
std::vector<std::shared_ptr<magSlide>> slides;
private:
// std::shared_ptr<magSlide> currentSlide;
std::vector<std::shared_ptr<magSlide>> activeSlides;
void enqueueSlide(std::shared_ptr<magSlide> slide, u_int64_t startTime);
u_int64_t lastTime;
u_int64_t deltaTime;
u_int64_t runningTime;
bool isInitialized = false;
bool isPlaying = false;
int currentSlideIndex = 0;
int direction = 1;
int loopCount = 0;
ofx::piMapper::DirectoryWatcher* directoryWatcher;
void fileAddedListener(const void *sender);
void fileRemovedListener(const void *sender);
bool doInit;
bool doPlayNextSlide = false;
};
#endif

60
src/Sources/magSlideTransition.cpp

@ -0,0 +1,60 @@
//
// magSlideTransition.cpp
// Copyright (c) 2017 Cristobal Mendoza
// http://cuppetellimendoza.com
//
#include "magSlideTransition.h"
void magSlideTransition::start(std::shared_ptr<magSlide> nextSlide)
{
runningTime = 0;
active = true;
this->nextSlide = nextSlide;
}
void magSlideTransition::update(u_int64_t timeDelta)
{
if (!active) return;
runningTime += timeDelta;
if (runningTime >= duration)
{
ofEventArgs arghh; // arghhhh...
nextSlide->setOpacity(255);
nextSlide->setPosition(0, 0);
end();
transitionCompleteEvent.notify(this, arghh);
active = false;
}
}
u_int64_t magSlideTransition::getRunningTime()
{
return runningTime;
}
float magSlideTransition::getNormalizedTime()
{
return (double)runningTime / (double)duration;
}
void magDissolveTransition::draw()
{
slide->setOpacity(255 - (getNormalizedTime() * 255));
nextSlide->setOpacity(getNormalizedTime()*255);
}
void magDissolveTransition::start(std::shared_ptr<magSlide> nextSlide)
{
magSlideTransition::start(nextSlide);
nextSlide->setOpacity(0);
}
void magDissolveTransition::end()
{
nextSlide->setOpacity(255);
slide->setOpacity(0);
}

129
src/Sources/magSlideTransition.h

@ -0,0 +1,129 @@
//
// magSlideTransition.h
// Copyright (c) 2017 Cristobal Mendoza
// http://cuppetellimendoza.com
//
#ifndef MAGSLIDETRANSITION_H
#define MAGSLIDETRANSITION_H
#include "magSlide.h"
class magSlideTransitionFactory;
class magSlideTransition
{
public:
/**
* Subclassing notes: Make sure to provide a value for name in your
* magSlideTransition subclass.
*/
magSlideTransition() { name = "Void"; }
/**
* Begins the transition. This needs to be called in order for the
* transition to do anything.
* @param nextSlide The subsequent slide in the slide show needs to be
* passed here.
*
* You can override this call to set any initial conditions to the transition,
* but make sure to call this method in your override.
*/
virtual void start(std::shared_ptr<magSlide> nextSlide);
/**
* Called automatically when the transition is complete. Useful to set
* end states for the parameters of the slide and nextSlide. Default implementation
* does nothing.
*/
virtual void end(){}
/**
* NOTE: The transition settings system is not yet implemented.
* Called when the transition is created. Loads settings for magSlideTransition
* subclasses. The default implementation does nothing.
* @param settings ofParameterGroup with settings for your custom magSlideTransition
* subclass.
*/
virtual void loadSettings(ofParameterGroup &settings){}
/**
* Updates the transition.
* @param timeDelta The elapsed time (in ms.) between the last call to update() and
* this one (i.e. the frame time).
*
* If you need to override update, make sure to call this implementation
* in your subclass.
*/
virtual void update(u_int64_t timeDelta);
/**
* Draws the transition. Default implementation does nothing.
*/
virtual void draw(){
ofLogNotice("magSlideTransition") << "transition draw - this should be overriden " << getNormalizedTime();
}
/**
* Current running time in milliseconds.
* @return u_int64_t
*/
u_int64_t getRunningTime();
/**
* Returns the current time in normalized form.
* 0 = start, 1 = end.
* @return Float between 0 and 1.
*/
float getNormalizedTime();
string const &getName() const
{
return name;
}
const shared_ptr<magSlide> &getNextSlide() const
{
return nextSlide;
}
bool isActive() const
{
return active;
}
/**
* Sender is a raw pointer to this magSlideTransition
*/
ofEvent<ofEventArgs> transitionCompleteEvent;
protected:
std::string name;
std::shared_ptr<magSlide> slide;
std::shared_ptr<magSlide> nextSlide;
u_int64_t runningTime;
u_int64_t duration;
u_int64_t endTime;
bool active = false;
protected:
friend class magSlideTransitionFactory;
};
class magDissolveTransition : public magSlideTransition
{
public:
magDissolveTransition()
{
name = "Dissolve";
}
void start(std::shared_ptr<magSlide> nextSlide) override;
void draw() override;
void end() override;
};
#endif

76
src/Sources/magSlideTransitionFactory.cpp

@ -0,0 +1,76 @@
//
// Created by Cristobal Mendoza on 12/3/17.
//
#include "magSlideTransitionFactory.h"
/*
*
*
*
class Base {};
class DerivedA : public Base {};
class DerivedB : public Base {};
class DerivedC : public Base {};
Base* create(const std::string& type)
{
static std::map<std::string, std::function<Base*()>> type_creator_map =
{
{"DerivedA", [](){return new DerivedA();}},
{"DerivedB", [](){return new DerivedB();}},
{"DerivedC", [](){return new DerivedC();}}
};
auto it = type_creator_map.find(type);
if(it != type_creator_map.end())
{
return it->second();
}
return nullptr;
}
*/
magSlideTransitionFactory* magSlideTransitionFactory::_instance = 0;
magSlideTransitionFactory::magSlideTransitionFactory()
{
magSlideTransition voidTransition;
magDissolveTransition dissolve;
registerTransition<magSlideTransition>(voidTransition);
registerTransition<magDissolveTransition>(dissolve);
}
magSlideTransitionFactory* magSlideTransitionFactory::instance()
{
if (_instance == 0)
{
_instance = new magSlideTransitionFactory();
}
return _instance;
}
std::shared_ptr<magSlideTransition>
magSlideTransitionFactory::createTransition(std::string transitionName, std::shared_ptr<magSlide> slide,
ofParameterGroup &settings, u_int64_t duration)
{
std::shared_ptr<magSlideTransition> transition;
if (transitionsMap.count(transitionName) > 0)
{
transition = transitionsMap[transitionName]();
}
else
{
transition = transitionsMap[transition->getName()]();
}
transition->slide = slide;
transition->duration = duration;
transition->loadSettings(settings);
return transition;
}

41
src/Sources/magSlideTransitionFactory.h

@ -0,0 +1,41 @@
//
// Created by Cristobal Mendoza on 12/3/17.
//
#ifndef MAGSLIDETRANSITIONFACTORY_H
#define MAGSLIDETRANSITIONFACTORY_H
#include "magSlide.h"
#include "magSlideTransition.h"
/**
* Factory class to register and instantiate transitions.
*/
class magSlideTransitionFactory
{
public:
static magSlideTransitionFactory* instance();
std::shared_ptr<magSlideTransition> createTransition(string transitionName,
std::shared_ptr<magSlide> slide,
ofParameterGroup &group,
u_int64_t duration);
template<typename T>
void registerTransition(T transition)
{
if (transitionsMap.count(transition.getName()) == 0)
{
transitionsMap[transition.getName()] = [](){
return std::make_shared<T>();
};
}
}
protected:
std::unordered_map<std::string, std::function<std::shared_ptr<magSlideTransition>()>> transitionsMap;
private:
static magSlideTransitionFactory* _instance;
magSlideTransitionFactory();
};
#endif //MAGSLIDETRANSITIONFACTORY_H
Loading…
Cancel
Save