24 changed files with 2186 additions and 582 deletions
@ -1,4 +1,4 @@ |
|||
ofxGui |
|||
ofxPiMapper |
|||
ofxXmlSettings |
|||
ofxGui |
|||
ofxOMXPlayer |
|||
|
@ -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> |
File diff suppressed because it is too large
@ -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; |
|||
} |
|||
|
@ -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 |
@ -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; |
|||
} |
|||
|
|||
|
@ -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 |
@ -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); |
|||
} |
@ -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 |
@ -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; |
|||
} |
@ -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…
Reference in new issue