summaryrefslogtreecommitdiff
path: root/nextus/src
diff options
context:
space:
mode:
Diffstat (limited to 'nextus/src')
-rw-r--r--nextus/src/ofApp.cpp4
-rw-r--r--nextus/src/vectorPlugin.h75
-rw-r--r--nextus/src/vectortext.h366
3 files changed, 434 insertions, 11 deletions
diff --git a/nextus/src/ofApp.cpp b/nextus/src/ofApp.cpp
index 48bcb9a..048a128 100644
--- a/nextus/src/ofApp.cpp
+++ b/nextus/src/ofApp.cpp
@@ -66,7 +66,7 @@ void ofApp::draw(){
textinput.draw();
//process the pipeline
- vector<colourPolyline> output=svginput.getLines();
+ vector<colourPolyline> output=svginput.getOutput();
ofPushMatrix();
ofTranslate(695,5);
@@ -82,7 +82,7 @@ void ofApp::draw(){
madlaser.panel.draw();
- madlaser.draw(output);
+ madlaser.drawNormalised(output);
}
diff --git a/nextus/src/vectorPlugin.h b/nextus/src/vectorPlugin.h
index d71b8fa..e20be8f 100644
--- a/nextus/src/vectorPlugin.h
+++ b/nextus/src/vectorPlugin.h
@@ -31,13 +31,14 @@ class vectorPanel {
ofPushMatrix();
ofTranslate(size/2);
ofScale(DISPLAYSIZE.x/2.0f);
- vector<colourPolyline> lines=getLines();
+ vector<colourPolyline> lines=getOutput();
for (auto& line:lines){
line.draw();
}
ofPopMatrix();
ofPopMatrix();
}
+ vector<colourPolyline> getOutput() {return getLines();};
virtual vector<colourPolyline> getLines() {};
virtual void update() {};
@@ -62,13 +63,64 @@ class defaultPanel: public vectorPanel{
void update(){};
};
-class svgPanel: public vectorPanel{
+class transformPanel: public vectorPanel{
+ //a base class which supports transformation
public:
- svgPanel(
+ transformPanel(
string _title="",
ofVec2f _size=DISPLAYSIZE,
ofVec2f _pos=ofPoint(5,5)
) : vectorPanel(_title,_size,_pos){
+ origin=ofPoint(0,0);
+ rotation=0;
+ panel.add(use_rotate.set("rotate",false));
+ panel.add(rotation_delta.set("rotation",0.1,-2.0,2.0));
+ }
+ void update(){
+ timedelta=ofGetElapsedTimef()-last_frame_time;
+ last_frame_time=ofGetElapsedTimef();
+ if (use_rotate) rotation+=rotation_delta*timedelta;
+ };
+ vector<colourPolyline> getOutput(){
+ vector<colourPolyline> lines=getLines();
+ if (use_rotate){
+ ofMatrix4x4 rm = ofMatrix4x4::newIdentityMatrix();
+ rm.rotateRad(rotation,0,0,1);
+
+ //if (use_scale){
+ // rm.scale(scale_amt,scale_amt,scale_amt);
+ //}
+ //rm.translate(outputWindowSize.x/2,outputWindowSize.y/2,0);
+ vector<colourPolyline> transformedLines;
+ for (auto& line:lines){
+ transformedLines.push_back(lineTransformer::polyLineTransform(rm,line));
+ }
+ return transformedLines;
+ }
+ return lines;
+ };
+ protected:
+ ofPoint origin;
+ float rotation;
+ ofParameter<bool> use_rotate;
+ ofParameter<float> rotation_delta;
+ float last_frame_time, timedelta;
+};
+
+/*
+next- enable transform offsets
+crop for display
+transformers
+easy midi
+*/
+
+class svgPanel: public transformPanel{
+ public:
+ svgPanel(
+ string _title="",
+ ofVec2f _size=DISPLAYSIZE,
+ ofVec2f _pos=ofPoint(5,5)
+ ) : transformPanel(_title,_size,_pos){
panel.add(shapeslabel.setup("SHAPES",""));
panel.add(shapes_randomise.set("randomise",false));
//panel.add(shapes_generate.setup("generate"));
@@ -88,12 +140,9 @@ class svgPanel: public vectorPanel{
vector<colourPolyline> getAllLines();
vector<colourPolyline> getLines();
void update(){
- timedelta=ofGetElapsedTimef()-last_frame_time;
- last_frame_time=ofGetElapsedTimef();
+ transformPanel::update();
phase=fmod(phase+(timedelta*segmenter_speed),1);
while(phase<0) phase+=1.0f;
- //what's the issue with segmenter in reverse
- ofLog()<<"phase: "<<phase;
}
private:
@@ -126,5 +175,13 @@ class svgPanel: public vectorPanel{
//float rotate_amt;
//float scale_phase,scale_amt;
- float last_frame_time, timedelta;
-}; \ No newline at end of file
+};
+
+class textPanel: public vectorPanel{
+ public:
+ textPanel(
+ string _title="",
+ ofVec2f _size=DISPLAYSIZE,
+ ofVec2f _pos=ofPoint(5,5)
+ ) : vectorPanel(_title,_size,_pos){}
+} \ No newline at end of file
diff --git a/nextus/src/vectortext.h b/nextus/src/vectortext.h
new file mode 100644
index 0000000..4625989
--- /dev/null
+++ b/nextus/src/vectortext.h
@@ -0,0 +1,366 @@
+#pragma once
+
+#include "ofMain.h"
+#include "ofxSvg.h"
+#include "lineTransformer.h"
+#include "colourPolyline.h"
+#include "ofxXmlSettings.h"
+
+class glyph{
+public:
+ glyph(char c,float w,vector<ofPolyline> lines){
+ glyph(c,w,lines,ofColor(0,0,0));
+ }
+ glyph(char c,float w,vector<ofPolyline> lines,ofColor col){
+ code=c;
+ 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);
+ }
+ char code;
+ float width;
+ vector<ofPolyline> outline;
+ ofColor colour;
+};
+
+class glyphWord{
+public:
+ glyphWord(){
+ amount=1.0f;
+ val=ofRandom(1.0f);
+ val2=ofRandom(1.0f);
+ width=0;
+ }
+ vector<glyph> glyphs;
+ float amount;
+ float val; //a random float used for word parameters
+ float val2;
+ float width;
+};
+
+#define STYLE_SENTENCE 1
+#define STYLE_OVERLAPPING 2
+
+class glyphbanner{
+ ofXml SVGFont;
+ vector<glyphWord> words;
+ vector<colourPolyline> outlines;
+ ofVec2f centre;
+ float lastUpdateTime;
+ float playhead;
+ float enspace;
+ float last_beat;
+ float last_beat_duration;
+ 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> stark={
+ ofColor::fromHex(0xFFFFFF),
+ ofColor::fromHex(0x000000),
+ ofColor::fromHex(0x000000),
+ ofColor::fromHex(0x000000)
+ };
+ }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;
+ }
+ float segment; //the fraction that the word has displayed
+public:
+ glyphbanner(){
+ palette=palettes.stark;
+ };
+ void init(string message){
+ createWords(message);
+ 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+=ofToString(g.code);
+ }
+ 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 load(string path){
+ ofFile file;
+ file.open(path, ofFile::ReadWrite, false);
+ ofBuffer buff = file.readToBuffer();
+ string s=buff.getText();
+ if (s.size()){
+ createWords(s);
+ }
+ }
+ void loadPalette(vector<ofColor> p){
+ palette=p;
+ }
+ void loadPalette(string path){
+ ofxXmlSettings xml;
+ if (xml.load(path)){
+ ofLog()<<path<<": found palette, "<<xml.getNumTags("colour")<<" colours";
+ vector<ofColor> palette;
+ for (int i=0;i<xml.getNumTags("colour");i++){
+ string s=xml.getValue("colour","", i);
+ try {
+ int c=std::stoul(s, nullptr, 16);
+ palette.push_back(ofColor::fromHex(c));
+ }
+ catch (const std::exception& e) {
+ ofLog()<<"palette loader: could not convert "<<s;
+ }
+ }
+ if (palette.size()) loadPalette(palette);
+ }
+ else {
+ ofLog()<<"could not load palette "<<path;
+ }
+ }
+ void createWords(string message,bool usePalette=false){
+ clear();
+ vector<string> m=split(message);
+ for (auto& word: m){
+ glyphWord w;
+ for (auto& c: word){
+ glyph g=getGlyph(c,
+ usePalette?
+ palette[ofRandom(palette.size())]:
+ //112->231 hue sat 0->255 brightness 255
+ ofColor::fromHsb(ofRandom(119)+112,ofRandom(255),255)
+ );
+ w.width+=g.width;
+ w.glyphs.push_back(g);
+ }
+ words.push_back(w);
+ }
+ ofLog()<<"glyphbanner 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.code,g.colour));
+ }
+ words.push_back(w);
+ }
+ }
+ glyph getGlyph(char c,ofColor col=ofColor(255,255,255)){
+ vector<ofPolyline> shapes;
+ ofPolyline shape;
+ string elementPath = "/svg/defs/font/glyph[@unicode='"+ofToString(c)+"']";
+
+ if(SVGFont.findFirst(elementPath) == 0 ){
+ elementPath = "/svg/defs/font/glyph[@unicode='?']";
+ }
+
+ 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(char 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();}
+ void update(float speed=1.0f,bool usePalette=false,bool use_beat=false,int onset_frame=0){
+
+ if (!words.size()) return;
+
+ float delta=ofGetElapsedTimef()-lastUpdateTime;
+ lastUpdateTime=ofGetElapsedTimef();
+ playhead+=delta*speed;
+
+ int theword=0;
+ if (false){ //1 word per second
+ theword=int(playhead)%words.size();
+ segment=playhead-int(playhead);
+ }
+
+ if (use_beat){ //change on the beat
+ if (!onset_frame){
+ theword=(theword+1)%words.size();
+ segment=0.0f;
+ last_beat_duration=ofGetElapsedTimef()-last_beat;
+ last_beat=ofGetElapsedTimef();
+ }
+ else {
+ segment=(ofGetElapsedTimef()-last_beat)/last_beat_duration;
+ }
+ }
+ 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,int style=STYLE_SENTENCE,float x=0,float y=0,bool anim=false,bool reverse=false, float vert_spread=0.0f, bool use_beat=false, float beat_duration=0.0f){
+ outlines.clear();
+
+ if (beat_duration>segment){ //I don't think this is what segment is
+ return outlines;
+ }
+ switch (style){
+ case STYLE_SENTENCE:{
+ float p=((-width())/2);
+ for (auto& w:words){
+ for (auto& g:w.glyphs){
+ if (w.amount>0.0f&&g.colour.getBrightness()>0){
+ for (auto& o:g.outline){
+ auto q=o;
+ q.scale(s,-s);
+ q.translate(glm::vec3((p*s)+x,y,0));
+ outlines.push_back(colourPolyline(q,g.colour*w.amount));
+ }
+ }
+ p+=g.width;
+ }
+ p+=enspace;
+ }
+ break;
+ }
+ case STYLE_OVERLAPPING:{
+ for (auto& w:words){
+ float p=(w.val*(ofGetWidth()-w.width));
+ float v=y+(vert_spread*w.val*(ofGetHeight()));
+ for (auto& g:w.glyphs){
+ if (w.amount>0.0f&&g.colour.getBrightness()>0){
+ for (auto& o:g.outline){
+ auto q=o;
+ float a=anim?reverse?1.0f-segment:segment:1.0;
+ q.scale(s*a,-s*a);
+ q.translate(glm::vec3((p*s*a)+x,v,0));
+ outlines.push_back(colourPolyline(q,g.colour*w.amount));
+ }
+ }
+ p+=g.width*(anim?reverse?1.0f-segment:segment:1.0);
+ }
+ }
+ }
+ default:{
+ break;
+ }
+ }
+ return outlines;
+ }
+}; \ No newline at end of file