/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2025 * * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * * software and associated documentation files (the "Software"), to deal in the Software * * without restriction, including without limitation the rights to use, copy, modify, * * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to the following * * conditions: * * * * The above copyright notice and this permission notice shall be included in all copies * * or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ #include #include #include #include #include using json = nlohmann::json; namespace openspace::volume { TransferFunction::TransferFunction(const std::string& string) { setEnvelopesFromString(string); } bool TransferFunction::setEnvelopesFromString(const std::string& s) { const json j = json::parse(s); for (const nlohmann::json& it : j) { Envelope env; std::vector tmpVec; const nlohmann::json& points = it["points"]; for (size_t i = 0; i < 4; i++) { const nlohmann::json& jt = points[i]; const std::string color = jt["color"].get(); const float xValue = jt["position"]["x"].get(); const float yValue = jt["position"]["y"].get(); tmpVec.emplace_back(color, xValue, yValue); } env.setPoints(tmpVec); _envelopes.emplace_back(env); } return true; } //TODO, implement this bool TransferFunction::setEnvelopesFromLua(lua_State* state) { ghoul_assert(false, "Implement this"); const bool success = (lua_istable(state, -1) == 1); if (success) { lua_pushnil(state); while (lua_next(state, -2)) { //Envelope env; //std::vector tmpVec; //lua_pushnil(state); //while (lua_next(state, -2)) { // lua_pushnil(state); // while (lua_next(state, -2)) { // PrintTable(state); // std::string color = static_cast( // lua_tostring(state, -1) // ); // lua_pop(state, 1); // lua_pushnil(state); // lua_next(state, -2); // float x_value = static_cast(lua_tonumber(state, -1)); // lua_pop(state, 1); // lua_next(state, -2); // float y_value = static_cast(lua_tonumber(state, -1)); // lua_pop(state, 1); // tmpVec.emplace_back(color, x_value, y_value); // lua_pop(state, 1); // } //} lua_pop(state, 2); } lua_pop(state, 1); } return success; } void TransferFunction::envelopesToLua(lua_State* state) const { lua_newtable(state); for (auto iter = _envelopes.begin(); iter != _envelopes.end(); iter++) { lua_newtable(state); iter->setEnvelopeLuaTable(state); lua_setfield( state, -2, ("[\"" + std::to_string(iter - _envelopes.begin() + 1) + "\"]").c_str() ); } } void TransferFunction::loadEnvelopesFromFile(const std::string& path) { lua_State* L = luaL_newstate(); ghoul::Dictionary dictionary; ghoul::lua::loadDictionaryFromFile(path, dictionary, L); for (const std::string_view key : dictionary.keys()) { const ghoul::Dictionary tfDictionary = dictionary.value(key); for (const std::string_view envelopeKey : tfDictionary.keys()) { const ghoul::Dictionary envelopeDictionary = tfDictionary.value(envelopeKey); Envelope env; std::vector tmpVec; for (const std::string_view pointKey : envelopeDictionary.keys()) { const ghoul::Dictionary pointDictionary = envelopeDictionary.value(pointKey); const ghoul::Dictionary positionDictionary = pointDictionary.value("position"); const std::string color = pointDictionary.value("color"); const double posX = positionDictionary.value("x"); const double posY = positionDictionary.value("y"); tmpVec.emplace_back( color, static_cast(posX), static_cast(posY) ); } env.setPoints(tmpVec); _envelopes.emplace_back(env); } } } void TransferFunction::saveEnvelopesToFile(const std::string& path) const { ghoul::Dictionary dictionary; lua_State* state = luaL_newstate(); envelopesToLua(state); ghoul::lua::luaDictionaryFromState(state, dictionary); std::ofstream tfFile; tfFile.open(path); tfFile << "return {"; tfFile << ghoul::formatLua(dictionary); tfFile << "}"; tfFile.close(); } bool TransferFunction::operator!=(const TransferFunction& tf) { if (_envelopes.size() != tf._envelopes.size()) { return true; } if (_loadableFilePath != tf._loadableFilePath) { return true; } auto iter = _envelopes.begin(); auto tfIter = tf._envelopes.begin(); for (; iter != _envelopes.end(); iter++, tfIter++) { if (*iter != *tfIter) { return true; } } return false; } bool TransferFunction::hasEnvelopes() const { return !_envelopes.empty(); } std::string TransferFunction::serializedToString() const { if (_envelopes.empty()) { return ""; } json j; for (auto envIter = _envelopes.begin(); envIter != _envelopes.end(); ++envIter) { j[std::distance(_envelopes.begin(), envIter)] = { envIter->jsonEnvelope() }; } return j.dump(); } bool TransferFunction::createTexture(ghoul::opengl::Texture& ptr) { if (_envelopes.empty()) { return false; } else { float* transferFunction = new float[_width * 4]; std::memset(transferFunction, 0, _width * 4 * sizeof(float)); for (int i = 0; i < _width ; i++) { const float position = static_cast(i) / static_cast(_width); int count = 0; glm::vec4 rgbFromEnvelopes(0.f); float alpha = 0.f; for (const Envelope& env : _envelopes) { if (env.isValueInEnvelope(position) && env.isEnvelopeValid()) { count++; const glm::vec4 tmp = env.valueAtPosition(position); rgbFromEnvelopes.r += tmp.r * tmp.a; rgbFromEnvelopes.g += tmp.g * tmp.a; rgbFromEnvelopes.b += tmp.b * tmp.a; alpha = std::min(alpha, tmp.a); } } rgbFromEnvelopes /= (count == 0) ? 1.f : static_cast(count); rgbFromEnvelopes.w = alpha; for (int channel = 0; channel < 4; ++channel) { const int p = 4 * i + channel; const float value = rgbFromEnvelopes[channel]; transferFunction[p] = value; } } ptr.setPixelData(transferFunction); return true; } } } // namespace openspace::volume