diff options
Diffstat (limited to 'futuregael/src/vectortext.h')
| -rw-r--r-- | futuregael/src/vectortext.h | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/futuregael/src/vectortext.h b/futuregael/src/vectortext.h new file mode 100644 index 0000000..0f79e58 --- /dev/null +++ b/futuregael/src/vectortext.h @@ -0,0 +1,381 @@ +#pragma once + +#include "ofMain.h" +#include "ofxSvg.h" +#include "colourPolyline.h" + +class glyph{ +public: + glyph(char c,float w,vector<ofPolyline> lines){ + glyph(c,w,lines,ofColor(0,0,0)); + } + glyph(string s,float w,vector<ofPolyline> lines){ + glyph(s,w,lines,ofColor(0,0,0)); + } + glyph(char c,float w,vector<ofPolyline> lines,ofColor col){ + string str(1, c); + glyph(str,w,lines,col); + } + glyph(string str,float w,vector<ofPolyline> lines,ofColor col){ + chr=str; + width=w; + outline=lines; + colour=col; + } + void draw(float x, float y){ + ofSetColor(colour); + ofPushMatrix(); + ofTranslate(x,y); + for (auto& v:outline) v.draw(); + ofPopMatrix(); + } + void randomiseColour(){ + colour=ofColor::fromHsb(ofRandom(255.0),225,255); + } + string chr; + float width; + vector<ofPolyline> outline; + ofColor colour; +}; + +class glyphWord{ +public: + glyphWord(){amount=1.0f;} + vector<glyph> glyphs; + float amount; +}; + +class SVGFont{ + ofXml svg; + float enspace; + bool bisLoaded; +public: + bool isLoaded() { + return bisLoaded; + } + bool load(filesystem::path path){ + if( svg.load(path) ){ + enspace=getGlyph(" ").width; + ofLog()<<"loaded "<<path.stem().string(); + bisLoaded=true; + }else{ + bisLoaded=false; + ofLog()<<"unable to load "<<path<<" check data/ folder"; + } + return bisLoaded; + } + SVGFont(){ + bisLoaded=false; + } + SVGFont(filesystem::path path){ + load(path); + } + glyph getGlyph(string c,ofColor col=ofColor(255,255,255)){ + vector<ofPolyline> shapes; + ofPolyline shape; + string elementPath; + + elementPath = "/svg/defs/font/glyph[@unicode='"+c+"']"; + + ofXml xmlElement = svg.findFirst(elementPath); + + float charWidth = ofToFloat(xmlElement.getAttribute("horiz-adv-x").getValue()); + + vector<string> splitGlyphPath = ofSplitString(xmlElement.getAttribute("d").getValue(), " ");//glyph path data in SVG looks like this: "M 139 -9.45 L 230 18.9 L 299 22.1 L 227 25.2" + + for(int i=0; i<splitGlyphPath.size(); i+=3){ + if(splitGlyphPath[i] == "M"){ + if (shape.size()) { + shapes.push_back(shape); + shape.clear(); + } + shape.addVertex(ofToFloat(splitGlyphPath[i+1]), ofToFloat(splitGlyphPath[i+2])); + }else if(splitGlyphPath[i] == "L"){ + shape.lineTo(ofToFloat(splitGlyphPath[i+1]), ofToFloat(splitGlyphPath[i+2])); + } + } + if (shape.size()) shapes.push_back(shape); + return glyph(c,charWidth,shapes,col); + } +}; + + +class glyphbanner{ + ofXml SVGFont; + vector<glyphWord> words; + vector<colourPolyline> outlines; + ofVec2f centre; + float lastUpdateTime; + float playhead; + float enspace; + vector<ofColor> palette; + struct { + vector<ofColor> balanced={ + ofColor::fromHex(0xE27D60), + ofColor::fromHex(0x085DCB), + ofColor::fromHex(0xE8A87C), + ofColor::fromHex(0xC38D9E), + ofColor::fromHex(0x41B3A3) + }; + vector<ofColor> uneasy={ + ofColor::fromHex(0x2154B9), + ofColor::fromHex(0xC45A62), + ofColor::fromHex(0x95A28A), + ofColor::fromHex(0x98546D), + ofColor::fromHex(0xE9DADA), + ofColor::fromHex(0x9FF3E9), + ofColor::fromHex(0xD07B37), + ofColor::fromHex(0x741710), + ofColor::fromHex(0x102ADC), + ofColor::fromHex(0x9FA1AC) + }; + vector<ofColor> als23={ + ofColor::fromHex(0x00A8FF), + ofColor::fromHex(0xFFB700), + ofColor::fromHex(0xFF3082) + }; + }palettes; + vector<string> split(string s) { + size_t pos_start = 0, pos_end; + string token; + vector<string> res; + + while ((pos_end = s.find (" ", pos_start)) != string::npos) { + token = s.substr (pos_start, pos_end - pos_start); + pos_start = pos_end + 1; + res.push_back (token); + } + + res.push_back (s.substr (pos_start)); + return res; + } +public: + glyphbanner(){ + palette=palettes.als23; + }; + void init(string message){ + createWords(message,true); + lastUpdateTime=ofGetElapsedTimef(); + playhead=0.0f; + } + int length(){ + int l=0; + for (auto& w:words) { + l+=w.glyphs.size(); + } + return l+max(0,(int)words.size()-1); + } + float width(){ + float _w=0.0f; + for (auto& w:words) { + for (auto& g:w.glyphs) _w+=g.width; + } + return _w+max((float)(words.size()-1)*enspace,0.0f); + } + int glyphCount(){ + int c=0; + for (auto& w:words){ + c+=w.glyphs.size(); + } + return c; + } + string text(){ + string s; + for (auto& w:words) { + for (auto& g:w.glyphs) s+=g.chr; + } + return s; + } + void loadFont(filesystem::path path){ + if( SVGFont.load(path) ){ + vector<glyphWord> w=words; + clear(); + createWords(w); + enspace=getGlyph(" ").width; + ofLog()<<"loaded "<<path.stem().string(); + }else{ + ofLog()<<"unable to load "<<path<<" check data/ folder"; + } + + } + void createWords(string message,bool usePalette=false){ + clear(); + vector<string> m=split(message); + for (auto& word: m){ + glyphWord w; + int pos=0; + for (auto& c: word){ + string uniglyph = ofUTF8Substring(word, pos, 1); + if (c<0){ + ofLog()<<"got unicode glyph, "<<uniglyph; + + } + w.glyphs.push_back(getGlyph(uniglyph, + usePalette? + palette[ofRandom(palette.size())]: + //112->231 hue sat 0->255 brightness 255 + ofColor::fromHsb(ofRandom(119)+112,ofRandom(255),255) + )); + pos++; + } + words.push_back(w); + } + //ofLog()<<"created "<<words.size()<<" words"; + } + void createWords(vector<glyphWord> _words){ + clear(); + for (auto& _w:_words) { + glyphWord w; + for (auto& g: _w.glyphs){ + w.glyphs.push_back(getGlyph(g.chr,g.colour)); + } + words.push_back(w); + } + } + glyph getGlyph(string c,ofColor col=ofColor(255,255,255)){ + vector<ofPolyline> shapes; + ofPolyline shape; + string elementPath; +/* +[notice ] getGlyph placed ? for missing char -61 (?) +[notice ] getGlyph placed ? for missing char -119 (?) +[notice ] getGlyph placed ? for missing char -61 (?) +[notice ] getGlyph placed ? for missing char -70 (?) +[notice ] getGlyph placed ? for missing char -61 (?) +[notice ] getGlyph placed ? for missing char -95 (?) + +hack to add unicode support for these chars + + if (c==-61){ + elementPath = '/svg/defs/font/glyph[@unicode="Ĕ"]'; //Ebreve + //this doesn't actually display the accent + ofLog()<<"getGlyph substituted 'Ĕ' for missing char -61"; + } + if (c==-119||c==-70||c==-95){ + elementPath = '/svg/defs/font/glyph[@unicode="È"]'; //Egrave + //this doesn't actually find anything + ofLog()<<"getGlyph substituted 'È' for missing char -119"; + } + + ofUTF8Substring(const std::string & utf8, size_t pos, size_t len); + else { + */ + elementPath = "/svg/defs/font/glyph[@unicode='"+ofToString(c)+"']"; + //} + + /* + if(SVGFont.findFirst(elementPath) == 0 ){ + elementPath = "/svg/defs/font/glyph[@unicode='?']"; + ofLog()<<"getGlyph placed ? for missing char "<<c; + } + */ + + ofXml xmlElement = SVGFont.findFirst(elementPath); + + float charWidth = ofToFloat(xmlElement.getAttribute("horiz-adv-x").getValue()); + + vector<string> splitGlyphPath = ofSplitString(xmlElement.getAttribute("d").getValue(), " ");//glyph path data in SVG looks like this: "M 139 -9.45 L 230 18.9 L 299 22.1 L 227 25.2" + + for(int i=0; i<splitGlyphPath.size(); i+=3){ + if(splitGlyphPath[i] == "M"){ + if (shape.size()) { + shapes.push_back(shape); + shape.clear(); + } + shape.addVertex(ofToFloat(splitGlyphPath[i+1]), ofToFloat(splitGlyphPath[i+2])); + }else if(splitGlyphPath[i] == "L"){ + shape.lineTo(ofToFloat(splitGlyphPath[i+1]), ofToFloat(splitGlyphPath[i+2])); + } + } + if (shape.size()) shapes.push_back(shape); + return glyph(c,charWidth,shapes,col); + } + void addGlyph(string g,bool usePalette=false){ + if (g==" ") words.push_back(glyphWord()); + else { + words[words.size()-1].glyphs.push_back(getGlyph(g, + usePalette? + palette[ofRandom(palette.size())]: + ofColor::fromHsb(ofRandom(119)+112,ofRandom(255),255) + )); + } + } + void removeGlyph(){ + if (words.size()){ + glyphWord lw=words[words.size()-1]; + lw.glyphs.pop_back(); + if (!lw.glyphs.size()){ + words.pop_back(); + } + } + } + void clear(){words.clear();} + bool firstPass(){ + if(!words.size()) return false; + return playhead<glyphCount(); + } + void update(float speed=1.0f,float usePalette=false){ + + if(!words.size()) return; + + float delta=ofGetElapsedTimef()-lastUpdateTime; + lastUpdateTime=ofGetElapsedTimef(); + playhead+=delta*speed; + + if (ofRandom(1.0f)<.001){ + playhead=ofRandom(100); + } + + int theword=0; + float segment=0.0f; + if (false){ //1 word per second + theword=int(playhead)%words.size(); + segment=playhead-int(playhead); + } + else { //proportional to #of letters + int theletter=int(playhead)%glyphCount(); + theword=0; + while(theletter>words[theword].glyphs.size()){ + theletter-=words[theword].glyphs.size(); + theword++; + } + float playfraction=playhead-int(playhead); + + //segment=(((float)theletter+words[theword].glyphs.size()+playhead-int(playhead))/words[theword].glyphs.size()); + segment=(((float)theletter+playfraction-1)/words[theword].glyphs.size()); + } + //calculate params for word/letter anim + for (int i=0;i<words.size();i++){ + words[i].amount=(i==theword?sin(segment*3.14):0); + for (auto& g:words[i].glyphs){ + if (ofRandom(100)<speed) { + g.colour= + usePalette? + palette[ofRandom(palette.size())]: + ofColor::fromHsb(ofRandom(119)+112,ofRandom(255),255); + } + } + } + + + } + vector<colourPolyline>& getOutlines(float s=1.0f){ + outlines.clear(); + float p=(-width())/2; + for (auto& w:words){ + for (auto& g:w.glyphs){ + if (w.amount>0.0f){ + for (auto& o:g.outline){ + auto q=o; + q.scale(s,-s); + q.translate(glm::vec3(p*s,-ofGetHeight()/3,0)); + outlines.push_back(colourPolyline(q,g.colour*w.amount)); + } + } + p+=g.width; + } + p+=enspace; + } + return outlines; + } +};
\ No newline at end of file |
