summaryrefslogtreecommitdiff
path: root/FESgui/src
diff options
context:
space:
mode:
authorTim Redfern <tim@getdrop.com>2023-04-17 19:44:00 +0100
committerTim Redfern <tim@getdrop.com>2023-04-17 19:44:00 +0100
commit19be92e0aff674b95bdae72fe7a2e409fd1bf77a (patch)
tree079acfc11ffbff8f11b120649f5cb3a623e354d6 /FESgui/src
parent5309ef89393aa56083d1c2238c517c3d576907ec (diff)
add to archive
Diffstat (limited to 'FESgui/src')
-rw-r--r--FESgui/src/PerlinPhasingFilter.cpp151
-rw-r--r--FESgui/src/PerlinPhasingFilter.h23
-rw-r--r--FESgui/src/audiobuffer.h42
-rw-r--r--FESgui/src/lineSegmenter.cpp109
-rw-r--r--FESgui/src/lineSegmenter.h48
-rw-r--r--FESgui/src/lineTransformer.cpp135
-rw-r--r--FESgui/src/lineTransformer.h22
-rw-r--r--FESgui/src/main.cpp46
-rw-r--r--FESgui/src/ofApp.cpp1064
-rw-r--r--FESgui/src/ofApp.h273
-rw-r--r--FESgui/src/selectpanel.h53
-rw-r--r--FESgui/src/vectortext.h366
12 files changed, 2332 insertions, 0 deletions
diff --git a/FESgui/src/PerlinPhasingFilter.cpp b/FESgui/src/PerlinPhasingFilter.cpp
new file mode 100644
index 0000000..897a776
--- /dev/null
+++ b/FESgui/src/PerlinPhasingFilter.cpp
@@ -0,0 +1,151 @@
+//
+// PerlinPhasingFilter.cpp
+// filterSandbox
+//
+// Created by Matthew Fargo on 2014/06/24.
+//
+//
+
+#include "PerlinPhasingFilter.h"
+
+PerlinPhasingFilter::PerlinPhasingFilter(float width, float height, float scale) : AbstractFilter(width, height) {
+ _name = "Perlin Pixellation";
+ _scale = scale;
+ _addParameter(new ParameterF("fractionalWidthOfPixel", 0.05));
+ _addParameter(new ParameterF("scale", _scale));
+ _addParameter(new ParameterF("phase", 0.0));
+ _setupShader();
+}
+PerlinPhasingFilter::~PerlinPhasingFilter() {}
+
+
+string PerlinPhasingFilter::_getFragSrc() {
+ return GLSL_STRING(120,
+ //precision highp float;
+ uniform sampler2D inputImageTexture;
+
+ uniform float fractionalWidthOfPixel;
+ uniform float scale;
+ uniform float phase;
+ //
+ // GLSL textureless classic 3D noise "cnoise",
+ // with an RSL-style periodic variant "pnoise".
+ // Author: Stefan Gustavson (stefan.gustavson@liu.se)
+ // Version: 2011-10-11
+ //
+ // Many thanks to Ian McEwan of Ashima Arts for the
+ // ideas for permutation and gradient selection.
+ //
+ // Copyright (c) 2011 Stefan Gustavson. All rights reserved.
+ // Distributed under the MIT license. See LICENSE file.
+ // https://github.com/stegu/webgl-noise
+ //
+
+ vec3 mod289(vec3 x)
+ {
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+ }
+
+ vec4 mod289(vec4 x)
+ {
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+ }
+
+ vec4 permute(vec4 x)
+ {
+ return mod289(((x*34.0)+10.0)*x);
+ }
+
+ vec4 taylorInvSqrt(vec4 r)
+ {
+ return 1.79284291400159 - 0.85373472095314 * r;
+ }
+
+ vec3 fade(vec3 t) {
+ return t*t*t*(t*(t*6.0-15.0)+10.0);
+ }
+
+ // Classic Perlin noise
+ float cnoise(vec3 P)
+ {
+ vec3 Pi0 = floor(P); // Integer part for indexing
+ vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
+ Pi0 = mod289(Pi0);
+ Pi1 = mod289(Pi1);
+ vec3 Pf0 = fract(P); // Fractional part for interpolation
+ vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
+ vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
+ vec4 iy = vec4(Pi0.yy, Pi1.yy);
+ vec4 iz0 = Pi0.zzzz;
+ vec4 iz1 = Pi1.zzzz;
+
+ vec4 ixy = permute(permute(ix) + iy);
+ vec4 ixy0 = permute(ixy + iz0);
+ vec4 ixy1 = permute(ixy + iz1);
+
+ vec4 gx0 = ixy0 * (1.0 / 7.0);
+ vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
+ gx0 = fract(gx0);
+ vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
+ vec4 sz0 = step(gz0, vec4(0.0));
+ gx0 -= sz0 * (step(0.0, gx0) - 0.5);
+ gy0 -= sz0 * (step(0.0, gy0) - 0.5);
+
+ vec4 gx1 = ixy1 * (1.0 / 7.0);
+ vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
+ gx1 = fract(gx1);
+ vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
+ vec4 sz1 = step(gz1, vec4(0.0));
+ gx1 -= sz1 * (step(0.0, gx1) - 0.5);
+ gy1 -= sz1 * (step(0.0, gy1) - 0.5);
+
+ vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
+ vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
+ vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
+ vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
+ vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
+ vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
+ vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
+ vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
+
+ vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
+ g000 *= norm0.x;
+ g010 *= norm0.y;
+ g100 *= norm0.z;
+ g110 *= norm0.w;
+ vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
+ g001 *= norm1.x;
+ g011 *= norm1.y;
+ g101 *= norm1.z;
+ g111 *= norm1.w;
+
+ float n000 = dot(g000, Pf0);
+ float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
+ float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
+ float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
+ float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
+ float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
+ float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
+ float n111 = dot(g111, Pf1);
+
+ vec3 fade_xyz = fade(Pf0);
+ vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
+ vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
+ float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
+ return 2.2 * n_xyz;
+ }
+
+ void main() {
+ vec2 uv = (gl_TexCoord[0].xy);
+
+ float n = cnoise( vec3(uv,phase) *scale)+
+ cnoise( vec3(uv,phase) *scale*3.71098f)+
+ cnoise( vec3(uv,phase) *scale*11.56476f);
+ vec2 sampleDivisor = vec2((((n + 1.0) / 2.0) + 0.5) * fractionalWidthOfPixel);
+
+ vec2 samplePos = uv - mod(uv, sampleDivisor);
+ gl_FragColor = texture2D(inputImageTexture, samplePos );
+ }
+
+ );
+}
diff --git a/FESgui/src/PerlinPhasingFilter.h b/FESgui/src/PerlinPhasingFilter.h
new file mode 100644
index 0000000..0c4ba36
--- /dev/null
+++ b/FESgui/src/PerlinPhasingFilter.h
@@ -0,0 +1,23 @@
+//
+// PerlinPhasingFilter.h
+// filterSandbox
+//
+// Created by Matthew Fargo on 2014/06/24.
+//
+//
+
+#pragma once
+
+#include "AbstractFilter.h"
+
+class PerlinPhasingFilter : public AbstractFilter {
+public:
+ PerlinPhasingFilter(float width, float height, float scale=8.f);
+ virtual ~PerlinPhasingFilter();
+
+
+protected:
+ virtual string _getFragSrc();
+ float _scale;
+};
+
diff --git a/FESgui/src/audiobuffer.h b/FESgui/src/audiobuffer.h
new file mode 100644
index 0000000..a56dd4b
--- /dev/null
+++ b/FESgui/src/audiobuffer.h
@@ -0,0 +1,42 @@
+#pragma once
+
+
+#define min(a,b) (a<b?a:b)
+#define max(a,b) (a>b?a:b)
+
+class Buffer{
+public:
+ Buffer(size_t sz=0){
+ if (sz){
+ data =new float[sz];
+ memset(data,0,sz*sizeof(float));
+ size=sz;
+ }
+ else data=NULL;
+ }
+ ~Buffer(){
+ if (data){
+ delete[] data; //why is this throwing an error
+ }
+ }
+ void add(float * input, int num){
+ //this assumes that num < size
+ memcpy(&data[writePoint],input,min(num,size-writePoint)*sizeof(float));
+ if (size-writePoint<num){ //we have to wrap
+ //copy the remaining chunk to the start and set the new writePoint
+ memcpy(data,&input[size-writePoint],(num-(size-writePoint))*sizeof(float));
+ writePoint=num-(size-writePoint);
+ }
+ else writePoint+=num;
+ }
+ float operator [] (int i) const {
+ return data[(writePoint>i?writePoint-i:size-(i-writePoint))-1];
+ }
+ float& operator [] (int i) {
+ return data[(writePoint>i?writePoint-i:size-(i-writePoint))-1];
+ }
+private:
+ size_t size;
+ float *data;
+ int writePoint;
+}; \ No newline at end of file
diff --git a/FESgui/src/lineSegmenter.cpp b/FESgui/src/lineSegmenter.cpp
new file mode 100644
index 0000000..3f434b1
--- /dev/null
+++ b/FESgui/src/lineSegmenter.cpp
@@ -0,0 +1,109 @@
+ #include "lineSegmenter.h"
+
+const vector <ofPolyline> & lineSegmenter::getSegments(int num,float coverage, float phase){
+ //num - number of segments
+ //coverage - amount that each segment fills it's slot from 0-1
+ //phase - from 0-1
+
+ //if the path is closed, we can make a segment that crosses the end/beginning
+ //however we want to be able to deal with open paths
+
+/*
+
+segments 0...n - 1
+phase 0...1
+
+phase 0
+
+segment 0 is (0 -> coverage) / n
+segment n - 1 is ((0 -> coverage) + (n-1)) /n
+
+phase 1: has to be the loop target, it has to look identical
+
+segment 0 is (1 -> coverage) / n
+segment n - 1 is (1 - > coverage) + (n-1)
+
+*/
+
+
+ segments.clear();
+
+ for (int i=0;i<num;i++){
+ float startIndex=line.getIndexAtPercent((phase+i)/num); //always <1
+ float endPoint=(phase+i+coverage)/num; //can be >1
+ float endIndex=line.getIndexAtPercent(endPoint>1.0f?endPoint-1.0f:endPoint);
+ ofPolyline segment;
+ segment.addVertex(line.getPointAtIndexInterpolated(startIndex));
+ for (int j=(int)ceil(startIndex);j<(endPoint>1?line.size():(int)ceil(endIndex));j++){
+ segment.addVertex(line[j]);
+ }
+ if (endPoint>1){
+ segments.push_back(segment);
+ segment.clear();
+ for (int j=0;j<(int)ceil(endIndex);j++){
+ segment.addVertex(line[j]);
+ }
+ segment.addVertex(line.getPointAtIndexInterpolated(endIndex));
+ }
+ else {
+ segment.addVertex(line.getPointAtIndexInterpolated(endIndex));
+ }
+ segments.push_back(segment);
+ }
+
+ return segments;
+}
+
+void lineSegmenter::draw(){
+ line.draw();
+ return;
+}
+int lineSegmenter::size(){
+ return line.size();
+}
+
+const vector <colourPolyline> & colourLineSegmenter::getSegments(int num,float coverage, float phase){
+
+ segments.clear();
+
+ for (int i=0;i<num;i++){
+ float startIndex=line.getIndexAtPercent((phase+i)/num); //always <1
+ float endPoint=(phase+i+coverage)/num; //can be >1
+ float endIndex=line.getIndexAtPercent(endPoint>1.0f?endPoint-1.0f:endPoint);
+ colourPolyline segment;
+ segment.addVertex(line.getPointAtIndexInterpolated(startIndex),line.getColourAtIndexInterpolated(startIndex));
+ for (int j=(int)ceil(startIndex);j<(endPoint>1?line.size():(int)ceil(endIndex));j++){
+ segment.addVertex(line[j],line.getColourAt(j));
+ }
+ if (endPoint>1){
+ segments.push_back(segment);
+ segment.clear();
+ for (int j=0;j<(int)ceil(endIndex);j++){
+ segment.addVertex(line[j],line.getColourAt(j));
+ }
+ segment.addVertex(line.getPointAtIndexInterpolated(endIndex),line.getColourAtIndexInterpolated(endIndex));
+ }
+ else {
+ segment.addVertex(line.getPointAtIndexInterpolated(endIndex),line.getColourAtIndexInterpolated(endIndex) );
+ }
+ segments.push_back(segment);
+ }
+
+ return segments;
+}
+
+void colourLineSegmenter::draw(){
+ line.draw();
+ return;
+}
+int colourLineSegmenter::size(){
+ return line.size();
+}
+
+
+/*
+
+
+
+
+*/ \ No newline at end of file
diff --git a/FESgui/src/lineSegmenter.h b/FESgui/src/lineSegmenter.h
new file mode 100644
index 0000000..2467c6a
--- /dev/null
+++ b/FESgui/src/lineSegmenter.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "ofMain.h"
+#include "colourPolyline.h"
+
+class lineSegmenter{
+ public:
+ lineSegmenter(ofPolyline &_line){
+ line=_line;
+ if (line.isClosed()){
+ line.addVertex(line[0]);
+ }
+ }
+ const vector <ofPolyline> &getSegments(int num,float coverage, float phase);
+ ofPolyline getPoly(){
+ return line;
+ }
+ void draw();
+ int size();
+ private:
+ ofPolyline line;
+ vector <ofPolyline> segments;
+};
+
+class colourLineSegmenter{
+ public:
+ colourLineSegmenter(colourPolyline &_line){
+ line=_line;
+ //if (line.isClosed()){
+ // line.addVertex(line[0]);
+ //}
+ }
+ colourLineSegmenter(ofPolyline &_line,const ofColor color=ofColor(255,255,255)){
+ line=colourPolyline(_line,color);
+ if (_line.isClosed()){
+ line.addVertex(line[0],line.getColourAt(0));
+ }
+ }
+ const vector <colourPolyline> &getSegments(int num,float coverage, float phase);
+ colourPolyline getPoly(){
+ return line;
+ }
+ void draw();
+ int size();
+ private:
+ colourPolyline line;
+ vector <colourPolyline> segments;
+}; \ No newline at end of file
diff --git a/FESgui/src/lineTransformer.cpp b/FESgui/src/lineTransformer.cpp
new file mode 100644
index 0000000..e138ff4
--- /dev/null
+++ b/FESgui/src/lineTransformer.cpp
@@ -0,0 +1,135 @@
+#include "lineTransformer.h"
+
+
+void lineTransformer::drawWarpFrame(glm::vec2 warpframe[4]){
+ ofSetColor(255,255,255);
+ ofNoFill();
+ for (int i=0;i<4;i++){
+ ofDrawCircle(warpframe[i],25);
+ ofDrawLine(warpframe[i],warpframe[(i+1)%4]);
+ }
+}
+
+void lineTransformer::drawWarpFrame(glm::vec3 warpframe[4]){
+ ofSetColor(255,255,255);
+ ofNoFill();
+ for (int i=0;i<4;i++){
+ ofDrawCircle(warpframe[i],25);
+ ofDrawLine(warpframe[i],warpframe[(i+1)%4]);
+ }
+}
+
+void lineTransformer::gaussianElimination(float * input, int n)
+{
+ auto i = 0;
+ auto j = 0;
+ auto m = n - 1;
+
+ while (i < m && j < n)
+ {
+ auto iMax = i;
+ for (auto k = i + 1; k < m; ++k)
+ {
+ if (fabs(input[k * n + j]) > fabs(input[iMax * n + j]))
+ {
+ iMax = k;
+ }
+ }
+
+ if (input[iMax * n + j] != 0)
+ {
+ if (i != iMax)
+ {
+ for (auto k = 0; k < n; ++k)
+ {
+ auto ikIn = input[i * n + k];
+ input[i * n + k] = input[iMax * n + k];
+ input[iMax * n + k] = ikIn;
+ }
+ }
+
+ float ijIn = input[i * n + j];
+ for (auto k = 0; k < n; ++k)
+ {
+ input[i * n + k] /= ijIn;
+ }
+
+ for (auto u = i + 1; u < m; ++u)
+ {
+ auto ujIn = input[u * n + j];
+ for (auto k = 0; k < n; ++k)
+ {
+ input[u * n + k] -= ujIn * input[i * n + k];
+ }
+ }
+
+ ++i;
+ }
+ ++j;
+ }
+
+ for (auto i = m - 2; i >= 0; --i)
+ {
+ for (auto j = i + 1; j < n - 1; ++j)
+ {
+ input[i * n + m] -= input[i * n + j] * input[j * n + m];
+ }
+ }
+}
+
+glm::mat4 lineTransformer::getPerspectiveTransformMatrix(const glm::vec2 src[4], const glm::vec2 dst[4])
+{
+ float p[8][9] =
+ {
+ { -src[0][0], -src[0][1], -1, 0, 0, 0, src[0][0] * dst[0][0], src[0][1] * dst[0][0], -dst[0][0] }, // h11
+ { 0, 0, 0, -src[0][0], -src[0][1], -1, src[0][0] * dst[0][1], src[0][1] * dst[0][1], -dst[0][1] }, // h12
+ { -src[1][0], -src[1][1], -1, 0, 0, 0, src[1][0] * dst[1][0], src[1][1] * dst[1][0], -dst[1][0] }, // h13
+ { 0, 0, 0, -src[1][0], -src[1][1], -1, src[1][0] * dst[1][1], src[1][1] * dst[1][1], -dst[1][1] }, // h21
+ { -src[2][0], -src[2][1], -1, 0, 0, 0, src[2][0] * dst[2][0], src[2][1] * dst[2][0], -dst[2][0] }, // h22
+ { 0, 0, 0, -src[2][0], -src[2][1], -1, src[2][0] * dst[2][1], src[2][1] * dst[2][1], -dst[2][1] }, // h23
+ { -src[3][0], -src[3][1], -1, 0, 0, 0, src[3][0] * dst[3][0], src[3][1] * dst[3][0], -dst[3][0] }, // h31
+ { 0, 0, 0, -src[3][0], -src[3][1], -1, src[3][0] * dst[3][1], src[3][1] * dst[3][1], -dst[3][1] }, // h32
+ };
+
+ gaussianElimination(&p[0][0], 9);
+
+ return glm::mat4(p[0][8], p[3][8], 0, p[6][8],
+ p[1][8], p[4][8], 0, p[7][8],
+ 0, 0, 1, 0,
+ p[2][8], p[5][8], 0, 1);
+}
+
+ofPolyline lineTransformer::polyLineTransform(const ofMatrix4x4 xform, const ofPolyline& poly){
+ ofPolyline tempPoly;
+ for (auto& p:poly){
+ tempPoly.addVertex(ofVec3f(p)*xform);
+ }
+ return tempPoly;
+}
+
+colourPolyline lineTransformer::polyLineTransform(const ofMatrix4x4 xform,colourPolyline& poly,float colourFade){
+ colourPolyline tempPoly;
+ for (int i=0;i<poly.size();i++){
+ tempPoly.addVertex(ofVec3f(poly[i])*xform,poly.getColourAt(i)*colourFade);
+ }
+ return tempPoly;
+}
+
+ofPolyline lineTransformer::makePolygon(int num,float diam){
+ ofPolyline poly;
+ float step=PI*2/num;
+ for (int i=0;i<=num;i++){
+ poly.addVertex(cos(step*i)*diam,sin(step*i)*diam);
+ }
+ return poly;
+}
+
+void lineTransformer::drawPoly(ofPolyline poly,float x,float y){
+ glPushMatrix();
+ ofTranslate(x,y);
+ poly.draw();
+ for (int i=0;i<poly.size();i++){
+ ofDrawBitmapString(poly.getDegreesAtIndex(i),poly[i].x+10,poly[i].y+10,0);
+ }
+ glPopMatrix();
+}
diff --git a/FESgui/src/lineTransformer.h b/FESgui/src/lineTransformer.h
new file mode 100644
index 0000000..23b2b0a
--- /dev/null
+++ b/FESgui/src/lineTransformer.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "ofMain.h"
+#include "colourPolyline.h"
+
+class lineTransformer {
+
+ public:
+ lineTransformer(){
+ }
+ void static drawWarpFrame(glm::vec2 warpframe[4]);
+ void static drawWarpFrame(glm::vec3 warpframe[4]);
+ void static gaussianElimination(float * input, int n);
+ glm::mat4 static getPerspectiveTransformMatrix(const glm::vec2 src[4], const glm::vec2 dst[4]);
+ ofPolyline static polyLineTransform(const ofMatrix4x4 xform,const ofPolyline& poly);
+ ofPolyline static polyLineTransform(ofPoint (*transferFunction)(const ofPoint),const ofPolyline& poly);
+ colourPolyline static polyLineTransform(const ofMatrix4x4 xform,colourPolyline& poly,float colourFade=1.0f);
+ colourPolyline static polyLineTransform(ofPoint (*transferFunction)(const ofPoint),colourPolyline& poly,float colourFade=1.0f);
+ ofPolyline static makePolygon(int num,float diam);
+ void static drawPoly(ofPolyline poly,float x,float y);
+
+}; \ No newline at end of file
diff --git a/FESgui/src/main.cpp b/FESgui/src/main.cpp
new file mode 100644
index 0000000..226ae24
--- /dev/null
+++ b/FESgui/src/main.cpp
@@ -0,0 +1,46 @@
+#include "ofMain.h"
+#include "ofApp.h"
+
+
+//========================================================================
+int main(int argc, char *argv[]){
+
+
+ ofGLFWWindowSettings settings;
+
+ //settings.setGLVersion(3,2); if using kinect
+
+ settings.decorated = true;
+ settings.setSize(outputWindowSize.x,outputWindowSize.y);
+ settings.setPosition(ofVec2f(1700,0));
+ settings.resizable = false;
+
+ shared_ptr<ofAppBaseWindow> mainWindow = ofCreateWindow(settings);
+ mainWindow->setVerticalSync(false);
+
+ settings.setSize(1200,1100);
+ settings.setPosition(ofVec2f(0,20));
+ settings.resizable = false;
+
+ // share OpenGL resources with other windows
+ settings.shareContextWith = mainWindow;
+
+ settings.decorated = true; //doesn't suppress FS title bar
+ shared_ptr<ofAppBaseWindow> guiWindow = ofCreateWindow(settings);
+ guiWindow->setVerticalSync(false);
+
+ shared_ptr<ofApp> mainApp(new ofApp);
+
+ ofAddListener(mainWindow->events().update,mainApp.get(),&ofApp::updateOutput);
+ ofAddListener(mainWindow->events().draw,mainApp.get(),&ofApp::drawOutput);
+ ofAddListener(mainWindow->events().windowResized,mainApp.get(),&ofApp::outputWindowResized);
+ ofAddListener(mainWindow->events().keyPressed,mainApp.get(),&ofApp::outputKeyPressed);
+ ofAddListener(mainWindow->events().keyReleased,mainApp.get(),&ofApp::outputKeyReleased);
+ ofAddListener(mainWindow->events().mouseDragged,mainApp.get(),&ofApp::outputMouseDragged);
+ ofAddListener(mainWindow->events().mousePressed,mainApp.get(),&ofApp::outputMousePressed);
+ ofAddListener(mainWindow->events().mouseReleased,mainApp.get(),&ofApp::outputMouseReleased);
+
+ ofRunApp(guiWindow, mainApp);
+ ofRunMainLoop();
+}
+ \ No newline at end of file
diff --git a/FESgui/src/ofApp.cpp b/FESgui/src/ofApp.cpp
new file mode 100644
index 0000000..a1c49be
--- /dev/null
+++ b/FESgui/src/ofApp.cpp
@@ -0,0 +1,1064 @@
+#include "ofApp.h"
+
+
+
+//====================== settings
+
+void ofApp::default_settings(){
+ warpframe[0]=glm::vec3(0,0,0);
+ warpframe[1]=glm::vec3(outputWindowSize.x,0,0);
+ warpframe[2]=glm::vec3(outputWindowSize.x,outputWindowSize.y,0);
+ warpframe[3]=glm::vec3(0,outputWindowSize.y,0);
+ outputPosition=ofPoint(0,0,0);
+ outputScale=1.0f;
+}
+
+void ofApp::save_settings(){
+ XML.setValue("WARP:p0:X", warpframe[0].x);
+ XML.setValue("WARP:p0:Y", warpframe[0].y);
+ XML.setValue("WARP:p1:X", warpframe[1].x);
+ XML.setValue("WARP:p1:Y", warpframe[1].y);
+ XML.setValue("WARP:p2:X", warpframe[2].x);
+ XML.setValue("WARP:p2:Y", warpframe[2].y);
+ XML.setValue("WARP:p3:X", warpframe[3].x);
+ XML.setValue("WARP:p3:Y", warpframe[3].y);
+
+ XML.setValue("SAFETY:p0:X", safety_frame[0].x);
+ XML.setValue("SAFETY:p0:Y", safety_frame[0].y);
+ XML.setValue("SAFETY:p1:X", safety_frame[1].x);
+ XML.setValue("SAFETY:p1:Y", safety_frame[1].y);
+ XML.setValue("SAFETY:p2:X", safety_frame[2].x);
+ XML.setValue("SAFETY:p2:Y", safety_frame[2].y);
+ XML.setValue("SAFETY:p3:X", safety_frame[3].x);
+ XML.setValue("SAFETY:p3:Y", safety_frame[3].y);
+
+ XML.setValue("POSITION:X", outputPosition.x);
+ XML.setValue("POSITION:Y", outputPosition.y);
+
+ XML.setValue("SCALE", outputScale);
+
+ if (XML.saveFile("settings.xml")){
+ cout << "settings.xml saved!" <<std::endl;
+ }
+ else {
+ cout << "could not save settings.xml!" <<std::endl;
+ }
+}
+
+void ofApp::setup_lasergui(int x, int y){
+
+ laser.setup(x,y);
+
+ bShowPositionInterface=false;
+ bOutputSelected=false;
+
+ outputOffsetScale=1.0f;
+ commandPressed=false;
+
+
+ select_warpframe=-1;
+ bDrawFrame=false;
+
+ select_safetyframe=-1;
+
+ if( XML.loadFile("settings.xml") ){
+ cout << "settings.xml loaded!" <<std::endl;
+
+ }
+ else{
+ cout << "unable to load settings.xml"<<std::endl;
+ }
+
+ warpframe[0]=glm::vec2(
+ XML.getValue("WARP:p0:X", 0),
+ XML.getValue("WARP:p0:Y", 0)
+ );
+ warpframe[1]=glm::vec2(
+ XML.getValue("WARP:p1:X", outputWindowSize.x),
+ XML.getValue("WARP:p1:Y", 0)
+ );
+ warpframe[2]=glm::vec2(
+ XML.getValue("WARP:p2:X", outputWindowSize.x),
+ XML.getValue("WARP:p2:Y", outputWindowSize.y)
+ );
+ warpframe[3]=glm::vec2(
+ XML.getValue("WARP:p3:X", 0),
+ XML.getValue("WARP:p3:Y", outputWindowSize.y)
+ );
+
+ safety_frame[0]=glm::vec3(
+ XML.getValue("SAFETY:p0:X", 0),
+ XML.getValue("SAFETY:p0:Y", 0),
+ 0
+ );
+ safety_frame[1]=glm::vec3(
+ XML.getValue("SAFETY:p1:X", outputWindowSize.x),
+ XML.getValue("SAFETY:p1:Y", 0),
+ 0
+ );
+ safety_frame[2]=glm::vec3(
+ XML.getValue("SAFETY:p2:X", outputWindowSize.x),
+ XML.getValue("SAFETY:p2:Y", outputWindowSize.y),
+ 0
+ );
+ safety_frame[3]=glm::vec3(
+ XML.getValue("SAFETY:p3:X", 0),
+ XML.getValue("SAFETY:p3:Y", outputWindowSize.y),
+ 0
+ );
+
+ outputPosition=ofPoint(
+ XML.getValue("POSITION:X", 0),
+ XML.getValue("POSITION:Y", 0),
+ 0
+ );
+
+ laser.set_centre(outputPosition);
+
+ outputScale=XML.getValue("SCALE", 1.0f);
+}
+
+void ofApp::setup(){
+
+ int frameRate=60;
+
+ ofSetFrameRate(frameRate);
+ ofBackground(0);
+
+ ofDisableArbTex(); //this is required for perlin filter
+ ofEnableSmoothing();
+ ofEnableAlphaBlending();
+ ofSetVerticalSync(true);
+
+ blockSize = SAMPLERATE / (frameRate*10); //80
+
+ ofSoundStreamListDevices();
+
+ soundStream.setup(this,0, 1, SAMPLERATE, blockSize, 1);
+
+ buffer=Buffer(SAMPLERATE);
+
+ //vScale=3.0f;
+ //hScale=8.0f;
+
+ //lineWidth=2.0f;
+
+ //panels are 210 wide
+
+ videoSourcePanel.setup("video sources","",5,5);
+ videoSourcePanel.add(captPreview.set("camera preview",true));
+ videoSourcePanel.add(captEnable.set("camera",false));
+ videoSourcePanel.add(videoOscEnable.set("oscilloscope",false));
+ videoSourcePanel.add(playerEnable.set("player",false));
+ videoSourcePanel.add(contoursVideoDraw.set("contours",false));
+ videoSourcePanel.add(SVGVideoDraw.set("SVG",false));
+ videoSourcePanel.add(textVideoDraw.set("text",false));
+ videoSourcePanel.add(video_speed.set("playback speed",1.0,0.0,3.0));
+
+ vectorSourcePanel.setup("vector sources","",5,370);
+ vectorSourcePanel.add(edit_safety.set("edit safety",false));
+ vectorSourcePanel.add(use_safety.set("use safety",true));
+ vectorSourcePanel.add(vectorOscEnable.set("oscilloscope",false));
+ vectorSourcePanel.add(contoursLaserDraw.set("contours",false));
+ vectorSourcePanel.add(SVGLaserDraw.set("SVG",false));
+ vectorSourcePanel.add(textLaserDraw.set("text",false));
+
+ setup_lasergui(970,500);
+
+ audiopanel.setup("oscilloscope","",5,220);
+ audiopanel.add(vScale.set("vertical",3.0,0.0,20.0)); //scaling
+ audiopanel.add(hScale.set("horizontal",1.0,0.1,10.0)); //ms
+ audiopanel.add(onset_threshold.set("onset threshold", 0.05f, 0.0f, 1.0f ));
+ audiopanel.add(use_onset.set("trigger onset",false));
+ audiopanel.add(onset_duration.set("duration", 10, 1, 100));
+
+ svggui.setup("SVG","",5,500);
+ svggui.add(shapes_randomise.set("randomise shapes", true));
+ svggui.add(shapes_amount.set("shapes amount", 0.2, 0.0, 1.0));
+ svggui.add(shapes_duration.set("shape duration", 5.0, 0, 10.0));
+ svggui.add(use_mask.set("use mask", true));
+ svggui.add(invert_mask.set("invert mask", false));
+
+ //segmenter
+ svggui.add(use_segmenter.set("use segmenter", false));
+ svggui.add(colour_segmenter.set("colour", false));
+ svggui.add(segmenter_speed.set("segmenter speed", 0.2, -1.0, 1.0));
+ svggui.add(segmenter_length.set("segmenter length", 0.2, 0.0, 1.0));
+ svggui.add(segmenter_number.set("segmenter number", 1, 1, 8));
+
+ text.setup(5,750);
+
+ drawingpanel.setup("drawing","",550,500);
+ drawingpanel.add(lineWidth.set("width",2.0,0.1,10.0));
+
+ useKinect=true;
+
+ if (useKinect) {
+ std::vector <ofxKinectV2::KinectDeviceInfo> deviceList = kinect.getDeviceList();
+ if (deviceList.size()){
+ useKinect=kinect.open(deviceList[0].serial);
+ }
+ else useKinect=false;
+ }
+
+ if (!useKinect) {
+ _video.initGrabber(1280, 720);
+ }
+
+ contourgui.setup("video contours","",550,550);
+ contourgui.add(video_outlines.set("enable",false));
+ contourgui.add(contour_adaptive.set("adaptive",false));
+ contourgui.add(contour_threshold.set("threshold", 140, 0, 255));
+ contourgui.add(contour_adaptive_window.set("window", 0.5, 0, 1.0));
+ contourgui.add(contour_simplify.set("simplify", 0.8, 0.0, 10.0));
+ //contourgui.add(contour_useColour.set("use colour",false)); //TODO
+
+ vectorTransforms.setup("vector transform","",550,750);
+ vectorTransforms.add(use_rotate.set("rotation",false));
+ vectorTransforms.add(xf_rotate.set("rotate speed", 0.0, -1.0, 1.0));
+ vectorTransforms.add(use_scale.set("scaling",false));
+ vectorTransforms.add(xf_scale_speed.set("scale speed", 1.0, 0.0, 10.0));
+ vectorTransforms.add(xf_scale_min.set("scale min", 1.0, 0.0, 3.0));
+ vectorTransforms.add(xf_scale_max.set("scale maz", 1.0, 0.0, 3.0));
+
+ perlin = new PerlinPhasingFilter(
+ outputWindowSize.x,
+ outputWindowSize.y,
+ 8.f
+ );
+
+ perlinpanel.setup("PerlinPhasingFilter","",760,500);
+ perlinpanel.add(perlinEnable.set("enable",false));
+ perlinpanel.add(perlinScale.set("scale",0.0,-1.0,1.0));
+ perlinpanel.add(perlinSpeed.set("speed",0.0,-1.0,1.0));
+ perlinpanel.add(perlinPixelWidth.set("pixel width",0.0,0.0,1.0));
+ perlinpanel.add(perlinPixelWidthSpeed.set("pixel speed",1.0,0.0,10.0));
+
+ preview.allocate(outputWindowSize.x,outputWindowSize.y);
+
+ gist.setUseForOnsetDetection(GIST_PEAK_ENERGY);
+ gist.setThreshold(GIST_PEAK_ENERGY, .05);//
+
+ ofAddListener(GistEvent::ON,this,&ofApp::onNoteOn);
+ ofAddListener(GistEvent::OFF,this,&ofApp::onNoteOff);
+
+ onset_frame=0;
+}
+
+//--------------------------------------------------------------
+void ofApp::updateOutput(ofEventArgs & args){
+ _player.setSpeed(video_speed);
+}
+
+void ofApp::update(){
+ laser.update();
+ text.update(onset_frame);
+
+ if (_player.isLoaded()){
+ _player.update();
+ }
+
+ if (captPreview||captEnable){
+ if (useKinect){
+ kinect.update();
+ if (kinect.isFrameNew()){
+ texRGB.setFromPixels(kinect.getPixels());
+ }
+ }
+ else {
+ _video.update();
+ texRGB.setFromPixels(_video.getPixels());
+ }
+ int tw=texRGB.getHeight()*1.333f;
+ int sl=(texRGB.getWidth()-tw)/2;
+ texRGB.crop(sl, 0, tw, texRGB.getHeight());
+ }
+
+ float interval=ofGetElapsedTimef()-prev_time;
+ rotate_amt+=interval*xf_rotate*5;
+
+ phase=fmod(phase+(interval*segmenter_speed),1); //for segmenter
+ prev_time=ofGetElapsedTimef();
+
+ while (phase<0.0f) {
+ phase+=1.0f;
+ }
+
+ scale_phase+=(interval*xf_scale_speed);
+
+ scale_amt=(((sin(scale_phase)*0.5)+0.5)*(xf_scale_max-xf_scale_min))+xf_scale_min;
+
+ gist.setThreshold(GIST_PEAK_ENERGY,onset_threshold);
+
+ onset_frame++;
+}
+
+//-------------------------------------------------------------- GUI
+void ofApp::draw(){
+ //draw the workspace window.
+ // 5 - 240 - panels
+ // 250 - 570 - sources (320x240)
+ // 580 - 1220 - preview (640x480)
+
+
+
+ ofBackground(0);
+
+ ofSetColor(255);
+
+
+ if (bShowPositionInterface){
+
+ glPushMatrix();
+
+ ofNoFill();
+
+ glTranslatef(20,20,0);
+
+ ofDrawRectangle(0,0,560,560);
+
+ glTranslatef(outputOffset.x,outputOffset.y,0);
+
+ glScalef(guiScale,guiScale,guiScale );
+
+ glTranslatef(2048.0f+outputPosition.x,2048.0f+outputPosition.y,0);
+
+ if (bOutputSelected) {
+ if (commandPressed) {
+ ofSetColor(0,255,0);
+ }
+ else {
+ ofSetColor(255,0,0);
+ }
+ }
+
+ ofDrawRectangle(
+ (-outputWindowSize.x/2)*outputScale*outputOffsetScale,
+ (-outputWindowSize.y/2)*outputScale*outputOffsetScale,
+ outputWindowSize.x*outputScale*outputOffsetScale,
+ outputWindowSize.y*outputScale*outputOffsetScale);
+
+ ofFill();
+
+ glPopMatrix();
+ }
+ else {
+ //draw gui, sources and preview
+
+ videoSourcePanel.draw();
+ vectorSourcePanel.draw();
+ audiopanel.draw();
+ drawingpanel.draw();
+ perlinpanel.draw();
+ contourgui.draw();
+ vectorTransforms.draw();
+ svggui.draw();
+ laser.drawgui();
+ text.drawgui();
+
+ float sourcescale=sourceframesize.x/outputWindowSize.x;
+
+ for (int i=0;i<4;i++){
+ glPushMatrix();
+ glTranslatef(210,249*i,0);
+ ofNoFill();
+ ofDrawRectangle(5,5,sourceframesize.x+4,sourceframesize.y+4);
+ ofFill();
+
+ glTranslatef(7,7,0);
+
+ //draw video sources within case statement
+ switch(i){
+ case 0:{
+ if (captPreview){
+ if (useKinect){
+ if (texRGB.isAllocated()){
+ texRGB.draw(0,0,sourceframesize.x,sourceframesize.y);
+ }
+ }
+ else {
+ texRGB.draw(0,0,sourceframesize.x,sourceframesize.y);
+ }
+ }
+ break;
+ }
+ case 1:{
+ if (_player.isLoaded()){
+ if (ofGetElapsedTimef()-videostart<2.54f){
+ int amt=(ofGetElapsedTimef()-videostart)*100;
+ ofSetColor(amt);
+ }
+ _player.draw(0,0,sourceframesize.x,sourceframesize.y);
+ ofSetColor(255);
+ }
+ break;
+ }
+ case 2:{
+ glPushMatrix();
+ glScalef(sourcescale,sourcescale,sourcescale);
+ for (auto shape=segmenters.begin();shape!=segmenters.end();shape++){
+ shape->getPoly().draw();
+ }
+ glPopMatrix();
+ ofSetColor(255);
+ break;
+ }
+ }
+
+
+
+ //draw vectors within case statement
+
+ glPopMatrix();
+ }
+
+ glPushMatrix();
+ glTranslatef(550,5,0);
+ ofNoFill();
+ ofDrawRectangle(0,0,previewframesize.x+4,previewframesize.y+4);
+ ofFill();
+
+ if (perlinEnable) perlin->begin();
+ preview.draw(2,2,previewframesize.x,previewframesize.y);
+ if (perlinEnable) perlin->end();
+
+ glPopMatrix();
+
+ }
+
+ ofSetWindowTitle(ofToString(phase, 3)+" "+ofToString(ofGetFrameRate(), 2)+" fps laser points: "+ofToString(laser.get_numpts()));
+}
+
+static float phase=0.0f;
+static float lastframetime=0.0f;
+static float scale=10.0f;
+static float pixelWidth=0.05f;
+
+static int num=0;
+
+void ofApp::drawOutput(ofEventArgs & args){
+/*
+ if (perlinScale!=ps){
+ perlin->updateParameter("scale", perlinScale);
+ ps=perlinScale;
+ //ofLog()<<"set perlin scale to "<<perlinScale;
+ }
+ */
+ //this is mental, phase will update but scale will not
+ //it's not related to name, or order - generating a change per interval like this works differently
+ float interval=ofGetElapsedTimef()-lastframetime;
+ lastframetime=ofGetElapsedTimef();
+ phase+=interval*perlinSpeed;
+ scale+=interval*perlinScale;
+ pixelWidth+=interval*perlinPixelWidth;
+ perlin->updateParameter("phase", phase);
+ perlin->updateParameter("scale", scale);
+ perlin->updateParameter("fractionalWidthOfPixel",0.05f+(sin(ofGetElapsedTimef()*perlinPixelWidthSpeed)*perlinPixelWidth));
+
+ //prepare vectors
+
+ float vpos=ofGetHeight()/2;
+
+ float scale=ofGetHeight()*vScale;
+
+ ofPolyline osc;
+
+ osc.addVertex(0,vpos+(buffer[0]*scale));
+
+ float i=0.0f;
+ float step=ofGetWidth()/(hScale*48);
+
+ while (i<(ofGetWidth()+step)){
+ i+=max(1.0f,(float)step);
+ osc.lineTo(i,vpos+(buffer[(int)i]*scale));
+ }
+
+ //render output to fbo and output
+
+ preview.begin();
+
+ ofBackground(0);
+ ofSetColor(255,255,255);
+
+
+ ofClear(0,0,0,0);
+
+ vector<colourPolyline> contouroutput;
+
+ if (captEnable||(_player.isLoaded()&&playerEnable)){
+ if (video_outlines){
+ //use capture or player to generate outlines
+ if (captEnable){
+ colorImg.setFromPixels(texRGB.getPixels());
+ }
+ else if (_player.isLoaded()&&playerEnable){
+ colorImg.setFromPixels(_player.getPixels());
+ }
+ ofPoint scale=ofPoint(outputWindowSize.x/colorImg.getWidth(),outputWindowSize.y/colorImg.getHeight());
+ if (grayImage.getWidth()!=colorImg.getWidth()||grayImage.getHeight()!=colorImg.getHeight()){
+ grayImage.clear();
+ }
+ grayImage=colorImg;
+ if(contour_adaptive){
+ grayImage.adaptiveThreshold(grayImage.getWidth()*contour_adaptive_window, contour_threshold,false,true);
+ }
+ else {
+ grayImage.threshold(contour_threshold);
+ }
+ contourFinder.findContours(grayImage, 20, (grayImage.getWidth()*grayImage.getHeight())/3, 10, true);
+ for (int i = 0; i < contourFinder.nBlobs; i++){
+ colourPolyline shape;
+ for (auto& point:contourFinder.blobs[i].pts){
+ ofVec3f p=point*scale;
+ ofColor c=colorImg.getPixels().getColor(point.x,point.y);
+ shape.addVertex(p,c); //contour_useColour?c:ofColor(laser_R,laser_G,laser_B));
+ }
+ shape.simplify(contour_simplify);
+ contouroutput.push_back(shape);
+ }
+ }
+ }
+
+ vector<colourPolyline> svgoutput;
+
+ if (SVGLaserDraw||SVGVideoDraw){
+ if (!use_onset||onset_frame<onset_duration||shapes_randomise){
+ if (shapes_randomise){
+ //if (framecounter==0){
+ // select_random_shapes();
+ // framecounter=shapes_duration;
+ //}
+ select_random_shapes(shapes_duration,!use_onset||onset_frame<onset_duration);
+ for (auto s:shape_selection){
+ if (use_segmenter){
+ auto segments=segmenters[s].getSegments(segmenter_number,segmenter_length,phase);
+ for (auto segment=segments.begin();segment!=segments.end();segment++){
+ //polyOutput.push_back(colourPolyline(*segment,ofColor(laser_R,laser_G,laser_B)));
+ svgoutput.push_back(*segment);
+ }
+ }
+ else {
+ //polyOutput.push_back(colourPolyline(segmenters[s].getPoly(),ofColor(laser_R,laser_G,laser_B)));
+ svgoutput.push_back(segmenters[s].getPoly());
+ }
+ }
+ framecounter--;
+ }
+ else {
+ for (auto shape=segmenters.begin();shape!=segmenters.end();shape++){
+ if (use_segmenter){
+ auto segments=shape->getSegments(segmenter_number,segmenter_length,phase);
+ for (auto segment=segments.begin();segment!=segments.end();segment++){
+ //polyOutput.push_back(colourPolyline(*segment,ofColor(laser_R,laser_G,laser_B)));
+ svgoutput.push_back(*segment);
+ }
+ }
+ else {
+ //polyOutput.push_back(colourPolyline(shape->getPoly(),ofColor(laser_R,laser_G,laser_B)));
+ svgoutput.push_back(shape->getPoly());
+ }
+ }
+ }
+ if (contour_useColour){
+ vector<colourPolyline> newPolys;
+ for (auto p: svgoutput){
+ newPolys.push_back(colourPolyline((ofPolyline)p,ofColor(255,255,255))); //laser_R,laser_G,laser_B))) ;
+ }
+ svgoutput=newPolys;
+ }
+ }
+ }
+
+ vector<colourPolyline> textoutput;
+
+ if (textVideoDraw||textLaserDraw){
+ textoutput = text.getOutlines(ofGetWidth()/2,ofGetHeight()/2);
+ /*
+ if (textoutput.size()!=num) {
+ ofLog()<<textoutput.size()<<" text shapes";
+ num=textoutput.size();
+ }
+ */
+
+ }
+
+ ofSetLineWidth(lineWidth);
+
+ if (contoursVideoDraw) {
+ for (auto c:contouroutput) c.draw();
+ }
+ else if (SVGVideoDraw) {
+ for (auto s:svgoutput) s.draw();
+ }
+ else if (textVideoDraw){
+ for (auto i:textoutput) i.draw();
+ }
+ else if (captEnable){
+ texRGB.draw(0,0,outputWindowSize.x,outputWindowSize.y);
+ }
+ else if (_player.isLoaded()&&playerEnable){
+ if (ofGetElapsedTimef()-videostart<2.54f){
+ int amt=(ofGetElapsedTimef()-videostart)*100;
+ ofSetColor(amt);
+ }
+ _player.draw(0,0,outputWindowSize.x,outputWindowSize.y);
+ }
+
+ if (videoOscEnable) osc.draw();
+
+ preview.end();
+
+ if (perlinEnable) perlin->begin();
+ //shader.begin();
+ preview.draw(0,0);
+ //shader.end();
+ if (perlinEnable) perlin->end();
+
+ vector<colourPolyline> output;
+
+ if (vectorOscEnable) output.push_back(osc);
+ else if (contoursLaserDraw) output=contouroutput;
+ else if (SVGLaserDraw) output=svgoutput;
+ else if (textLaserDraw) output=textoutput;
+
+ //laser transformation comes here
+
+ vector <colourPolyline> transformedOutput;
+
+ if (use_rotate||use_scale){
+ ofMatrix4x4 rm = ofMatrix4x4::newIdentityMatrix();
+ rm.translate(-outputWindowSize.x/2,-outputWindowSize.y/2,0);
+ if (use_rotate){
+ rm.rotateRad(rotate_amt,0,0,1);
+ }
+ if (use_scale){
+ rm.scale(scale_amt,scale_amt,scale_amt);
+ }
+ rm.translate(outputWindowSize.x/2,outputWindowSize.y/2,0);
+ for (auto& shape:output){
+ transformedOutput.push_back(lineTransformer::polyLineTransform(rm,shape));
+ }
+ }
+ else {
+ transformedOutput=output;
+ }
+
+ //laser warping comes here
+
+ glm::vec2 src[]={
+ glm::vec2(0,0),
+ glm::vec2(ofGetWidth(),0),
+ glm::vec2(ofGetWidth(),ofGetHeight()),
+ glm::vec2(0,ofGetHeight())
+ };
+
+ glm::vec2 mp=glm::vec2(outputWindowSize.x/2,outputWindowSize.y/2);
+
+ glm::vec2 scaled_dest[]={
+ ((warpframe[0]-mp)*outputScale*outputOffsetScale)+mp,
+ ((warpframe[1]-mp)*outputScale*outputOffsetScale)+mp,
+ ((warpframe[2]-mp)*outputScale*outputOffsetScale)+mp,
+ ((warpframe[3]-mp)*outputScale*outputOffsetScale)+mp
+ };
+
+ ofMatrix4x4 scaled_warp =lineTransformer::getPerspectiveTransformMatrix(src,scaled_dest);
+ ofMatrix4x4 warp =lineTransformer::getPerspectiveTransformMatrix(src,warpframe);
+
+ vector <colourPolyline> warpedOutput;
+ vector <colourPolyline> scaledWarpedOutput;
+
+ for (auto s:transformedOutput){
+ warpedOutput.push_back(lineTransformer::polyLineTransform(warp,s));
+ scaledWarpedOutput.push_back(lineTransformer::polyLineTransform(scaled_warp,s));
+ }
+
+ ofPolyline safety;
+ safety.addVertex(safety_frame[0]);
+ safety.addVertex(safety_frame[1]);
+ safety.addVertex(safety_frame[2]);
+ safety.addVertex(safety_frame[3]);
+ //safety.close(); //addVertex(safety_frame[0]);
+
+ ofPolyline warpedScaledSafety=lineTransformer::polyLineTransform(scaled_warp,safety);
+
+ warpedScaledSafety.close();
+
+ vector <colourPolyline> clippedOutput;
+
+ if (use_safety){
+ vector <ofPolyline> masks;
+ masks.push_back(warpedScaledSafety); //create polyline
+
+ for (auto& poly: scaledWarpedOutput)
+ {
+ clipper.Clear();
+ clipper.addPolylines(masks, ClipperLib::ptClip);
+ vector <ofPolyline> shapes; //TODO make clipper clip colourpolylines
+ shapes.push_back(poly);
+ clipper.addPolylines(shapes,ClipperLib::ptSubject);
+ vector <ofPolyline> clipped;
+ //if (invert_mask){
+ // clipped = clipper.getClippedLines(ClipperLib::ctDifference);
+ //}else {
+ clipped = clipper.getClippedLines(ClipperLib::ctIntersection);
+ //}
+ for (auto& clip: clipped)
+ {
+ clip.simplify(contour_simplify);
+ clippedOutput.push_back(colourPolyline(clip,poly.getColourAt(0)));
+ }
+ }
+ }
+ else {
+ clippedOutput=scaledWarpedOutput;
+ }
+
+ //laser sorting comes here
+
+ if (edit_safety){
+ lineTransformer::drawWarpFrame(safety_frame);
+ laser.draw(warpedScaledSafety);
+ }
+ else if (clippedOutput.size()) {
+ laser.draw(clippedOutput);
+ }
+ else {
+ colourPolyline blank;
+ for (int i=0;i<100;i++){
+ blank.addVertex(ofGetWidth()/2,ofGetHeight()/2,0,0,0);
+ }
+ laser.draw(blank);
+ }
+
+ if (bDrawFrame){
+ lineTransformer::drawWarpFrame(warpframe);
+ }
+}
+
+void ofApp::onNoteOn(GistEvent &e){
+ //ofLog() << "<<NOTE ON>>";
+ onset_frame=0;
+ onset_number++;
+ //noteOnRadius = 100;
+};
+
+
+void ofApp::onNoteOff(GistEvent &e){
+ //ofLog() << "<<NOTE OFF>>";
+ //turn off?
+ //noteOnRadius = 0;
+};
+
+void ofApp::audioIn(float * input, int blockSize, int nChannels){
+ buffer.add(input,blockSize);
+
+ vector<float>buf;
+ buf.assign(&input[0],&input[blockSize-1]);
+ gist.processAudio(buf, blockSize, nChannels,SAMPLERATE);
+}
+
+
+void ofApp::select_random_shapes(float duration,bool generate){
+ float timedelta=ofGetElapsedTimef()-last_frame_time;
+ last_frame_time=ofGetElapsedTimef();
+ //track how long each shape has been selected
+ for (int i=0;i<shape_selection_durations.size();i++){
+ if (shape_selection_durations[i]>0.0f){
+ shape_selection_durations[i]=shape_selection_durations[i]-timedelta;
+ }
+ }
+
+ shape_selection.clear();
+
+ for (int i=0;i<shape_selection_durations.size();i++){
+ if (shape_selection_durations[i]>0.0f){
+ shape_selection.insert(i);
+ }
+ }
+ std::stringstream strom;
+ for (auto& s:shape_selection){
+ strom << s <<":"<<shape_selection_durations[s]<<" ";
+ }
+
+ //cout << timedelta <<": decay selection: "<<shape_selection.size()<<" shapes (" << strom.str() <<") \n";
+ if (generate){
+
+ while (shape_selection.size()<(segmenters.size()*shapes_amount)){
+ int selection=rand()%segmenters.size();
+ if (shape_selection.find(selection)==shape_selection.end()){
+ shape_selection_durations[selection]=duration+ofRandom(1.0f);
+ shape_selection.insert(selection);
+ }
+ }
+ std::stringstream strm;
+ for (auto& s:shape_selection){
+ strm << s <<":"<<shape_selection_durations[s]<<" ";
+ }
+ }
+ //cout << "random selection: "<<shape_selection.size()<<" shapes (" << strm.str() <<") \n";
+}
+
+
+//--------------------------------------------------------------
+void ofApp::exit() {
+
+
+}
+
+
+
+//--------------------------------------------------------------
+void ofApp::outputKeyPressed(ofKeyEventArgs &args){
+
+ if (args.key=='f') {
+ ofToggleFullscreen();
+ preview.allocate(ofGetWidth(),ofGetHeight());
+ }
+
+ keyPressed(args);
+
+
+}
+
+void ofApp::keyPressed(ofKeyEventArgs &args){
+ if (args.key==OF_KEY_COMMAND){
+ commandPressed=true;
+ }
+
+ switch(args.key){
+ case '`':{
+ bShowPositionInterface=!bShowPositionInterface;
+ break;
+ }
+ case 'w':{
+ bDrawFrame=!bDrawFrame;
+ break;
+ }
+ case 'd':{
+ default_settings();
+ break;
+ }
+ case 's':{
+ save_settings();
+ break;
+ }
+ case '1':{
+ scale=1.0f;
+ break;
+ }
+ case '2':{
+ scale=2.0f;
+ break;
+ }
+ case '3':{
+ scale=3.0f;
+ break;
+ }
+ case '4':{
+ scale=4.0f;
+ break;
+ }
+ case '5':{
+ scale=5.0f;
+ break;
+ }
+ case '6':{
+ scale=6.0f;
+ break;
+ }
+ case '7':{
+ scale=7.0f;
+ break;
+ }
+ case '8':{
+ scale=8.0f;
+ break;
+ }
+ case '9':{
+ scale=9.0f;
+ break;
+ }
+ }
+}
+
+//--------------------------------------------------------------
+void ofApp::outputKeyReleased(ofKeyEventArgs &args){
+ keyReleased(args);
+}
+
+void ofApp::keyReleased(ofKeyEventArgs &args){
+ if (args.key==OF_KEY_COMMAND){
+ commandPressed=false;
+ }
+}
+
+
+//--------------------------------------------------------------
+void ofApp::mouseMoved(int x, int y ){
+}
+
+//--------------------------------------------------------------
+void ofApp::outputMouseDragged(ofMouseEventArgs & args){
+ if (select_warpframe>-1){
+ warpframe[select_warpframe]=glm::vec2(args.x,args.y);
+ }
+ if (select_safetyframe>-1){
+ safety_frame[select_safetyframe]=glm::vec3(args.x,args.y,0);
+ }
+
+}
+
+void ofApp::mouseDragged(int x, int y, int button){
+ if (bOutputSelected){
+ if (commandPressed){
+ float startDistance=((outputPosition*guiScale)+ofPoint(300,300)).distance(outputSelectionPoint);
+ float currentDistance=((outputPosition*guiScale)+ofPoint(300,300)).distance(ofPoint(x,y));
+ outputOffsetScale=currentDistance/startDistance;
+ }
+ else {
+ outputOffset=ofPoint(x,y)-outputSelectionPoint;
+ laser.set_centre(ofPoint(
+ outputPosition.x+(outputOffset.x/guiScale),
+ outputPosition.y+(outputOffset.y/guiScale)
+ ));
+ }
+ }
+}
+
+//--------------------------------------------------------------
+void ofApp::outputMousePressed(ofMouseEventArgs & args){
+ for (int i=0;i<4;i++){
+ if (ofPoint(args.x,args.y).distance(warpframe[i])<25){
+ select_warpframe=i;
+ }
+ }
+ if (edit_safety){
+ for (int i=0;i<4;i++){
+ if (ofPoint(args.x,args.y).distance(safety_frame[i])<25){
+ select_safetyframe=i;
+ }
+ }
+ }
+}
+
+void ofApp::mousePressed(int x, int y, int button){
+ if (bShowPositionInterface){
+ if (x>(300+((outputPosition.x-((outputWindowSize.x/2)*outputScale))*guiScale))&&
+ x<(300+((outputPosition.x+((outputWindowSize.x/2)*outputScale))*guiScale))&&
+ y>(300+((outputPosition.y-((outputWindowSize.y/2)*outputScale))*guiScale))&&
+ y<(300+((outputPosition.y+((outputWindowSize.y/2)*outputScale))*guiScale))
+ ){
+ outputSelectionPoint=ofPoint(x,y);
+ bOutputSelected=true;
+ }
+ }
+
+}
+
+//--------------------------------------------------------------
+void ofApp::outputMouseReleased(ofMouseEventArgs & args){
+ select_warpframe=-1;
+ select_safetyframe=-1;
+}
+
+void ofApp::mouseReleased(int x, int y, int button){
+ if (bOutputSelected){
+ if (commandPressed){
+ outputScale*=outputOffsetScale;
+ }
+ else {
+ outputPosition+=outputOffset/guiScale;
+ }
+ bOutputSelected=false;
+ outputOffset=ofPoint(0,0);
+ outputOffsetScale=1.0f;
+ }
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseEntered(int x, int y){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::mouseExited(int x, int y){
+
+}
+
+//--------------------------------------------------------------
+void ofApp::outputWindowResized(ofResizeEventArgs &resizeargs){
+
+}
+
+void ofApp::windowResized(int w, int h){
+
+}
+
+
+
+//--------------------------------------------------------------
+void ofApp::dragEvent(ofDragInfo dragInfo){
+ std::string filename=dragInfo.files[0];
+ for (int i=0;i<4;i++){
+ if (dragInfo.position.x>215&&dragInfo.position.x<540
+ &&dragInfo.position.y>249*i+5&&dragInfo.position.y<249*i+249){
+ ofLog()<<"slot "<<i<<" received "<<filename<<" : "<<dragInfo.position.x;
+
+ if (i==1) {
+ if (_player.load(filename)){
+ videostart=ofGetElapsedTimef();
+ }
+ }
+
+ if (i==2) {
+ svg.load(filename);
+ vector <ofPath> imagepaths= svg.getPaths();
+
+ std::stringstream strm;
+
+ if (imagepaths.size()){
+ segmenters.clear();
+ shape_selection_durations.clear();
+ for (auto& path:imagepaths){
+ path.setPolyWindingMode(OF_POLY_WINDING_ODD);
+
+ vector <ofPolyline> outlines= path.getOutline();
+ for (auto& outline:outlines){
+ strm << outline.size() << "->";
+ outline.simplify(contour_simplify);
+ strm << outline.size() << " ";
+ segmenters.push_back(colourLineSegmenter(outline,path.getStrokeColor()));
+ shape_selection_durations.push_back(0.0f);
+ }
+
+ strm << " , ";
+ }
+
+ std::stringstream strom;
+
+ shape_selection.clear();
+ while (shape_selection.size()<(segmenters.size()*shapes_amount)){
+ int selection=(int)ofRandom(0,segmenters.size());
+ shape_selection.insert(selection);
+ }
+ for (auto s:shape_selection){
+ float spawn=(ofRandom(0,(float)shapes_duration));
+ shape_selection_durations[s]=spawn;
+ strom << s << ":"<<spawn<<" ";
+ }
+ last_frame_time=ofGetElapsedTimef();
+ //cout << "SVG: selected paths [ "<<strom.str() << " ]" <<std::endl;
+ //cout << "SVG: found " << imagepaths.size() << " paths with " << segmenters.size() << " shapes [ " << strm.str() << " ]" <<std::endl;
+ //select_random_shapes(shapes_duration);
+ }
+ }
+
+ if (i==3) {
+ text.loadfile(filename);
+ }
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/FESgui/src/ofApp.h b/FESgui/src/ofApp.h
new file mode 100644
index 0000000..8519f01
--- /dev/null
+++ b/FESgui/src/ofApp.h
@@ -0,0 +1,273 @@
+#pragma once
+
+#include "ofMain.h"
+#include "ofxGui.h"
+#include "ofxXmlSettings.h"
+#include "ofxKinectV2.h"
+#include "ofxOpenCv.h"
+#include "ofxClipper.h"
+#include "ofxSVG.h"
+#include "ofxGist.h"
+#include "ofxOpenALSoundPlayer.h"
+
+#include "PerlinPhasingFilter.h"
+#include "lasergui.h"
+#include "audiobuffer.h"
+#include "linetransformer.h"
+#include "linesegmenter.h"
+#include "selectpanel.h"
+#include "vectortext.h"
+
+#define SAMPLERATE 48000
+
+const ofPoint outputWindowSize=ofPoint(1024,768);
+const float guiScale=560.0f/4096.0f;
+const ofPoint sourceframesize=ofPoint(320,240);
+const ofPoint previewframesize=ofPoint(640,480);
+
+class textgui : public ofxPanel{
+ public:
+ ofParameter<bool> use_beat;
+ ofParameter<float> beat_duration;
+ ofParameter<float> text_speed;
+ ofParameter<float> text_scale;
+ ofParameter<bool> enable_anim;
+ ofParameter<bool> anim_rev;
+ ofParameter<float> vert_pos;
+ ofParameter<float> vert_spread;
+ glyphbanner text;
+ int onset_frame;
+ void loadfile(const string & f){
+ if (!strcmp(strchr(f.c_str(), '\0') - 4, ".plt")){
+ text.loadPalette(f);
+ }
+ else {
+ text.load(f);
+ }
+ }
+ void setup(int x, int y){
+ //text.loadFont("fonts/EMSSpaceRocks.svg");
+ text.loadFont("fonts/EMSOsmotron.svg");
+
+ ofxPanel::setup("text","",x,y);
+ ofxPanel::add(use_beat.set("use beat", false));
+ ofxPanel::add(beat_duration.set("duration factor", 0.5f, 0.0f, 1.0f));
+ ofxPanel::add(text_speed.set("speed", 5.0f, 0.0f, 25.0f));
+ ofxPanel::add(text_scale.set("scale", 0.1f, 0.0f, 0.5f));
+ ofxPanel::add(enable_anim.set("animate", false));
+ ofxPanel::add(anim_rev.set("reverse", false));
+ ofxPanel::add(vert_pos.set("vert_pos", 0.0f, -0.3f, 0.3f));
+ ofxPanel::add(vert_spread.set("vert_spread", 0.0f, -0.3f, 0.3f));
+
+ }
+ void update(int oframe=0){
+ onset_frame=oframe;
+ text.update(text_speed,true);
+ }
+ void drawgui(int oframe=0){
+ onset_frame=oframe;
+ ofxPanel::draw();
+ }
+ void createWords(string t){
+ text.createWords(t);
+ }
+ vector<colourPolyline>& getOutlines(float x=0, float y=0){
+ return text.getOutlines(text_scale,STYLE_OVERLAPPING,x,y+(ofGetHeight()*vert_pos),enable_anim,anim_rev,vert_spread,use_beat,beat_duration);
+ }
+
+};
+
+class ofApp: public ofBaseApp {
+
+ public:
+ void setup();
+ void update();
+ void draw();
+ void exit();
+
+ void keyPressed(ofKeyEventArgs &keyargs);
+ void keyReleased(ofKeyEventArgs & args);
+ void mouseMoved(int x, int y );
+ void mouseDragged(int x, int y, int button);
+ void mousePressed(int x, int y, int button);
+ void mouseReleased(int x, int y, int button);
+ void mouseEntered(int x, int y);
+ void mouseExited(int x, int y);
+ void windowResized(int w, int h);
+ void dragEvent(ofDragInfo dragInfo);
+
+ void updateOutput(ofEventArgs & args);
+ void drawOutput(ofEventArgs & args);
+ void outputKeyPressed(ofKeyEventArgs & args);
+ void outputKeyReleased(ofKeyEventArgs & args);
+ void outputMouseDragged(ofMouseEventArgs & args);
+ void outputMousePressed(ofMouseEventArgs & args);
+ void outputMouseReleased(ofMouseEventArgs & args);
+ void outputWindowResized(ofResizeEventArgs &resizeargs);
+//--laser interface and settings------------------------------------------------
+ lasergui laser;
+
+ ofxXmlSettings XML;
+ void default_settings();
+ void save_settings();
+
+ void setup_lasergui(int x, int y);
+
+ void select_random_shapes(float duration,bool generate);
+
+ bool commandPressed;
+
+ glm::vec2 warpframe[4];
+ int select_warpframe;
+ bool bDrawFrame;
+
+ bool bShowPositionInterface;
+ bool bOutputSelected;
+ ofPoint outputSelectionPoint;
+ ofPoint outputPosition;
+ ofPoint outputOffset;
+ float outputScale;
+ float outputOffsetScale;
+
+ glm::vec3 safety_frame[4];
+ int select_safetyframe;
+//--audio------------------------------------------------
+
+ ofSoundStream soundStream;
+ int blockSize;
+
+ void audioIn(float * input, int blockSize, int nChannels);
+
+ ofxGist gist;
+ void onNoteOn(GistEvent &e);
+ void onNoteOff(GistEvent &e);
+
+ ofxPanel audiopanel;
+ ofParameter<float> vScale;
+ ofParameter<float> hScale;
+ ofParameter<float> onset_threshold;
+ ofParameter<bool> use_onset;
+ ofParameter<int> onset_duration;
+
+ int onset_frame;
+ int onset_number;
+
+ Buffer buffer;
+
+//--vector drawing-------------------------------------
+
+ ofxPanel drawingpanel;
+ ofParameter<float> lineWidth;
+
+//--perlin filter
+
+ PerlinPhasingFilter *perlin;
+ ofxPanel perlinpanel;
+ ofParameter<bool> perlinEnable;
+ ofParameter<float> perlinScale;
+ ofParameter<float> perlinSpeed;
+ ofParameter<float> perlinPixelWidth;
+ ofParameter<float> perlinPixelWidthSpeed;
+
+//--video capture (automatic)
+
+ bool useKinect;
+ ofxKinectV2 kinect;
+ ofImage texRGB;
+ ofVideoGrabber _video;
+
+//--preview and sources
+
+ ofFbo preview;
+
+ ofxPanel videoSourcePanel;
+ ofParameter<bool> captPreview;
+ ofParameter<bool> captEnable;
+ ofParameter<bool> videoOscEnable;
+ ofParameter<bool> playerEnable;
+ ofParameter<bool> contoursVideoDraw;
+ ofParameter<bool> SVGVideoDraw;
+ ofParameter<bool> textVideoDraw;
+ ofParameter<float> video_speed; //move this
+
+
+ ofxPanel vectorSourcePanel;
+ ofParameter<bool> edit_safety;
+ ofParameter<bool> use_safety;
+ ofParameter<bool> vectorOscEnable;
+ ofParameter<bool> contoursLaserDraw;
+ ofParameter<bool> SVGLaserDraw;
+ ofParameter<bool> textLaserDraw;
+
+ ofxPanel vectorTransforms;
+ ofParameter<bool> use_rotate;
+ ofParameter<float> xf_rotate;
+ ofParameter<bool> use_scale;
+ ofParameter<float> xf_scale_speed;
+ ofParameter<float> xf_scale_min;
+ ofParameter<float> xf_scale_max;
+
+ int framecounter;
+ float phase,prev_time; //to calculate phase
+ float rotate_amt;
+ float scale_phase,scale_amt;
+
+//--video player
+
+ ofVideoPlayer _player;
+ float videostart;
+
+//--contours
+
+ ofxCvColorImage colorImg;
+ ofxCvGrayscaleImage grayImage;
+ ofxCvContourFinder contourFinder;
+
+ ofxPanel contourgui;
+ ofParameter<bool> video_outlines;
+ ofParameter<bool> contour_adaptive;
+ ofParameter<int> contour_threshold;
+ ofParameter<float> contour_adaptive_window;
+ ofParameter<float> contour_simplify;
+ ofParameter<bool> contour_useColour;
+
+//get colour somewhere else
+// ofxIntSlider laser_R;
+// ofxIntSlider laser_G;
+// ofxIntSlider laser_B;
+
+//==//SVG player===================================
+
+ ofxSVG svg;
+ vector <colourLineSegmenter> segmenters;
+ vector <float> shape_selection_durations;
+
+ set <int> shape_selection;
+ float last_frame_time;
+
+ //svg gui
+ ofxPanel svggui;
+ ofParameter<bool> shapes_randomise;
+ ofParameter<float> shapes_amount;
+ ofParameter<float> shapes_duration;
+ ofParameter<bool> use_mask;
+ ofParameter<bool> invert_mask;
+
+ //segmenter
+ ofParameter<bool> use_segmenter;
+ ofParameter<bool> colour_segmenter;
+ ofParameter<float> segmenter_speed;
+ ofParameter<float> segmenter_length;
+ ofParameter<int> segmenter_number;
+
+
+//==/Mask clipping=================================== /
+
+
+ ofx::Clipper clipper;
+
+//==/text=================================== /
+
+ textgui text;
+
+}; \ No newline at end of file
diff --git a/FESgui/src/selectpanel.h b/FESgui/src/selectpanel.h
new file mode 100644
index 0000000..a6811bc
--- /dev/null
+++ b/FESgui/src/selectpanel.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include "ofMain.h"
+#include "ofxGui.h"
+
+/* a gui panel that receives key and drag events. */
+
+class selectPanel : public ofxPanel {
+ public:
+ selectPanel(string _name=""){
+ name=_name;
+ ofRegisterKeyEvents(this, defaultEventsPriority);
+ ofRegisterDragEvents(this, defaultEventsPriority);
+ }
+ bool mouseMoved(ofMouseEventArgs & args){
+ if (args.x>getPosition().x&&
+ args.x-getPosition().x<getWidth()&&
+ args.y>getPosition().y&&
+ args.y-getPosition().y<getHeight()){
+ isSelected=true;
+ setHeaderBackgroundColor(ofColor(255,128,0));
+ }
+ else {
+ isSelected=false;
+ setHeaderBackgroundColor(ofColor(80,80,80));
+ }
+ }
+ bool keyPressed(ofKeyEventArgs & args){
+ if (isSelected){
+ ofLog()<<name<<" KEY> "<<args.key;
+ switch(args.key){
+ default:
+ break;
+ }
+ }
+ }
+ bool keyReleased(ofKeyEventArgs & args){
+ //required in order to call ofRegisterKeyEvents
+ }
+ virtual void loadfile(const string & f){};
+ bool dragEvent(ofDragInfo & dragInfo){
+ if (dragInfo.position.x>getPosition().x&&
+ dragInfo.position.x-getPosition().x<getWidth()&&
+ dragInfo.position.y>getPosition().y&&
+ dragInfo.position.y-getPosition().y<getHeight()){
+ ofLog()<<name<<" DRAG> "<<dragInfo.files[0];
+ loadfile(dragInfo.files[0]);
+ }
+ }
+private:
+ bool isSelected;
+ string name;
+}; \ No newline at end of file
diff --git a/FESgui/src/vectortext.h b/FESgui/src/vectortext.h
new file mode 100644
index 0000000..4625989
--- /dev/null
+++ b/FESgui/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