#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(); std::vector emb; for (const auto& value: entry["vector"]){ if(value.is_number()){ emb.push_back(value.get()); } 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; }