|
|
|
#include "Map.h"
|
|
|
|
#include "algorithm"
|
|
|
|
|
|
|
|
Map::Map(){
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Map::Setup(){
|
|
|
|
scale = 10000;
|
|
|
|
isDebug = true;
|
|
|
|
|
|
|
|
json_embeddings = ofLoadJson(jsonPath);
|
|
|
|
if(!json_embeddings.is_array()){
|
|
|
|
ofLogError() << "JSON is not an array";
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
std::cout << "JSON LOADED" << std::endl;
|
|
|
|
SetupTSNE();
|
|
|
|
}
|
|
|
|
|
|
|
|
mapFbo.allocate(ofGetWindowWidth() / 2, ofGetWindowHeight(), GL_RGB);
|
|
|
|
|
|
|
|
fboImage.allocate(ofGetWindowWidth() / 2, ofGetWindowHeight(), OF_IMAGE_COLOR);
|
|
|
|
|
|
|
|
Setup3D();
|
|
|
|
|
|
|
|
SetupNodes();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Map::Update(){
|
|
|
|
time = ofGetElapsedTimef();
|
|
|
|
for(auto& n : nodes){
|
|
|
|
glm::vec2 offset = n.amplitude * sin(n.speed * time);
|
|
|
|
n.offset = glm::vec3(offset.x, offset.y, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Map::Draw(){
|
|
|
|
mapFbo.begin();
|
|
|
|
|
|
|
|
cam.begin();
|
|
|
|
|
|
|
|
ofClear(0, 0, 0, 1);
|
|
|
|
|
|
|
|
ofMatrix4x4 projectionMatrix = cam.getProjectionMatrix();
|
|
|
|
ofMatrix4x4 viewMatrix = cam.getModelViewMatrix();
|
|
|
|
ofMatrix4x4 mvpMatrix = projectionMatrix * viewMatrix;
|
|
|
|
|
|
|
|
for (auto& n :nodes){
|
|
|
|
glm::vec3 node_position = n.position + n.offset;
|
|
|
|
|
|
|
|
if(isFrustum(node_position, 50)){
|
|
|
|
n.texture.getTexture().bind();
|
|
|
|
ofPushMatrix();
|
|
|
|
ofFill();
|
|
|
|
n.geom.setPosition(node_position);
|
|
|
|
n.geom.draw();
|
|
|
|
ofPopMatrix();
|
|
|
|
n.texture.getTexture().unbind();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cam.end();
|
|
|
|
|
|
|
|
mapFbo.end();
|
|
|
|
|
|
|
|
mapFbo.readToPixels(fboPixels);
|
|
|
|
|
|
|
|
fboImage.setFromPixels(fboPixels);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Setup texture for each node
|
|
|
|
*/
|
|
|
|
void Map::SetupNodes(){
|
|
|
|
std::cout << "Setting up nodes.." << std::endl;
|
|
|
|
for (auto& node : nodes){
|
|
|
|
ofImage img;
|
|
|
|
ofPlanePrimitive plane;
|
|
|
|
|
|
|
|
img.load("data/" + node.image_path);
|
|
|
|
int imageW = img.getWidth();
|
|
|
|
int imageH = img.getHeight();
|
|
|
|
int maxWidth = 100;
|
|
|
|
int maxHeight = 100;
|
|
|
|
float aspectRatio = (float)imageW / (float)imageH;
|
|
|
|
|
|
|
|
// Determine plane dimensions based on aspect ratio and constraints
|
|
|
|
float planeW, planeH;
|
|
|
|
if (aspectRatio > (float)maxWidth / maxHeight) {
|
|
|
|
// Image is wider relative to the max constraints
|
|
|
|
planeW = maxWidth;
|
|
|
|
planeH = maxWidth / aspectRatio;
|
|
|
|
} else {
|
|
|
|
// Image is taller relative to the max constraints
|
|
|
|
planeH = maxHeight;
|
|
|
|
planeW = maxHeight * aspectRatio;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that dimensions do not exceed constraints
|
|
|
|
if (planeH > maxHeight) {
|
|
|
|
planeH = maxHeight;
|
|
|
|
planeW = maxHeight * aspectRatio;
|
|
|
|
}
|
|
|
|
if (planeW > maxWidth) {
|
|
|
|
planeW = maxWidth;
|
|
|
|
planeH = maxWidth / aspectRatio;
|
|
|
|
}
|
|
|
|
|
|
|
|
plane.set(planeW, planeH);
|
|
|
|
plane.setPosition(node.position);
|
|
|
|
plane.setResolution(2, 2);
|
|
|
|
plane.mapTexCoordsFromTexture(img.getTexture());
|
|
|
|
img.getTexture().setTextureWrap(GL_REPEAT, GL_REPEAT);
|
|
|
|
plane.setScale(1);
|
|
|
|
|
|
|
|
node.geom = plane;
|
|
|
|
node.texture = img;
|
|
|
|
node.speed = glm::vec2(ofRandom(0.1, 2), ofRandom(0.1, 2));
|
|
|
|
node.amplitude = glm::vec2(ofRandom(1, 5), ofRandom(1, 5));
|
|
|
|
node.phase = glm::vec2(ofRandom(0, TWO_PI), ofRandom(0, TWO_PI));
|
|
|
|
}
|
|
|
|
std::cout << "Finished setting up nodes!" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Setup TSNE - reads JSON, converts to points, creates nodes & hashmap
|
|
|
|
*/
|
|
|
|
void Map::SetupTSNE(){
|
|
|
|
for (const auto& entry: json_embeddings) {
|
|
|
|
if (entry.contains("vector") && entry["vector"].is_array()) {
|
|
|
|
Node n;
|
|
|
|
n.foldername = entry["folder"];
|
|
|
|
n.image_path = entry["image"];
|
|
|
|
n.isLost = entry["lost"].get<int>();
|
|
|
|
|
|
|
|
std::vector<float> emb;
|
|
|
|
|
|
|
|
for (const auto& value: entry["vector"]){
|
|
|
|
if(value.is_number()){
|
|
|
|
emb.push_back(value.get<float>());
|
|
|
|
} else {
|
|
|
|
ofLogError() << "Vector value is not a number";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
n.emotion_vector = emb;
|
|
|
|
|
|
|
|
nodes.push_back(n);
|
|
|
|
embeddings.push_back(emb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << nodes.size() << std::endl;
|
|
|
|
|
|
|
|
points = tsne.run(embeddings, dims, perplexity, theta, normalise, runManually);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < points.size(); i++){
|
|
|
|
const auto& vec = points[i];
|
|
|
|
auto& n = nodes[i];
|
|
|
|
n.position = ( (glm::vec3(vec[0], vec[1], vec[2]) * scale) - (scale / 2) );
|
|
|
|
node_hash_map[n.image_path] = &n;
|
|
|
|
}
|
|
|
|
|
|
|
|
SortNodes();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Setup 3D environment
|
|
|
|
*/
|
|
|
|
void Map::Setup3D(){
|
|
|
|
cam.setFov(20);
|
|
|
|
cam.removeAllInteractions();
|
|
|
|
cam.addInteraction(ofEasyCam::TRANSFORM_TRANSLATE_XY, OF_MOUSE_BUTTON_LEFT);
|
|
|
|
cam.addInteraction(ofEasyCam::TRANSFORM_TRANSLATE_Z, OF_MOUSE_BUTTON_MIDDLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Query hashmap
|
|
|
|
*/
|
|
|
|
void Map::SearchMap(){
|
|
|
|
std::string t = "dataset/20230601_webexclusi-irelandsey_cl11536538/segmented_images/000000000_0.png";
|
|
|
|
if(node_hash_map.find(t) != node_hash_map.end()){
|
|
|
|
Node* n = node_hash_map[t];
|
|
|
|
std::cout << n->foldername << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Sort nodes by z position, for draw calls (lowest -> highest)
|
|
|
|
*/
|
|
|
|
void Map::SortNodes(){
|
|
|
|
std::cout << "Sorting Nodes.." << std::endl;
|
|
|
|
std::sort(nodes.begin(), nodes.end(), [](const Node& a, const Node&b){
|
|
|
|
return a.position.z < b.position.z;
|
|
|
|
});
|
|
|
|
std::cout << "Finished Sorting!" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Map::isFrustum(const glm::vec3& position, float radius){
|
|
|
|
float box_w = 2000;
|
|
|
|
float box_h = 2000;
|
|
|
|
glm::vec3 cam_position = cam.getPosition();
|
|
|
|
|
|
|
|
float left = cam_position.x - box_w / 2.0f;
|
|
|
|
float right = cam_position.x + box_w / 2.0f;
|
|
|
|
float top = cam_position.y + box_h / 2.0f;
|
|
|
|
float bottom = cam_position.y - box_h / 2.0f;
|
|
|
|
|
|
|
|
// Check if the object (with radius) is within the bounding box
|
|
|
|
if (position.x + radius < left || position.x - radius > right) return false;
|
|
|
|
if (position.y + radius < bottom || position.y - radius > top) return false;
|
|
|
|
|
|
|
|
// Z-depth doesn't matter in this approach, so we're ignoring it
|
|
|
|
return true;
|
|
|
|
}
|