GPUFont with layers

This commit is contained in:
jrkb 2023-04-13 17:15:35 +02:00
parent dce5e7c57a
commit 70eb4f97f4
8 changed files with 236 additions and 109 deletions

View file

@ -19,6 +19,9 @@
namespace ofxVariableLab { namespace ofxVariableLab {
struct AtlasLayerComboSettings {
};
struct ComboIdentifier { struct ComboIdentifier {
std::string fontPath = "data/celines-fonts/Version-2-var.ttf"; std::string fontPath = "data/celines-fonts/Version-2-var.ttf";
Layer::Type type = Layer::MSDFGEN; Layer::Type type = Layer::MSDFGEN;
@ -31,9 +34,11 @@ struct ComboIdentifier {
class AtlasLayerCombo { class AtlasLayerCombo {
public: public:
virtual void setup(const ComboIdentifier & identifier) = 0; virtual void setup(const ComboIdentifier & identifier,
AtlasLayerComboSettings settings) = 0;
virtual void update() = 0; virtual void update() = 0;
virtual void careForChild(shared_ptr <Layer> layer) = 0; virtual void careForChild(shared_ptr <Layer> layer) = 0;
virtual void abandonChild(shared_ptr <Layer> layer) = 0;
virtual const ComboIdentifier & getIdentifier() const = 0; virtual const ComboIdentifier & getIdentifier() const = 0;
virtual void setVFlip(VFlipState vFlipState) = 0; virtual void setVFlip(VFlipState vFlipState) = 0;

View file

@ -1,5 +1,6 @@
#include "GPUFontAtlasLayerCombo.h" #include "GPUFontAtlasLayerCombo.h"
#include "Utils.h" #include "Utils.h"
#include "font.hpp"
#include "ofColor.h" #include "ofColor.h"
#include "ofEvents.h" #include "ofEvents.h"
#include "ofGraphics.h" #include "ofGraphics.h"
@ -7,8 +8,10 @@
namespace ofxVariableLab { namespace ofxVariableLab {
void GPUFontAtlasLayerCombo::setup(const ComboIdentifier & identifier){ void GPUFontAtlasLayerCombo::setup(const ComboIdentifier & identifier,
AtlasLayerComboSettings settings){
this->identifier = identifier; this->identifier = identifier;
this->settings = static_cast <GPUFontAtlasLayerComboSettings &>(settings);
#ifdef TARGET_EMSCRIPTEN #ifdef TARGET_EMSCRIPTEN
string shaderDir = "data/ofxGPUFont/shaders/DEBUG_ES3"; string shaderDir = "data/ofxGPUFont/shaders/DEBUG_ES3";
#else #else
@ -25,16 +28,14 @@ void GPUFontAtlasLayerCombo::setup(const ComboIdentifier & identifier){
ofExit(); ofExit();
} }
} }
currentFontPath = "data/celines-fonts/Version-2-var.ttf";
mainText = "whatever";
ofxGPUFont::tryUpdateMainFont(library, ofxGPUFont::tryUpdateMainFont(library,
currentFontPath, this->identifier.fontPath,
mainText, mainText,
font, font,
bb); this->settings.gpuTextureOffset);
font->listFontVariationAxes(fontVariationAxesParameters, library); font->listFontVariationAxes(fontVariationAxesParameters, library);
cout << currentFontPath << " : fontVariationAxes :" << endl; cout << this->identifier.fontPath << " : fontVariationAxes :" << endl;
for(const auto & axis : fontVariationAxesParameters){ for(const auto & axis : fontVariationAxesParameters){
cout << std::fixed << std::setprecision(10) << axis.name << " :\n" cout << std::fixed << std::setprecision(10) << axis.name << " :\n"
<< "\tmin: " << std::to_string(axis.minValue) << "\tmin: " << std::to_string(axis.minValue)
@ -49,29 +50,76 @@ void GPUFontAtlasLayerCombo::setup(const ComboIdentifier & identifier){
cout << "Render type " << r->getType() << endl; cout << "Render type " << r->getType() << endl;
} }
void GPUFontAtlasLayerCombo::update(){ void GPUFontAtlasLayerCombo::update(){
//#ifndef TARGET_OPENGLES //float animationSpeed = 1.0;
//if(ofGetFrameNum() % 10 == 0){ //float threshold = 1.0;
//shaderCatalog->requestUpdate("font"); //for(int i = 0; i < fontVariationAxes.size(); i++){
//}else if(ofGetFrameNum() % 10 == 5){ //ofxGPUFont::Font::FontVariationAxis & axis = fontVariationAxes[i];
//shaderCatalog->update(); //ofxGPUFont::Font::FontVariationAxisParameters & axisParams = fontVariationAxesParameters[i];
//double newValue = ofMap(sin(ofGetElapsedTimef() * animationSpeed),
//-1, 1,
//axisParams.minValue, axisParams.maxValue);
//if(abs(newValue - axis.value) > threshold){
//font->setFontVariationAxis(library, axis.name, newValue);
//axis.value = newValue;
//font->prepareGlyphsForText(mainText, true);
//} //}
//#endif //}
float animationSpeed = 1.0; if(isDirty){
float threshold = 1.0; //ofxGPUFont::tryUpdateMainFont(library,
for(int i = 0; i < fontVariationAxes.size(); i++){ //this->identifier.fontPath,
ofxGPUFont::Font::FontVariationAxis & axis = fontVariationAxes[i]; //mainText,
ofxGPUFont::Font::FontVariationAxisParameters & axisParams = fontVariationAxesParameters[i]; //font,
double newValue = ofMap(sin(ofGetElapsedTimef() * animationSpeed), //this->settings.gpuTextureOffset);
-1, 1, font->prepareGlyphsForText(mainText, true);
axisParams.minValue, axisParams.maxValue); isDirty = false;
if(abs(newValue - axis.value) > threshold){
font->setFontVariationAxis(library, axis.name, newValue);
axis.value = newValue;
font->prepareGlyphsForText(mainText, true);
}
} }
} }
void GPUFontAtlasLayerCombo::careForChild(shared_ptr <Layer> layer){ void GPUFontAtlasLayerCombo::careForChild(shared_ptr <Layer> layer){
if(layer->getType() == Layer::GPUFONT){
shared_ptr <GPUFontLayer> gpuFontLayer = static_pointer_cast <GPUFontLayer>(layer);
layers.push_back(gpuFontLayer);
std::string oldText = mainText;
std::string layerText = gpuFontLayer->getProps().text;
std::string text = oldText + layerText;
totalCharacters += layerText.length();
removeDuplicateCharacters(text);
// check if we added any characters
// TODO: check for variation variations
if(text.length() != oldText.length()){
mainText = text;
isDirty = true;
}
}else{
ofLogError("GPUFontAtlasLayerCombo::careForChild()") << __LINE__ << ": child is not recognized as Layer::GPUFONT";
}
}
void GPUFontAtlasLayerCombo::abandonChild(shared_ptr <Layer> layer){
// TODO: handle removing child text from buffer
shared_ptr <GPUFontLayer> gpuFontLayer = static_pointer_cast <GPUFontLayer>(layer);
layers.erase(
std::remove_if(
layers.begin(),
layers.end(),
[gpuFontLayer](shared_ptr <GPUFontLayer> const & l)
{
return l == gpuFontLayer;
}), layers.end());
// update the text
// this doesn't happen very often, so let's just
// iterate over all layers and calculate fresh
std::string text = "";
totalCharacters = 0;
for(const auto & layer : layers){
std::string layerText = layer->getProps().text;
totalCharacters += layerText.length();
text += layerText;
}
removeDuplicateCharacters(text);
mainText = text;
// NOTE: we do not want to update buffers now,
// as it's probably more costly than just keeping
// the old glyphs around...
} }
const ComboIdentifier & GPUFontAtlasLayerCombo::getIdentifier() const { const ComboIdentifier & GPUFontAtlasLayerCombo::getIdentifier() const {
return identifier; return identifier;
@ -89,13 +137,6 @@ void GPUFontAtlasLayerCombo::draw(){
int height = ofGetHeight(); int height = ofGetHeight();
glm::vec2 mouse = glm::vec2(ofGetMouseX(), ofGetMouseY()); glm::vec2 mouse = glm::vec2(ofGetMouseX(), ofGetMouseY());
float targetFontSize = 128;
float width_unit = 1;
float height_unit = (float)height / width;
//ofClear(125, 255, 125, 255);
//glm::mat4 projection = transform.getProjectionMatrix((float)width / height); //glm::mat4 projection = transform.getProjectionMatrix((float)width / height);
glm::mat4 projection = transform.getOrthoProjectionMatrix(width, glm::mat4 projection = transform.getOrthoProjectionMatrix(width,
height, height,
@ -158,37 +199,20 @@ void GPUFontAtlasLayerCombo::draw(){
//mouse.y = 0; //mouse.y = 0;
std::vector <ofxGPUFont::Font::BufferVertex> vertices; std::vector <ofxGPUFont::Font::BufferVertex> vertices;
std::vector <int32_t> indices; std::vector <int32_t> indices;
string text = "whatever";
int n_texts = 240;
vertices.resize(text.length() * n_texts * 4);
indices.resize(text.length() * n_texts * 6);
cx = 0.5f * (bb.minX + bb.maxX);
cy = 0.5f * (bb.minY + bb.maxY);
for(int i = 0; i < n_texts - 1; i++){ for(const auto & layer : layers){
float pipi = ((PI * 2 * i) / (n_texts - 1)); font->collectVerticesAndIndices(glm::vec3(layer->getProps().x,
float shift = (400 * cos(ofGetElapsedTimef() * 0.125 + pipi)); layer->getProps().y,
float shift_x = (600 * sin(ofGetElapsedTimef() * 0.125 + pipi)); 0),
float shift_o = (100 * sin(ofGetElapsedTimef() * 0.4 + pipi)); layer->getProps().text,
font->collectVerticesAndIndices( vertices, indices, true,
glm::vec3(mouse.x + shift_x, layer->getProps().fontSize_px);
mouse.y + shift + shift_o,
0),
text,
vertices,
indices,
true,
int(ofMap(sin(ofGetElapsedTimef() + pipi), -1, 1, 42, 96)));
} }
font->collectVerticesAndIndices( vertices.resize(totalCharacters * 4);
glm::vec3(mouse.x, indices.resize(totalCharacters * 6);
mouse.y, //cx = 0.5f * (bb.minX + bb.maxX);
0), //cy = 0.5f * (bb.minY + bb.maxY);
text,
vertices,
indices,
true,
ofMap(sin(ofGetElapsedTimef() * 0.25f), -1.0f, 1.0f, 42.0f, 96.0f));
font->draw(vertices, indices); font->draw(vertices, indices);
//ofRectangle rectangle(0, 0, width, height); //ofRectangle rectangle(0, 0, width, height);
//ofDrawRectangle(rectangle); //ofDrawRectangle(rectangle);
@ -208,9 +232,7 @@ void GPUFontAtlasLayerCombo::draw(){
ofDrawBitmapStringHighlight( ofDrawBitmapStringHighlight(
"fps: " + ofToString(ofGetFrameRate()) + "\n" "fps: " + ofToString(ofGetFrameRate()) + "\n"
+ "font: " + currentFontPath + "\n" + "font: " + this->identifier.fontPath + "\n"
+ "cx: " + ofToString(cx) + "\n"
+ "cy: " + ofToString(cy) + "\n"
, 20, 20); , 20, 20);
} }

View file

@ -10,6 +10,9 @@
#endif #endif
namespace ofxVariableLab { namespace ofxVariableLab {
struct GPUFontAtlasLayerComboSettings : public AtlasLayerComboSettings {
int gpuTextureOffset = 0;
};
class GPUFontAtlasLayerCombo : public AtlasLayerCombo { class GPUFontAtlasLayerCombo : public AtlasLayerCombo {
struct Transform { struct Transform {
enum Origin { enum Origin {
@ -53,9 +56,11 @@ class GPUFontAtlasLayerCombo : public AtlasLayerCombo {
} }
}; };
public: public:
void setup(const ComboIdentifier & identifier) override; void setup(const ComboIdentifier & identifier,
AtlasLayerComboSettings settings = GPUFontAtlasLayerComboSettings()) override;
void update() override; void update() override;
void careForChild(shared_ptr <Layer> layer) override; void careForChild(shared_ptr <Layer> layer) override;
void abandonChild(shared_ptr <Layer> layer) override;
const ComboIdentifier & getIdentifier() const override; const ComboIdentifier & getIdentifier() const override;
void setVFlip(VFlipState vFlipState) override; void setVFlip(VFlipState vFlipState) override;
@ -63,8 +68,11 @@ class GPUFontAtlasLayerCombo : public AtlasLayerCombo {
void draw(); void draw();
shared_ptr <ofxGPUFont::Font> font; shared_ptr <ofxGPUFont::Font> font;
string mainText = "";
bool isDirty = false;
private: private:
GPUFontAtlasLayerComboSettings settings;
FT_Library library; FT_Library library;
vector <shared_ptr <GPUFontLayer> > layers; vector <shared_ptr <GPUFontLayer> > layers;
ComboIdentifier identifier; ComboIdentifier identifier;
@ -76,12 +84,8 @@ class GPUFontAtlasLayerCombo : public AtlasLayerCombo {
//std::shared_ptr <ShaderCatalog::Entry> backgroundShader; //std::shared_ptr <ShaderCatalog::Entry> backgroundShader;
std::shared_ptr <ofxGPUFont::ShaderCatalog::Entry> fontShader; std::shared_ptr <ofxGPUFont::ShaderCatalog::Entry> fontShader;
ofxGPUFont::Font::BoundingBox bb;
Transform transform; Transform transform;
string currentFontPath;
string mainText;
// Size of the window (in pixels) used for 1-dimensional anti-aliasing along each rays. // Size of the window (in pixels) used for 1-dimensional anti-aliasing along each rays.
// 0 - no anti-aliasing // 0 - no anti-aliasing
// 1 - normal anti-aliasing // 1 - normal anti-aliasing
@ -97,7 +101,7 @@ class GPUFontAtlasLayerCombo : public AtlasLayerCombo {
std::vector <ofxGPUFont::Font::FontVariationAxis> fontVariationAxes; std::vector <ofxGPUFont::Font::FontVariationAxis> fontVariationAxes;
VFlipState vFlip; VFlipState vFlip;
bool isDirty = false; int totalCharacters = 0;
}; };
} }

View file

@ -1,5 +1,6 @@
#include "LayerComposition.h" #include "LayerComposition.h"
#include "Atlas.h" #include "Atlas.h"
#include "GPUFontAtlasLayerCombo.h"
#include "MsdfLayer.h" #include "MsdfLayer.h"
#include "Utils.h" #include "Utils.h"
#include "ofUtils.h" #include "ofUtils.h"
@ -8,10 +9,10 @@
namespace ofxVariableLab { namespace ofxVariableLab {
void LayerComposition::setup(){ void LayerComposition::setup(){
auto combo = make_shared <GPUFontAtlasLayerCombo>(); //auto combo = make_shared <GPUFontAtlasLayerCombo>();
ComboIdentifier comboIdentifier = {"lol", Layer::GPUFONT}; //ComboIdentifier comboIdentifier = {"lol", Layer::GPUFONT};
combo->setup(comboIdentifier); //combo->setup(comboIdentifier);
atlasLayerCombos[comboIdentifier] = combo; //atlasLayerCombos[comboIdentifier] = combo;
} }
void LayerComposition::update(){ void LayerComposition::update(){
@ -35,40 +36,66 @@ LayerID LayerComposition::addLayer(const ComboIdentifier & identifier,
const Layer::Props & props, const Layer::Props & props,
const std::vector <FontVariation> & variations){ const std::vector <FontVariation> & variations){
string layerID = ""; string layerID = "";
if(atlasLayerCombos.count(identifier) <= 0){ switch(identifier.type){
switch(identifier.type){ case Layer::GPUFONT: {
case Layer::GPUFONT: { shared_ptr <GPUFontAtlasLayerCombo> combo;
ofLogError("LayerComposition::addLayer") auto comboIterator = atlasLayerCombos.find(identifier);
<< "GPUFont not implemented" << endl; if(comboIterator == atlasLayerCombos.end()){
break; // we don't have one yet
// so let's create it
combo = make_shared <GPUFontAtlasLayerCombo>();
GPUFontAtlasLayerComboSettings settings;
settings.gpuTextureOffset = nextGpuTextureOffset;
combo->setup(identifier, settings);
nextGpuTextureOffset++;
atlasLayerCombos[identifier] = combo;
}else{
// use existing combo
combo = dynamic_pointer_cast <GPUFontAtlasLayerCombo>(comboIterator->second);
} }
default: auto layer = make_shared <GPUFontLayer>();
case Layer::MSDFGEN: { layer->setProps(props);
// TODO: put most stuff in combo setup combo->careForChild(layer);
auto combo = make_shared <MsdfAtlasLayerCombo>(); layerID = layer->getId();
combo->setup(identifier); // TODO: add here text and variations layers[layerID] = layer;
auto layer = make_shared <MsdfLayer>();
layer->vFlip = vFlipState; break;
layer->setProps(props); }
layer->setup();
std::vector <ofxMsdfgen::FontVariation> msdfVariations; default:
for(const auto & v : variations){ case Layer::MSDFGEN: {
msdfVariations.push_back({v.name, v.value}); // TODO: put most stuff in combo setup
} auto combo = make_shared <MsdfAtlasLayerCombo>();
// TODO: do not add Variation to atlas, but to combo, combo->setup(identifier); // TODO: add here text and variations
// so we know it's dirty auto layer = make_shared <MsdfLayer>();
combo->atlas->addVariations(msdfVariations); layer->vFlip = vFlipState;
combo->careForChild(layer); layer->setProps(props);
layers[layer->getId()] = layer; layer->setup();
atlasLayerCombos[identifier] = std::move(combo); std::vector <ofxMsdfgen::FontVariation> msdfVariations;
layerID = layer->getId(); for(const auto & v : variations){
msdfVariations.push_back({v.name, v.value});
} }
} // TODO: do not add Variation to atlas, but to combo,
// so we know it's dirty
combo->atlas->addVariations(msdfVariations);
combo->careForChild(layer);
layers[layer->getId()] = layer;
atlasLayerCombos[identifier] = std::move(combo);
layerID = layer->getId();
}
} }
return layerID; return layerID;
} }
void LayerComposition::removeLayer(const LayerID & id){
auto layer = getLayer(id);
for(auto & it : atlasLayerCombos){
it.second->abandonChild(layer);
}
layers.erase(id);
}
shared_ptr <Layer> LayerComposition::getLayer(const LayerID & layerID){ shared_ptr <Layer> LayerComposition::getLayer(const LayerID & layerID){
return layers[layerID]; return layers[layerID];
} }

View file

@ -6,6 +6,7 @@
#include "MsdfLayer.h" #include "MsdfLayer.h"
#include "GPUFontAtlasLayerCombo.h" #include "GPUFontAtlasLayerCombo.h"
#include "GPUFontLayer.h" #include "GPUFontLayer.h"
#include "Utils.h"
namespace ofxVariableLab { namespace ofxVariableLab {
@ -20,14 +21,22 @@ class LayerComposition {
LayerID addLayer(const ComboIdentifier & identifier, LayerID addLayer(const ComboIdentifier & identifier,
const Layer::Props & props, const Layer::Props & props,
const std::vector <FontVariation> & variations); const std::vector <FontVariation> & variations);
shared_ptr <ofxVariableLab::Layer> getLayer(const LayerID & layerID); void removeLayer(const LayerID & id); // TODO: make bool, to catch nonexisting
shared_ptr <Layer> getLayer(const LayerID & layerID);
const unordered_map <ComboIdentifier, shared_ptr <AtlasLayerCombo> > & getAtlasLayerCombos() const; const unordered_map <ComboIdentifier, shared_ptr <AtlasLayerCombo> > & getAtlasLayerCombos() const;
void setVFlip(bool vFlip); void setVFlip(bool vFlip);
private: private:
VFlipState vFlipState = V_FLIP_UNKNOWN; VFlipState vFlipState = V_FLIP_UNKNOWN;
unordered_map <LayerID, shared_ptr <ofxVariableLab::Layer> > layers; unordered_map <LayerID, shared_ptr <Layer> > layers;
unordered_map <ComboIdentifier, shared_ptr <AtlasLayerCombo> > atlasLayerCombos; unordered_map <ComboIdentifier, shared_ptr <AtlasLayerCombo> > atlasLayerCombos;
/**
* @brief lastGpuTextureOffset GL_TEXTURE0 + offset
* we want to different textures for different fonts
* this value is incremented/decremented when adding/removing GPUFont layers
*/
int nextGpuTextureOffset = 0;
//unordered_map <LayerIdentifier, shared_ptr <ofxVariableLab::Layer> > layers; //unordered_map <LayerIdentifier, shared_ptr <ofxVariableLab::Layer> > layers;
}; };

View file

@ -1,16 +1,19 @@
#include "MsdfAtlasLayerCombo.h" #include "MsdfAtlasLayerCombo.h"
#include "AtlasLayerCombo.h"
#include "Utils.h" #include "Utils.h"
namespace ofxVariableLab { namespace ofxVariableLab {
void MsdfAtlasLayerCombo::setup(const ComboIdentifier & layerIdentifier){ void MsdfAtlasLayerCombo::setup(const ComboIdentifier & layerIdentifier,
AtlasLayerComboSettings settings){
this->identifier = layerIdentifier; this->identifier = layerIdentifier;
this->settings = static_cast <MsdfAtlasLayerComboSettings &>(settings);
ofxMsdfgen::AtlasSettings atlasSettings; ofxMsdfgen::AtlasSettings atlasSettings;
//settings.characters = "ABCDEFGHIJKL"; //settings.characters = "ABCDEFGHIJKL";
atlasSettings.scale = 64; atlasSettings.scale = this->settings.scale;
atlasSettings.minimumScale = 64; atlasSettings.minimumScale = this->settings.minimumScale;
atlasSettings.characters = ""; atlasSettings.characters = "";
atlasSettings.maxInterpolationStepSize = 50.0; atlasSettings.maxInterpolationStepSize = this->settings.maxInterpolationStepSize;
atlas = make_shared <ofxMsdfgen::Atlas>(); atlas = make_shared <ofxMsdfgen::Atlas>();
@ -45,6 +48,17 @@ void MsdfAtlasLayerCombo::careForChild(shared_ptr <Layer> layer){
} }
layers.push_back(msdfLayer); layers.push_back(msdfLayer);
} }
void MsdfAtlasLayerCombo::abandonChild(shared_ptr <Layer> layer){
shared_ptr <MsdfLayer> msdfLayer = dynamic_pointer_cast <MsdfLayer>(layer);
layers.erase(
std::remove_if(
layers.begin(),
layers.end(),
[msdfLayer](shared_ptr <MsdfLayer> const & l)
{
return l == msdfLayer;
}), layers.end());
}
const ComboIdentifier & MsdfAtlasLayerCombo::getIdentifier() const { const ComboIdentifier & MsdfAtlasLayerCombo::getIdentifier() const {
return identifier; return identifier;

View file

@ -5,11 +5,18 @@
#include "Utils.h" #include "Utils.h"
namespace ofxVariableLab { namespace ofxVariableLab {
struct MsdfAtlasLayerComboSettings : public AtlasLayerComboSettings {
float scale = 64;
float minimumScale = 64;
float maxInterpolationStepSize = 50.0;
};
class MsdfAtlasLayerCombo : public AtlasLayerCombo { class MsdfAtlasLayerCombo : public AtlasLayerCombo {
public: public:
void setup(const ComboIdentifier & identifier) override; void setup(const ComboIdentifier & identifier,
AtlasLayerComboSettings settings = MsdfAtlasLayerComboSettings()) override;
void update() override; void update() override;
void careForChild(shared_ptr <Layer> layer) override; void careForChild(shared_ptr <Layer> layer) override;
void abandonChild(shared_ptr <Layer> layer) override;
const ComboIdentifier & getIdentifier() const override; const ComboIdentifier & getIdentifier() const override;
void setVFlip(VFlipState vFlipState) override; void setVFlip(VFlipState vFlipState) override;
const ofImage & getAtlasImage(); const ofImage & getAtlasImage();
@ -18,6 +25,7 @@ class MsdfAtlasLayerCombo : public AtlasLayerCombo {
shared_ptr <ofxMsdfgen::Atlas> atlas; shared_ptr <ofxMsdfgen::Atlas> atlas;
private: private:
MsdfAtlasLayerComboSettings settings;
vector <shared_ptr <MsdfLayer> > layers; vector <shared_ptr <MsdfLayer> > layers;
vector <ofxMsdfgen::GlyphGeometry> glyphGeometries; vector <ofxMsdfgen::GlyphGeometry> glyphGeometries;
shared_ptr <ofShader> msdfShader; shared_ptr <ofShader> msdfShader;

View file

@ -1,6 +1,8 @@
#pragma once #pragma once
#include <algorithm>
#include <string> #include <string>
#include <set>
#include <glm/glm.hpp> #include <glm/glm.hpp>
namespace ofxVariableLab { namespace ofxVariableLab {
@ -22,6 +24,42 @@ inline void hash_combine(std::size_t & seed, const T & v){
std::hash <T> hasher; std::hash <T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
} }
//template <class ForwardIt, class UnaryPredicate>
//ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p){
//first = std::find_if(first, last, p);
//if(first != last){
//for(ForwardIt i = first; ++i != last;){
//if(!p(*i)){
//*first++ = std::move(*i);
//}
//}
//}
//return first;
//}
inline void removeDuplicateCharacters(std::string & str){
std::set <char> chars;
str.erase(
std::remove_if(
str.begin(),
str.end(),
[&chars](char i){
// If encountered character, remove this one.
if(chars.count(i)){
return true;
}
// Otherwise, mark this character encountered and don't remove.
chars.insert(i);
return false;
}
),
str.end()
);
}
enum VFlipState { enum VFlipState {
V_FLIP_UNKNOWN = 0, V_FLIP_UNKNOWN = 0,
V_FLIP_ON, V_FLIP_ON,