diff --git a/.gitignore b/.gitignore index 391d6c6..6fd6177 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ CMakeFiles/ CMakeCache.txt Makefile .vs/ -out/ \ No newline at end of file +out/ +.tile_cache/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e95a07..8b5996d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ target_include_directories(sackviz PUBLIC ${pubsub_INCLUDE_DIRS} ${librucksack_INCLUDE_DIRS}) target_link_libraries(sackviz GWEN_opengl GWEN_static ${pubsub_LIBRARIES} - debug ${librucksack_DEBUG_LIBRARIES} + debug ${librucksack_LIBRARIES} optimized ${librucksack_LIBRARIES}) set_property(TARGET sackviz PROPERTY CXX_STANDARD 11) diff --git a/CMakeSettings.json b/CMakeSettings.json deleted file mode 100644 index 5b8aad6..0000000 --- a/CMakeSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "configurations": [ - { - "name": "x64-Debug", - "generator": "Ninja", - "configurationType": "Debug", - "inheritEnvironments": [ "msvc_x64_x64" ], - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "", - "buildCommandArgs": "", - "ctestCommandArgs": "" - }, - { - "name": "x64-Release", - "generator": "Ninja", - "configurationType": "RelWithDebInfo", - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "", - "buildCommandArgs": "", - "ctestCommandArgs": "", - "inheritEnvironments": [ "msvc_x64_x64" ], - "variables": [] - } - ] -} \ No newline at end of file diff --git a/DefaultSkin.png b/DefaultSkin.png index 5fe2653..1865462 100644 Binary files a/DefaultSkin.png and b/DefaultSkin.png differ diff --git a/LocalXY.h b/LocalXY.h index 44f414b..d824048 100644 --- a/LocalXY.h +++ b/LocalXY.h @@ -50,7 +50,7 @@ class LocalXYUtil rho_lon_ = (rho_n - depth) * cos(origin_lat_); } - void ToLatLon(double x, double y, double& lat, double& lon) + void ToLatLon(double x, double y, double& lat, double& lon) const { double dLon = 1.0 * x - 0.0 * y; double dLat = 0.0 * x + 1.0 * y; @@ -61,7 +61,7 @@ class LocalXYUtil lon = RadiansToDegrees(rlon); } - void FromLatLon(double lat, double lon, double& x, double& y) + void FromLatLon(double lat, double lon, double& x, double& y) const { double rLat = DegreesToRadians(lat); double rLon = DegreesToRadians(lon); @@ -72,25 +72,25 @@ class LocalXYUtil y = -0.0 * dLon + 1.0 * dLat; } - inline double OriginLatitude() + inline double OriginLatitude() const { return RadiansToDegrees(origin_lat_); } - inline double OriginLongitude() + inline double OriginLongitude() const { return RadiansToDegrees(origin_lon_); } - inline double DegreesToRadians(double angle) + inline double DegreesToRadians(double angle) const { return angle * M_PI / 180.0; } - inline double RadiansToDegrees(double radians) + inline double RadiansToDegrees(double radians) const { return (180.0 / M_PI) * radians; } }; -#endif \ No newline at end of file +#endif diff --git a/Plugin.h b/Plugin.h index 1e7005c..62b7a41 100644 --- a/Plugin.h +++ b/Plugin.h @@ -63,6 +63,12 @@ namespace pubviz // Returns info about a selected item including bounds (todo) virtual std::map Select(uint32_t index, AABB& size) { return {}; } + // Applies only for 2d. Return true if event is handled. + virtual bool OnMapDoubleClick(double x, double y) { return false; } + + // Called on right click to add items to a context menu + virtual std::vector>> ContextMenu(double x, double y) { return {}; } + // Returns if the plugin is enabled and should be rendered bool Enabled() { @@ -89,6 +95,7 @@ namespace pubviz // Indicate that we want a redraw void Redraw() { + // todo add a rate limit here props_->Redraw(); } @@ -104,8 +111,14 @@ namespace pubviz out += tree_node_->GetToggleButton()->GetToggleState() ? "false" : "true"; // lets just write it as CSV int i = 0; - for (auto& prop : properties_) + + for (const auto& prop : properties_) { + // skip ButtonProperties + if (dynamic_cast(prop.second)) + { + continue; + } out += ","; out += prop.first; out += ","; @@ -195,9 +208,9 @@ namespace pubviz } TopicProperty* AddTopicProperty(Gwen::Controls::Properties* tree, const char* name, std::string topic, - const std::string& description = "", const std::string& type = "", bool use_for_title = true) + const std::string& description = "", const std::string& type = "", bool use_for_title = true, bool published = true) { - auto prop = new TopicProperty(tree, name, topic, description, type); + auto prop = new TopicProperty(tree, name, topic, description, type, published); properties_[name] = prop; auto p = (Gwen::Controls::PropertyTreeNode*)tree->GetParent(); if (use_for_title) @@ -218,6 +231,14 @@ namespace pubviz return prop; } + FileProperty* AddFileProperty(Gwen::Controls::Properties* tree, const char* name, std::string val, + const std::string& description = "") + { + auto prop = new FileProperty(tree, name, val, description); + properties_[name] = prop; + return prop; + } + EnumProperty* AddEnumProperty(Gwen::Controls::Properties* tree, const char* name, std::string def, std::vector enums, const std::string& description = "") { @@ -225,6 +246,14 @@ namespace pubviz properties_[name] = prop; return prop; } + + ButtonProperty* AddButtonProperty(Gwen::Controls::Properties* tree, const char* name, + const std::string& description = "") + { + auto prop = new ButtonProperty(tree, name, description); + properties_[name] = prop; + return prop; + } }; } #endif diff --git a/README.md b/README.md index 3f51059..08c1ee4 100755 --- a/README.md +++ b/README.md @@ -11,13 +11,15 @@ This repository contains three tools: A tool for visualizing incoming data in both 2D and 3D. Largely uses immediate mode OpenGL for rendering for ease of implementation. -![Example screenshot of pubviz.](pubviz_screenshot.png) +![Example screenshot of a 2D view in pubviz.](pubviz_2d.png) + +![Example screenshot of a 3D view in pubviz.](pubviz_3d.png) It supports visualizing: * Point Clouds * Costmaps -* Basic Markers (to be further improved) +* Various Markers (to be further improved) * Grids * Images * Time series plots of individual data fields @@ -27,7 +29,7 @@ It supports visualizing: # Sackviz -A tool for introspecting and playing back Rucksack files. Also supports both time series and scatter plots of data. +A tool for introspecting and playing back Rucksack files. Supports viewing both time series and scatter plots of data. ![Example screenshot of sackviz.](sackviz_screenshot.png) diff --git a/controls/GraphCanvas.cpp b/controls/GraphCanvas.cpp index f87b382..534e43f 100644 --- a/controls/GraphCanvas.cpp +++ b/controls/GraphCanvas.cpp @@ -49,7 +49,6 @@ GraphSubscriber::GraphSubscriber(const std::string& topic) ps_subscriber_options_init(&options); options.skip = 0; options.queue_size = 0; - options.want_message_def = true; options.allocator = 0; options.ignore_local = false; options.preferred_transport = false ? 1 : 0; @@ -156,7 +155,7 @@ GWEN_CONTROL_CONSTRUCTOR( GraphCanvas ) ps_node_init_ex(&node, "pubviz", "", false, false); //ps_node_init(&node, "pubviz", "", false); - node.adv_cb = [](const char* topic, const char* type, const char* node, const ps_advertise_req_t* data) + node.adv_cb = [](const char* topic, const char* type, const char* node, const ps_advertise_req_t* data, void* cb_data) { // check if we already have the topic if (_graph_topics.find(topic) != _graph_topics.end()) diff --git a/controls/OpenGLCanvas.cpp b/controls/OpenGLCanvas.cpp index 1e8183b..813c884 100644 --- a/controls/OpenGLCanvas.cpp +++ b/controls/OpenGLCanvas.cpp @@ -4,18 +4,18 @@ #include "OpenGLCanvas.h" -#include - #include "../Plugin.h" #include "pubviz.h" + +#include + #ifndef _WIN32 #include #include -#include -#include #endif + #include #include @@ -26,8 +26,8 @@ using namespace Gwen::Controls; GWEN_CONTROL_CONSTRUCTOR( OpenGLCanvas ) { - view_height_m_ = 150.0; m_Color = Gwen::Color( 50, 50, 50, 255 ); + glewInit(); } bool OpenGLCanvas::OnMouseWheeled( int delta ) @@ -47,15 +47,18 @@ bool OpenGLCanvas::OnMouseWheeled( int delta ) return true; } + double start_height = view_h_->GetValue(); + double rate = std::max(0.1, log(start_height/4.0)); if (delta < 0) { - view_height_m_ += 0.1*(double)delta; - view_height_m_ = std::max(1.0, view_height_m_); + start_height += 0.1*(double)delta*rate; + start_height = std::max(1.0, start_height); } else { - view_height_m_ += 0.1*(double)delta; + start_height += 0.1*(double)delta*rate; } + view_h_->SetValue(start_height); // Mark the window as dirty so it redraws Redraw(); @@ -65,240 +68,387 @@ bool OpenGLCanvas::OnMouseWheeled( int delta ) void OpenGLCanvas::ResetView() { - view_height_m_ = 150.0; + view_h_->SetValue(150.0); view_x_->SetValue(0.0); view_y_->SetValue(0.0); view_z_->SetValue(0.0); - view_abs_x_ = 0.0; - view_abs_y_ = 0.0; - view_abs_z_ = 0.0; pitch_->SetValue(0.0); yaw_->SetValue(0.0); Redraw(); } -void OpenGLCanvas::OnMouseClickLeft( int /*x*/, int /*y*/, bool down ) +void OpenGLCanvas::OnMouseDoubleClickLeft( int x, int y ) { - mouse_down_ = down; + if (view_type_->GetValue() == ViewType::TopDown) + { + for (auto& plugin: plugins_) + { + if (plugin->Enabled() && plugin->OnMapDoubleClick(x_mouse_position_, y_mouse_position_)) + { + break; + } + } + } } -void OpenGLCanvas::OnMouseClickRight( int x, int y, bool bDown ) +void OpenGLCanvas::OnMouseLeave() { - if (selecting_) + // do a mouse up + if (mouse_down_) { - auto r = GetSkin()->GetRender(); - // finish selecting. do a render + OnMouseClickLeft(0, 0, false); + } +} - // first create render target - if (selection_texture_ == 0) +void OpenGLCanvas::OnMouseClickLeft( int x, int y, bool down ) +{ + mouse_down_ = down; + + // okay, implement selecting here + + if (down) + { + selecting_ = true; + select_start_ = select_end_ = Gwen::Point(x, y); + if (Gwen::Input::IsKeyDown(Gwen::Key::Shift)) { - glGenTextures(1, &selection_texture_); - glGenFramebuffers(1, &selection_frame_buffer_); + // select mode + shift_select_ = true; + + return; } + } - // "Bind" the newly created texture : all future texture functions will modify this texture - glBindTexture(GL_TEXTURE_2D, selection_texture_); + if (!down) + { + // select if we didnt move more than a few pixels + if (selecting_) + { + select_end_ = Gwen::Point(x, y); + DoPick(); + } - // todo size it properly - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 50, 50, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + shift_select_ = false; + selecting_ = false; + } +} + +//todo how to implement context menu? - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glBindTexture(GL_TEXTURE_2D, 0); +//maybe add an oncontextmenu callback to each plugin? thats a cheap way to do it +// options for right click should be a name, callback, and disabled/enabled - // Now create the framebuffer using that texture as the color buffer - glBindFramebuffer(GL_FRAMEBUFFER, selection_frame_buffer_); +void OpenGLCanvas::DoPick() +{ + auto r = GetSkin()->GetRender(); + // finish selecting. do a render + + // first create render target + if (selection_texture_ == 0) + { + glGenTextures(1, &selection_texture_); + glGenFramebuffers(1, &selection_frame_buffer_); + } - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, selection_texture_, 0); + // "Bind" the newly created texture : all future texture functions will modify this texture + glBindTexture(GL_TEXTURE_2D, selection_texture_); - // Do a dummy check - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) - { - printf("Error creating framebuffer, it is incomplete!\n"); - } + // todo size it properly + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 50, 50, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D, 0); + + // Now create the framebuffer using that texture as the color buffer + glBindFramebuffer(GL_FRAMEBUFFER, selection_frame_buffer_); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, selection_texture_, 0); - GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0}; - glDrawBuffers(1, DrawBuffers); + // Do a dummy check + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + printf("Error creating framebuffer, it is incomplete!\n"); + } - glBindFramebuffer(GL_FRAMEBUFFER, 0); + GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0}; + glDrawBuffers(1, DrawBuffers); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); - // Resize the image to our new target size - glBindTexture(GL_TEXTURE_2D, selection_texture_); + // Resize the image to our new target size + glBindTexture(GL_TEXTURE_2D, selection_texture_); - auto scale = GetCanvas()->Scale(); - int width = Width()*scale; - int height = Height()*scale; - - // Give an empty image to OpenGL ( the last "0" ) - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + auto scale = GetCanvas()->Scale(); + int width = Width()*scale; + int height = Height()*scale; + + // Give an empty image to OpenGL ( the last "0" ) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glBindTexture(GL_TEXTURE_2D, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D, 0); - glBindFramebuffer(GL_FRAMEBUFFER, selection_frame_buffer_); + glBindFramebuffer(GL_FRAMEBUFFER, selection_frame_buffer_); - float vp[4]; - glGetFloatv(GL_VIEWPORT, vp); - glViewport(0, 0, width, height); + float vp[4]; + glGetFloatv(GL_VIEWPORT, vp); + glViewport(0, 0, width, height); - glClearColor(1.0, 1.0, 1.0, 1.0); + glClearColor(1.0, 1.0, 1.0, 1.0); - glClear( GL_COLOR_BUFFER_BIT ); + glClear( GL_COLOR_BUFFER_BIT ); - auto origin = LocalPosToCanvas(); - origin.y -= 20;// skip past menu bar + auto origin = LocalPosToCanvas(); + origin.y -= 20;// skip past menu bar - // force a flush essentially - r->EndClip(); - r->StartClip(); + // force a flush essentially + r->EndClip(); + r->StartClip(); - glMatrixMode(GL_TEXTURE); - glPushMatrix(); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glPushAttrib(GL_ALL_ATTRIB_BITS); + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glPushAttrib(GL_ALL_ATTRIB_BITS); - SetupViewMatrices(); + SetupViewMatrices(); - // now render - uint32_t current_id = 0xFF000000; - struct plugin_info - { - uint32_t start_id; - uint32_t end_id; - pubviz::Plugin* plugin; - }; - std::vector visible_plugins; - for (auto plugin: plugins_) + // now render + uint32_t current_id = 0xFF000000; + struct plugin_info + { + uint32_t start_id; + uint32_t end_id; + pubviz::Plugin* plugin; + }; + std::vector visible_plugins; + for (auto plugin: plugins_) + { + if (plugin->Enabled()) { - if (plugin->Enabled()) + uint32_t start_id = current_id; + current_id = plugin->RenderSelect(current_id); + uint32_t end_id = current_id; + current_id += 1; + // save the id ranges for this plugin so we can map from select id to plugin + if (start_id != end_id) { - uint32_t start_id = current_id; - current_id = plugin->RenderSelect(current_id); - uint32_t end_id = current_id; - current_id += 1; - // save the id ranges for this plugin so we can map from select id to plugin - if (start_id != end_id) - { - plugin_info info; - info.start_id = start_id & 0xFFFFFF; - info.end_id = end_id & 0xFFFFFF; - info.plugin = plugin; - visible_plugins.push_back(info); - } + plugin_info info; + info.start_id = start_id & 0xFFFFFF; + info.end_id = end_id & 0xFFFFFF; + info.plugin = plugin; + visible_plugins.push_back(info); } } + } - // Force a flush - r->EndClip(); - - - glFlush(); - glFinish(); + // Force a flush + r->EndClip(); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glFlush(); + glFinish(); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - // Now read out the selected area - auto start = CanvasPosToLocal(select_start_); - auto end = CanvasPosToLocal(select_end_); - int32_t start_x = std::min(start.x, end.x)*scale; - int32_t sw = std::max(std::abs(start.x - end.x)*scale,1); - int32_t sh = std::max(std::abs(start.y - end.y)*scale,1); - int32_t start_y = std::min(height - start.y*scale, height - end.y*scale); - uint32_t* pixels = new uint32_t[sw*sh]; - glReadPixels(start_x, start_y, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, (uint8_t*)pixels); + // Now read out the selected area + auto start = CanvasPosToLocal(select_start_); + auto end = CanvasPosToLocal(select_end_); + int32_t start_x = std::min(start.x, end.x)*scale; + int32_t sw = std::max(std::abs(start.x - end.x)*scale,1); + int32_t sh = std::max(std::abs(start.y - end.y)*scale,1); + int32_t start_y = std::min(height - start.y*scale, height - end.y*scale); + uint32_t* pixels = new uint32_t[sw*sh]; + glReadPixels(start_x, start_y, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, (uint8_t*)pixels); - // Find all selections - std::unordered_map selected; - for (int i = 0; i < sw*sh; i++) + // Find all selections + std::unordered_map selected; + for (int i = 0; i < sw*sh; i++) + { + if (pixels[i] != 0xFFFFFFFF) { - if (pixels[i] != 0xFFFFFFFF) - { - selected[pixels[i] & 0x00FFFFFF] = true; - } + selected[pixels[i] & 0x00FFFFFF] = true; } - // Then select all selections - PubViz* p = (PubViz*)GetParent(); + } + // Then select all selections + PubViz* p = (PubViz*)GetParent(); - // add to selection if shift is pressed - // todo remove from selection if ctrl is pressed - if (!Gwen::Input::IsKeyDown(Gwen::Key::Shift)) - { - // todo dont allow double selection in shift mode - p->GetSelection()->Clear(); - selected_aabbs_.clear(); - } - auto sel = p->GetSelection(); - for (auto id: selected) + // add to selection if shift is pressed + // todo remove from selection if ctrl is pressed + if (!Gwen::Input::IsKeyDown(Gwen::Key::Shift)) + { + // todo dont allow double selection in shift mode + p->GetSelection()->Clear(); + selected_aabbs_.clear(); + } + auto sel = p->GetSelection(); + for (auto id: selected) + { + // Find the associated plugin with this index + for (const auto& p: visible_plugins) { - // Find the associated plugin with this index - for (const auto& p: visible_plugins) + if (id.first <= p.end_id && id.first >= p.start_id) { - if (id.first <= p.end_id && id.first >= p.start_id) + //printf("Selected %i from plugin %s\n", id.first, p.plugin->GetTitle().c_str()); + // Add properties about this selected item to our selection list + auto node = sel->AddNode("Point (" + std::to_string(id.first) + ")"); + pubviz::AABB aabb; + auto map = p.plugin->Select(id.first - p.start_id, aabb); + for (auto& kv: map) { - //printf("Selected %i from plugin %s\n", id.first, p.plugin->GetTitle().c_str()); - // Add properties about this selected item to our selection list - auto node = sel->AddNode("Point (" + std::to_string(id.first) + ")"); - pubviz::AABB aabb; - auto map = p.plugin->Select(id.first - p.start_id, aabb); - for (auto& kv: map) - { - node->AddNode(kv.first + ": " + kv.second); - } - - // add it to the list of selections to render - selected_aabbs_.push_back(aabb); - //todo need selection size so we can draw it - break; + node->AddNode(kv.first + ": " + kv.second); } + + // add it to the list of selections to render + selected_aabbs_.push_back(aabb); + //todo need selection size so we can draw it + break; } } - delete[] pixels; + } + delete[] pixels; - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); - // restore matrices and viewport - glPopAttrib(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_TEXTURE); - glPopMatrix(); + // restore matrices and viewport + glPopAttrib(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_TEXTURE); + glPopMatrix(); - glDisable(GL_DEPTH_TEST); + glDisable(GL_DEPTH_TEST); - glViewport(vp[0], vp[1], vp[2], vp[3]); + glViewport(vp[0], vp[1], vp[2], vp[3]); - Redraw(); + Redraw(); +} + +void OpenGLCanvas::OnClear(Gwen::Controls::Base* c) +{ + for (auto p: plugins_) + { + p->Clear(); } +} - selecting_ = bDown; - select_start_ = select_end_ = Gwen::Point(x, y); +void OpenGLCanvas::OnMouseClickRight( int x, int y, bool down ) +{ + // show context menu if we have any plugins + if (down && plugins_.size()) + { + //auto pos = CanvasPosToLocal(Gwen::Point(x, y)); + Gwen::Controls::Menu* menu = new Gwen::Controls::Menu(GetCanvas()); + bool added = false; + for (auto p: plugins_) + { + if (!p->Enabled()) + { + continue; + } + auto values = p->ContextMenu(x_mouse_position_, y_mouse_position_);// todo map coords + if (added && values.size()) + { + // add a divider + menu->AddDivider(); + } + for (auto& v: values) + { + // add each option + added = true; + menu->AddItem(v.first)->onMenuItemSelected.Add(this, v.second); + } + } + // add defaults and a divider if necessary + if (added) + { + menu->AddDivider(); + } + menu->AddItem("Clear History")->SetAction(this, &ThisClass::OnClear); + menu->SetDeleteOnClose(true); + menu->SetPos(Gwen::Point(x,y)); + menu->Show(); + } } -void OpenGLCanvas::OnMouseMoved(int x, int y, int dx, int dy) +void OpenGLCanvas::WorldToPixel(double x, double y, double z, int& px, int& py) { - // now convert to units - double pixels_per_meter = GetCanvas()->Height()/view_height_m_; - x_mouse_position_ = (x - GetCanvas()->Width()*0.5)/pixels_per_meter + view_x_->GetValue(); - y_mouse_position_ = (GetCanvas()->Height()*0.5 - y)/pixels_per_meter + view_y_->GetValue(); + auto scale = GetCanvas()->Scale(); + auto height = Height()*scale; + /*auto width = Width()*scale; + + // for now only works with 2d todo should use matrices + x -= view_x_->GetValue(); + y -= view_y_->GetValue(); + + double pixels_per_meter = height/view_height_m_; + x *= pixels_per_meter; + y *= pixels_per_meter; + + x += width*0.5; + y -= height*0.5; + px = x; + py = -y;*/ + double wx, wy, wz; + double m[16]; + double p[16]; + for (int i = 0; i < 16; i++) + { + m[i] = model_[i]; + p[i] = proj_[i]; + } + gluProject(x, y, z, m, p, vp_, &wx, &wy, &wz); + px = wx/scale; + py = wy/scale; + + auto np = CanvasPosToLocal({px,py}); + px = np.x; + py = Height()*scale - np.y; + py -= 20; + //printf("x: %f y: %f z: %f\n", x, y, z); + //printf("x: %i y: %i\n", px, py); +} + +void OpenGLCanvas::OnMouseMoved(int canvas_x, int canvas_y, int dx, int dy) +{ + // convert mouse position to physical units + auto scale = GetCanvas()->Scale(); + auto np = CanvasPosToLocal({canvas_x, canvas_y}); + float x = np.x; + float y = np.y; + auto height = Height()*scale; + auto width = Width()*scale; + double pixels_per_meter = height/view_h_->GetValue(); + + double cx, cy, cz; + GetViewCenter(cx, cy, cz); + x_mouse_position_ = (x - width*0.5)/pixels_per_meter + cx; + y_mouse_position_ = (height*0.5 - y)/pixels_per_meter + cy; // now apply offset if (mouse_down_) { - if (view_type_->GetValue() == ViewType::TopDown) + if (selecting_ && !shift_select_) + { + if (std::abs(canvas_x - select_start_.x) > 1 || std::abs(canvas_y - select_start_.y) > 1) + { + selecting_ = false; + } + } + if (shift_select_) + { + + } + else if (view_type_->GetValue() == ViewType::TopDown) { view_x_->SetValue(view_x_->GetValue() - dx/pixels_per_meter); view_y_->SetValue(view_y_->GetValue() + dy/pixels_per_meter); - - view_abs_x_ -= dx / pixels_per_meter; - view_abs_y_ += dy / pixels_per_meter; } else { @@ -315,9 +465,10 @@ void OpenGLCanvas::OnMouseMoved(int x, int y, int dx, int dy) if (selecting_) { - select_end_ = Gwen::Point(x,y); + select_end_ = Gwen::Point(canvas_x, canvas_y); Redraw(); } + Redraw();// to get numbers to update } #include @@ -376,6 +527,8 @@ std::map OpenGLCanvas::CreateProperties(Gwen::Contro props["View Y"] = view_y_; view_z_ = new FloatProperty(tree, "View Z", 0, -100000, 100000); props["View Z"] = view_z_; + view_h_ = new FloatProperty(tree, "Height", 150, 1, 1000000); + props["Height"] = view_h_; view_type_->onChange = [this](std::string value) { @@ -407,23 +560,18 @@ void OpenGLCanvas::SetupViewMatrices() { auto width = Width(); auto height = Height(); - double view_x = view_x_->GetValue(); - double view_y = view_y_->GetValue(); - double view_z = view_z_->GetValue(); - if (wgs84_mode_) - { - view_x = view_abs_x_; - view_y = view_abs_y_; - view_z = view_abs_z_; - } + double view_x, view_y, view_z; + GetViewCenter(view_x, view_y, view_z); double yaw = yaw_->GetValue(); double pitch = pitch_->GetValue(); auto view_type = view_type_->GetValue(); if (view_type == ViewType::TopDown) { // set up the view matrix for the current zoom level (ortho, topdown) - float half_height = view_height_m_/2.0; + float half_height = view_h_->GetValue()/2.0; float half_width = half_height*((float)width/(float)height); + view_width_ = half_width*2; + view_height_ = half_height*2; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho( -half_width + view_x, half_width + view_x, -half_height + view_y, half_height + view_y, -10000.0, 10000.0 ); @@ -461,7 +609,7 @@ void OpenGLCanvas::SetupViewMatrices() else if (view_type == ViewType::Orbit) { // set up the view matrix for the current zoom level (orbit) - float half_height = view_height_m_/2.0; + float half_height = view_h_->GetValue()/2.0; float half_width = half_height*((float)width/(float)height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); @@ -485,6 +633,11 @@ void OpenGLCanvas::SetupViewMatrices() glClear(GL_DEPTH_BUFFER_BIT); glDepthFunc(GL_LEQUAL); } + + // save the matrices + glGetFloatv(GL_MODELVIEW_MATRIX, model_); + glGetFloatv(GL_PROJECTION_MATRIX, proj_); + glGetIntegerv(GL_VIEWPORT, vp_); } void OpenGLCanvas::Render( Skin::Base* skin ) diff --git a/controls/OpenGLCanvas.h b/controls/OpenGLCanvas.h index d1e88fe..c3bcf90 100644 --- a/controls/OpenGLCanvas.h +++ b/controls/OpenGLCanvas.h @@ -9,6 +9,8 @@ #include #include "../properties.h" +#include + #include "../LocalXY.h" #include "../AABB.h" @@ -32,8 +34,28 @@ class OpenGLCanvas : public Gwen::Controls::Base unsigned int selection_frame_buffer_ = 0; void SetupViewMatrices(); + + bool wgs84_mode_ = false; + + bool show_origin_ = true; + + double center_x_ = 0; + double center_y_ = 0; + double center_z_ = 0; + + + double view_width_ = 0; + double view_height_ = 0; + + Matrix3x4d map_to_odom_; + Matrix3x4d odom_to_map_; + Matrix3x4d vehicle_to_odom_; + Matrix3x4d vehicle_to_map_; public: + inline double view_width() { return view_width_; } + inline double view_height() { return view_height_; } + LocalXYUtil local_xy_; GWEN_CONTROL( OpenGLCanvas, Gwen::Controls::Base ); @@ -62,6 +84,92 @@ class OpenGLCanvas : public Gwen::Controls::Base Redraw(); } + enum Frame + { + Map, + Odom, + WGS84, + Vehicle// only supported as a src frame, and Map and Odom are only supported dst frames + }; + + void TransformToFrame(Frame src, Frame dst, Vec3d& pos) const + { + if (dst == Map) + { + if (src == Map) + { + // do nothing + } + else if (src == Odom) + { + // transform to map + pos = odom_to_map_.transform(pos); + } + else if (src == Vehicle) + { + pos = vehicle_to_map_.transform(pos); + } + else//wgs84 + { + // wgs84 to map + double x, y; + local_xy_.FromLatLon(pos.x, pos.y, x, y); + pos.x = x; + pos.y = y; + } + } + else if (dst == WGS84) + { + if (src == Map) + { + double lat, lon; + local_xy_.ToLatLon(pos.x, pos.y, lat, lon); + pos.x = lat; + pos.y = lon; + } + else if (src == Odom) + { + pos = odom_to_map_.transform(Vec3d(pos.x, pos.y, pos.z)); + double lat, lon; + local_xy_.ToLatLon(pos.x, pos.y, lat, lon); + pos.x = lat; + pos.y = lon; + } + else// wgs84 + { + // do nothing + } + } + else// odom + { + if (src == Map) + { + // transform to odom + pos = map_to_odom_.transform(pos); + } + else if (src == Odom) + { + // do nothing + } + else if (src == Vehicle) + { + pos = vehicle_to_odom_.transform(pos); + } + else// wgs84 + { + // transform to map then odom + double x, y; + local_xy_.FromLatLon(pos.x, pos.y, x, y); + pos = map_to_odom_.transform(Vec3d(x, y, pos.z)); + } + } + } + + void TransformToView(Frame frame, Vec3d& pos) const + { + TransformToFrame(frame, wgs84_mode_ ? Map : Odom, pos); + } + inline std::string GetViewType() { return view_type_->GetValue(); @@ -77,50 +185,78 @@ class OpenGLCanvas : public Gwen::Controls::Base paused_ = paused; } - void ResetViewOrigin() + void ResetViewPosition() { view_x_->SetValue(0.0); view_y_->SetValue(0.0); view_z_->SetValue(0.0); - view_abs_x_ = 0.0; - view_abs_y_ = 0.0; - view_abs_z_ = 0.0; - view_lat_ = local_xy_.OriginLatitude(); - view_lon_ = local_xy_.OriginLongitude(); - view_alt_ = 0.0; Redraw(); } - double view_abs_x_ = 0.0; - double view_abs_y_ = 0.0; - double view_abs_z_ = 0.0; - - double view_lat_ = 0.0; - double view_lon_ = 0.0; - double view_alt_ = 0.0; + // okay, so the view has two parts, an origin (either 0 or set by a pose) + // then an offset + void GetViewCenter(double& x, double& y, double& z) + { + x = center_x_ + view_x_->GetValue(); + y = center_y_ + view_y_->GetValue(); + z = center_z_ + view_z_->GetValue(); + } // sets the origin if it hasnt already been set void SetLocalXY(double lat, double lon) { if (!local_xy_.Initialized() && lat != 0.0 && lon != 0.0) { + printf("initialized local xy to %f %f\n", lat, lon); local_xy_ = LocalXYUtil(lat, lon); } } + + // Sets the transform between wgs84 and odom + void SetTransform(double x, double y, double z, double yaw, double lat, double lon, double alt, double ayaw) + { + SetLocalXY(lat, lon); + // okay, setup map to odom tran + // okay, lets calculate the transform between map and odom + + // todo change all this to doubles later + + // first we want to get the position in vehicle frame + // todo add angles + Quaternion odom_rot = Quaternion::FromAngleAxis(yaw, Vec3f(0,0,1)); + Matrix3x4d odom_to_vehicle(Quaternion(), Vec3d(-x,-y,-z)); + Matrix3x4d rot_vehicle(odom_rot.inverse(), Vec3d(0,0,0)); + Quaternion map_rot = Quaternion::FromAngleAxis(ayaw, Vec3f(0,0,1)); +//ah ha, missing these + double abs_x, abs_y; + local_xy_.FromLatLon(lat, lon, abs_x, abs_y); + Matrix3x4d vehicle_to_map(map_rot, Vec3d(abs_x, abs_y, alt)); + + odom_to_map_ = (odom_to_vehicle*rot_vehicle)*vehicle_to_map; + + // then we want to add the vehicl + map_to_odom_ = odom_to_map_; + map_to_odom_.invert(); + + vehicle_to_odom_ = Matrix3x4d(odom_rot, Vec3d(x,y,z)); + vehicle_to_map_ = vehicle_to_map; + } + // Sets the view origin void SetViewOrigin(double x, double y, double z, double lat, double lon, double alt) { - view_x_->SetValue(x); - view_y_->SetValue(y); - view_z_->SetValue(z); - view_lat_ = lat; - view_lon_ = lon; - view_alt_ = alt; - view_abs_z_ = alt; SetLocalXY(lat, lon); - local_xy_.FromLatLon(view_lat_, view_lon_, view_abs_x_, view_abs_y_); + + if (!wgs84_mode_) { + center_x_ = x; + center_y_ = y; + center_z_ = z; + } else { + local_xy_.FromLatLon(lat, lon, center_x_, center_y_); + center_z_ = alt; + } Redraw(); } @@ -132,13 +268,25 @@ class OpenGLCanvas : public Gwen::Controls::Base void Screenshot(); - bool wgs84_mode_ = false; void SetFrame(bool wgs84) { + if (wgs84 != wgs84_mode_) + { + // recenter if we have one + // todo + center_x_ = 0; + center_y_ = 0; + center_z_ = 0; + view_x_->SetValue(0.0); + view_y_->SetValue(0.0); + view_z_->SetValue(0.0); + } wgs84_mode_ = wgs84; + Redraw(); } - bool show_origin_ = true; + inline bool wgs84_mode() { return wgs84_mode_; } + void ShowOrigin(bool yn) { show_origin_ = yn; @@ -159,30 +307,36 @@ class OpenGLCanvas : public Gwen::Controls::Base } } + void WorldToPixel(double x, double y, double z, int& px, int& py); + + // always on 0 plane + //void PixelToWorld(int px, int py, double x, double y) + std::map CreateProperties(Gwen::Controls::Properties* props); protected: + + bool shift_select_ = false; + void DoPick(); void OnMouseMoved(int x, int y, int dx, int dy) override; bool OnMouseWheeled( int iDelta ) override; void OnMouseClickLeft( int /*x*/, int /*y*/, bool /*bDown*/ ) override; void OnMouseClickRight( int /*x*/, int /*y*/, bool /*bDown*/ ) override; + void OnMouseDoubleClickLeft(int, int) override; + void OnMouseLeave() override; + + void OnClear(Gwen::Controls::Base* c); Gwen::Color m_Color; - double view_height_m_; + //double view_height_m_; bool mouse_down_ = false; Gwen::Point select_start_,select_end_; bool selecting_ = false; std::vector selected_aabbs_; - - //double view_x_ = 0.0; - //double view_y_ = 0.0; - //double view_z_ = 0.0; bool paused_ = false; - - //ViewType view_type_ = ViewType::Orbit; // Properties EnumProperty* view_type_; @@ -191,9 +345,14 @@ class OpenGLCanvas : public Gwen::Controls::Base FloatProperty* view_x_; FloatProperty* view_y_; FloatProperty* view_z_; + FloatProperty* view_h_; double x_mouse_position_ = 0.0; double y_mouse_position_ = 0.0; + + float proj_[16]; + float model_[16]; + int vp_[4]; }; #endif diff --git a/controls/Parameters.cpp b/controls/Parameters.cpp index 39f5af3..38f032d 100644 --- a/controls/Parameters.cpp +++ b/controls/Parameters.cpp @@ -31,22 +31,18 @@ GWEN_CONTROL_CONSTRUCTOR( Parameters ) //p->SetPos(0,0); } -Parameters* myself = 0; - void Parameters::SetNode(ps_node_t* node) { // lets also set up everything we need here node_ = node; - myself = this; - node->param_confirm_cb = Parameters::AckCB; + node->param_confirm_cb_data = (void*)this; struct ps_subscriber_options options; ps_subscriber_options_init(&options); options.skip = 0; options.queue_size = 100; - options.want_message_def = false; options.allocator = 0; options.ignore_local = false; options.preferred_transport = true ? 1 : 0; @@ -63,7 +59,7 @@ void Parameters::SetNode(ps_node_t* node) void Parameters::Layout( Gwen::Skin::Base* skin ) { // Update all of the parameters - /*for (auto param : params_) + for (auto param : params_) { param.second->Update(); } @@ -74,9 +70,9 @@ void Parameters::Layout( Gwen::Skin::Base* skin ) while (data = (pubsub::msg::Parameters*)ps_sub_deque(¶m_sub_)) { // assert that its properly formatted - if (data->name_length != data->min_length || - data->name_length != data->max_length || - data->name_length != data->value_length) + if (data->name.size() != data->min.size() || + data->name.size() != data->max.size() || + data->name.size() != data->value.size()) { printf("ERROR: Invalid parameter message.\n"); @@ -84,9 +80,11 @@ void Parameters::Layout( Gwen::Skin::Base* skin ) free(data); continue; } + + //printf("got parameter message\n"); // add parameters to our list - for (int i = 0; i < data->name_length; i++) + for (int i = 0; i < data->name.size(); i++) { // todo handle more than double DoubleParameter* param = 0; @@ -96,21 +94,23 @@ void Parameters::Layout( Gwen::Skin::Base* skin ) param = new DoubleParameter(this); param->Dock(Gwen::Pos::Top); param->SetNode(node_); + param->SetName(data->name[i]); + param->SetRange(data->min[i], data->max[i]); + param->SetRemoteValue(std::atof(data->value[i])); + param->SetLocalValue(std::atof(data->value[i]), false); params_[data->name[i]] = param; } else { + // configure things that could have changed (not name or local value) param = params_[data->name[i]]; + param->SetRange(data->min[i], data->max[i]); + param->SetRemoteValue(std::atof(data->value[i])); } - - // configure it - param->SetName(data->name[i]); - param->SetRange(data->min[i], data->max[i]); - param->SetValue(std::atof(data->value[i])); } data->~Parameters(); free(data); - }*/ + } Invalidate();// we are being hacky, just always invalidate so we keep laying out } diff --git a/controls/Parameters.h b/controls/Parameters.h index 5471090..a1ce817 100644 --- a/controls/Parameters.h +++ b/controls/Parameters.h @@ -62,7 +62,7 @@ class DoubleParameter : public Gwen::Controls::Base max_ = new Gwen::Controls::Label( this ); max_->SetAlignment( Gwen::Pos::CenterV | Gwen::Pos::Left ); - max_->SetText( "10.0" ); + max_->SetText( "0.0" ); max_->Dock( Gwen::Pos::Right ); max_->SetTabable( false ); max_->SetKeyboardInputEnabled( false ); @@ -80,21 +80,23 @@ class DoubleParameter : public Gwen::Controls::Base label_->SetText(name); name_ = name; } - - void SetValue(double val) + + double remote_value_; + void SetRemoteValue(double val) { - text_box_->SetText(std::to_string(val)); - slider_->SetFloatValue(val); - last_commanded_value_ = val; + remote_value_ = val; + //printf("setting remote to %f\n", val); } - - // from acks - void UpdateValue(double val) + + void SetLocalValue(double val, bool send = true) { + //printf("setting local to %f\n", val); + if (send == false) + { + last_commanded_value_ = val; + } text_box_->SetText(std::to_string(val)); slider_->SetFloatValue(val); - - // todo, how to handle this messing up the slider } void SetRange(double min, double max) @@ -111,13 +113,25 @@ class DoubleParameter : public Gwen::Controls::Base void Update() { + // if the request timed out, reset to the last received value + if (!FloatEqual(last_commanded_value_, remote_value_)) + { + // if we fail to update after so long, give up + if (pubsub::Time::now() > last_changed_time_ + pubsub::Duration(2.0)) + { + SetLocalValue(remote_value_, false); + printf("Change timed out on parameter '%s', resetting to previous value %lf\n", name_.c_str(), remote_value_); + } + } + // if value still not equal to expected and the last request timed out, try again - if (slider_->GetFloatValue() == last_commanded_value_) + if (FloatEqual(remote_value_, last_commanded_value_)) { + label_->SetTextColor(Gwen::Color(0,0,0)); return; } - printf("%f != %f\n", slider_->GetFloatValue(), last_commanded_value_); + //printf("%f != %f\n", remote_value_, last_commanded_value_); // retry if value is not equal to expected after a little bit if (pubsub::Time::now() > last_commanded_time_ + pubsub::Duration(0.2)) @@ -128,26 +142,41 @@ class DoubleParameter : public Gwen::Controls::Base } private: + + static bool FloatEqual(double a, double b) + { + return std::abs(a-b) < 0.000001; + } void TextChanged(Gwen::Controls::Base* control) { double value = std::atof(text_box_->GetValue().c_str()); slider_->SetFloatValue(value); - SendUpdatedValue(value); + last_changed_time_ = pubsub::Time::now(); + if (!FloatEqual(value, last_commanded_value_))// prevents double sends on update + { + SendUpdatedValue(value); + } } void SliderMoved(Gwen::Controls::Base* control) { - text_box_->SetText(std::to_string(slider_->GetFloatValue())); - SendUpdatedValue(slider_->GetFloatValue()); + double value = slider_->GetFloatValue(); + text_box_->SetText(std::to_string(value)); + last_changed_time_ = pubsub::Time::now(); + if (!FloatEqual(value, last_commanded_value_))// prevents double sends on update + { + SendUpdatedValue(value); + } } void SendUpdatedValue(double value) { - // todo send the param change + //printf("Sending set param to set %s to %f\n", name_.c_str(), value); ps_node_set_parameter(node_, name_.c_str(), value); last_commanded_value_ = value; last_commanded_time_ = pubsub::Time::now(); + label_->SetTextColor(Gwen::Color(255,0,0)); } Gwen::Controls::HorizontalSlider* slider_; @@ -163,12 +192,10 @@ class DoubleParameter : public Gwen::Controls::Base ps_node_t* node_; double last_commanded_value_ = 0.0; + pubsub::Time last_changed_time_ = pubsub::Time(0); pubsub::Time last_commanded_time_ = pubsub::Time(0); }; -class Parameters; -extern Parameters* myself; - class PubViz; class Parameters : public Gwen::Controls::Base { @@ -189,9 +216,10 @@ class Parameters : public Gwen::Controls::Base ps_sub_destroy(¶m_sub_); } - static void AckCB(const char* name, double value) + static void AckCB(const char* name, double value, void* data) { - printf("Got ack for %s %f\n", name, value); + Parameters* myself = (Parameters*)data; + //printf("Got param change ack for %s %f\n", name, value); auto iter = myself->params_.find(name); if (iter == myself->params_.end()) @@ -199,7 +227,7 @@ class Parameters : public Gwen::Controls::Base return; } - iter->second->UpdateValue(value); + iter->second->SetRemoteValue(value); } void SetNode(ps_node_t* node); diff --git a/controls/pubviz.cpp b/controls/pubviz.cpp index c35ac68..1ccbb86 100644 --- a/controls/pubviz.cpp +++ b/controls/pubviz.cpp @@ -51,7 +51,11 @@ #include "../plugins/Path.h" #include "../plugins/Image.h" #include "../plugins/GPS.h" -#include "../plugins/Dial.h" +#include "../plugins/Gauge.h" +#include "../plugins/PlanPath.h" +#include "../plugins/Measure.h" +#include "../plugins/Map.h" +#include "../plugins/RobotImage.h" #include #include @@ -87,6 +91,19 @@ PubViz::~PubViz() } void PubViz::OnConfigSave(Gwen::Event::Info info) +{ + SaveConfig(info.String.c_str()); + current_config_file_ = info.String.c_str(); + std::string title = "Pubviz (" + std::string(current_config_file_) + ")"; + ((Gwen::Controls::WindowCanvas*)GetParent())->SetTitle(title); +} + +void PubViz::OnConfigSaveAs(Gwen::Event::Info info) +{ + SaveConfig(info.String.c_str()); +} + +void PubViz::SaveConfig(const std::string& file) { std::string config; // save our own configuration first @@ -132,7 +149,7 @@ void PubViz::OnConfigSave(Gwen::Event::Info info) config += "\n"; } - FILE* f = fopen(info.String.c_str(), "wb"); + FILE* f = fopen(file.c_str(), "wb"); fwrite(config.c_str(), 1, config.length(), f); fclose(f); } @@ -168,8 +185,16 @@ void PubViz::LoadConfig(const char* filename) FILE *f = fopen(filename, "rb"); if (f == 0) { + ((Gwen::Controls::WindowCanvas*)GetParent())->SetTitle("Pubviz"); + current_config_file_ = ""; return;// failed to open file } + + current_config_file_ = filename; + + std::string title = "Pubviz (" + std::string(filename) + ")"; + ((Gwen::Controls::WindowCanvas*)GetParent())->SetTitle(title); + fseek(f, 0, SEEK_END); long fsize = ftell(f); fseek(f, 0, SEEK_SET); /* same as rewind(f); */ @@ -223,8 +248,11 @@ void PubViz::LoadConfig(const char* filename) printf("Invalid config\n"); return; } + auto window = (Gwen::Controls::WindowCanvas*)GetParent(); double pitch = 0.0; double yaw = 0.0; + int width = window->Width(); + int height = window->Height(); for (int i = 0; i < pts.size(); i += 2) { auto key = pts[i]; @@ -244,11 +272,11 @@ void PubViz::LoadConfig(const char* filename) } else if (key == "window_width") { - GetParent()->SetWidth(std::atoi(value.c_str())); + width = std::atoi(value.c_str()); } else if (key == "window_height") { - GetParent()->SetHeight(std::atoi(value.c_str())); + height = std::atoi(value.c_str()); } else { @@ -260,6 +288,7 @@ void PubViz::LoadConfig(const char* filename) } } } + window->SetWindowSize(width, height); continue; } printf("loaded plugin %s\n", data[0].c_str()); @@ -332,9 +361,21 @@ void PubViz::MenuItemSelect(Controls::Base* pControl) graphs_[graph] = true; } + else if (pMenuItem->GetText() == L"Save Config") + { + if (current_config_file_.length()) + { + // save! + SaveConfig(current_config_file_); + } + else + { + Gwen::Dialogs::FileSave(true, String("Save Config"), String("pubviz.config"), String(".config|*.config|All|*.*"), this, &ThisClass::OnConfigSave); + } + } else if (pMenuItem->GetText() == L"Save Config As") { - Gwen::Dialogs::FileSave(true, String("Save Config"), String("pubviz.config"), String(".config|*.config|All|*.*"), this, &ThisClass::OnConfigSave); + Gwen::Dialogs::FileSave(true, String("Save Config"), String("pubviz.config"), String(".config|*.config|All|*.*"), this, &ThisClass::OnConfigSaveAs); } else if (pMenuItem->GetText() == L"Load Config") { @@ -406,8 +447,9 @@ void PubViz::Layout(Skin::Base* skin) Invalidate(); } -std::map> _topics; -std::map _found_topics; +std::map> _published_topics; +std::map> _subscribed_topics; +std::map _found_pub_topics, _found_sub_topics; GWEN_CONTROL_CONSTRUCTOR(PubViz) { @@ -422,7 +464,8 @@ GWEN_CONTROL_CONSTRUCTOR(PubViz) Gwen::Controls::MenuItem* pRoot = menu_->AddItem(L"File"); pRoot->GetMenu()->AddItem(L"Clear Plugins", "", "")->SetAction(this, &ThisClass::MenuItemSelect); pRoot->GetMenu()->AddItem(L"Load Config", "", "Ctrl+O")->SetAction(this, &ThisClass::MenuItemSelect); - pRoot->GetMenu()->AddItem(L"Save Config As", "", "Ctrl+S")->SetAction(this, &ThisClass::MenuItemSelect); + pRoot->GetMenu()->AddItem(L"Save Config", "", "Ctrl+S")->SetAction(this, &ThisClass::MenuItemSelect); + pRoot->GetMenu()->AddItem(L"Save Config As", "", "Ctrl+Shift+S")->SetAction(this, &ThisClass::MenuItemSelect); pRoot->GetMenu()->AddDivider(); pRoot->GetMenu()->AddItem(L"Screenshot", "", "")->SetAction(this, &ThisClass::MenuItemSelect); pRoot->GetMenu()->AddDivider(); @@ -448,12 +491,12 @@ GWEN_CONTROL_CONSTRUCTOR(PubViz) pRoot->GetMenu()->AddDivider(); pRoot->GetMenu()->AddItem(L"Plot", "", "Ctrl+P")->SetAction(this, &ThisClass::MenuItemSelect); pRoot->GetMenu()->AddItem(L"Change Parameters", "", "Shift+P")->SetAction(this, &ThisClass::MenuItemSelect); - pause_item_ = pRoot->GetMenu()->AddItem(L"Pause", "", "")->SetAction(this, &ThisClass::MenuItemSelect); + pause_item_ = pRoot->GetMenu()->AddItem(L"Pause", "", "Ctrl+Space")->SetAction(this, &ThisClass::MenuItemSelect); pRoot->GetMenu()->AddItem(L"Clear History", "", "")->SetAction(this, &ThisClass::MenuItemSelect); pRoot->GetMenu()->AddDivider(); pRoot->GetMenu()->AddItem(L"Orbit", "", "Ctrl+O")->SetAction(this, &ThisClass::MenuItemSelect); pRoot->GetMenu()->AddItem(L"Top Down", "", "Ctrl+T")->SetAction(this, &ThisClass::MenuItemSelect); - pRoot->GetMenu()->AddItem(L"First Person", "", "Ctrl+T")->SetAction(this, &ThisClass::MenuItemSelect); + pRoot->GetMenu()->AddItem(L"First Person", "", "")->SetAction(this, &ThisClass::MenuItemSelect); pRoot->GetMenu()->AddDivider(); pRoot->GetMenu()->AddItem(L"Top", "", "")->SetAction(this, &ThisClass::MenuItemSelect); pRoot->GetMenu()->AddItem(L"Left", "", "")->SetAction(this, &ThisClass::MenuItemSelect); @@ -514,7 +557,7 @@ GWEN_CONTROL_CONSTRUCTOR(PubViz) add_button->SetText( L"Add Plugin" ); //ps_node_init_ex(&node_, "pubviz_real", "", false, false); - ps_node_init_ex(&node_, "pubviz_real", "", true, false); + ps_node_init_ex(&node_, "pubviz", "", true, false); struct ps_transport_t tcp_transport; ps_tcp_transport_init(&tcp_transport, &node_); @@ -522,18 +565,45 @@ GWEN_CONTROL_CONSTRUCTOR(PubViz) ps_node_system_query(&node_); - node_.adv_cb = [](const char* topic, const char* type, const char* node, const ps_advertise_req_t* data) + node_.adv_cb = [](const char* topic, const char* type, const char* node, const ps_advertise_req_t* data, void* cb_data) { // check if we already have the topic - if (_found_topics.find(topic) != _found_topics.end()) + if (_found_pub_topics.find(topic) != _found_pub_topics.end()) + { + return; + } + + // ignore topics from ourselves + if (strcmp(node, "pubviz") == 0) { return; } //printf("Discovered topic %s..\n", topic); - _topics[type].push_back(topic); - _found_topics[topic] = true; + _published_topics[type].push_back(topic); + _found_pub_topics[topic] = true; + }; + + // sometimes we also want subscribed topics + node_.sub_cb = [](const char* topic, const char* type, const char* node, const struct ps_subscribe_req_t* sub_data, void* data) + { + // check if we already have the topic + if (_found_sub_topics.find(topic) != _found_sub_topics.end()) + { + return; + } + + // ignore topics from ourselves + if (strcmp(node, "pubviz") == 0) + { + return; + } + + //printf("Discovered topic %s..\n", topic); + + _subscribed_topics[type].push_back(topic); + _found_sub_topics[topic] = true; }; canvas_ = new OpenGLCanvas(this); @@ -547,17 +617,8 @@ GWEN_CONTROL_CONSTRUCTOR(PubViz) { properties_[p.first] = p.second; } - - //AddPlugin("image"); - AddPlugin("dial"); AddPlugin("grid"); - AddPlugin("gps"); - AddPlugin("costmap"); - AddPlugin("marker"); - AddPlugin("pointcloud"); - AddPlugin("path"); - AddPlugin("pose"); add_button->onPress.Add( this, &ThisClass::OnAddPlugin ); @@ -671,6 +732,19 @@ pubviz::Plugin* PubViz::AddPlugin(const std::string& name) box->SetChecked(true); box->SetMargin(Padding(0, 0, 5, 0)); + auto b = new Gwen::Controls::Button(node->GetButton()); + b->SetText("v"); + b->SizeToContents(); + b->Dock(Pos::Right); + b->onDown.Add(this, &PubViz::OnDownPlugin);// should be on up, but it gets triggered twice somehow + b->UserData.Set("plugin", plugin); + b = new Gwen::Controls::Button(node->GetButton()); + b->SetText("^"); + b->SizeToContents(); + b->Dock(Pos::Right); + b->onDown.Add(this, &PubViz::OnUpPlugin); + b->UserData.Set("plugin", plugin); + plugin->tree_node_ = node; plugin->enabled_ = box; plugin->Initialize(props); @@ -690,13 +764,105 @@ pubviz::Plugin* PubViz::AddPlugin(const std::string& name) return plugin; } +template +int find(const T& t, const B& value) +{ + for (int i = 0; i < t.size(); i++) + { + if (t[i] == value) + { + return i; + } + } + return -1; +} + +void PubViz::OnUpPlugin(Gwen::Controls::Base* control) +{ + auto plugin = control->UserData.Get("plugin"); + + auto p = plugin->props_->GetParent(); + auto pp = p->GetParent(); + auto& children = ((Gwen::Controls::TreeNode*)pp)->GetChildNodes(); + + // then reorder our plugins + auto old = plugins_; + int index = find(old, plugin); + if (index == 0) + { + return;// nothing to do + } + + plugins_.clear(); + for (int i = 0; i < index - 1; i++) + { + plugins_.push_back(old[i]); + } + plugins_.push_back(plugin); + plugins_.push_back(old[index-1]); + for (int i = index+1; i < old.size(); i++) + { + plugins_.push_back(old[i]); + } + canvas_->plugins_ = plugins_; + + // then add back all the children + auto start = children.front(); + children.clear(); + children.push_back(start); + for (const auto& p: plugins_) + { + children.push_back(p->props_->GetParent()); + } + p->InvalidateParent(); +} + +void PubViz::OnDownPlugin(Gwen::Controls::Base* control) +{ + auto plugin = control->UserData.Get("plugin"); + auto p = plugin->props_->GetParent(); + auto pp = p->GetParent(); + auto& children = ((Gwen::Controls::TreeNode*)pp)->GetChildNodes(); + + // then reorder our plugins + auto old = plugins_; + int index = find(old, plugin); + if (index == (old.size()-1)) + { + return;// nothing to do + } + + plugins_.clear(); + for (int i = 0; i <= index - 1; i++) + { + plugins_.push_back(old[i]); + } + plugins_.push_back(old[index+1]); + plugins_.push_back(plugin); + for (int i = index+2; i < old.size(); i++) + { + plugins_.push_back(old[i]); + } + canvas_->plugins_ = plugins_; + + // then add back all the children + auto start = children.front(); + children.clear(); + children.push_back(start); + for (const auto& p: plugins_) + { + children.push_back(p->props_->GetParent()); + } + p->InvalidateParent(); +} + void PubViz::OnAddPlugin(Gwen::Controls::Base* control) { Controls::WindowControl* window = new Controls::WindowControl( GetCanvas() ); window->SetTitle( L"Add Plugin" ); window->SetSize( 200, 100 ); window->MakeModal( true ); - window->Position( Pos::Center ); + window->SetPos(GetCanvas()->Width()/2 - 100, GetCanvas()->Height()/2 - 50); window->SetDeleteOnClose( true ); Gwen::Controls::ComboBox* combo = new Gwen::Controls::ComboBox( window ); @@ -762,17 +928,28 @@ void PubViz::Render(Gwen::Skin::Base* skin) { double x, y; canvas_->GetMousePosition(x, y); - m_StatusBar->SetText(Gwen::Utility::Format(L"%i fps X: %f Y: %f", val * 2, x, y)); + if (canvas_->wgs84_mode_) + { + double lat, lon; + canvas_->local_xy_.ToLatLon(x, y, lat, lon); + m_StatusBar->SetText(Gwen::Utility::Format(L"%i fps Lat: %f Lon: %f", val * 2, lat, lon)); + } + else + { + m_StatusBar->SetText(Gwen::Utility::Format(L"%i fps X: %f Y: %f", val * 2, x, y)); + } } else { if (canvas_->wgs84_mode_) { - m_StatusBar->SetText(Gwen::Utility::Format(L"%i fps Lat: %f Lon: %f Alt: %f", val * 2, canvas_->view_lat_, canvas_->view_lon_, canvas_->view_alt_)); + m_StatusBar->SetText(Gwen::Utility::Format(L"%i fps", val * 2)); } else { - m_StatusBar->SetText(Gwen::Utility::Format(L"%i fps X: %f Y: %f Z: %f", val * 2, canvas_->view_x_, canvas_->view_y_, canvas_->view_z_)); + double x, y, z; + canvas_->GetViewCenter(x, y, z); + m_StatusBar->SetText(Gwen::Utility::Format(L"%i fps X: %f Y: %f Z: %f", val * 2, x, y, z)); } } diff --git a/controls/pubviz.h b/controls/pubviz.h index 103bd84..ee607ca 100644 --- a/controls/pubviz.h +++ b/controls/pubviz.h @@ -68,6 +68,7 @@ class GraphCanvas; class PubViz: public Gwen::Controls::DockBase { std::map properties_; + std::string current_config_file_; public: GWEN_CONTROL(PubViz, Gwen::Controls::DockBase); @@ -83,6 +84,7 @@ class PubViz: public Gwen::Controls::DockBase pubviz::Plugin* AddPlugin(const std::string& name); void LoadConfig(const char* filename); + void SaveConfig(const std::string& file); inline Gwen::Controls::TreeControl* GetSelection() { return selection_; } @@ -92,6 +94,8 @@ class PubViz: public Gwen::Controls::DockBase void OnAddPlugin(Gwen::Controls::Base* control); void OnRemovePlugin( Gwen::Controls::Base* control); void OnAddPluginFinish(Gwen::Controls::Base* control); + void OnUpPlugin(Gwen::Controls::Base* control); + void OnDownPlugin(Gwen::Controls::Base* control); void OnCenter(Gwen::Controls::Base* control); void OnShowConfigChanged(Gwen::Controls::Base* control); void OnShowSelectionChanged(Gwen::Controls::Base* control); @@ -104,6 +108,7 @@ class PubViz: public Gwen::Controls::DockBase } void OnConfigSave(Gwen::Event::Info info); + void OnConfigSaveAs(Gwen::Event::Info info); void OnConfigLoad(Gwen::Event::Info info); void OnGraphClosed(Gwen::Controls::Base* base); diff --git a/plugins/Costmap.h b/plugins/Costmap.h index 95cc8b4..dc03e2e 100644 --- a/plugins/Costmap.h +++ b/plugins/Costmap.h @@ -33,18 +33,20 @@ class CostmapPlugin: public pubviz::Plugin FloatProperty* alpha_; ColorProperty* color_; BooleanProperty* show_outline_; + NumberProperty* alpha_threshold_; TopicProperty* topic_; bool sub_open_ = false; ps_sub_t subscriber_; - pubsub::msg::Costmap last_msg_; + pubsub::msg::Costmap* last_msg_ = 0; unsigned int texture_ = -1; void UpdateFromMessage() { + if (last_msg_ == 0) return; Redraw(); if (texture_ != -1) @@ -52,24 +54,27 @@ class CostmapPlugin: public pubviz::Plugin glDeleteTextures(1, &texture_); } - if (last_msg_.width* last_msg_.height != last_msg_.data_length) + if (last_msg_->width * last_msg_->height != last_msg_->data.size()) { - last_msg_.data_length = 0; + delete last_msg_; + last_msg_ = 0; printf("ERROR: bad costmap size\n"); return; } // make the color buffer std::vector pixels; - pixels.resize(last_msg_.width*last_msg_.height); + pixels.resize(last_msg_->width*last_msg_->height); int texture_size = pixels.size()*4; + + int32_t alpha_threshold = alpha_threshold_->GetValue(); // now fill in each pixel with the intensity - for (int i = 0; i < last_msg_.data_length; i++) + for (int i = 0; i < last_msg_->data.size(); i++) { - uint8_t px = last_msg_.data[i]; - uint8_t a = 255; + uint8_t px = last_msg_->data[i]; + uint8_t a = px <= alpha_threshold ? 0 : 255; pixels[i] = (a << 24) | (px << 16) | (px << 8) | px; } @@ -90,8 +95,8 @@ class CostmapPlugin: public pubviz::Plugin GL_TEXTURE_2D, 0, GL_RGBA, - last_msg_.width, - last_msg_.height, + last_msg_->width, + last_msg_->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, @@ -122,18 +127,7 @@ class CostmapPlugin: public pubviz::Plugin public: CostmapPlugin() - { - // just make a quick test costmap - last_msg_.frame = 0; - last_msg_.width = 10; - last_msg_.height = 10; - last_msg_.resolution = 5.0; - last_msg_.left = 0.0; - last_msg_.bottom = 0.0; - last_msg_.data_length = 0;//10*10; - //last_msg_.data = new uint8_t[10*10]; - //UpdateFromMessage(); - + { // dont use pubsub here } @@ -142,6 +136,11 @@ class CostmapPlugin: public pubviz::Plugin delete color_; delete alpha_; delete show_outline_; + + if (last_msg_) + { + delete last_msg_; + } if (sub_open_) { @@ -161,14 +160,12 @@ class CostmapPlugin: public pubviz::Plugin if (texture_ != -1) { glDeleteTextures(1, &texture_); - last_msg_.data_length = 0; - free(last_msg_.data); - last_msg_.data = 0; + delete last_msg_; + last_msg_ = 0; texture_ = -1; } } - virtual void Update() { // process any messages @@ -180,15 +177,16 @@ class CostmapPlugin: public pubviz::Plugin { if (Paused()) { - free(data->data); - free(data);//todo use allocator free + delete data; continue; } // user is responsible for freeing the message and its arrays - last_msg_ = *data; - free(data->data); - free(data);//todo use allocator free + if (last_msg_) + { + delete last_msg_; + } + last_msg_ = data; UpdateFromMessage(); } } @@ -197,19 +195,26 @@ class CostmapPlugin: public pubviz::Plugin virtual void Render() { // exit early if we dont have a messag - if (last_msg_.data_length == 0) + if (last_msg_ == 0) { return; } + + double width = last_msg_->resolution*last_msg_->width; + double height = last_msg_->resolution*last_msg_->height; + + Vec3d pts[4]; + pts[0] = Vec3d(last_msg_->left, last_msg_->bottom, 0); + pts[1] = Vec3d(last_msg_->left, last_msg_->bottom + height, 0); + pts[2] = Vec3d(last_msg_->left + width, last_msg_->bottom + height, 0); + pts[3] = Vec3d(last_msg_->left + width, last_msg_->bottom, 0); - if (GetCanvas()->wgs84_mode_) + // transform to view frame + for (int i = 0; i < 4; i++) { - return;// not supported for the moment + GetCanvas()->TransformToView(OpenGLCanvas::Odom, pts[i]); } - double width = last_msg_.resolution*last_msg_.width; - double height = last_msg_.resolution*last_msg_.height; - // draw the bounds of the costmap if (show_outline_->GetValue()) { @@ -219,16 +224,18 @@ class CostmapPlugin: public pubviz::Plugin Gwen::Color color = color_->GetValue(); glColor3f(color.r/255.0, color.g/255.0, color.b/255.0); - glVertex2f(last_msg_.left, last_msg_.bottom); - glVertex2f(last_msg_.left, last_msg_.bottom + height); - glVertex2f(last_msg_.left + width, last_msg_.bottom + height); - glVertex2f(last_msg_.left + width, last_msg_.bottom); - glVertex2f(last_msg_.left, last_msg_.bottom); + glVertex2f(pts[0].x, pts[0].y); + glVertex2f(pts[1].x, pts[1].y); + glVertex2f(pts[2].x, pts[2].y); + glVertex2f(pts[3].x, pts[3].y); + glVertex2f(pts[0].x, pts[0].y); glEnd(); } glEnable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0); // Now draw the costmap itself glEnable(GL_TEXTURE_2D); @@ -238,24 +245,25 @@ class CostmapPlugin: public pubviz::Plugin glColor4f(1.0f, 1.0f, 1.0f, alpha_->GetValue() ); glTexCoord2d(0, 0); - glVertex2d(last_msg_.left + 0, last_msg_.bottom + 0); + glVertex2f(pts[0].x, pts[0].y); glTexCoord2d(1.0, 0); - glVertex2d(last_msg_.left + width, last_msg_.bottom + 0); + glVertex2f(pts[3].x, pts[3].y); glTexCoord2d(1.0, 1.0); - glVertex2d(last_msg_.left + width, last_msg_.bottom + height); + glVertex2f(pts[2].x, pts[2].y); glTexCoord2d(0, 0); - glVertex2d(last_msg_.left + 0, last_msg_.bottom + 0); + glVertex2f(pts[0].x, pts[0].y); glTexCoord2d(1.0, 1.0); - glVertex2d(last_msg_.left + width, last_msg_.bottom + height); + glVertex2f(pts[2].x, pts[2].y); glTexCoord2d(0, 1.0); - glVertex2d(last_msg_.left + 0, last_msg_.bottom + height); + glVertex2f(pts[1].x, pts[1].y); glEnd(); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); + glDisable(GL_ALPHA_TEST); glDisable(GL_BLEND); } @@ -269,6 +277,12 @@ class CostmapPlugin: public pubviz::Plugin show_outline_ = AddBooleanProperty(tree, "Show Outline", true, "If true, draw an outline around the costmap."); color_ = AddColorProperty(tree, "Outline Color", Gwen::Color(255,50,50), "Color to use to draw border of costmap."); + + alpha_threshold_ = AddNumberProperty(tree, "Alpha Threshold", -1, -1, 255, 1, "Costs at or below which costmap cells are invisible."); + alpha_threshold_->onChange = [this](int number) + { + UpdateFromMessage(); + }; Subscribe(topic_->GetValue()); } diff --git a/plugins/FreeImage.h b/plugins/FreeImage.h deleted file mode 100644 index 549a195..0000000 --- a/plugins/FreeImage.h +++ /dev/null @@ -1,1117 +0,0 @@ -// ========================================================== -// FreeImage 3 -// -// Design and implementation by -// - Floris van den Berg (flvdberg@wxs.nl) -// - Hervé Drolon (drolon@infonie.fr) -// -// Contributors: -// - see changes log named 'Whatsnew.txt', see header of each .h and .cpp file -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#ifndef FREEIMAGE_H -#define FREEIMAGE_H - -// Version information ------------------------------------------------------ - -#define FREEIMAGE_MAJOR_VERSION 3 -#define FREEIMAGE_MINOR_VERSION 15 -#define FREEIMAGE_RELEASE_SERIAL 0 - -// Compiler options --------------------------------------------------------- - -#include // needed for UNICODE functions - -#if defined(FREEIMAGE_LIB) -#define DLL_API -#define DLL_CALLCONV -#else -#if defined(_WIN32) || defined(__WIN32__) -#define DLL_CALLCONV __stdcall -// The following ifdef block is the standard way of creating macros which make exporting -// from a DLL simpler. All files within this DLL are compiled with the FREEIMAGE_EXPORTS -// symbol defined on the command line. this symbol should not be defined on any project -// that uses this DLL. This way any other project whose source files include this file see -// DLL_API functions as being imported from a DLL, wheras this DLL sees symbols -// defined with this macro as being exported. -#ifdef FREEIMAGE_EXPORTS -#define DLL_API __declspec(dllexport) -#else -#define DLL_API __declspec(dllimport) -#endif // FREEIMAGE_EXPORTS -#else -// try the gcc visibility support (see http://gcc.gnu.org/wiki/Visibility) -#if defined(__GNUC__) && ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) -#ifndef GCC_HASCLASSVISIBILITY -#define GCC_HASCLASSVISIBILITY -#endif -#endif // __GNUC__ -#define DLL_CALLCONV -#if defined(GCC_HASCLASSVISIBILITY) -#define DLL_API __attribute__ ((visibility("default"))) -#else -#define DLL_API -#endif -#endif // WIN32 / !WIN32 -#endif // FREEIMAGE_LIB - -// Some versions of gcc may have BYTE_ORDER or __BYTE_ORDER defined -// If your big endian system isn't being detected, add an OS specific check -#if (defined(BYTE_ORDER) && BYTE_ORDER==BIG_ENDIAN) || \ - (defined(__BYTE_ORDER) && __BYTE_ORDER==__BIG_ENDIAN) || \ - defined(__BIG_ENDIAN__) -#define FREEIMAGE_BIGENDIAN -#endif // BYTE_ORDER - -// This really only affects 24 and 32 bit formats, the rest are always RGB order. -#define FREEIMAGE_COLORORDER_BGR 0 -#define FREEIMAGE_COLORORDER_RGB 1 -#if defined(FREEIMAGE_BIGENDIAN) -#define FREEIMAGE_COLORORDER FREEIMAGE_COLORORDER_RGB -#else -#define FREEIMAGE_COLORORDER FREEIMAGE_COLORORDER_BGR -#endif - -// Ensure 4-byte enums if we're using Borland C++ compilers -#if defined(__BORLANDC__) -#pragma option push -b -#endif - -// For C compatibility -------------------------------------------------------- - -#ifdef __cplusplus -#define FI_DEFAULT(x) = x -#define FI_ENUM(x) enum x -#define FI_STRUCT(x) struct x -#else -#define FI_DEFAULT(x) -#define FI_ENUM(x) typedef int x; enum x -#define FI_STRUCT(x) typedef struct x x; struct x -#endif - -// Bitmap types ------------------------------------------------------------- - -FI_STRUCT( FIBITMAP ) { void* data; }; -FI_STRUCT( FIMULTIBITMAP ) { void* data; }; - -// Types used in the library (directly copied from Windows) ----------------- - -#if defined(__MINGW32__) && defined(_WINDOWS_H) -#define _WINDOWS_ // prevent a bug in MinGW32 -#endif // __MINGW32__ - -#ifndef _WINDOWS_ -#define _WINDOWS_ - -#ifndef FALSE -#define FALSE 0 -#endif -#ifndef TRUE -#define TRUE 1 -#endif -#ifndef NULL -#define NULL 0 -#endif - -#ifndef SEEK_SET -#define SEEK_SET 0 -#define SEEK_CUR 1 -#define SEEK_END 2 -#endif - -#ifndef _MSC_VER -// define portable types for 32-bit / 64-bit OS -#include -typedef int32_t BOOL; -typedef uint8_t BYTE; -typedef uint16_t WORD; -typedef uint32_t DWORD; -typedef int32_t LONG; -#else -// MS is not C99 ISO compliant -typedef long BOOL; -typedef unsigned char BYTE; -typedef unsigned short WORD; -typedef unsigned long DWORD; -typedef long LONG; -#endif // _MSC_VER - -#if (defined(_WIN32) || defined(__WIN32__)) -#pragma pack(push, 1) -#else -#pragma pack(1) -#endif // WIN32 - -typedef struct tagRGBQUAD -{ -#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR - BYTE rgbBlue; - BYTE rgbGreen; - BYTE rgbRed; -#else - BYTE rgbRed; - BYTE rgbGreen; - BYTE rgbBlue; -#endif // FREEIMAGE_COLORORDER - BYTE rgbReserved; -} RGBQUAD; - -typedef struct tagRGBTRIPLE -{ -#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR - BYTE rgbtBlue; - BYTE rgbtGreen; - BYTE rgbtRed; -#else - BYTE rgbtRed; - BYTE rgbtGreen; - BYTE rgbtBlue; -#endif // FREEIMAGE_COLORORDER -} RGBTRIPLE; - -#if (defined(_WIN32) || defined(__WIN32__)) -#pragma pack(pop) -#else -#pragma pack() -#endif // WIN32 - -typedef struct tagBITMAPINFOHEADER -{ - DWORD biSize; - LONG biWidth; - LONG biHeight; - WORD biPlanes; - WORD biBitCount; - DWORD biCompression; - DWORD biSizeImage; - LONG biXPelsPerMeter; - LONG biYPelsPerMeter; - DWORD biClrUsed; - DWORD biClrImportant; -} BITMAPINFOHEADER, *PBITMAPINFOHEADER; - -typedef struct tagBITMAPINFO -{ - BITMAPINFOHEADER bmiHeader; - RGBQUAD bmiColors[1]; -} BITMAPINFO, *PBITMAPINFO; - -#endif // _WINDOWS_ - -// Types used in the library (specific to FreeImage) ------------------------ - -#if (defined(_WIN32) || defined(__WIN32__)) -#pragma pack(push, 1) -#else -#pragma pack(1) -#endif // WIN32 - -/** 48-bit RGB -*/ -typedef struct tagFIRGB16 -{ - WORD red; - WORD green; - WORD blue; -} FIRGB16; - -/** 64-bit RGBA -*/ -typedef struct tagFIRGBA16 -{ - WORD red; - WORD green; - WORD blue; - WORD alpha; -} FIRGBA16; - -/** 96-bit RGB Float -*/ -typedef struct tagFIRGBF -{ - float red; - float green; - float blue; -} FIRGBF; - -/** 128-bit RGBA Float -*/ -typedef struct tagFIRGBAF -{ - float red; - float green; - float blue; - float alpha; -} FIRGBAF; - -/** Data structure for COMPLEX type (complex number) -*/ -typedef struct tagFICOMPLEX -{ - /// real part - double r; - /// imaginary part - double i; -} FICOMPLEX; - -#if (defined(_WIN32) || defined(__WIN32__)) -#pragma pack(pop) -#else -#pragma pack() -#endif // WIN32 - -// Indexes for byte arrays, masks and shifts for treating pixels as words --- -// These coincide with the order of RGBQUAD and RGBTRIPLE ------------------- - -#ifndef FREEIMAGE_BIGENDIAN -#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR -// Little Endian (x86 / MS Windows, Linux) : BGR(A) order -#define FI_RGBA_RED 2 -#define FI_RGBA_GREEN 1 -#define FI_RGBA_BLUE 0 -#define FI_RGBA_ALPHA 3 -#define FI_RGBA_RED_MASK 0x00FF0000 -#define FI_RGBA_GREEN_MASK 0x0000FF00 -#define FI_RGBA_BLUE_MASK 0x000000FF -#define FI_RGBA_ALPHA_MASK 0xFF000000 -#define FI_RGBA_RED_SHIFT 16 -#define FI_RGBA_GREEN_SHIFT 8 -#define FI_RGBA_BLUE_SHIFT 0 -#define FI_RGBA_ALPHA_SHIFT 24 -#else -// Little Endian (x86 / MaxOSX) : RGB(A) order -#define FI_RGBA_RED 0 -#define FI_RGBA_GREEN 1 -#define FI_RGBA_BLUE 2 -#define FI_RGBA_ALPHA 3 -#define FI_RGBA_RED_MASK 0x000000FF -#define FI_RGBA_GREEN_MASK 0x0000FF00 -#define FI_RGBA_BLUE_MASK 0x00FF0000 -#define FI_RGBA_ALPHA_MASK 0xFF000000 -#define FI_RGBA_RED_SHIFT 0 -#define FI_RGBA_GREEN_SHIFT 8 -#define FI_RGBA_BLUE_SHIFT 16 -#define FI_RGBA_ALPHA_SHIFT 24 -#endif // FREEIMAGE_COLORORDER -#else -#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR -// Big Endian (PPC / none) : BGR(A) order -#define FI_RGBA_RED 2 -#define FI_RGBA_GREEN 1 -#define FI_RGBA_BLUE 0 -#define FI_RGBA_ALPHA 3 -#define FI_RGBA_RED_MASK 0x0000FF00 -#define FI_RGBA_GREEN_MASK 0x00FF0000 -#define FI_RGBA_BLUE_MASK 0xFF000000 -#define FI_RGBA_ALPHA_MASK 0x000000FF -#define FI_RGBA_RED_SHIFT 8 -#define FI_RGBA_GREEN_SHIFT 16 -#define FI_RGBA_BLUE_SHIFT 24 -#define FI_RGBA_ALPHA_SHIFT 0 -#else -// Big Endian (PPC / Linux, MaxOSX) : RGB(A) order -#define FI_RGBA_RED 0 -#define FI_RGBA_GREEN 1 -#define FI_RGBA_BLUE 2 -#define FI_RGBA_ALPHA 3 -#define FI_RGBA_RED_MASK 0xFF000000 -#define FI_RGBA_GREEN_MASK 0x00FF0000 -#define FI_RGBA_BLUE_MASK 0x0000FF00 -#define FI_RGBA_ALPHA_MASK 0x000000FF -#define FI_RGBA_RED_SHIFT 24 -#define FI_RGBA_GREEN_SHIFT 16 -#define FI_RGBA_BLUE_SHIFT 8 -#define FI_RGBA_ALPHA_SHIFT 0 -#endif // FREEIMAGE_COLORORDER -#endif // FREEIMAGE_BIGENDIAN - -#define FI_RGBA_RGB_MASK (FI_RGBA_RED_MASK|FI_RGBA_GREEN_MASK|FI_RGBA_BLUE_MASK) - -// The 16bit macros only include masks and shifts, since each color element is not byte aligned - -#define FI16_555_RED_MASK 0x7C00 -#define FI16_555_GREEN_MASK 0x03E0 -#define FI16_555_BLUE_MASK 0x001F -#define FI16_555_RED_SHIFT 10 -#define FI16_555_GREEN_SHIFT 5 -#define FI16_555_BLUE_SHIFT 0 -#define FI16_565_RED_MASK 0xF800 -#define FI16_565_GREEN_MASK 0x07E0 -#define FI16_565_BLUE_MASK 0x001F -#define FI16_565_RED_SHIFT 11 -#define FI16_565_GREEN_SHIFT 5 -#define FI16_565_BLUE_SHIFT 0 - -// ICC profile support ------------------------------------------------------ - -#define FIICC_DEFAULT 0x00 -#define FIICC_COLOR_IS_CMYK 0x01 - -FI_STRUCT( FIICCPROFILE ) -{ - WORD flags; // info flag - DWORD size; // profile's size measured in bytes - void* data; // points to a block of contiguous memory containing the profile -}; - -// Important enums ---------------------------------------------------------- - -/** I/O image format identifiers. -*/ -FI_ENUM( FREE_IMAGE_FORMAT ) -{ - FIF_UNKNOWN = -1, - FIF_BMP = 0, - FIF_ICO = 1, - FIF_JPEG = 2, - FIF_JNG = 3, - FIF_KOALA = 4, - FIF_LBM = 5, - FIF_IFF = FIF_LBM, - FIF_MNG = 6, - FIF_PBM = 7, - FIF_PBMRAW = 8, - FIF_PCD = 9, - FIF_PCX = 10, - FIF_PGM = 11, - FIF_PGMRAW = 12, - FIF_PNG = 13, - FIF_PPM = 14, - FIF_PPMRAW = 15, - FIF_RAS = 16, - FIF_TARGA = 17, - FIF_TIFF = 18, - FIF_WBMP = 19, - FIF_PSD = 20, - FIF_CUT = 21, - FIF_XBM = 22, - FIF_XPM = 23, - FIF_DDS = 24, - FIF_GIF = 25, - FIF_HDR = 26, - FIF_FAXG3 = 27, - FIF_SGI = 28, - FIF_EXR = 29, - FIF_J2K = 30, - FIF_JP2 = 31, - FIF_PFM = 32, - FIF_PICT = 33, - FIF_RAW = 34 -}; - -/** Image type used in FreeImage. -*/ -FI_ENUM( FREE_IMAGE_TYPE ) -{ - FIT_UNKNOWN = 0, // unknown type - FIT_BITMAP = 1, // standard image : 1-, 4-, 8-, 16-, 24-, 32-bit - FIT_UINT16 = 2, // array of unsigned short : unsigned 16-bit - FIT_INT16 = 3, // array of short : signed 16-bit - FIT_UINT32 = 4, // array of unsigned long : unsigned 32-bit - FIT_INT32 = 5, // array of long : signed 32-bit - FIT_FLOAT = 6, // array of float : 32-bit IEEE floating point - FIT_DOUBLE = 7, // array of double : 64-bit IEEE floating point - FIT_COMPLEX = 8, // array of FICOMPLEX : 2 x 64-bit IEEE floating point - FIT_RGB16 = 9, // 48-bit RGB image : 3 x 16-bit - FIT_RGBA16 = 10, // 64-bit RGBA image : 4 x 16-bit - FIT_RGBF = 11, // 96-bit RGB float image : 3 x 32-bit IEEE floating point - FIT_RGBAF = 12 // 128-bit RGBA float image : 4 x 32-bit IEEE floating point -}; - -/** Image color type used in FreeImage. -*/ -FI_ENUM( FREE_IMAGE_COLOR_TYPE ) -{ - FIC_MINISWHITE = 0, // min value is white - FIC_MINISBLACK = 1, // min value is black - FIC_RGB = 2, // RGB color model - FIC_PALETTE = 3, // color map indexed - FIC_RGBALPHA = 4, // RGB color model with alpha channel - FIC_CMYK = 5 // CMYK color model -}; - -/** Color quantization algorithms. -Constants used in FreeImage_ColorQuantize. -*/ -FI_ENUM( FREE_IMAGE_QUANTIZE ) -{ - FIQ_WUQUANT = 0, // Xiaolin Wu color quantization algorithm - FIQ_NNQUANT = 1 // NeuQuant neural-net quantization algorithm by Anthony Dekker -}; - -/** Dithering algorithms. -Constants used in FreeImage_Dither. -*/ -FI_ENUM( FREE_IMAGE_DITHER ) -{ - FID_FS = 0, // Floyd & Steinberg error diffusion - FID_BAYER4x4 = 1, // Bayer ordered dispersed dot dithering (order 2 dithering matrix) - FID_BAYER8x8 = 2, // Bayer ordered dispersed dot dithering (order 3 dithering matrix) - FID_CLUSTER6x6 = 3, // Ordered clustered dot dithering (order 3 - 6x6 matrix) - FID_CLUSTER8x8 = 4, // Ordered clustered dot dithering (order 4 - 8x8 matrix) - FID_CLUSTER16x16 = 5, // Ordered clustered dot dithering (order 8 - 16x16 matrix) - FID_BAYER16x16 = 6 // Bayer ordered dispersed dot dithering (order 4 dithering matrix) -}; - -/** Lossless JPEG transformations -Constants used in FreeImage_JPEGTransform -*/ -FI_ENUM( FREE_IMAGE_JPEG_OPERATION ) -{ - FIJPEG_OP_NONE = 0, // no transformation - FIJPEG_OP_FLIP_H = 1, // horizontal flip - FIJPEG_OP_FLIP_V = 2, // vertical flip - FIJPEG_OP_TRANSPOSE = 3, // transpose across UL-to-LR axis - FIJPEG_OP_TRANSVERSE = 4, // transpose across UR-to-LL axis - FIJPEG_OP_ROTATE_90 = 5, // 90-degree clockwise rotation - FIJPEG_OP_ROTATE_180 = 6, // 180-degree rotation - FIJPEG_OP_ROTATE_270 = 7 // 270-degree clockwise (or 90 ccw) -}; - -/** Tone mapping operators. -Constants used in FreeImage_ToneMapping. -*/ -FI_ENUM( FREE_IMAGE_TMO ) -{ - FITMO_DRAGO03 = 0, // Adaptive logarithmic mapping (F. Drago, 2003) - FITMO_REINHARD05 = 1, // Dynamic range reduction inspired by photoreceptor physiology (E. Reinhard, 2005) - FITMO_FATTAL02 = 2 // Gradient domain high dynamic range compression (R. Fattal, 2002) -}; - -/** Upsampling / downsampling filters. -Constants used in FreeImage_Rescale. -*/ -FI_ENUM( FREE_IMAGE_FILTER ) -{ - FILTER_BOX = 0, // Box, pulse, Fourier window, 1st order (constant) b-spline - FILTER_BICUBIC = 1, // Mitchell & Netravali's two-param cubic filter - FILTER_BILINEAR = 2, // Bilinear filter - FILTER_BSPLINE = 3, // 4th order (cubic) b-spline - FILTER_CATMULLROM = 4, // Catmull-Rom spline, Overhauser spline - FILTER_LANCZOS3 = 5 // Lanczos3 filter -}; - -/** Color channels. -Constants used in color manipulation routines. -*/ -FI_ENUM( FREE_IMAGE_COLOR_CHANNEL ) -{ - FICC_RGB = 0, // Use red, green and blue channels - FICC_RED = 1, // Use red channel - FICC_GREEN = 2, // Use green channel - FICC_BLUE = 3, // Use blue channel - FICC_ALPHA = 4, // Use alpha channel - FICC_BLACK = 5, // Use black channel - FICC_REAL = 6, // Complex images: use real part - FICC_IMAG = 7, // Complex images: use imaginary part - FICC_MAG = 8, // Complex images: use magnitude - FICC_PHASE = 9 // Complex images: use phase -}; - -// Metadata support --------------------------------------------------------- - -/** - Tag data type information (based on TIFF specifications) - - Note: RATIONALs are the ratio of two 32-bit integer values. -*/ -FI_ENUM( FREE_IMAGE_MDTYPE ) -{ - FIDT_NOTYPE = 0, // placeholder - FIDT_BYTE = 1, // 8-bit unsigned integer - FIDT_ASCII = 2, // 8-bit bytes w/ last byte null - FIDT_SHORT = 3, // 16-bit unsigned integer - FIDT_LONG = 4, // 32-bit unsigned integer - FIDT_RATIONAL = 5, // 64-bit unsigned fraction - FIDT_SBYTE = 6, // 8-bit signed integer - FIDT_UNDEFINED = 7, // 8-bit untyped data - FIDT_SSHORT = 8, // 16-bit signed integer - FIDT_SLONG = 9, // 32-bit signed integer - FIDT_SRATIONAL = 10, // 64-bit signed fraction - FIDT_FLOAT = 11, // 32-bit IEEE floating point - FIDT_DOUBLE = 12, // 64-bit IEEE floating point - FIDT_IFD = 13, // 32-bit unsigned integer (offset) - FIDT_PALETTE = 14 // 32-bit RGBQUAD -}; - -/** - Metadata models supported by FreeImage -*/ -FI_ENUM( FREE_IMAGE_MDMODEL ) -{ - FIMD_NODATA = -1, - FIMD_COMMENTS = 0, // single comment or keywords - FIMD_EXIF_MAIN = 1, // Exif-TIFF metadata - FIMD_EXIF_EXIF = 2, // Exif-specific metadata - FIMD_EXIF_GPS = 3, // Exif GPS metadata - FIMD_EXIF_MAKERNOTE = 4, // Exif maker note metadata - FIMD_EXIF_INTEROP = 5, // Exif interoperability metadata - FIMD_IPTC = 6, // IPTC/NAA metadata - FIMD_XMP = 7, // Abobe XMP metadata - FIMD_GEOTIFF = 8, // GeoTIFF metadata - FIMD_ANIMATION = 9, // Animation metadata - FIMD_CUSTOM = 10, // Used to attach other metadata types to a dib - FIMD_EXIF_RAW = 11 // Exif metadata as a raw buffer -}; - -/** - Handle to a metadata model -*/ -FI_STRUCT( FIMETADATA ) { void* data; }; - -/** - Handle to a FreeImage tag -*/ -FI_STRUCT( FITAG ) { void* data; }; - -// File IO routines --------------------------------------------------------- - -#ifndef FREEIMAGE_IO -#define FREEIMAGE_IO - -typedef void* fi_handle; -typedef unsigned( DLL_CALLCONV* FI_ReadProc )( void* buffer, unsigned size, unsigned count, fi_handle handle ); -typedef unsigned( DLL_CALLCONV* FI_WriteProc )( void* buffer, unsigned size, unsigned count, fi_handle handle ); -typedef int ( DLL_CALLCONV* FI_SeekProc )( fi_handle handle, long offset, int origin ); -typedef long( DLL_CALLCONV* FI_TellProc )( fi_handle handle ); - -#if (defined(_WIN32) || defined(__WIN32__)) -#pragma pack(push, 1) -#else -#pragma pack(1) -#endif // WIN32 - -FI_STRUCT( FreeImageIO ) -{ - FI_ReadProc read_proc; // pointer to the function used to read data - FI_WriteProc write_proc; // pointer to the function used to write data - FI_SeekProc seek_proc; // pointer to the function used to seek - FI_TellProc tell_proc; // pointer to the function used to aquire the current position -}; - -#if (defined(_WIN32) || defined(__WIN32__)) -#pragma pack(pop) -#else -#pragma pack() -#endif // WIN32 - -/** -Handle to a memory I/O stream -*/ -FI_STRUCT( FIMEMORY ) { void* data; }; - -#endif // FREEIMAGE_IO - -// Plugin routines ---------------------------------------------------------- - -#ifndef PLUGINS -#define PLUGINS - -typedef const char* ( DLL_CALLCONV* FI_FormatProc )( void ); -typedef const char* ( DLL_CALLCONV* FI_DescriptionProc )( void ); -typedef const char* ( DLL_CALLCONV* FI_ExtensionListProc )( void ); -typedef const char* ( DLL_CALLCONV* FI_RegExprProc )( void ); -typedef void* ( DLL_CALLCONV* FI_OpenProc )( FreeImageIO* io, fi_handle handle, BOOL read ); -typedef void ( DLL_CALLCONV* FI_CloseProc )( FreeImageIO* io, fi_handle handle, void* data ); -typedef int ( DLL_CALLCONV* FI_PageCountProc )( FreeImageIO* io, fi_handle handle, void* data ); -typedef int ( DLL_CALLCONV* FI_PageCapabilityProc )( FreeImageIO* io, fi_handle handle, void* data ); -typedef FIBITMAP* ( DLL_CALLCONV* FI_LoadProc )( FreeImageIO* io, fi_handle handle, int page, int flags, void* data ); -typedef BOOL ( DLL_CALLCONV* FI_SaveProc )( FreeImageIO* io, FIBITMAP* dib, fi_handle handle, int page, int flags, void* data ); -typedef BOOL ( DLL_CALLCONV* FI_ValidateProc )( FreeImageIO* io, fi_handle handle ); -typedef const char* ( DLL_CALLCONV* FI_MimeProc )( void ); -typedef BOOL ( DLL_CALLCONV* FI_SupportsExportBPPProc )( int bpp ); -typedef BOOL ( DLL_CALLCONV* FI_SupportsExportTypeProc )( FREE_IMAGE_TYPE type ); -typedef BOOL ( DLL_CALLCONV* FI_SupportsICCProfilesProc )( void ); -typedef BOOL ( DLL_CALLCONV* FI_SupportsNoPixelsProc )( void ); - -FI_STRUCT( Plugin ) -{ - FI_FormatProc format_proc; - FI_DescriptionProc description_proc; - FI_ExtensionListProc extension_proc; - FI_RegExprProc regexpr_proc; - FI_OpenProc open_proc; - FI_CloseProc close_proc; - FI_PageCountProc pagecount_proc; - FI_PageCapabilityProc pagecapability_proc; - FI_LoadProc load_proc; - FI_SaveProc save_proc; - FI_ValidateProc validate_proc; - FI_MimeProc mime_proc; - FI_SupportsExportBPPProc supports_export_bpp_proc; - FI_SupportsExportTypeProc supports_export_type_proc; - FI_SupportsICCProfilesProc supports_icc_profiles_proc; - FI_SupportsNoPixelsProc supports_no_pixels_proc; -}; - -typedef void ( DLL_CALLCONV* FI_InitProc )( Plugin* plugin, int format_id ); - -#endif // PLUGINS - - -// Load / Save flag constants ----------------------------------------------- - -#define FIF_LOAD_NOPIXELS 0x8000 // loading: load the image header only (not supported by all plugins) - -#define BMP_DEFAULT 0 -#define BMP_SAVE_RLE 1 -#define CUT_DEFAULT 0 -#define DDS_DEFAULT 0 -#define EXR_DEFAULT 0 // save data as half with piz-based wavelet compression -#define EXR_FLOAT 0x0001 // save data as float instead of as half (not recommended) -#define EXR_NONE 0x0002 // save with no compression -#define EXR_ZIP 0x0004 // save with zlib compression, in blocks of 16 scan lines -#define EXR_PIZ 0x0008 // save with piz-based wavelet compression -#define EXR_PXR24 0x0010 // save with lossy 24-bit float compression -#define EXR_B44 0x0020 // save with lossy 44% float compression - goes to 22% when combined with EXR_LC -#define EXR_LC 0x0040 // save images with one luminance and two chroma channels, rather than as RGB (lossy compression) -#define FAXG3_DEFAULT 0 -#define GIF_DEFAULT 0 -#define GIF_LOAD256 1 // Load the image as a 256 color image with ununsed palette entries, if it's 16 or 2 color -#define GIF_PLAYBACK 2 // 'Play' the GIF to generate each frame (as 32bpp) instead of returning raw frame data when loading -#define HDR_DEFAULT 0 -#define ICO_DEFAULT 0 -#define ICO_MAKEALPHA 1 // convert to 32bpp and create an alpha channel from the AND-mask when loading -#define IFF_DEFAULT 0 -#define J2K_DEFAULT 0 // save with a 16:1 rate -#define JP2_DEFAULT 0 // save with a 16:1 rate -#define JPEG_DEFAULT 0 // loading (see JPEG_FAST); saving (see JPEG_QUALITYGOOD|JPEG_SUBSAMPLING_420) -#define JPEG_FAST 0x0001 // load the file as fast as possible, sacrificing some quality -#define JPEG_ACCURATE 0x0002 // load the file with the best quality, sacrificing some speed -#define JPEG_CMYK 0x0004 // load separated CMYK "as is" (use | to combine with other load flags) -#define JPEG_EXIFROTATE 0x0008 // load and rotate according to Exif 'Orientation' tag if available -#define JPEG_QUALITYSUPERB 0x80 // save with superb quality (100:1) -#define JPEG_QUALITYGOOD 0x0100 // save with good quality (75:1) -#define JPEG_QUALITYNORMAL 0x0200 // save with normal quality (50:1) -#define JPEG_QUALITYAVERAGE 0x0400 // save with average quality (25:1) -#define JPEG_QUALITYBAD 0x0800 // save with bad quality (10:1) -#define JPEG_PROGRESSIVE 0x2000 // save as a progressive-JPEG (use | to combine with other save flags) -#define JPEG_SUBSAMPLING_411 0x1000 // save with high 4x1 chroma subsampling (4:1:1) -#define JPEG_SUBSAMPLING_420 0x4000 // save with medium 2x2 medium chroma subsampling (4:2:0) - default value -#define JPEG_SUBSAMPLING_422 0x8000 // save with low 2x1 chroma subsampling (4:2:2) -#define JPEG_SUBSAMPLING_444 0x10000 // save with no chroma subsampling (4:4:4) -#define JPEG_OPTIMIZE 0x20000 // on saving, compute optimal Huffman coding tables (can reduce a few percent of file size) -#define JPEG_BASELINE 0x40000 // save basic JPEG, without metadata or any markers -#define KOALA_DEFAULT 0 -#define LBM_DEFAULT 0 -#define MNG_DEFAULT 0 -#define PCD_DEFAULT 0 -#define PCD_BASE 1 // load the bitmap sized 768 x 512 -#define PCD_BASEDIV4 2 // load the bitmap sized 384 x 256 -#define PCD_BASEDIV16 3 // load the bitmap sized 192 x 128 -#define PCX_DEFAULT 0 -#define PFM_DEFAULT 0 -#define PICT_DEFAULT 0 -#define PNG_DEFAULT 0 -#define PNG_IGNOREGAMMA 1 // loading: avoid gamma correction -#define PNG_Z_BEST_SPEED 0x0001 // save using ZLib level 1 compression flag (default value is 6) -#define PNG_Z_DEFAULT_COMPRESSION 0x0006 // save using ZLib level 6 compression flag (default recommended value) -#define PNG_Z_BEST_COMPRESSION 0x0009 // save using ZLib level 9 compression flag (default value is 6) -#define PNG_Z_NO_COMPRESSION 0x0100 // save without ZLib compression -#define PNG_INTERLACED 0x0200 // save using Adam7 interlacing (use | to combine with other save flags) -#define PNM_DEFAULT 0 -#define PNM_SAVE_RAW 0 // If set the writer saves in RAW format (i.e. P4, P5 or P6) -#define PNM_SAVE_ASCII 1 // If set the writer saves in ASCII format (i.e. P1, P2 or P3) -#define PSD_DEFAULT 0 -#define PSD_CMYK 1 // reads tags for separated CMYK (default is conversion to RGB) -#define PSD_LAB 2 // reads tags for CIELab (default is conversion to RGB) -#define RAS_DEFAULT 0 -#define RAW_DEFAULT 0 // load the file as linear RGB 48-bit -#define RAW_PREVIEW 1 // try to load the embedded JPEG preview with included Exif Data or default to RGB 24-bit -#define RAW_DISPLAY 2 // load the file as RGB 24-bit -#define SGI_DEFAULT 0 -#define TARGA_DEFAULT 0 -#define TARGA_LOAD_RGB888 1 // If set the loader converts RGB555 and ARGB8888 -> RGB888. -#define TARGA_SAVE_RLE 2 // If set, the writer saves with RLE compression -#define TIFF_DEFAULT 0 -#define TIFF_CMYK 0x0001 // reads/stores tags for separated CMYK (use | to combine with compression flags) -#define TIFF_PACKBITS 0x0100 // save using PACKBITS compression -#define TIFF_DEFLATE 0x0200 // save using DEFLATE compression (a.k.a. ZLIB compression) -#define TIFF_ADOBE_DEFLATE 0x0400 // save using ADOBE DEFLATE compression -#define TIFF_NONE 0x0800 // save without any compression -#define TIFF_CCITTFAX3 0x1000 // save using CCITT Group 3 fax encoding -#define TIFF_CCITTFAX4 0x2000 // save using CCITT Group 4 fax encoding -#define TIFF_LZW 0x4000 // save using LZW compression -#define TIFF_JPEG 0x8000 // save using JPEG compression -#define TIFF_LOGLUV 0x10000 // save using LogLuv compression -#define WBMP_DEFAULT 0 -#define XBM_DEFAULT 0 -#define XPM_DEFAULT 0 - -// Background filling options --------------------------------------------------------- -// Constants used in FreeImage_FillBackground and FreeImage_EnlargeCanvas - -#define FI_COLOR_IS_RGB_COLOR 0x00 // RGBQUAD color is a RGB color (contains no valid alpha channel) -#define FI_COLOR_IS_RGBA_COLOR 0x01 // RGBQUAD color is a RGBA color (contains a valid alpha channel) -#define FI_COLOR_FIND_EQUAL_COLOR 0x02 // For palettized images: lookup equal RGB color from palette -#define FI_COLOR_ALPHA_IS_INDEX 0x04 // The color's rgbReserved member (alpha) contains the palette index to be used -#define FI_COLOR_PALETTE_SEARCH_MASK (FI_COLOR_FIND_EQUAL_COLOR | FI_COLOR_ALPHA_IS_INDEX) // No color lookup is performed - - -#ifdef __cplusplus -extern "C" { -#endif - - // Init / Error routines ---------------------------------------------------- - - DLL_API void DLL_CALLCONV FreeImage_Initialise( BOOL load_local_plugins_only FI_DEFAULT( FALSE ) ); - DLL_API void DLL_CALLCONV FreeImage_DeInitialise( void ); - - // Version routines --------------------------------------------------------- - - DLL_API const char* DLL_CALLCONV FreeImage_GetVersion( void ); - DLL_API const char* DLL_CALLCONV FreeImage_GetCopyrightMessage( void ); - - // Message output functions ------------------------------------------------- - - typedef void ( *FreeImage_OutputMessageFunction )( FREE_IMAGE_FORMAT fif, const char* msg ); - typedef void ( DLL_CALLCONV* FreeImage_OutputMessageFunctionStdCall )( FREE_IMAGE_FORMAT fif, const char* msg ); - - DLL_API void DLL_CALLCONV FreeImage_SetOutputMessageStdCall( FreeImage_OutputMessageFunctionStdCall omf ); - DLL_API void DLL_CALLCONV FreeImage_SetOutputMessage( FreeImage_OutputMessageFunction omf ); - DLL_API void DLL_CALLCONV FreeImage_OutputMessageProc( int fif, const char* fmt, ... ); - - // Allocate / Clone / Unload routines --------------------------------------- - - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_Allocate( int width, int height, int bpp, unsigned red_mask FI_DEFAULT( 0 ), unsigned green_mask FI_DEFAULT( 0 ), unsigned blue_mask FI_DEFAULT( 0 ) ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_AllocateT( FREE_IMAGE_TYPE type, int width, int height, int bpp FI_DEFAULT( 8 ), unsigned red_mask FI_DEFAULT( 0 ), unsigned green_mask FI_DEFAULT( 0 ), unsigned blue_mask FI_DEFAULT( 0 ) ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_Clone( FIBITMAP* dib ); - DLL_API void DLL_CALLCONV FreeImage_Unload( FIBITMAP* dib ); - - // Header loading routines - DLL_API BOOL DLL_CALLCONV FreeImage_HasPixels( FIBITMAP* dib ); - - // Load / Save routines ----------------------------------------------------- - - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_Load( FREE_IMAGE_FORMAT fif, const char* filename, int flags FI_DEFAULT( 0 ) ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_LoadU( FREE_IMAGE_FORMAT fif, const wchar_t* filename, int flags FI_DEFAULT( 0 ) ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_LoadFromHandle( FREE_IMAGE_FORMAT fif, FreeImageIO* io, fi_handle handle, int flags FI_DEFAULT( 0 ) ); - DLL_API BOOL DLL_CALLCONV FreeImage_Save( FREE_IMAGE_FORMAT fif, FIBITMAP* dib, const char* filename, int flags FI_DEFAULT( 0 ) ); - DLL_API BOOL DLL_CALLCONV FreeImage_SaveU( FREE_IMAGE_FORMAT fif, FIBITMAP* dib, const wchar_t* filename, int flags FI_DEFAULT( 0 ) ); - DLL_API BOOL DLL_CALLCONV FreeImage_SaveToHandle( FREE_IMAGE_FORMAT fif, FIBITMAP* dib, FreeImageIO* io, fi_handle handle, int flags FI_DEFAULT( 0 ) ); - - // Memory I/O stream routines ----------------------------------------------- - - DLL_API FIMEMORY* DLL_CALLCONV FreeImage_OpenMemory( BYTE* data FI_DEFAULT( 0 ), DWORD size_in_bytes FI_DEFAULT( 0 ) ); - DLL_API void DLL_CALLCONV FreeImage_CloseMemory( FIMEMORY* stream ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_LoadFromMemory( FREE_IMAGE_FORMAT fif, FIMEMORY* stream, int flags FI_DEFAULT( 0 ) ); - DLL_API BOOL DLL_CALLCONV FreeImage_SaveToMemory( FREE_IMAGE_FORMAT fif, FIBITMAP* dib, FIMEMORY* stream, int flags FI_DEFAULT( 0 ) ); - DLL_API long DLL_CALLCONV FreeImage_TellMemory( FIMEMORY* stream ); - DLL_API BOOL DLL_CALLCONV FreeImage_SeekMemory( FIMEMORY* stream, long offset, int origin ); - DLL_API BOOL DLL_CALLCONV FreeImage_AcquireMemory( FIMEMORY* stream, BYTE** data, DWORD* size_in_bytes ); - DLL_API unsigned DLL_CALLCONV FreeImage_ReadMemory( void* buffer, unsigned size, unsigned count, FIMEMORY* stream ); - DLL_API unsigned DLL_CALLCONV FreeImage_WriteMemory( const void* buffer, unsigned size, unsigned count, FIMEMORY* stream ); - - DLL_API FIMULTIBITMAP* DLL_CALLCONV FreeImage_LoadMultiBitmapFromMemory( FREE_IMAGE_FORMAT fif, FIMEMORY* stream, int flags FI_DEFAULT( 0 ) ); - DLL_API BOOL DLL_CALLCONV FreeImage_SaveMultiBitmapToMemory( FREE_IMAGE_FORMAT fif, FIMULTIBITMAP* bitmap, FIMEMORY* stream, int flags ); - - // Plugin Interface --------------------------------------------------------- - - DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_RegisterLocalPlugin( FI_InitProc proc_address, const char* format FI_DEFAULT( 0 ), const char* description FI_DEFAULT( 0 ), const char* extension FI_DEFAULT( 0 ), const char* regexpr FI_DEFAULT( 0 ) ); - DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_RegisterExternalPlugin( const char* path, const char* format FI_DEFAULT( 0 ), const char* description FI_DEFAULT( 0 ), const char* extension FI_DEFAULT( 0 ), const char* regexpr FI_DEFAULT( 0 ) ); - DLL_API int DLL_CALLCONV FreeImage_GetFIFCount( void ); - DLL_API int DLL_CALLCONV FreeImage_SetPluginEnabled( FREE_IMAGE_FORMAT fif, BOOL enable ); - DLL_API int DLL_CALLCONV FreeImage_IsPluginEnabled( FREE_IMAGE_FORMAT fif ); - DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromFormat( const char* format ); - DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromMime( const char* mime ); - DLL_API const char* DLL_CALLCONV FreeImage_GetFormatFromFIF( FREE_IMAGE_FORMAT fif ); - DLL_API const char* DLL_CALLCONV FreeImage_GetFIFExtensionList( FREE_IMAGE_FORMAT fif ); - DLL_API const char* DLL_CALLCONV FreeImage_GetFIFDescription( FREE_IMAGE_FORMAT fif ); - DLL_API const char* DLL_CALLCONV FreeImage_GetFIFRegExpr( FREE_IMAGE_FORMAT fif ); - DLL_API const char* DLL_CALLCONV FreeImage_GetFIFMimeType( FREE_IMAGE_FORMAT fif ); - DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromFilename( const char* filename ); - DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromFilenameU( const wchar_t* filename ); - DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsReading( FREE_IMAGE_FORMAT fif ); - DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsWriting( FREE_IMAGE_FORMAT fif ); - DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsExportBPP( FREE_IMAGE_FORMAT fif, int bpp ); - DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsExportType( FREE_IMAGE_FORMAT fif, FREE_IMAGE_TYPE type ); - DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsICCProfiles( FREE_IMAGE_FORMAT fif ); - DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsNoPixels( FREE_IMAGE_FORMAT fif ); - - // Multipaging interface ---------------------------------------------------- - - DLL_API FIMULTIBITMAP* DLL_CALLCONV FreeImage_OpenMultiBitmap( FREE_IMAGE_FORMAT fif, const char* filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory FI_DEFAULT( FALSE ), int flags FI_DEFAULT( 0 ) ); - DLL_API FIMULTIBITMAP* DLL_CALLCONV FreeImage_OpenMultiBitmapFromHandle( FREE_IMAGE_FORMAT fif, FreeImageIO* io, fi_handle handle, int flags FI_DEFAULT( 0 ) ); - DLL_API BOOL DLL_CALLCONV FreeImage_SaveMultiBitmapToHandle( FREE_IMAGE_FORMAT fif, FIMULTIBITMAP* bitmap, FreeImageIO* io, fi_handle handle, int flags FI_DEFAULT( 0 ) ); - DLL_API BOOL DLL_CALLCONV FreeImage_CloseMultiBitmap( FIMULTIBITMAP* bitmap, int flags FI_DEFAULT( 0 ) ); - DLL_API int DLL_CALLCONV FreeImage_GetPageCount( FIMULTIBITMAP* bitmap ); - DLL_API void DLL_CALLCONV FreeImage_AppendPage( FIMULTIBITMAP* bitmap, FIBITMAP* data ); - DLL_API void DLL_CALLCONV FreeImage_InsertPage( FIMULTIBITMAP* bitmap, int page, FIBITMAP* data ); - DLL_API void DLL_CALLCONV FreeImage_DeletePage( FIMULTIBITMAP* bitmap, int page ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_LockPage( FIMULTIBITMAP* bitmap, int page ); - DLL_API void DLL_CALLCONV FreeImage_UnlockPage( FIMULTIBITMAP* bitmap, FIBITMAP* data, BOOL changed ); - DLL_API BOOL DLL_CALLCONV FreeImage_MovePage( FIMULTIBITMAP* bitmap, int target, int source ); - DLL_API BOOL DLL_CALLCONV FreeImage_GetLockedPageNumbers( FIMULTIBITMAP* bitmap, int* pages, int* count ); - - // Filetype request routines ------------------------------------------------ - - DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileType( const char* filename, int size FI_DEFAULT( 0 ) ); - DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileTypeU( const wchar_t* filename, int size FI_DEFAULT( 0 ) ); - DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileTypeFromHandle( FreeImageIO* io, fi_handle handle, int size FI_DEFAULT( 0 ) ); - DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileTypeFromMemory( FIMEMORY* stream, int size FI_DEFAULT( 0 ) ); - - // Image type request routine ----------------------------------------------- - - DLL_API FREE_IMAGE_TYPE DLL_CALLCONV FreeImage_GetImageType( FIBITMAP* dib ); - - // FreeImage helper routines ------------------------------------------------ - - DLL_API BOOL DLL_CALLCONV FreeImage_IsLittleEndian( void ); - DLL_API BOOL DLL_CALLCONV FreeImage_LookupX11Color( const char* szColor, BYTE* nRed, BYTE* nGreen, BYTE* nBlue ); - DLL_API BOOL DLL_CALLCONV FreeImage_LookupSVGColor( const char* szColor, BYTE* nRed, BYTE* nGreen, BYTE* nBlue ); - - // Pixel access routines ---------------------------------------------------- - - DLL_API BYTE* DLL_CALLCONV FreeImage_GetBits( FIBITMAP* dib ); - DLL_API BYTE* DLL_CALLCONV FreeImage_GetScanLine( FIBITMAP* dib, int scanline ); - - DLL_API BOOL DLL_CALLCONV FreeImage_GetPixelIndex( FIBITMAP* dib, unsigned x, unsigned y, BYTE* value ); - DLL_API BOOL DLL_CALLCONV FreeImage_GetPixelColor( FIBITMAP* dib, unsigned x, unsigned y, RGBQUAD* value ); - DLL_API BOOL DLL_CALLCONV FreeImage_SetPixelIndex( FIBITMAP* dib, unsigned x, unsigned y, BYTE* value ); - DLL_API BOOL DLL_CALLCONV FreeImage_SetPixelColor( FIBITMAP* dib, unsigned x, unsigned y, RGBQUAD* value ); - - // DIB info routines -------------------------------------------------------- - - DLL_API unsigned DLL_CALLCONV FreeImage_GetColorsUsed( FIBITMAP* dib ); - DLL_API unsigned DLL_CALLCONV FreeImage_GetBPP( FIBITMAP* dib ); - DLL_API unsigned DLL_CALLCONV FreeImage_GetWidth( FIBITMAP* dib ); - DLL_API unsigned DLL_CALLCONV FreeImage_GetHeight( FIBITMAP* dib ); - DLL_API unsigned DLL_CALLCONV FreeImage_GetLine( FIBITMAP* dib ); - DLL_API unsigned DLL_CALLCONV FreeImage_GetPitch( FIBITMAP* dib ); - DLL_API unsigned DLL_CALLCONV FreeImage_GetDIBSize( FIBITMAP* dib ); - DLL_API RGBQUAD* DLL_CALLCONV FreeImage_GetPalette( FIBITMAP* dib ); - - DLL_API unsigned DLL_CALLCONV FreeImage_GetDotsPerMeterX( FIBITMAP* dib ); - DLL_API unsigned DLL_CALLCONV FreeImage_GetDotsPerMeterY( FIBITMAP* dib ); - DLL_API void DLL_CALLCONV FreeImage_SetDotsPerMeterX( FIBITMAP* dib, unsigned res ); - DLL_API void DLL_CALLCONV FreeImage_SetDotsPerMeterY( FIBITMAP* dib, unsigned res ); - - DLL_API BITMAPINFOHEADER* DLL_CALLCONV FreeImage_GetInfoHeader( FIBITMAP* dib ); - DLL_API BITMAPINFO* DLL_CALLCONV FreeImage_GetInfo( FIBITMAP* dib ); - DLL_API FREE_IMAGE_COLOR_TYPE DLL_CALLCONV FreeImage_GetColorType( FIBITMAP* dib ); - - DLL_API unsigned DLL_CALLCONV FreeImage_GetRedMask( FIBITMAP* dib ); - DLL_API unsigned DLL_CALLCONV FreeImage_GetGreenMask( FIBITMAP* dib ); - DLL_API unsigned DLL_CALLCONV FreeImage_GetBlueMask( FIBITMAP* dib ); - - DLL_API unsigned DLL_CALLCONV FreeImage_GetTransparencyCount( FIBITMAP* dib ); - DLL_API BYTE* DLL_CALLCONV FreeImage_GetTransparencyTable( FIBITMAP* dib ); - DLL_API void DLL_CALLCONV FreeImage_SetTransparent( FIBITMAP* dib, BOOL enabled ); - DLL_API void DLL_CALLCONV FreeImage_SetTransparencyTable( FIBITMAP* dib, BYTE* table, int count ); - DLL_API BOOL DLL_CALLCONV FreeImage_IsTransparent( FIBITMAP* dib ); - DLL_API void DLL_CALLCONV FreeImage_SetTransparentIndex( FIBITMAP* dib, int index ); - DLL_API int DLL_CALLCONV FreeImage_GetTransparentIndex( FIBITMAP* dib ); - - DLL_API BOOL DLL_CALLCONV FreeImage_HasBackgroundColor( FIBITMAP* dib ); - DLL_API BOOL DLL_CALLCONV FreeImage_GetBackgroundColor( FIBITMAP* dib, RGBQUAD* bkcolor ); - DLL_API BOOL DLL_CALLCONV FreeImage_SetBackgroundColor( FIBITMAP* dib, RGBQUAD* bkcolor ); - - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_GetThumbnail( FIBITMAP* dib ); - DLL_API BOOL DLL_CALLCONV FreeImage_SetThumbnail( FIBITMAP* dib, FIBITMAP* thumbnail ); - - // ICC profile routines ----------------------------------------------------- - - DLL_API FIICCPROFILE* DLL_CALLCONV FreeImage_GetICCProfile( FIBITMAP* dib ); - DLL_API FIICCPROFILE* DLL_CALLCONV FreeImage_CreateICCProfile( FIBITMAP* dib, void* data, long size ); - DLL_API void DLL_CALLCONV FreeImage_DestroyICCProfile( FIBITMAP* dib ); - - // Line conversion routines ------------------------------------------------- - - DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To4( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To4( BYTE* target, BYTE* source, int width_in_pixels, RGBQUAD* palette ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To4_555( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To4_565( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To4( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To4( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To8( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To8( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To8_555( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To8_565( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To8( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To8( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To16_555( BYTE* target, BYTE* source, int width_in_pixels, RGBQUAD* palette ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To16_555( BYTE* target, BYTE* source, int width_in_pixels, RGBQUAD* palette ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To16_555( BYTE* target, BYTE* source, int width_in_pixels, RGBQUAD* palette ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine16_565_To16_555( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To16_555( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To16_555( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To16_565( BYTE* target, BYTE* source, int width_in_pixels, RGBQUAD* palette ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To16_565( BYTE* target, BYTE* source, int width_in_pixels, RGBQUAD* palette ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To16_565( BYTE* target, BYTE* source, int width_in_pixels, RGBQUAD* palette ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine16_555_To16_565( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To16_565( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To16_565( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To24( BYTE* target, BYTE* source, int width_in_pixels, RGBQUAD* palette ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To24( BYTE* target, BYTE* source, int width_in_pixels, RGBQUAD* palette ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To24( BYTE* target, BYTE* source, int width_in_pixels, RGBQUAD* palette ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To24_555( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To24_565( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To24( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To32( BYTE* target, BYTE* source, int width_in_pixels, RGBQUAD* palette ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To32( BYTE* target, BYTE* source, int width_in_pixels, RGBQUAD* palette ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To32( BYTE* target, BYTE* source, int width_in_pixels, RGBQUAD* palette ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To32_555( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To32_565( BYTE* target, BYTE* source, int width_in_pixels ); - DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To32( BYTE* target, BYTE* source, int width_in_pixels ); - - // Smart conversion routines ------------------------------------------------ - - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ConvertTo4Bits( FIBITMAP* dib ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ConvertTo8Bits( FIBITMAP* dib ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ConvertToGreyscale( FIBITMAP* dib ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ConvertTo16Bits555( FIBITMAP* dib ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ConvertTo16Bits565( FIBITMAP* dib ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ConvertTo24Bits( FIBITMAP* dib ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ConvertTo32Bits( FIBITMAP* dib ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ColorQuantize( FIBITMAP* dib, FREE_IMAGE_QUANTIZE quantize ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ColorQuantizeEx( FIBITMAP* dib, FREE_IMAGE_QUANTIZE quantize FI_DEFAULT( FIQ_WUQUANT ), int PaletteSize FI_DEFAULT( 256 ), int ReserveSize FI_DEFAULT( 0 ), RGBQUAD* ReservePalette FI_DEFAULT( NULL ) ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_Threshold( FIBITMAP* dib, BYTE T ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_Dither( FIBITMAP* dib, FREE_IMAGE_DITHER algorithm ); - - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ConvertFromRawBits( BYTE* bits, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT( FALSE ) ); - DLL_API void DLL_CALLCONV FreeImage_ConvertToRawBits( BYTE* bits, FIBITMAP* dib, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT( FALSE ) ); - - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ConvertToFloat( FIBITMAP* dib ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ConvertToRGBF( FIBITMAP* dib ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ConvertToUINT16( FIBITMAP* dib ); - - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ConvertToStandardType( FIBITMAP* src, BOOL scale_linear FI_DEFAULT( TRUE ) ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ConvertToType( FIBITMAP* src, FREE_IMAGE_TYPE dst_type, BOOL scale_linear FI_DEFAULT( TRUE ) ); - - // tone mapping operators - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_ToneMapping( FIBITMAP* dib, FREE_IMAGE_TMO tmo, double first_param FI_DEFAULT( 0 ), double second_param FI_DEFAULT( 0 ) ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_TmoDrago03( FIBITMAP* src, double gamma FI_DEFAULT( 2.2 ), double exposure FI_DEFAULT( 0 ) ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_TmoReinhard05( FIBITMAP* src, double intensity FI_DEFAULT( 0 ), double contrast FI_DEFAULT( 0 ) ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_TmoReinhard05Ex( FIBITMAP* src, double intensity FI_DEFAULT( 0 ), double contrast FI_DEFAULT( 0 ), double adaptation FI_DEFAULT( 1 ), double color_correction FI_DEFAULT( 0 ) ); - - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_TmoFattal02( FIBITMAP* src, double color_saturation FI_DEFAULT( 0.5 ), double attenuation FI_DEFAULT( 0.85 ) ); - - // ZLib interface ----------------------------------------------------------- - - DLL_API DWORD DLL_CALLCONV FreeImage_ZLibCompress( BYTE* target, DWORD target_size, BYTE* source, DWORD source_size ); - DLL_API DWORD DLL_CALLCONV FreeImage_ZLibUncompress( BYTE* target, DWORD target_size, BYTE* source, DWORD source_size ); - DLL_API DWORD DLL_CALLCONV FreeImage_ZLibGZip( BYTE* target, DWORD target_size, BYTE* source, DWORD source_size ); - DLL_API DWORD DLL_CALLCONV FreeImage_ZLibGUnzip( BYTE* target, DWORD target_size, BYTE* source, DWORD source_size ); - DLL_API DWORD DLL_CALLCONV FreeImage_ZLibCRC32( DWORD crc, BYTE* source, DWORD source_size ); - - // -------------------------------------------------------------------------- - // Metadata routines -------------------------------------------------------- - // -------------------------------------------------------------------------- - - // tag creation / destruction - DLL_API FITAG* DLL_CALLCONV FreeImage_CreateTag( void ); - DLL_API void DLL_CALLCONV FreeImage_DeleteTag( FITAG* tag ); - DLL_API FITAG* DLL_CALLCONV FreeImage_CloneTag( FITAG* tag ); - - // tag getters and setters - DLL_API const char* DLL_CALLCONV FreeImage_GetTagKey( FITAG* tag ); - DLL_API const char* DLL_CALLCONV FreeImage_GetTagDescription( FITAG* tag ); - DLL_API WORD DLL_CALLCONV FreeImage_GetTagID( FITAG* tag ); - DLL_API FREE_IMAGE_MDTYPE DLL_CALLCONV FreeImage_GetTagType( FITAG* tag ); - DLL_API DWORD DLL_CALLCONV FreeImage_GetTagCount( FITAG* tag ); - DLL_API DWORD DLL_CALLCONV FreeImage_GetTagLength( FITAG* tag ); - DLL_API const void* DLL_CALLCONV FreeImage_GetTagValue( FITAG* tag ); - - DLL_API BOOL DLL_CALLCONV FreeImage_SetTagKey( FITAG* tag, const char* key ); - DLL_API BOOL DLL_CALLCONV FreeImage_SetTagDescription( FITAG* tag, const char* description ); - DLL_API BOOL DLL_CALLCONV FreeImage_SetTagID( FITAG* tag, WORD id ); - DLL_API BOOL DLL_CALLCONV FreeImage_SetTagType( FITAG* tag, FREE_IMAGE_MDTYPE type ); - DLL_API BOOL DLL_CALLCONV FreeImage_SetTagCount( FITAG* tag, DWORD count ); - DLL_API BOOL DLL_CALLCONV FreeImage_SetTagLength( FITAG* tag, DWORD length ); - DLL_API BOOL DLL_CALLCONV FreeImage_SetTagValue( FITAG* tag, const void* value ); - - // iterator - DLL_API FIMETADATA* DLL_CALLCONV FreeImage_FindFirstMetadata( FREE_IMAGE_MDMODEL model, FIBITMAP* dib, FITAG** tag ); - DLL_API BOOL DLL_CALLCONV FreeImage_FindNextMetadata( FIMETADATA* mdhandle, FITAG** tag ); - DLL_API void DLL_CALLCONV FreeImage_FindCloseMetadata( FIMETADATA* mdhandle ); - - // metadata setter and getter - DLL_API BOOL DLL_CALLCONV FreeImage_SetMetadata( FREE_IMAGE_MDMODEL model, FIBITMAP* dib, const char* key, FITAG* tag ); - DLL_API BOOL DLL_CALLCONV FreeImage_GetMetadata( FREE_IMAGE_MDMODEL model, FIBITMAP* dib, const char* key, FITAG** tag ); - - // helpers - DLL_API unsigned DLL_CALLCONV FreeImage_GetMetadataCount( FREE_IMAGE_MDMODEL model, FIBITMAP* dib ); - DLL_API BOOL DLL_CALLCONV FreeImage_CloneMetadata( FIBITMAP* dst, FIBITMAP* src ); - - // tag to C string conversion - DLL_API const char* DLL_CALLCONV FreeImage_TagToString( FREE_IMAGE_MDMODEL model, FITAG* tag, char* Make FI_DEFAULT( NULL ) ); - - // -------------------------------------------------------------------------- - // Image manipulation toolkit ----------------------------------------------- - // -------------------------------------------------------------------------- - - // rotation and flipping - /// @deprecated see FreeImage_Rotate - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_RotateClassic( FIBITMAP* dib, double angle ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_Rotate( FIBITMAP* dib, double angle, const void* bkcolor FI_DEFAULT( NULL ) ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_RotateEx( FIBITMAP* dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, BOOL use_mask ); - DLL_API BOOL DLL_CALLCONV FreeImage_FlipHorizontal( FIBITMAP* dib ); - DLL_API BOOL DLL_CALLCONV FreeImage_FlipVertical( FIBITMAP* dib ); - DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransform( const char* src_file, const char* dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect FI_DEFAULT( FALSE ) ); - DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformU( const wchar_t* src_file, const wchar_t* dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect FI_DEFAULT( FALSE ) ); - - // upsampling / downsampling - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_Rescale( FIBITMAP* dib, int dst_width, int dst_height, FREE_IMAGE_FILTER filter ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_MakeThumbnail( FIBITMAP* dib, int max_pixel_size, BOOL convert FI_DEFAULT( TRUE ) ); - - // color manipulation routines (point operations) - DLL_API BOOL DLL_CALLCONV FreeImage_AdjustCurve( FIBITMAP* dib, BYTE* LUT, FREE_IMAGE_COLOR_CHANNEL channel ); - DLL_API BOOL DLL_CALLCONV FreeImage_AdjustGamma( FIBITMAP* dib, double gamma ); - DLL_API BOOL DLL_CALLCONV FreeImage_AdjustBrightness( FIBITMAP* dib, double percentage ); - DLL_API BOOL DLL_CALLCONV FreeImage_AdjustContrast( FIBITMAP* dib, double percentage ); - DLL_API BOOL DLL_CALLCONV FreeImage_Invert( FIBITMAP* dib ); - DLL_API BOOL DLL_CALLCONV FreeImage_GetHistogram( FIBITMAP* dib, DWORD* histo, FREE_IMAGE_COLOR_CHANNEL channel FI_DEFAULT( FICC_BLACK ) ); - DLL_API int DLL_CALLCONV FreeImage_GetAdjustColorsLookupTable( BYTE* LUT, double brightness, double contrast, double gamma, BOOL invert ); - DLL_API BOOL DLL_CALLCONV FreeImage_AdjustColors( FIBITMAP* dib, double brightness, double contrast, double gamma, BOOL invert FI_DEFAULT( FALSE ) ); - DLL_API unsigned DLL_CALLCONV FreeImage_ApplyColorMapping( FIBITMAP* dib, RGBQUAD* srccolors, RGBQUAD* dstcolors, unsigned count, BOOL ignore_alpha, BOOL swap ); - DLL_API unsigned DLL_CALLCONV FreeImage_SwapColors( FIBITMAP* dib, RGBQUAD* color_a, RGBQUAD* color_b, BOOL ignore_alpha ); - DLL_API unsigned DLL_CALLCONV FreeImage_ApplyPaletteIndexMapping( FIBITMAP* dib, BYTE* srcindices, BYTE* dstindices, unsigned count, BOOL swap ); - DLL_API unsigned DLL_CALLCONV FreeImage_SwapPaletteIndices( FIBITMAP* dib, BYTE* index_a, BYTE* index_b ); - - // channel processing routines - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_GetChannel( FIBITMAP* dib, FREE_IMAGE_COLOR_CHANNEL channel ); - DLL_API BOOL DLL_CALLCONV FreeImage_SetChannel( FIBITMAP* dst, FIBITMAP* src, FREE_IMAGE_COLOR_CHANNEL channel ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_GetComplexChannel( FIBITMAP* src, FREE_IMAGE_COLOR_CHANNEL channel ); - DLL_API BOOL DLL_CALLCONV FreeImage_SetComplexChannel( FIBITMAP* dst, FIBITMAP* src, FREE_IMAGE_COLOR_CHANNEL channel ); - - // copy / paste / composite routines - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_Copy( FIBITMAP* dib, int left, int top, int right, int bottom ); - DLL_API BOOL DLL_CALLCONV FreeImage_Paste( FIBITMAP* dst, FIBITMAP* src, int left, int top, int alpha ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_Composite( FIBITMAP* fg, BOOL useFileBkg FI_DEFAULT( FALSE ), RGBQUAD* appBkColor FI_DEFAULT( NULL ), FIBITMAP* bg FI_DEFAULT( NULL ) ); - DLL_API BOOL DLL_CALLCONV FreeImage_JPEGCrop( const char* src_file, const char* dst_file, int left, int top, int right, int bottom ); - DLL_API BOOL DLL_CALLCONV FreeImage_JPEGCropU( const wchar_t* src_file, const wchar_t* dst_file, int left, int top, int right, int bottom ); - DLL_API BOOL DLL_CALLCONV FreeImage_PreMultiplyWithAlpha( FIBITMAP* dib ); - - // background filling routines - DLL_API BOOL DLL_CALLCONV FreeImage_FillBackground( FIBITMAP* dib, const void* color, int options FI_DEFAULT( 0 ) ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_EnlargeCanvas( FIBITMAP* src, int left, int top, int right, int bottom, const void* color, int options FI_DEFAULT( 0 ) ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_AllocateEx( int width, int height, int bpp, const RGBQUAD* color, int options FI_DEFAULT( 0 ), const RGBQUAD* palette FI_DEFAULT( NULL ), unsigned red_mask FI_DEFAULT( 0 ), unsigned green_mask FI_DEFAULT( 0 ), unsigned blue_mask FI_DEFAULT( 0 ) ); - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_AllocateExT( FREE_IMAGE_TYPE type, int width, int height, int bpp, const void* color, int options FI_DEFAULT( 0 ), const RGBQUAD* palette FI_DEFAULT( NULL ), unsigned red_mask FI_DEFAULT( 0 ), unsigned green_mask FI_DEFAULT( 0 ), unsigned blue_mask FI_DEFAULT( 0 ) ); - - // miscellaneous algorithms - DLL_API FIBITMAP* DLL_CALLCONV FreeImage_MultigridPoissonSolver( FIBITMAP* Laplacian, int ncycle FI_DEFAULT( 3 ) ); - - // restore the borland-specific enum size option -#if defined(__BORLANDC__) -#pragma option pop -#endif - -#ifdef __cplusplus -} -#endif - -#endif // FREEIMAGE_H diff --git a/plugins/GPS.h b/plugins/GPS.h index 144c528..38ca844 100644 --- a/plugins/GPS.h +++ b/plugins/GPS.h @@ -54,14 +54,6 @@ class GPSPlugin: public pubviz::Plugin } - void OnFollowChange(bool state) - { - if (!state) - { - GetCanvas()->ResetViewOrigin(); - } - } - void OnHistoryChange(int length) { if (messages_.size() > length) @@ -206,7 +198,7 @@ class GPSPlugin: public pubviz::Plugin { double x, y; - if (GetCanvas()->wgs84_mode_) + if (GetCanvas()->wgs84_mode()) { GetCanvas()->local_xy_.FromLatLon(p.latitude, p.longitude, x, y); } @@ -263,7 +255,7 @@ class GPSPlugin: public pubviz::Plugin { double x, y; - if (GetCanvas()->wgs84_mode_) + if (GetCanvas()->wgs84_mode()) { GetCanvas()->local_xy_.FromLatLon(p.latitude, p.longitude, x, y); //p.z = 0.0; diff --git a/plugins/Dial.h b/plugins/Gauge.h similarity index 96% rename from plugins/Dial.h rename to plugins/Gauge.h index b13c111..c9189a3 100644 --- a/plugins/Dial.h +++ b/plugins/Gauge.h @@ -29,7 +29,7 @@ #include -class DialPlugin: public pubviz::Plugin +class GaugePlugin: public pubviz::Plugin { FloatProperty* min_value_; FloatProperty* max_value_; @@ -59,19 +59,18 @@ class DialPlugin: public pubviz::Plugin struct ps_subscriber_options options; ps_subscriber_options_init(&options); options.preferred_transport = 1;// tcp yo - options.want_message_def = true; ps_node_create_subscriber_adv(GetNode(), current_topic_.c_str(), NULL, &subscriber_, &options); sub_open_ = true; } public: - DialPlugin() + GaugePlugin() { } - virtual ~DialPlugin() + virtual ~GaugePlugin() { if (sub_open_) { @@ -304,7 +303,7 @@ class DialPlugin: public pubviz::Plugin { // add any properties topic_ = AddTopicProperty(tree, "Topic", "/image", "", ""); - topic_->onChange = std::bind(&DialPlugin::Subscribe, this, std::placeholders::_1); + topic_->onChange = std::bind(&GaugePlugin::Subscribe, this, std::placeholders::_1); field_ = AddStringProperty(tree, "Field", "width"); @@ -320,10 +319,10 @@ class DialPlugin: public pubviz::Plugin std::string GetTitle() override { - return "Dial"; + return "Gauge"; } }; -REGISTER_PLUGIN("dial", DialPlugin) +REGISTER_PLUGIN("gauge", GaugePlugin) #endif diff --git a/plugins/Grid.h b/plugins/Grid.h index 04ccc95..dd0c791 100644 --- a/plugins/Grid.h +++ b/plugins/Grid.h @@ -94,8 +94,8 @@ class GridPlugin: public pubviz::Plugin virtual void Initialize(Gwen::Controls::Properties* tree) { // add any properties - x_count_ = AddNumberProperty(tree, "X Count", 10, 0, 100, 1, "Number of grid cells along X axis."); - y_count_ = AddNumberProperty(tree, "Y Count", 10, 0, 100, 1, "Number of grid cells along Y axis."); + x_count_ = AddNumberProperty(tree, "X Count", 10, 0, 1000, 1, "Number of grid cells along X axis."); + y_count_ = AddNumberProperty(tree, "Y Count", 10, 0, 1000, 1, "Number of grid cells along Y axis."); start_x_ = AddFloatProperty(tree, "Start X", -50.0, -100000, 100000, 1, "Left X position of grid."); start_y_ = AddFloatProperty(tree, "Start Y", -50.0, -100000, 100000, 1, "Bottom Y position of grid."); diff --git a/plugins/Image.h b/plugins/Image.h index 5bc30b1..8538356 100644 --- a/plugins/Image.h +++ b/plugins/Image.h @@ -14,7 +14,7 @@ #include #include -#include "FreeImage.h" +#include "../FreeImage.h" #define GLEW_STATIC #include @@ -46,26 +46,25 @@ class ImagePlugin: public pubviz::Plugin bool sub_open_ = false; ps_sub_t subscriber_; - pubsub::msg::Image last_msg_; + pubsub::msg::Image* last_msg_ = 0; unsigned int texture_ = -1;//okay, now show image in a new popout // returns false if the message is invalid - bool CheckSize(const pubsub::msg::Image& image, int bpp) + bool CheckSize(int bpp) { - int expected = image.height*image.width*bpp; + int expected = last_msg_->height*last_msg_->width*bpp; - if (image.data_length != expected) + if (last_msg_->data.size() != expected) { - printf("ERROR: Invalid image data length on topic '%s' got %i but expected %i\n", - topic_->GetValue().c_str(), image.data_length, expected); + printf("ERROR: Invalid image data length on topic '%s' got %i but expected %i bytes.\n", + topic_->GetValue().c_str(), last_msg_->data.size(), expected); // mark message as invalid - if (last_msg_.data) + if (last_msg_) { - free(last_msg_.data); - last_msg_.data = 0; - last_msg_.data_length = 0; + free(last_msg_); + last_msg_ = 0; } return false; } @@ -84,65 +83,65 @@ class ImagePlugin: public pubviz::Plugin // make the color buffer std::vector pixels; - pixels.resize(last_msg_.width*last_msg_.height); + pixels.resize(last_msg_->width*last_msg_->height); // now fill in each pixel GLenum texture_format = GL_RGBA; - if (last_msg_.type == pubsub::msg::Image::R8G8B8A8) + if (last_msg_->type == pubsub::msg::Image::R8G8B8A8) { - if (!CheckSize(last_msg_, 4)) { return; } - memcpy(pixels.data(), last_msg_.data, pixels.size()); + if (!CheckSize(4)) { return; } + memcpy(pixels.data(), last_msg_->data.data(), pixels.size()); } - else if (last_msg_.type == pubsub::msg::Image::R8G8B8) + else if (last_msg_->type == pubsub::msg::Image::R8G8B8) { - if (!CheckSize(last_msg_, 3)) { return; } + if (!CheckSize(3)) { return; } for (int i = 0; i < pixels.size(); i++) { uint8_t a = 255; - uint8_t pr = last_msg_.data[i * 3]; - uint8_t pg = last_msg_.data[i * 3 + 1]; - uint8_t pb = last_msg_.data[i * 3 + 2]; + uint8_t pr = last_msg_->data[i * 3]; + uint8_t pg = last_msg_->data[i * 3 + 1]; + uint8_t pb = last_msg_->data[i * 3 + 2]; pixels[i] = (a << 24) | (pb << 16) | (pg << 8) | pr; } } - else if (last_msg_.type == pubsub::msg::Image::R32) + else if (last_msg_->type == pubsub::msg::Image::R32) { - if (!CheckSize(last_msg_, 4)) { return; } - for (int i = 0; i < last_msg_.data_length; i++) + if (!CheckSize(4)) { return; } + for (int i = 0; i < last_msg_->data.size()/4; i++) { - uint8_t px = last_msg_.data[i*4 + 3];// just use the high byte + uint8_t px = last_msg_->data[i*4 + 3];// just use the high byte uint8_t a = 255; pixels[i] = (a << 24) | (px << 16) | (px << 8) | px; } } - else if (last_msg_.type == pubsub::msg::Image::R16) + else if (last_msg_->type == pubsub::msg::Image::R16) { - if (!CheckSize(last_msg_, 2)) { return; } - for (int i = 0; i < last_msg_.data_length; i++) + if (!CheckSize(2)) { return; } + for (int i = 0; i < last_msg_->data.size()/2; i++) { - uint8_t px = last_msg_.data[i*2 + 1];// just use the high byte + uint8_t px = last_msg_->data[i*2];// at the moment use the low byte uint8_t a = 255; pixels[i] = (a << 24) | (px << 16) | (px << 8) | px; } } - else if (last_msg_.type == pubsub::msg::Image::R8) + else if (last_msg_->type == pubsub::msg::Image::R8) { - if (!CheckSize(last_msg_, 1)) { return; } - for (int i = 0; i < last_msg_.data_length; i++) + if (!CheckSize(1)) { return; } + for (int i = 0; i < last_msg_->data.size(); i++) { - uint8_t px = last_msg_.data[i]; + uint8_t px = last_msg_->data[i]; uint8_t a = 255; pixels[i] = (a << 24) | (px << 16) | (px << 8) | px; } } - else if (last_msg_.type == pubsub::msg::Image::YUYV) + else if (last_msg_->type == pubsub::msg::Image::YUYV) { - if (!CheckSize(last_msg_, 2)) { return; } + if (!CheckSize(2)) { return; } for (int i = 1; i < pixels.size(); i++) { - auto y = last_msg_.data[i*2]; - auto u = last_msg_.data[i*2-1]; - auto v = last_msg_.data[i*2+1]; + auto y = last_msg_->data[i*2]; + auto u = last_msg_->data[i*2-1]; + auto v = last_msg_->data[i*2+1]; if ((i&0b1) == 0) std::swap(u,v); int8_t c = (int8_t) (y - 16); @@ -156,14 +155,14 @@ class ImagePlugin: public pubviz::Plugin pixels[i] = (a << 24) | (b << 16) | (g << 8) | r; } } - else if (last_msg_.type == pubsub::msg::Image::JPEG) + else if (last_msg_->type == pubsub::msg::Image::JPEG) { - FIMEMORY* mem = FreeImage_OpenMemory(last_msg_.data, last_msg_.data_length); + FIMEMORY* mem = FreeImage_OpenMemory(last_msg_->data.data(), last_msg_->data.size()); FIBITMAP* img = FreeImage_LoadFromMemory(FIF_JPEG, mem); FIBITMAP* bits32 = FreeImage_ConvertTo32Bits( img ); FreeImage_FlipVertical( bits32 ); // copy! - memcpy(pixels.data(), FreeImage_GetBits( bits32 ), 4*last_msg_.height*last_msg_.width); + memcpy(pixels.data(), FreeImage_GetBits( bits32 ), 4*last_msg_->height*last_msg_->width); FreeImage_Unload( bits32 ); FreeImage_Unload( img ); FreeImage_CloseMemory(mem); @@ -201,8 +200,8 @@ class ImagePlugin: public pubviz::Plugin GL_TEXTURE_2D, 0, GL_RGBA, - last_msg_.width, - last_msg_.height, + last_msg_->width, + last_msg_->height, 0, texture_format, GL_UNSIGNED_BYTE, @@ -213,8 +212,8 @@ class ImagePlugin: public pubviz::Plugin Gwen::Texture tex; - tex.width = last_msg_.width; - tex.height = last_msg_.height; + tex.width = last_msg_->width; + tex.height = last_msg_->height; tex.data = (void*)&texture_; image_panel_->SetTexture(tex); } @@ -242,10 +241,7 @@ class ImagePlugin: public pubviz::Plugin ImagePlugin() { - // just make a quick test costmap - last_msg_.width = 0; - last_msg_.height = 0; - last_msg_.data_length = 0; + // dont use pubsub here } virtual ~ImagePlugin() @@ -253,6 +249,11 @@ class ImagePlugin: public pubviz::Plugin Gwen::Texture tex; image_panel_->SetTexture(tex); page_->Close(); + + if (last_msg_) + { + delete last_msg_; + } if (sub_open_) { @@ -272,9 +273,8 @@ class ImagePlugin: public pubviz::Plugin if (texture_ != -1) { glDeleteTextures(1, &texture_); - last_msg_.data_length = 0; - free(last_msg_.data); - last_msg_.data = 0; + delete last_msg_; + last_msg_ = 0; texture_ = -1; } } @@ -290,15 +290,16 @@ class ImagePlugin: public pubviz::Plugin { if (Paused()) { - free(data->data); - free(data);//todo use allocator free + delete data; continue; } // user is responsible for freeing the message and its arrays - last_msg_ = *data; - free(data->data); - free(data);//todo use allocator free + if (last_msg_) + { + delete last_msg_; + } + last_msg_ = data; UpdateFromMessage(); } } diff --git a/plugins/Map.h b/plugins/Map.h new file mode 100644 index 0000000..cff0c96 --- /dev/null +++ b/plugins/Map.h @@ -0,0 +1,808 @@ + +#ifndef PUBVIZ_PLUGIN_MAP_H +#define PUBVIZ_PLUGIN_MAP_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GLEW_STATIC +#include + +#ifndef _WIN32 +#include +#include +#include +#include +#endif + +#include "../Plugin.h" +#include "../properties.h" + +#include +#include +#include +#include + +struct Coord +{ + int x, y, z; + + bool operator <(const Coord& o) const + { + return key() < o.key(); + } + + int64_t key() const + { + return z*100000000000000 + x + y*10000000; + } +}; + + +#include +#include +#include /* getprotobyname */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +std::string http_request(std::string hostname, int server_port, std::string url) { + char buffer[BUFSIZ]; + enum CONSTEXPR { MAX_REQUEST_LEN = 1024}; + char request[MAX_REQUEST_LEN]; + char request_template[] = "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: pubviz\r\nConnection: close\r\n\r\n"; + struct protoent *protoent; + int request_len; + int socket_file_descriptor; + ssize_t nbytes_total, nbytes_last; + struct sockaddr_in sockaddr_in; + + request_len = snprintf(request, MAX_REQUEST_LEN, request_template, url.c_str(), hostname.c_str()); + if (request_len >= MAX_REQUEST_LEN) { + fprintf(stderr, "request length large: %d\n", request_len); + return "";// fail + } + + /* Build the socket. */ + protoent = getprotobyname("tcp"); + if (protoent == NULL) { + perror("getprotobyname"); + //exit(EXIT_FAILURE); + return "";// fail + } + + /* Build the address in sockaddr_in + * Possibly does a DNS query to get the IP from a hostanme. */ + { + struct hostent *hostent = gethostbyname(hostname.c_str()); + if (hostent == NULL) { + fprintf(stderr, "error: gethostbyname(\"%s\")\n", hostname.c_str()); + //exit(EXIT_FAILURE); + return "";// fail + } + in_addr_t in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list))); + if (in_addr == (in_addr_t)-1) { + fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list)); + //exit(EXIT_FAILURE); + return "";// fail + } + sockaddr_in.sin_addr.s_addr = in_addr; + sockaddr_in.sin_family = AF_INET; + sockaddr_in.sin_port = htons(server_port); + //fprintf(stderr, "debug: IP: %s\n", inet_ntoa(sockaddr_in.sin_addr)); + } + + socket_file_descriptor = socket(AF_INET, SOCK_STREAM, protoent->p_proto); + if (socket_file_descriptor == -1) { + perror("socket"); + //exit(EXIT_FAILURE); + return "";// fail + } + + /* Actually connect. */ + if (connect(socket_file_descriptor, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) { + perror("connect"); + //exit(EXIT_FAILURE); + close(socket_file_descriptor); + return "";// fail + } + + /* Send HTTP request. */ + nbytes_total = 0; + while (nbytes_total < request_len) { + nbytes_last = write(socket_file_descriptor, request + nbytes_total, request_len - nbytes_total); + if (nbytes_last == -1) { + perror("write"); + close(socket_file_descriptor); + //exit(EXIT_FAILURE); + return "";// fail + } + nbytes_total += nbytes_last; + } + + /* Read the response. */ + //fprintf(stderr, "debug: before first read\n"); + std::string output; + while ((nbytes_total = read(socket_file_descriptor, buffer, BUFSIZ)) > 0) { + //fprintf(stderr, "debug: after a read\n"); + for (int i = 0; i < nbytes_total; i++) + { + output += buffer[i]; + } + //write(STDOUT_FILENO, buffer, nbytes_total); + } + //fprintf(stderr, "debug: after last read\n"); + if (nbytes_total == -1) { + perror("read"); + close(socket_file_descriptor); + //exit(EXIT_FAILURE); + return "";// fail + } + + close(socket_file_descriptor); + + auto start = output.find("\r\n\r\n"); + return output.substr(start+4); +} + +struct Result +{ + Coord coord; + int texture; +}; + +struct ImageCache +{ + std::thread request_thread; + std::mutex map_mutex; + std::mutex queue_mutex; + std::deque request_queue; + struct CacheItem + { + int texture; + pubsub::Time last_time_used; + }; + std::map textures;// todo limit size + + struct Incoming + { + Coord coord; + char* data; + int width, height; + GLenum format; + }; + std::deque texture_queue; + + bool run_thread_ = true; + void StartThread() + { + request_thread = std::thread([&](){ + // create cache directory if it doesnt exist + mkdir(".tile_cache", 0700); + while (ps_okay() && run_thread_) + { + queue_mutex.unlock(); + if (request_queue.size() == 0) + { + queue_mutex.unlock(); + ps_sleep(10); + continue; + } + + auto coord = request_queue.front(); + request_queue.pop_front(); + queue_mutex.unlock(); + + // Load whatever we got from the filesystem first + if (LoadClosestCached(coord)) + { + //printf("Not loading from server since cache hit.\n"); + continue; + } + + // then load from the server + char url[500]; + sprintf(url, "/%i/%i/%i.png", coord.z, coord.x, coord.y); + std::string res = http_request("tile.openstreetmap.org", 80, url); + //printf("Got %i bytes\n", res.size()); + if (res.size() == 0) + { + printf("Request failed.\n"); + continue; + } + + auto i = LoadPNG(res.data(), res.size()); + i.coord = coord; + queue_mutex.lock(); + texture_queue.push_back(i); + queue_mutex.unlock(); + + // save to cache + char buf[500]; + sprintf(buf, ".tile_cache/%i_%i_%i.png", coord.z, coord.x, coord.y); + FILE* f = fopen(buf, "wb"); + fwrite(res.data(), res.size(), 1, f); + fclose(f); + } + }); + } + + ~ImageCache() + { + for (auto& i: textures) + { + if (i.second.texture >= 0) + { + GLuint tex = i.second.texture; + glDeleteTextures(1, &tex); + } + } + run_thread_ = false; + if (request_thread.joinable()) + { + request_thread.join(); + } + } + + // returns a coord and texture + Result GetTile(int x, int y, int zoom) + { + Coord coord = {x,y,zoom}; + map_mutex.lock(); + + // request the tile and insert if we havent already + if (textures.find(coord) == textures.end()) + { + // request this texture and mark it as requested + CacheItem item; + item.texture = -1; + item.last_time_used = pubsub::Time(0);// todo + textures[coord] = item; + + queue_mutex.lock(); + request_queue.push_front(coord); + queue_mutex.unlock(); + } + + // try and find a close enough texture + Coord ncoord = coord; + int range = 4; + for (int z = zoom; z >= std::max(zoom-range, 0); z--) + { + auto r = textures.find(ncoord); + if (r != textures.end() && r->second.texture >= 0) + { + map_mutex.unlock(); + return {ncoord, r->second.texture}; + } + ncoord.x /= 2; + ncoord.y /= 2; + ncoord.z -= 1; + } + map_mutex.unlock(); + return {coord, -1}; + } + + bool Process() + { + // turn any queued memories into textures and add them + bool loaded = false; + while (true) + { + queue_mutex.lock(); + if (texture_queue.size() == 0) + { + queue_mutex.unlock(); + return loaded; + } + auto item = texture_queue.front(); + texture_queue.pop_front(); + queue_mutex.unlock(); + + // create the texture + int texture = CreateTexture(item.data, 256, 256, item.format); + + //printf("Created texture for %i %i %i\n", item.coord.z, item.coord.x, item.coord.y); + map_mutex.lock(); + textures[item.coord] = {texture, pubsub::Time(0)}; + map_mutex.unlock(); + delete[] item.data; + loaded = true; + } + return loaded;// should never get executed + } + +private: + // returns true if we had the requested tile cached + bool LoadClosestCached(Coord c) + { + // fill in the texture for the closest one to requested if it isnt already + auto oc = c; + int range = 4;// try down this many levels + for (int z = oc.z; z >= std::max(oc.z-range, 0); z--) + { + map_mutex.lock(); + auto r = textures.find(c); + Incoming tex; + tex.data = 0; + if (r == textures.end() || r->second.texture < 0) + { + map_mutex.unlock(); + tex = TryCached(c.x, c.y, z); + } + else + { + map_mutex.unlock(); + } + if (tex.data != 0) + { + // add to queue! we got something close enough + queue_mutex.lock(); + texture_queue.push_back(tex); + queue_mutex.unlock(); + return oc.z == c.z; + } + //printf("http://tile.openstreetmap.org/%i/%i/%i.png missing from cache\n", c.z, c.x, c.y); + c.x /= 2; + c.y /= 2; + c.z -= 1; + } + //printf("No usable cached tile found for %i %i %i\n", oc.z, oc.x, oc.y); + return false; + } + + Incoming TryCached(int x, int y, int z) + { + char buf[500]; + sprintf(buf, ".tile_cache/%i_%i_%i.png", z, x, y); + FILE* f = fopen(buf, "rb"); + if (f) + { + //printf("Loaded %i %i %i from file.\n", z, x, y); + fseek(f, 0, SEEK_END); + auto fsize = ftell(f); + rewind(f); + + auto fcontent = (char*) malloc(sizeof(char) * fsize); + fread(fcontent, 1, fsize, f); + + auto i = LoadPNG(fcontent, fsize); + i.coord.x = x; + i.coord.y = y; + i.coord.z = z; + free(fcontent); + fclose(f); + + return i; + } + else + { + return {{0,0,0}, 0, 0}; + } + } + + static int CreateTexture(char* data, int width, int height, GLenum texture_format) + { + GLuint texture = 0; + glGenTextures(1, &texture); + //printf("Texture: %i\n", texture); + + glBindTexture(GL_TEXTURE_2D, texture); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA, + width, + height, + 0, + texture_format, + GL_UNSIGNED_BYTE, + data); + + glBindTexture(GL_TEXTURE_2D, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + return texture; + } + + Incoming LoadPNG(char* data, int size) + { + Incoming i; + i.width = 256; + i.height = 256; + i.data = new char[i.width*i.height*4]; +#ifdef FREEIMAGE_BIGENDIAN + i.format = GL_RGBA; +#else + i.format = GL_BGRA; +#endif + // todo get width from file rather than assume + FIMEMORY* mem = FreeImage_OpenMemory((unsigned char*)data, size); + FIBITMAP* img = FreeImage_LoadFromMemory(FIF_PNG, mem); + FIBITMAP* bits32 = FreeImage_ConvertTo32Bits( img ); + FreeImage_FlipVertical( bits32 ); + // copy + memcpy(i.data, FreeImage_GetBits( bits32 ), 4*i.height*i.width); + FreeImage_Unload( bits32 ); + FreeImage_Unload( img ); + FreeImage_CloseMemory(mem); + + return i; + } +}; + + + +class MapPlugin: public pubviz::Plugin +{ + std::unique_ptr alpha_; + std::unique_ptr max_zoom_, default_zoom_, default_tiles_; + std::unique_ptr show_outline_; + + ImageCache cache_; + +public: + + MapPlugin() + { + // dont use pubsub here + cache_.StartThread(); + } + + virtual ~MapPlugin() + { + + } + + // Clear out any historical data so the view gets cleared + virtual void Clear() + { + + } + + virtual void Update() + { + // redraw if we loaded a texture + if (cache_.Process()) + { + Redraw(); + } + } + + static void get_tile_at(double lat, double lon, int zoom, int&x, int&y) + { + double lat_rad = lat*M_PI/180.0; + double n = pow(2.0, zoom); + x = int((lon + 180.0)/360.0*n); + y = int((1.0 - log(tan(lat_rad) + (1/cos(lat_rad)))/M_PI)/2.0*n); + } + + static void get_tile_ll(int x, int y, int zoom, double&lat, double&lon) + { + double n = pow(2.0, zoom); + lon = x/n*360.0 - 180.0; + double lat_rad = atan(sinh(M_PI*(1.0-2.0*y/n))); + lat = lat_rad*180.0/M_PI; + } + + double pixels_per_meter(double lat, int zoom) + { + double tile_width = 40075016.686*cos(lat*M_PI/180.0)/pow(2.0, zoom); + return 256.0/tile_width; + } + + virtual void Paint() + { + // Draw license attribution + auto canvas = GetCanvas(); + auto font = canvas->GetSkin()->GetDefaultFont(); + + auto r = canvas->GetSkin()->GetRender(); + + std::string string = "OpenStreetMap"; + auto res = r->MeasureText(font, string); + r->SetDrawColor( Gwen::Color(255,255,255,255) ); + r->DrawFilledRect(Gwen::Rect(canvas->Width()-res.x, canvas->Height()-res.y, res.x, res.y)); + r->SetDrawColor( Gwen::Color(0,0,0,255) ); + r->RenderText(font, Gwen::PointF(canvas->Width()-res.x, canvas->Height()-res.y ), string); + } + + virtual void Render() + { + auto canvas = GetCanvas(); + if (!canvas->local_xy_.Initialized()) + { + return;// we need an origin + } + + int zoom = default_zoom_->GetValue(); + const int max_zoom = max_zoom_->GetValue(); + zoom = std::min(max_zoom, zoom); + + bool show_outline = show_outline_->GetValue(); + + double ax, ay, az; + canvas->GetViewCenter(ax, ay, az); + + auto xy = &canvas->local_xy_; + int xmin, xmax, ymin, ymax; + if (canvas->GetViewType() == ViewType::TopDown) + { + // okay, lets get each corner of the view in current frame coordinates + double w2 = canvas->view_width()/2.0; + double h2 = canvas->view_height()/2.0; + Vec3d corners[4]; + corners[0] = Vec3d(ax-w2, ay-h2, 0); + corners[1] = Vec3d(ax-w2, ay+h2, 0); + corners[2] = Vec3d(ax+w2, ay+h2, 0); + corners[3] = Vec3d(ax+w2, ay-h2, 0); + + // then loop through and get wgs84 min and max for each corner + double minlat, minlon; + double maxlat, maxlon; + for (int i = 0; i < 4; i++) + { + // transform to map if necessary + auto src_frame = canvas->wgs84_mode() ? OpenGLCanvas::Map : OpenGLCanvas::Odom; + canvas->TransformToFrame(src_frame, OpenGLCanvas::WGS84, corners[i]); + + double lat = corners[i].x; + double lon = corners[i].y; + if (i == 0) + { + minlat = maxlat = lat; + minlon = maxlon = lon; + } + else + { + minlon = std::min(minlon, lon); + minlat = std::min(minlat, lat); + maxlon = std::max(maxlon, lon); + maxlat = std::max(maxlat, lat); + } + } + + // determine best zoom level to use + // find first higher resolution tile + double view_ppm = canvas->Width()*canvas->GetCanvas()->Scale()/canvas->view_width(); + zoom = max_zoom; + for (int i = 1; i <= max_zoom; i++) + { + double zoom_ppm = pixels_per_meter(minlat, i); + //printf("ppm %i %f\n", i, zoom_ppm); + if (zoom_ppm > view_ppm) + { + zoom = std::max(1, i-1);// todo, maybe dont do this, but it makes things faster + break; + } + } + + double zoom_ppm = pixels_per_meter(minlat, zoom); + + // Finally convert to tile coords + get_tile_at(minlat, minlon, zoom, xmin, ymin); + get_tile_at(maxlat, maxlon, zoom, xmax, ymax); + + //printf("Lat: %f to %f\nLon: %f to %f\n", minlat, maxlat, minlon, maxlon); + //printf("Zoom: %i TilePPM: %f ViewPPM: %f\n", zoom, zoom_ppm, view_ppm); + } + else + { + //set to center + default_zoom with default_tiles on each side + zoom = default_zoom_->GetValue(); + + double lat, lon; + xy->ToLatLon(ax, ay, lat, lon); + + int x,y; + get_tile_at(lat, lon, zoom, x, y); + + int tiles = default_tiles_->GetValue(); + xmin = std::max(0, x - tiles); + ymin = std::max(0, y - tiles); + int maxn = pow(2, zoom) - 1; + xmax = std::min(maxn, x + tiles); + ymax = std::min(maxn, y + tiles); + + // to do this i would need to intersect frustum with plane to get a shape + // then would need to estimate a zoom and intersecting chunks + } + + if (xmax < xmin) std::swap(xmin, xmax); + if (ymax < ymin) std::swap(ymin, ymax); + + //printf("X: %i to %i\nY: %i to %i\n", xmin, xmax, ymin, ymax); + + // todo this probably craps itself at map edge + // First get a list of all tiles to render in the area + std::map to_render;// todo try something with fewer allocations? + for (int x = xmin; x <= xmax; x++) + { + for (int y = ymin; y <= ymax; y++) + { + // todo need to wrap coords around edges properly + //printf("Tile: http://tile.openstreetmap.org/%i/%i/%i.png\n", zoom, x, y); + + auto tile = cache_.GetTile(x, y, zoom); + if (tile.texture >= 0) + { + to_render[tile.coord] = tile.texture; + } + + if (show_outline) + { + glLineWidth(4.0f); + glBegin(GL_LINE_STRIP); + + glColor3f(1.0, 1.0, 1.0); + + double lat, lon; + Vec3d p; + get_tile_ll(x, y, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + get_tile_ll(x+1, y, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + get_tile_ll(x+1, y+1, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + get_tile_ll(x, y+1, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + get_tile_ll(x, y, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + + glEnd(); + } + } + } + + // this is kinda silly, but works well + // Then sort the tiles by zoom level and render + for (auto tile: to_render) + { + int x = tile.first.x; + int y = tile.first.y; + int zoom = tile.first.z; + int texture = tile.second; + double lat, lon; + double px, py; + glEnable(GL_BLEND); + + //printf("RTexture: %i\n", texture); + + // Now draw the costmap itself + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + glBegin(GL_TRIANGLES); + + glColor4f(1.0f, 1.0f, 1.0f, alpha_->GetValue() ); + + Vec3d p; + glTexCoord2d(0, 0); + get_tile_ll(x, y, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + glTexCoord2d(1.0, 0); + get_tile_ll(x+1, y, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + glTexCoord2d(1.0, 1.0); + get_tile_ll(x+1, y+1, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + + glTexCoord2d(0, 0); + get_tile_ll(x, y, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + glTexCoord2d(1.0, 1.0); + get_tile_ll(x+1, y+1, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + glTexCoord2d(0, 1.0); + get_tile_ll(x, y+1, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + glDisable(GL_BLEND); + + if (show_outline) + { + glLineWidth(4.0f); + glBegin(GL_LINE_STRIP); + + glColor3f(1.0, 0.0, 0.0); + + double lat, lon; + get_tile_ll(x, y, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + get_tile_ll(x+1, y, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + get_tile_ll(x+1, y+1, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + get_tile_ll(x, y+1, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + get_tile_ll(x, y, zoom, lat, lon); + p = Vec3d(lat, lon, 0); + canvas->TransformToView(OpenGLCanvas::WGS84, p); + glVertex2f(p.x, p.y); + + glEnd(); + } + } + } + + virtual void Initialize(Gwen::Controls::Properties* tree) + { + // add any properties + alpha_.reset(AddFloatProperty(tree, "Alpha", 1.0, 0.0, 1.0, 0.1, "Tile transparency.")); + + show_outline_.reset(AddBooleanProperty(tree, "Show Outline", false, "If true, draw outlines around the tiles.")); + + max_zoom_.reset(AddNumberProperty(tree, "Max Zoom", 19, 1, 22, 1, "Max zoom level to download/show.")); + + default_zoom_.reset(AddNumberProperty(tree, "3D Zoom Level", 17, 1, 22, 1, "Zoom level to use in views other than top down.")); + + default_tiles_.reset(AddNumberProperty(tree, "3D Tiles", 2, 0, 5, 1, "Number of tiles to show around center tile in views other than top down.")); + } + + std::string GetTitle() override + { + return "Map"; + } +}; + +REGISTER_PLUGIN("map", MapPlugin) + +#endif diff --git a/plugins/Marker.h b/plugins/Marker.h index a008cb1..fb01883 100644 --- a/plugins/Marker.h +++ b/plugins/Marker.h @@ -92,15 +92,13 @@ class MarkerPlugin: public pubviz::Plugin { if (Paused()) { - free(data->data); - free(data);//todo use allocator free + delete data; continue; } // user is responsible for freeing the message and its arrays markers_[data->id] = *data; - free(data->data); - free(data);//todo use allocator free + delete data; Redraw(); } @@ -126,14 +124,20 @@ class MarkerPlugin: public pubviz::Plugin // draw the marker Gwen::Color color = color_->GetValue(); glLineWidth(line_width_->GetValue()); + auto frame = OpenGLCanvas::WGS84; + if (last_msg_.frame != pubsub::msg::Marker::FRAME_WGS84) + { + frame = OpenGLCanvas::Odom; + } + auto canvas = GetCanvas(); if (last_msg_.marker_type == pubsub::msg::Marker::LINE_LIST_2D) { // 2d lines glBegin(GL_LINES); - for (int i = 0; i + 1 < last_msg_.data_length; i += 2) + for (int i = 0; i + 1 < last_msg_.data.size(); i += 2) { int ci = i / 2; - if (ci < last_msg_.colors_length) + if (ci < last_msg_.colors.size()) { uint32_t c = last_msg_.colors[ci]; uint8_t r = (c & 0xFF0000) >> 16; @@ -145,30 +149,10 @@ class MarkerPlugin: public pubviz::Plugin { glColor3f(color.r / 255.0, color.g / 255.0, color.b / 255.0); } - if (last_msg_.frame == pubsub::msg::Marker::FRAME_WGS84) - { - if (GetCanvas()->wgs84_mode_) - { - double x, y; - GetCanvas()->local_xy_.FromLatLon(last_msg_.data[i], last_msg_.data[i + 1], x, y); - glVertex2f(x, y); - } - else - { - // todo - } - } - else - { - if (GetCanvas()->wgs84_mode_) - { - // todo - } - else - { - glVertex2f(last_msg_.data[i], last_msg_.data[i + 1]); - } - } + + Vec3d pos(last_msg_.data[i], last_msg_.data[i+1], 0); + canvas->TransformToView(frame, pos); + glVertex2f(pos.x, pos.y); } glEnd(); } @@ -176,7 +160,8 @@ class MarkerPlugin: public pubviz::Plugin { // 2d line segments int i = 0; - while (i < last_msg_.data_length) + int ci = 0; + while (i < last_msg_.data.size()) { int count = last_msg_.data[i]; int end_index = i + count*2; @@ -184,10 +169,9 @@ class MarkerPlugin: public pubviz::Plugin // draw a line segment glBegin(GL_LINE_STRIP); glColor3f(color.r/255.0, color.g/255.0, color.b/255.0); - for (; i < std::min(end_index, last_msg_.data_length-1); i += 2) + for (; i < std::min(end_index, last_msg_.data.size()-1); i += 2) { - int ci = i / 2; - if (ci < last_msg_.colors_length) + if (ci < last_msg_.colors.size()) { uint32_t c = last_msg_.colors[ci]; uint8_t r = (c & 0xFF0000) >> 16; @@ -195,41 +179,19 @@ class MarkerPlugin: public pubviz::Plugin uint8_t b = (c & 0xFF); glColor3f(r / 255.0, g / 255.0, b / 255.0); } - if (last_msg_.frame == pubsub::msg::Marker::FRAME_WGS84) - { - if (GetCanvas()->wgs84_mode_) - { - double x, y; - GetCanvas()->local_xy_.FromLatLon(last_msg_.data[i], last_msg_.data[i + 1], x, y); - //x += GetCanvas()->origin_x_; - //y += GetCanvas()->origin_y_; - glVertex2f(x, y); - } - else - { - // todo - } - } - else - { - if (GetCanvas()->wgs84_mode_) - { - // todo - } - else - { - glVertex2f(last_msg_.data[i], last_msg_.data[i + 1]); - } - } + Vec3d pos(last_msg_.data[i], last_msg_.data[i+1], 0); + canvas->TransformToView(frame, pos); + glVertex2f(pos.x, pos.y); } glEnd(); + ci++; } } else if (last_msg_.marker_type == pubsub::msg::Marker::POLYGON_2D) { // 2d polygons (just draw outline atm) int i = 0; - while (i < last_msg_.data_length) + while (i < last_msg_.data.size()) { int count = last_msg_.data[i]; int start_index = i; @@ -238,11 +200,46 @@ class MarkerPlugin: public pubviz::Plugin // draw a line segment glBegin(GL_LINE_STRIP); glColor3f(color.r/255.0, color.g/255.0, color.b/255.0); - for (; i < std::min(end_index, last_msg_.data_length-1); i += 2) + for (; i < std::min(end_index, last_msg_.data.size()-1); i += 2) + { + Vec3d pos(last_msg_.data[i], last_msg_.data[i+1], 0); + canvas->TransformToView(frame, pos); + glVertex2f(pos.x, pos.y); + } + Vec3d pos(last_msg_.data[start_index], last_msg_.data[start_index+1], 0); + canvas->TransformToView(frame, pos); + glVertex2f(pos.x, pos.y); + glEnd(); + } + } + else if (last_msg_.marker_type == pubsub::msg::Marker::POINT_LIST_3D) + { + // 3d points with a radius in pixels? maybe negative can be pixels, positive in meters? + for (int i = 0; i + 3 < last_msg_.data.size(); i += 4) + { + const double x = last_msg_.data[i]; + const double y = last_msg_.data[i+1]; + const double z = last_msg_.data[i+2]; + const double size = last_msg_.data[i+3]; + + glPointSize(size); + glBegin(GL_POINTS); + int ci = i / 4; + if (ci < last_msg_.colors.size()) + { + uint32_t c = last_msg_.colors[ci]; + uint8_t r = (c & 0xFF0000) >> 16; + uint8_t g = (c & 0xFF00) >> 8; + uint8_t b = (c & 0xFF); + glColor3f(r / 255.0, g / 255.0, b / 255.0); + } + else { - glVertex2f(last_msg_.data[i], last_msg_.data[i+1]); + glColor3f(color.r/255.0, color.g/255.0, color.b/255.0); } - glVertex2f(last_msg_.data[start_index], last_msg_.data[start_index+1]); + Vec3d pos(x, y, z); + canvas->TransformToView(frame, pos); + glVertex3f(pos.x, pos.y, pos.z); glEnd(); } } diff --git a/plugins/Measure.h b/plugins/Measure.h new file mode 100644 index 0000000..7f6e893 --- /dev/null +++ b/plugins/Measure.h @@ -0,0 +1,200 @@ + +#ifndef PUBVIZ_PLUGIN_MEASURE_H +#define PUBVIZ_PLUGIN_MEASURE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define GLEW_STATIC +#include + +#ifndef _WIN32 +#include +#include +#include +#include +#endif + +#include "../Plugin.h" +#include "../properties.h" + +#include + +class MeasurePlugin : public pubviz::Plugin +{ + FloatProperty* alpha_; + ColorProperty* color_; + NumberProperty* line_width_; + NumberProperty* point_size_; + + ButtonProperty* clear_; + +public: + + MeasurePlugin() + { + // dont use pubsub here + } + + virtual ~MeasurePlugin() + { + delete color_; + delete alpha_; + delete line_width_; + //delete show_points_; + delete point_size_; + delete clear_; + } + + // Clear out any historical data so the view gets cleared + virtual void Clear() + { + points_.clear(); + UpdatePoints(); + } + + virtual void Update() + { + + } + + struct Vertex + { + double x; + double y; + double z; + + Vertex() {} + Vertex(double x, double y, double z) + { + this->x = x; + this->y = y; + this->z = z; + } + }; + + std::vector points_; + std::vector transformed_pts_; + + virtual void Paint() + { + // draw labels between each point + auto r = GetCanvas()->GetSkin()->GetRender(); + + for (int i = 0; i < ((int)transformed_pts_.size())-1; i++) + { + auto start = transformed_pts_[i]; + auto end = transformed_pts_[i+1]; + + Vertex middle; + middle.x = (start.x + end.x)*0.5; + middle.y = (start.y + end.y)*0.5; + middle.z = (start.z + end.z)*0.5; + + int x, y; + GetCanvas()->WorldToPixel(middle.x, middle.y, middle.z, x, y); + + double distance = std::sqrt(std::pow(start.x - end.x, 2.0) + + std::pow(start.y - end.y, 2.0) + + std::pow(start.z - end.z, 2.0)); + r->SetDrawColor( Gwen::Color(255,255,255,255) ); + char buf[50]; + sprintf(buf, "%g", distance); + r->RenderText(GetCanvas()->GetSkin()->GetDefaultFont(), Gwen::PointF( x, y ), (std::string)buf); + } + } + + void UpdatePoints() + { + // Now transform the points + transformed_pts_.clear(); + transformed_pts_.reserve(points_.size()); + for (auto& pt : points_) + { + + } + Redraw(); + } + + virtual void Render() + { + if (points_.size() == 0) + { + return; + } + + Gwen::Color color = color_->GetValue(); + + //UpdatePoints(); + + // Now render the points + glLineWidth(line_width_->GetValue()); + glBegin(GL_LINE_STRIP); + glColor3f(color.r / 255.0, color.g / 255.0, color.b / 255.0); + for (const auto& vert : transformed_pts_) + { + glVertex3f(vert.x, vert.y, vert.z); + } + glEnd(); + + if (true)//show_points_->GetValue()) + { + glPointSize(point_size_->GetValue()); + glBegin(GL_POINTS); + glColor3f(color.r / 255.0, color.g / 255.0, color.b / 255.0); + for (const auto& vert : transformed_pts_) + { + glVertex3f(vert.x, vert.y, vert.z); + } + glEnd(); + } + } + + virtual void Initialize(Gwen::Controls::Properties* tree) + { + // add any properties + alpha_ = AddFloatProperty(tree, "Alpha", 1.0, 0.0, 1.0, 0.1); + + color_ = AddColorProperty(tree, "Color", Gwen::Color(255, 50, 50)); + + line_width_ = AddNumberProperty(tree, "Line Width", 4, 1, 100, 2); + + point_size_ = AddNumberProperty(tree, "Point Size", 6, 1, 100, 2); + + clear_ = AddButtonProperty(tree, "Clear"); + clear_->onChange = [this]() + { + points_.clear(); + UpdatePoints(); + }; + } + + std::string GetTitle() override + { + return "Measure"; + } + + // Applies only for 2d + virtual bool OnMapClick(double x, double y) + { + points_.push_back(Vertex(x,y,0)); + + UpdatePoints(); + return true; +//okay, need to get the frame for this, then add to list on tap + } +}; + +REGISTER_PLUGIN("measure", MeasurePlugin) + +#endif diff --git a/plugins/Path.h b/plugins/Path.h index 887d40c..63421f5 100644 --- a/plugins/Path.h +++ b/plugins/Path.h @@ -43,7 +43,7 @@ class PathPlugin : public pubviz::Plugin bool sub_open_ = false; ps_sub_t subscriber_; - pubsub::msg::Path last_msg_; + pubsub::msg::Path* last_msg_ = 0; std::string current_topic_; void Subscribe(std::string str) @@ -78,6 +78,11 @@ class PathPlugin : public pubviz::Plugin delete show_points_; delete point_size_; + if (last_msg_) + { + delete last_msg_; + } + if (sub_open_) { ps_sub_destroy(&subscriber_); @@ -88,7 +93,9 @@ class PathPlugin : public pubviz::Plugin // Clear out any historical data so the view gets cleared virtual void Clear() { - last_msg_.points_length = 0; + if (last_msg_) + delete last_msg_; + last_msg_ = 0; } virtual void Update() @@ -102,15 +109,16 @@ class PathPlugin : public pubviz::Plugin { if (Paused()) { - free(data->points); - free(data);//todo use allocator free + delete data; continue; } // user is responsible for freeing the message and its arrays - last_msg_ = *data; - free(data->points); - free(data);//todo use allocator free + if (last_msg_) + { + delete last_msg_; + } + last_msg_ = data; Redraw(); } @@ -135,7 +143,7 @@ class PathPlugin : public pubviz::Plugin std::vector transformed_pts_; virtual void Render() { - if (last_msg_.points_length == 0) + if (last_msg_ == 0) { return; } @@ -143,32 +151,32 @@ class PathPlugin : public pubviz::Plugin Gwen::Color color = color_->GetValue(); points_.clear(); - if (last_msg_.path_type == pubsub::msg::Path::PATH_XY) + if (last_msg_->path_type == pubsub::msg::Path::PATH_XY) { - for (int i = 0; i < (int)last_msg_.points_length - 1; i += 2) + for (int i = 0; i < (int)last_msg_->points.size() - 1; i += 2) { - points_.push_back({ last_msg_.points[i], last_msg_.points[i + 1], 0.0 }); + points_.push_back({ last_msg_->points[i], last_msg_->points[i + 1], 0.0 }); } } - if (last_msg_.path_type == pubsub::msg::Path::PATH_XY_Y) + else if (last_msg_->path_type == pubsub::msg::Path::PATH_XY_Y) { - for (int i = 0; i < (int)last_msg_.points_length - 2; i += 3) + for (int i = 0; i < (int)last_msg_->points.size() - 2; i += 3) { - points_.push_back({ last_msg_.points[i], last_msg_.points[i + 1], 0.0 }); + points_.push_back({ last_msg_->points[i], last_msg_->points[i + 1], 0.0 }); } } - else if (last_msg_.path_type == pubsub::msg::Path::PATH_XYZ) + else if (last_msg_->path_type == pubsub::msg::Path::PATH_XYZ) { - for (int i = 0; i < (int)last_msg_.points_length - 2; i += 3) + for (int i = 0; i < (int)last_msg_->points.size() - 2; i += 3) { - points_.push_back({ last_msg_.points[i], last_msg_.points[i + 1], last_msg_.points[i + 2] }); + points_.push_back({ last_msg_->points[i], last_msg_->points[i + 1], last_msg_->points[i + 2] }); } } - else if (last_msg_.path_type == pubsub::msg::Path::PATH_XYZ_Y) + else if (last_msg_->path_type == pubsub::msg::Path::PATH_XYZ_Y) { - for (int i = 0; i < (int)last_msg_.points_length - 3; i += 4) + for (int i = 0; i < (int)last_msg_->points.size() - 3; i += 4) { - points_.push_back({ last_msg_.points[i], last_msg_.points[i + 1], last_msg_.points[i + 2] }); + points_.push_back({ last_msg_->points[i], last_msg_->points[i + 1], last_msg_->points[i + 2] }); } } else @@ -176,36 +184,19 @@ class PathPlugin : public pubviz::Plugin printf("ERROR: Unknown path type\n"); } + auto frame = OpenGLCanvas::WGS84; + if (last_msg_->frame != pubsub::msg::Path::FRAME_WGS84) + { + frame = OpenGLCanvas::Odom; + } // Now transform the points transformed_pts_.clear(); transformed_pts_.reserve(points_.size()); for (auto& pt : points_) { - if (last_msg_.frame == pubsub::msg::Path::FRAME_WGS84) - { - if (GetCanvas()->wgs84_mode_) - { - double x, y; - GetCanvas()->local_xy_.FromLatLon(pt.x, pt.y, x, y); - - transformed_pts_.push_back({ x, y, pt.z }); - } - else - { - // todo - } - } - else - { - if (GetCanvas()->wgs84_mode_) - { - // todo - } - else - { - transformed_pts_.push_back(pt); - } - } + Vec3 p(pt.x, pt.y, pt.z); + GetCanvas()->TransformToView(frame, p); + transformed_pts_.push_back({p.x,p.y,p.z}); } // Now render the points diff --git a/plugins/PlanPath.h b/plugins/PlanPath.h new file mode 100644 index 0000000..edbe073 --- /dev/null +++ b/plugins/PlanPath.h @@ -0,0 +1,249 @@ + +#ifndef PUBVIZ_PLUGIN_PLAN_PATH_H +#define PUBVIZ_PLUGIN_PLAN_PATH_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define GLEW_STATIC +#include + +#ifndef _WIN32 +#include +#include +#include +#include +#endif + +#include "../Plugin.h" +#include "../properties.h" + +#include + +#include + +class PlanPathPlugin : public pubviz::Plugin +{ + FloatProperty* alpha_; + ColorProperty* color_; + NumberProperty* line_width_; + NumberProperty* point_size_; + + EnumProperty* frame_; + + TopicProperty* topic_; + + ButtonProperty* publish_; + ButtonProperty* clear_; + + bool pub_open_ = false; + ps_pub_t publisher_; + + std::string current_topic_; + void ChangeTopic(std::string str) + { + if (pub_open_) + { + ps_pub_destroy(&publisher_); + } + + current_topic_ = str; + ps_node_create_publisher(GetNode(), current_topic_.c_str(), &pubsub__Path_def, &publisher_, true); + pub_open_ = true; + } + + void FrameChanged(std::string str) + { + + } + +public: + + int frame_enum_ = pubsub::msg::Path::FRAME_ODOM; + PlanPathPlugin() + { + // dont use pubsub here + + } + + virtual ~PlanPathPlugin() + { + delete color_; + delete alpha_; + delete line_width_; + //delete show_points_; + delete point_size_; + delete publish_; + delete clear_; + + if (pub_open_) + { + ps_pub_destroy(&publisher_); + pub_open_ = false; + } + } + + // Clear out any historical data so the view gets cleared + virtual void Clear() + { + points_.clear(); + } + + virtual void Update() + { + + } + + struct Vertex + { + double x; + double y; + double z; + + Vertex(double x, double y, double z) + { + this->x = x; + this->y = y; + this->z = z; + } + }; + + std::vector points_; + std::vector transformed_pts_; + virtual void Render() + { + if (points_.size() == 0) + { + return; + } + + Gwen::Color color = color_->GetValue(); + + // Now transform the points + transformed_pts_.clear(); + transformed_pts_.reserve(points_.size()); + for (auto& pt : points_) + { + auto f = frame_enum_ == pubsub::msg::Path::FRAME_WGS84 ? OpenGLCanvas::WGS84 : OpenGLCanvas::Odom; + Vec3d pos(pt.x, pt.y, 0); + GetCanvas()->TransformToView(f, pos); + transformed_pts_.push_back({pos.x, pos.y, pos.z}); + } + + // Now render the points + glLineWidth(line_width_->GetValue()); + glBegin(GL_LINE_STRIP); + glColor3f(color.r / 255.0, color.g / 255.0, color.b / 255.0); + for (const auto& vert : transformed_pts_) + { + glVertex3f(vert.x, vert.y, vert.z); + } + glEnd(); + + if (true) + { + glPointSize(point_size_->GetValue()); + glBegin(GL_POINTS); + glColor3f(color.r / 255.0, color.g / 255.0, color.b / 255.0); + for (const auto& vert : transformed_pts_) + { + glVertex3f(vert.x, vert.y, vert.z); + } + glEnd(); + } + } + + virtual void Initialize(Gwen::Controls::Properties* tree) + { + // add any properties + alpha_ = AddFloatProperty(tree, "Alpha", 1.0, 0.0, 1.0, 0.1); + + color_ = AddColorProperty(tree, "Color", Gwen::Color(255, 50, 50)); + + topic_ = AddTopicProperty(tree, "Topic", "/path", "", "pubsub__Path", true, false); + topic_->onChange = std::bind(&PlanPathPlugin::ChangeTopic, this, std::placeholders::_1); + + line_width_ = AddNumberProperty(tree, "Line Width", 4, 1, 100, 2); + + frame_ = AddEnumProperty(tree, "Frame", "Odom", {"Odom", "WGS84"}, "Frame to publish path in."); + frame_->onChange = [this](std::string frame) + { + //clear the path and set the frame + points_.clear(); + frame_enum_ = (frame == "Odom" ? pubsub::msg::Path::FRAME_ODOM : pubsub::msg::Path::FRAME_WGS84); + }; + + point_size_ = AddNumberProperty(tree, "Point Size", 6, 1, 100, 2); + + publish_ = AddButtonProperty(tree, "Publish"); + publish_->onChange = [this]() + { + Publish(); + }; + clear_ = AddButtonProperty(tree, "Clear"); + clear_->onChange = [this]() + { + points_.clear(); + Redraw(); + }; + + ChangeTopic(topic_->GetValue()); + } + + void Publish() + { + std::vector points; + for (const auto& pt: points_) + { + points.push_back(pt.x); + points.push_back(pt.y); + points.push_back(pt.z); + } + pubsub::msg::Path msg; + msg.frame = frame_enum_; + msg.path_type = pubsub::msg::Path::PATH_XY_Y; + msg.points = points; + ps_pub_publish_ez(&publisher_, &msg); + } + + std::string GetTitle() override + { + return "Plan Path"; + } + + virtual std::vector>> ContextMenu(double x, double y) override + { + return {{"Add Path Point", [this, x, y]() { OnMapDoubleClick(x, y); }}, + {"Clear Path", [this]() {points_.clear(); Redraw();}}, + {"Publish Path", [this]() { Publish(); }}}; + } + + // Applies only for 2d + virtual bool OnMapDoubleClick(double x, double y) override + { + //printf("map_click %f %f\n", x, y); + Vec3d pos(x, y, 0); + auto src = GetCanvas()->wgs84_mode() ? OpenGLCanvas::Map : OpenGLCanvas::Odom; + auto dst = frame_enum_ == pubsub::msg::Path::FRAME_WGS84 ? OpenGLCanvas::WGS84 : OpenGLCanvas::Odom; + GetCanvas()->TransformToFrame(src, dst, pos); + // transform to correct frame + points_.push_back(Vertex(pos.x, pos.y, std::numeric_limits::quiet_NaN())); + + Redraw(); + return true; + } +}; + +REGISTER_PLUGIN("plan_path", PlanPathPlugin) + +#endif diff --git a/plugins/PointCloud.h b/plugins/PointCloud.h index 20d020f..785586e 100644 --- a/plugins/PointCloud.h +++ b/plugins/PointCloud.h @@ -18,6 +18,8 @@ #include #include +#include + #define GLEW_STATIC #include @@ -29,42 +31,42 @@ #include #endif - +#include #undef min #undef max class PointCloudPlugin: public pubviz::Plugin { - FloatProperty* alpha_; - StringProperty* point_text_; - NumberProperty* point_size_; - NumberProperty* history_length_; + std::unique_ptr alpha_; + std::unique_ptr point_text_; + std::unique_ptr point_size_; + std::unique_ptr history_length_; // Coloring Properties - EnumProperty* coloring_mode_; + std::unique_ptr coloring_mode_; // Used in all modes besides single color - EnumProperty* coloring_field_;// point field to use for coloring + std::unique_ptr coloring_field_;// point field to use for coloring // Used in interpolated mode and clamped mode - ColorProperty* min_color_; - ColorProperty* max_color_; - BooleanProperty* auto_min_max_; + std::unique_ptr min_color_; + std::unique_ptr max_color_; + std::unique_ptr auto_min_max_; // Used when auto min_max is false - FloatProperty* min_value_; - FloatProperty* max_value_; + std::unique_ptr min_value_; + std::unique_ptr max_value_; // Used in clamped mode - ColorProperty* floored_color_; - ColorProperty* ceiled_color_; + std::unique_ptr floored_color_; + std::unique_ptr ceiled_color_; - // Used + std::unique_ptr yaw_, pitch_, roll_; // Used in single color mode - ColorProperty* single_color_; + std::unique_ptr single_color_; - TopicProperty* topic_; + std::unique_ptr topic_; bool sub_open_ = false; ps_sub_t subscriber_; @@ -82,6 +84,7 @@ class PointCloudPlugin: public pubviz::Plugin unsigned int point_vbo; unsigned int color_vbo; + int stride;// number of floats per point pubsub::msg::PointCloud* original_cloud;// used for re-coloring // position of the cloud @@ -94,162 +97,180 @@ class PointCloudPlugin: public pubviz::Plugin { glDeleteBuffers(1, &point_vbo); glDeleteBuffers(1, &color_vbo); - free(original_cloud->data); - free(original_cloud); + delete original_cloud; } }; + float latest_min_ = std::numeric_limits::max(); + float latest_max_ = std::numeric_limits::lowest(); void BuildCloudBuffers(Cloud* cloud) { auto data = cloud->original_cloud; point_buf_.resize(data->num_points); color_buf_.resize(data->num_points); - uint8_t alpha = 255.0*alpha_->GetValue(); - Gwen::Color min_color = min_color_->GetValue(); - Gwen::Color max_color = max_color_->GetValue(); + uint8_t alpha = 255.0*alpha_->GetValue(); + Gwen::Color min_color = min_color_->GetValue(); + Gwen::Color max_color = max_color_->GetValue(); - // now make the right kind of points then render - if (data->point_type == pubsub::msg::PointCloud::POINT_XYZ) + auto fdata = (float*)data->data.data(); + + // x,y,z,intensity as floats + int color_offset = 3;// intensity, give option for other values + auto field = coloring_field_->GetValue(); + if (field == "X") + { + color_offset = 0; + } + else if (field == "Y") + { + color_offset = 1; + } + else if (field == "Z") + { + color_offset = 2; + } + else if (field == "Intensity") + { + color_offset = 3; + } + else if (field == "Other") + { + color_offset = 4; + } + + // make sure we dont read too much + const int stride = cloud->stride; + if (color_offset >= stride) + { + // todo enforce this better + printf("WARNING: The field %s is not in this pointcloud.\n", field.c_str()); + color_offset = stride - 1; + } + + // get min and max for the field + float min = std::numeric_limits::max(); + float max = std::numeric_limits::lowest(); + if (auto_min_max_->GetValue()) + { + for (int i = 0; i < data->num_points; i++) + { + auto value = fdata[i*stride+color_offset]; + min = std::min(min, value); + max = std::max(max, value); + } + latest_min_ = min; + latest_max_ = max; + } + else + { + min = min_value_->GetValue(); + max = max_value_->GetValue(); + } + + float byte_scale = 255.0/(max-min); + float scale = 1.0/(max-min); + + if (std::abs(max - min) < 0.0001) + { + byte_scale = 0.0; + scale = 0.0; + } + + auto mode = coloring_mode_->GetValue(); + if (mode == "Jet" || mode == "Rainbow") + { + auto table = mode == "Jet" ? jet_table_ : rainbow_table_; + for (int i = 0; i < data->num_points; i++) + { + point_buf_[i].x = fdata[i*stride]; + point_buf_[i].y = fdata[i*stride+1]; + point_buf_[i].z = fdata[i*stride+2]; + int index = (fdata[i*stride+color_offset]-min)*byte_scale; + if (index < 0) + index = 0; + else if (index > 255) + index = 255; + uint8_t r = table[index].r; + uint8_t g = table[index].g; + uint8_t b = table[index].b; + + color_buf_[i] = (alpha << 24) | r | (g << 8) | (b << 16); + } + } + else if (mode == "Single Color") + { + auto color = single_color_->GetValue(); + for (int i = 0; i < data->num_points; i++) + { + point_buf_[i].x = fdata[i*stride]; + point_buf_[i].y = fdata[i*stride+1]; + point_buf_[i].z = fdata[i*stride+2]; + uint8_t r = color.r; + uint8_t g = color.g; + uint8_t b = color.b; + + color_buf_[i] = (alpha << 24) | r | (g << 8) | (b << 16); + } + } + else + { + Gwen::Color lt_min_color = min_color; + Gwen::Color gt_max_color = max_color; + if (mode == "Clamped") + { + lt_min_color = floored_color_->GetValue(); + gt_max_color = ceiled_color_->GetValue(); + } + for (int i = 0; i < data->num_points; i++) + { + point_buf_[i].x = fdata[i*stride]; + point_buf_[i].y = fdata[i*stride+1]; + point_buf_[i].z = fdata[i*stride+2]; + float frac = (fdata[i*stride+color_offset]-min)*scale; + if (frac < 0) { - // x,y,z as floats - for (int i = 0; i < data->num_points; i++) - { - point_buf_[i].x = ((float*)data->data)[i*3]; - point_buf_[i].y = ((float*)data->data)[i*3+1]; - point_buf_[i].z = ((float*)data->data)[i*3+2]; - color_buf_[i] = (alpha << 24) | (max_color.b << 16) | (max_color.g << 8) | max_color.r; - } + frac = 0; + color_buf_[i] = (alpha << 24) | lt_min_color.r | (lt_min_color.g << 8) | (lt_min_color.b << 16); + continue; } - else if (data->point_type == pubsub::msg::PointCloud::POINT_XYZI) + else if (frac > 1.0) { - // x,y,z,intensity as floats - int color_offset = 3;// intensity, give option for other values - auto field = coloring_field_->GetValue(); - if (field == "X") - { - color_offset = 0; - } - else if (field == "Y") - { - color_offset = 1; - } - else if (field == "Z") - { - color_offset = 2; - } - else if (field == "Intensity") - { - color_offset = 3; - } - - // get min and max for the field - float min = 100000000.0; - float max = -100000000.0; - if (auto_min_max_->GetValue()) - { - for (int i = 0; i < data->num_points; i++) - { - auto value = ((float*)data->data)[i*4+color_offset]; - min = std::min(min, value); - max = std::max(max, value); - } - } - else - { - min = min_value_->GetValue(); - max = max_value_->GetValue(); - } - - float byte_scale = 255.0/(max-min); - float scale = 1.0/(max-min); - - if (std::abs(max - min) < 0.0001) - { - byte_scale = 0.0; - scale = 0.0; - } - - auto mode = coloring_mode_->GetValue(); - if (mode == "Jet" || mode == "Rainbow") - { - auto table = mode == "Jet" ? jet_table_ : rainbow_table_; - for (int i = 0; i < data->num_points; i++) - { - point_buf_[i].x = ((float*)data->data)[i*4]; - point_buf_[i].y = ((float*)data->data)[i*4+1]; - point_buf_[i].z = ((float*)data->data)[i*4+2]; - int index = (((float*)data->data)[i*4+color_offset]-min)*byte_scale; - if (index < 0) - index = 0; - else if (index > 255) - index = 255; - uint8_t r = table[index].r; - uint8_t g = table[index].g; - uint8_t b = table[index].b; - - color_buf_[i] = (alpha << 24) | r | (g << 8) | (b << 16); - } - } - else if (mode == "Single Color") - { - auto color = single_color_->GetValue(); - for (int i = 0; i < data->num_points; i++) - { - point_buf_[i].x = ((float*)data->data)[i*4]; - point_buf_[i].y = ((float*)data->data)[i*4+1]; - point_buf_[i].z = ((float*)data->data)[i*4+2]; - uint8_t r = color.r; - uint8_t g = color.g; - uint8_t b = color.b; - - color_buf_[i] = (alpha << 24) | r | (g << 8) | (b << 16); - } - } - else - { - Gwen::Color lt_min_color = min_color; - Gwen::Color gt_max_color = max_color; - if (mode == "Clamped") - { - lt_min_color = floored_color_->GetValue(); - gt_max_color = ceiled_color_->GetValue(); - } - for (int i = 0; i < data->num_points; i++) - { - point_buf_[i].x = ((float*)data->data)[i*4]; - point_buf_[i].y = ((float*)data->data)[i*4+1]; - point_buf_[i].z = ((float*)data->data)[i*4+2]; - float frac = (((float*)data->data)[i*4+color_offset]-min)*scale; - if (frac < 0) - { - frac = 0; - color_buf_[i] = (alpha << 24) | lt_min_color.r | (lt_min_color.g << 8) | (lt_min_color.b << 16); - continue; - } - else if (frac > 1.0) - { - frac = 1.0; -color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max_color.b << 16); - continue; - } - uint8_t r = frac*(max_color.r - min_color.r) + min_color.r; - uint8_t g = frac*(max_color.g - min_color.g) + min_color.g; - uint8_t b = frac*(max_color.b - min_color.b) + min_color.b; - - color_buf_[i] = (alpha << 24) | r | (g << 8) | (b << 16); - } - } + frac = 1.0; + color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max_color.b << 16); + continue; } + uint8_t r = frac*(max_color.r - min_color.r) + min_color.r; + uint8_t g = frac*(max_color.g - min_color.g) + min_color.g; + uint8_t b = frac*(max_color.b - min_color.b) + min_color.b; + + color_buf_[i] = (alpha << 24) | r | (g << 8) | (b << 16); + } + } + + // transform the points + Quaternion rotx, roty, rotz; + rotz.FromAngleAxis(yaw_->GetValue()*3.141592/180.0, Vec3f(0,0,1)); + roty.FromAngleAxis(pitch_->GetValue()*3.141592/180.0, Vec3f(0,1,0)); + rotx.FromAngleAxis(roll_->GetValue()*3.141592/180.0, Vec3f(1,0,0)); + Quaternion rot = (rotz*roty)*rotx; + Matrix3x4 matrix(rot, Vec3d(0,0,0)); + for (int i = 0; i < data->num_points; i++) + { + auto res = matrix.transform(Vec3d(point_buf_[i].x, point_buf_[i].y, point_buf_[i].z)); + point_buf_[i].x = res.x;//fdata[i*stride]; + point_buf_[i].y = res.y;//fdata[i*stride+1]; + point_buf_[i].z = res.z;//fdata[i*stride+2]; + } - // upload! - glBindBuffer(GL_ARRAY_BUFFER, cloud->point_vbo); - glBufferData(GL_ARRAY_BUFFER, point_buf_.size()*12, point_buf_.data(), GL_STATIC_DRAW); - - glBindBuffer(GL_ARRAY_BUFFER, cloud->color_vbo); - glBufferData(GL_ARRAY_BUFFER, color_buf_.size()*4, color_buf_.data(), GL_STATIC_DRAW); + // upload! + glBindBuffer(GL_ARRAY_BUFFER, cloud->point_vbo); + glBufferData(GL_ARRAY_BUFFER, point_buf_.size()*12, point_buf_.data(), GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, cloud->color_vbo); + glBufferData(GL_ARRAY_BUFFER, color_buf_.size()*4, color_buf_.data(), GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); } std::deque clouds_; @@ -319,6 +340,7 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max void AutoMinMaxChange(bool value) { + // if we disabled it, update max and min values from latest if (value) { min_value_->Hide(); @@ -328,6 +350,12 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max { min_value_->Show(); max_value_->Show(); + + if (latest_max_ >= latest_min_) + { + min_value_->SetValue(latest_min_); + max_value_->SetValue(latest_max_); + } } // Recolor all clouds @@ -356,7 +384,7 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max } auto r = tree->GetSkin()->GetRender(); - r->SetDrawColor(Gwen::Color(255, 0, 255, 255)); + r->SetDrawColor(Gwen::Color(255, 255, 255, 255)); // Measure the text to determine the best texture size auto s = r->MeasureText(f, value); @@ -425,17 +453,7 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max } virtual ~PointCloudPlugin() - { - delete min_color_; - delete max_color_; - delete alpha_; - delete point_size_; - delete history_length_; - delete point_text_; - delete coloring_mode_; - delete single_color_; - // todo use smart pointers - + { glDeleteFramebuffers(1, &frame_buffer_); glDeleteTextures(1, &render_texture_); @@ -470,8 +488,7 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max // user is responsible for freeing the message and its arrays if (Paused()) { - free(data->data); - free(data);//todo use allocator free + delete data; continue; } @@ -483,8 +500,7 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max clouds_.push_front(clouds_.back()); clouds_.pop_back(); cloud = &clouds_[0]; - free(cloud->original_cloud->data); - free(cloud->original_cloud); + delete cloud->original_cloud; } else { @@ -495,6 +511,11 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max cloud = &clouds_[clouds_.size()-1]; } + cloud->stride = 5; + if (data->point_type == pubsub::msg::PointCloud::POINT_XYZ) + cloud->stride = 3; + if (data->point_type == pubsub::msg::PointCloud::POINT_XYZI) + cloud->stride = 4; cloud->num_points = data->num_points; cloud->original_cloud = data; @@ -521,7 +542,7 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max virtual void Render() { - if (GetCanvas()->wgs84_mode_) + if (GetCanvas()->wgs84_mode()) { return;// not supported for the moment } @@ -536,6 +557,7 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max { glEnable(GL_BLEND); glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0); glEnable( GL_TEXTURE_2D ); glBindTexture(GL_TEXTURE_2D, render_texture_); glEnable(GL_POINT_SPRITE); @@ -585,8 +607,11 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max if (render_texture) { - glDisable(GL_BLEND); glDisable(GL_ALPHA_TEST); + glDisable(GL_BLEND); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + glDisable(GL_POINT_SPRITE); } glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -604,11 +629,12 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max { // its in this cloud auto data = cloud.original_cloud; + float* fdata = (float*)data->data.data(); int i = index - current; - int increment = data->point_type == pubsub::msg::PointCloud::POINT_XYZ ? 3 : 4; - float x = ((float*)data->data)[i*increment]; - float y = ((float*)data->data)[i*increment+1]; - float z = ((float*)data->data)[i*increment+2]; + int increment = cloud.stride; + float x = fdata[i*increment]; + float y = fdata[i*increment+1]; + float z = fdata[i*increment+2]; props["x"] = std::to_string(x); props["y"] = std::to_string(y); props["z"] = std::to_string(z); @@ -618,7 +644,7 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max size.sx = size.sy = size.sz = 0.2; if (data->point_type == pubsub::msg::PointCloud::POINT_XYZI) { - props["i"] = std::to_string(((float*)data->data)[i*increment+3]); + props["i"] = std::to_string(fdata[i*increment+3]); } break; } @@ -639,17 +665,19 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max glPointSize(point_size_->GetValue()); glBegin(GL_POINTS); auto data = cloud.original_cloud; - int increment = data->point_type == pubsub::msg::PointCloud::POINT_XYZ ? 3 : 4; + int increment = cloud.stride; + // todo handle more point cloud types + float* fdata = (float*)data->data.data(); for (int i = 0; i < data->num_points; i++) { //glColor4f(1.0, 0.0, 0.0, 1.0); //glColor4ub(start_index & 0xFF, (start_index & 0xFF00) >> 8, (start_index & 0xFF0000) >> 16, 255); glColor4ubv((unsigned char*)&start_index); start_index++; - glVertex3f(((float*)data->data)[i*increment], - ((float*)data->data)[i*increment+1], - ((float*)data->data)[i*increment+2]); + glVertex3f(fdata[i*increment], + fdata[i*increment+1], + fdata[i*increment+2]); } glEnd(); } @@ -694,47 +722,53 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max glBindFramebuffer(GL_FRAMEBUFFER, 0); // add any properties - alpha_ = AddFloatProperty(tree, "Alpha", 1.0, 0.0, 1.0, 0.1, "Point transparency."); + alpha_.reset(AddFloatProperty(tree, "Alpha", 1.0, 0.0, 1.0, 0.1, "Point transparency.")); alpha_->onChange = std::bind(&PointCloudPlugin::FloatConfigChanged, this, std::placeholders::_1); - topic_ = AddTopicProperty(tree, "Topic", "/pointcloud", "", "pubsub__PointCloud"); + topic_.reset(AddTopicProperty(tree, "Topic", "/pointcloud", "", "pubsub__PointCloud")); topic_->onChange = std::bind(&PointCloudPlugin::Subscribe, this, std::placeholders::_1); - point_text_ = AddStringProperty(tree, "Text", "", "If set, visualize the points as the given characters."); + point_text_.reset(AddStringProperty(tree, "Text", "", "If set, visualize the points as the given characters.")); point_text_->onChange = std::bind(&PointCloudPlugin::TextChange, this, std::placeholders::_1); TextChange(point_text_->GetValue()); - history_length_ = AddNumberProperty(tree, "History Length", 1, 1, 100, 1, "Number of past pointclouds to show."); + history_length_.reset(AddNumberProperty(tree, "History Length", 1, 1, 100, 1, "Number of past pointclouds to show.")); history_length_->onChange = std::bind(&PointCloudPlugin::HistoryLengthChange, this, std::placeholders::_1); - point_size_ = AddNumberProperty(tree, "Point Size", 4, 1, 100, 2, "Size in pixels for points."); + point_size_.reset(AddNumberProperty(tree, "Point Size", 4, 1, 100, 2, "Size in pixels for points.")); - coloring_mode_ = AddEnumProperty(tree, "Coloring Mode", "Interpolated", {"Interpolated", "Clamped", "Jet", "Rainbow", "Single Color"}, "Coloring mode."); + coloring_mode_ .reset(AddEnumProperty(tree, "Coloring Mode", "Jet", {"Interpolated", "Clamped", "Jet", "Rainbow", "Single Color"}, "Coloring mode.")); coloring_mode_->onChange = std::bind(&PointCloudPlugin::ColoringModeChange, this, std::placeholders::_1); - coloring_field_ = AddEnumProperty(tree, "Coloring Field", "Intensity", {"Intensity", "X", "Y", "Z"}, "Point field to use for coloring points."); + coloring_field_.reset(AddEnumProperty(tree, "Coloring Field", "Intensity", {"Intensity", "X", "Y", "Z", "Other"}, "Point field to use for coloring points.")); + coloring_field_->onChange = std::bind(&PointCloudPlugin::StringConfigChanged, this, std::placeholders::_1); - floored_color_ = AddColorProperty(tree, "Floor Color", Gwen::Color(0,255,0), "Color for points below the minimum value."); + floored_color_.reset(AddColorProperty(tree, "Floor Color", Gwen::Color(0,255,0), "Color for points below the minimum value.")); floored_color_->onChange = std::bind(&PointCloudPlugin::ColorConfigChanged, this, std::placeholders::_1); - ceiled_color_ = AddColorProperty(tree, "Ceiling Color", Gwen::Color(255,255,0), "Color for points above the minimum value."); + ceiled_color_.reset(AddColorProperty(tree, "Ceiling Color", Gwen::Color(255,255,0), "Color for points above the minimum value.")); ceiled_color_->onChange = std::bind(&PointCloudPlugin::ColorConfigChanged, this, std::placeholders::_1); - min_color_ = AddColorProperty(tree, "Min Color", Gwen::Color(255,255,255), "Color for the minimum value."); + min_color_.reset(AddColorProperty(tree, "Min Color", Gwen::Color(255,255,255), "Color for the minimum value.")); min_color_->onChange = std::bind(&PointCloudPlugin::ColorConfigChanged, this, std::placeholders::_1); - max_color_ = AddColorProperty(tree, "Max Color", Gwen::Color(255,0,0), "Color for the maximum value."); + max_color_.reset(AddColorProperty(tree, "Max Color", Gwen::Color(255,0,0), "Color for the maximum value.")); max_color_->onChange = std::bind(&PointCloudPlugin::ColorConfigChanged, this, std::placeholders::_1); - single_color_ = AddColorProperty(tree, "Point Color", Gwen::Color(255,255,255), "Color for points."); + single_color_.reset(AddColorProperty(tree, "Point Color", Gwen::Color(255,255,255), "Color for points.")); single_color_->onChange = std::bind(&PointCloudPlugin::ColorConfigChanged, this, std::placeholders::_1); - auto_min_max_ = AddBooleanProperty(tree, "Auto Min/Max", true, "If true, determine the min max values from each pointcloud automatically."); + auto_min_max_.reset(AddBooleanProperty(tree, "Auto Min/Max", true, "If true, determine the min max values from each pointcloud automatically.")); auto_min_max_->onChange = std::bind(&PointCloudPlugin::AutoMinMaxChange, this, std::placeholders::_1); - min_value_ = AddFloatProperty(tree, "Min Value", 0.0, -1000000.0, 1000000.0, 1.0, "Value at which min color is used."); + min_value_.reset(AddFloatProperty(tree, "Min Value", 0.0, -1000000.0, 1000000.0, 1.0, "Value at which min color is used.")); min_value_->onChange = std::bind(&PointCloudPlugin::FloatConfigChanged, this, std::placeholders::_1); - max_value_ = AddFloatProperty(tree, "Max Value", 255.0, -1000000.0, 1000000.0, 1.0, "Value at which max color is used."); + max_value_.reset(AddFloatProperty(tree, "Max Value", 255.0, -1000000.0, 1000000.0, 1.0, "Value at which max color is used.")); max_value_->onChange = std::bind(&PointCloudPlugin::FloatConfigChanged, this, std::placeholders::_1); + yaw_.reset(AddFloatProperty(tree, "Yaw", 0, -360, 360, 1, "")); + pitch_.reset(AddFloatProperty(tree, "Pitch", 0, -360, 360, 1, "")); + + roll_.reset(AddFloatProperty(tree, "Roll", 0, -360, 360, 1, "")); + // build color lookup tables for (int i = 0; i < 256; i++) @@ -747,7 +781,7 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max jet_table_[i] = GetJetColour(i, 0, 255); } - ColoringModeChange("Interpolated"); + ColoringModeChange("Jet"); Subscribe(topic_->GetValue()); } @@ -761,6 +795,15 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max } } + void StringConfigChanged(const std::string& str) + { + // Recolor all clouds + for (auto& cloud: clouds_) + { + BuildCloudBuffers(&cloud); + } + } + void FloatConfigChanged(float) { // Recolor all clouds @@ -771,7 +814,7 @@ color_buf_[i] = (alpha << 24) | gt_max_color.r | (gt_max_color.g << 8) | (gt_max } // Returns interpolated color ramp values in a Jet like fashion - Gwen::Color GetJetColour(double v,double vmin,double vmax) + Gwen::Color GetJetColour(double v, double vmin, double vmax) { Gwen::Color c(255,255,255); diff --git a/plugins/Pose.h b/plugins/Pose.h index c915227..1e374b5 100644 --- a/plugins/Pose.h +++ b/plugins/Pose.h @@ -14,6 +14,7 @@ #include #include +#include #define GLEW_STATIC #include @@ -41,9 +42,11 @@ class PosePlugin : public pubviz::Plugin BooleanProperty* follow_pose_; NumberProperty* history_length_; FloatProperty* sample_distance_; - BooleanProperty* draw_line_; NumberProperty* point_size_; EnumProperty* draw_style_; + EnumProperty* history_style_; + + std::unique_ptr use_transforms_; TopicProperty* topic_; @@ -61,15 +64,26 @@ class PosePlugin : public pubviz::Plugin { if (!state) { - GetCanvas()->ResetViewOrigin(); + // set the view to no longer follow but stay at same position + GetCanvas()->ResetViewPosition(); + } + else + { + // center on the latest pose so it works while paused + if (messages_.size()) + { + auto data = &messages_.back(); + GetCanvas()->SetViewOrigin(data->x, data->y, data->z, data->latitude, data->longitude, data->altitude); + GetCanvas()->ResetViewPosition(); + } } } void OnHistoryChange(int length) { - if (messages_.size() > length) + while (messages_.size() > length) { - messages_.resize(length); + messages_.pop_front(); } } @@ -189,13 +203,16 @@ class PosePlugin : public pubviz::Plugin messages_.pop_front(); } - // todo is there a better way to do this? - GetCanvas()->SetLocalXY(data->latitude, data->longitude); + if (use_transforms_->GetValue()) + { + // update our transform between wgs84 and odom + GetCanvas()->SetTransform(data->x, data->y, data->z, data->odom_yaw, data->latitude, data->longitude, data->altitude, data->yaw); + } if (follow_pose_->GetValue()) { // center view on me - GetCanvas()->SetViewOrigin(data->x, data->y, data->z, data->latitude, data->longitude, 0.0); + GetCanvas()->SetViewOrigin(data->x, data->y, data->z, data->latitude, data->longitude, data->altitude); } free(data);//todo use allocator free @@ -211,66 +228,114 @@ class PosePlugin : public pubviz::Plugin messages_.clear(); } + inline void draw_frame(float alpha, float line_length, const pubsub::msg::Pose& p) + { + // first handle yaw + float x_x = 1.0 * cos(p.odom_yaw) - 0.0 * sin(p.odom_yaw); + float x_y = 1.0 * sin(p.odom_yaw) + 0.0 * cos(p.odom_yaw); + float x_z = 0.0; + + float y_x = 0.0 * cos(p.odom_yaw) - 1.0 * sin(p.odom_yaw); + float y_y = 0.0 * sin(p.odom_yaw) + 1.0 * cos(p.odom_yaw); + float y_z = 0.0; + + float z_x = 0.0; + float z_y = 0.0; + float z_z = 1.0; + + // todo call begin and end less + + // Draw axes + glBegin(GL_LINES); + // Red line to the right (x) + glColor4f(1, 0, 0, alpha); + glVertex3f(p.x + 0, p.y + 0, p.z + 0); + glVertex3f(p.x + x_x * line_length, p.y + x_y * line_length, p.z + x_z * line_length); + + // Green line to the top (y) + glColor4f(0, 1, 0, alpha); + glVertex3f(p.x + 0, p.y + 0, p.z + 0); + glVertex3f(p.x + y_x * line_length, p.y + y_y * line_length, p.z + y_z * line_length); + + // Blue line up (z) + glColor4f(0, 0, 1, alpha); + glVertex3f(p.x + 0, p.y + 0, p.z + 0); + glVertex3f(p.x + z_x * line_length, p.y + z_y * line_length, p.z + z_z * line_length); + glEnd(); + } + virtual void Render() { const double line_length = line_length_->GetValue(); glLineWidth(line_width_->GetValue()); glEnable(GL_BLEND); - //glEnable(GL_POINT_SMOOTH); + glEnable(GL_POINT_SMOOTH); float alpha = alpha_->GetValue(); auto color = color_->GetValue(); bool draw_points = draw_style_->GetValue() == "Points"; - if (draw_line_->GetValue()) + + // first draw history according to the history mode (so it is on bottom) + std::string style = history_style_->GetValue(); + bool is_line = style == "Line"; + bool is_point = style == "Points"; + if (is_line) glBegin(GL_LINE_STRIP); + for (size_t i = 0; i < messages_.size(); i++) { - glBegin(GL_LINE_STRIP); + auto p = messages_[i]; + if (!is_line && i == 0) + { + continue; + } + // start with just rendering axes + // + // todo handle rotation - for (auto p : messages_) + if (GetCanvas()->wgs84_mode()) + { + GetCanvas()->local_xy_.FromLatLon(p.latitude, p.longitude, p.x, p.y); + p.z = p.altitude; + p.odom_yaw = p.yaw; + } + + if (is_line) { - if (GetCanvas()->wgs84_mode_) - { - GetCanvas()->local_xy_.FromLatLon(p.latitude, p.longitude, p.x, p.y); - p.z = 0.0; - p.odom_yaw = p.yaw; - } glColor4f(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, alpha); glVertex3f(p.x, p.y, p.z); } - - glEnd(); + else if (is_point) + { + glPointSize(point_size_->GetValue()); + glBegin(GL_POINTS); + glColor4f(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, alpha); + glVertex3f(p.x, p.y, p.z); + glEnd(); + } + else + { + draw_frame(alpha, line_length, p); + } } + if (is_line) glEnd(); - for (auto p : messages_) + // finally draw the latest message on top + if (messages_.size()) { + auto p = messages_.back(); // start with just rendering axes // // todo handle rotation - - if (GetCanvas()->wgs84_mode_) + // todo dont do this hack + if (GetCanvas()->wgs84_mode()) { GetCanvas()->local_xy_.FromLatLon(p.latitude, p.longitude, p.x, p.y); - p.z = 0.0; + p.z = p.altitude; p.odom_yaw = p.yaw; } - // first handle yaw - float x_x = 1.0 * cos(p.odom_yaw) - 0.0 * sin(p.odom_yaw); - float x_y = 1.0 * sin(p.odom_yaw) + 0.0 * cos(p.odom_yaw); - float x_z = 0.0; - - float y_x = 0.0 * cos(p.odom_yaw) - 1.0 * sin(p.odom_yaw); - float y_y = 0.0 * sin(p.odom_yaw) + 1.0 * cos(p.odom_yaw); - float y_z = 0.0; - - float z_x = 0.0; - float z_y = 0.0; - float z_z = 1.0; - - // todo handle pitch and roll later - if (draw_points) { glPointSize(point_size_->GetValue()); @@ -281,28 +346,11 @@ class PosePlugin : public pubviz::Plugin } else { - // todo call begin and end less - - // Draw axes - glBegin(GL_LINES); - // Red line to the right (x) - glColor4f(1, 0, 0, alpha); - glVertex3f(p.x + 0, p.y + 0, p.z + 0); - glVertex3f(p.x + x_x * line_length, p.y + x_y * line_length, p.z + x_z * line_length); - - // Green line to the top (y) - glColor4f(0, 1, 0, alpha); - glVertex3f(p.x + 0, p.y + 0, p.z + 0); - glVertex3f(p.x + y_x * line_length, p.y + y_y * line_length, p.z + y_z * line_length); - - // Blue line up (z) - glColor4f(0, 0, 1, alpha); - glVertex3f(p.x + 0, p.y + 0, p.z + 0); - glVertex3f(p.x + z_x * line_length, p.y + z_y * line_length, p.z + z_z * line_length); - glEnd(); + draw_frame(alpha, line_length, p); } } - //glDisable(GL_POINT_SMOOTH); + + glDisable(GL_POINT_SMOOTH); glDisable(GL_BLEND); } @@ -333,7 +381,9 @@ class PosePlugin : public pubviz::Plugin sample_distance_ = AddFloatProperty(tree, "Sample Distance", 1.0, 0.0, 100, 1, "Minimum distance before dropping another history pose."); sample_distance_->onChange = std::bind(&PosePlugin::OnSampleDistanceChange, this, std::placeholders::_1); - draw_line_ = AddBooleanProperty(tree, "Show History Line", false, "If true, draw a line between historical poses."); + history_style_ = AddEnumProperty(tree, "History Mode", "None", { "None", "Frames", "Line", "Points" }, "Draw style for history."); + + use_transforms_.reset(AddBooleanProperty(tree, "Use Transform", true, "If true, uses this pose for the transform between odom and WGS84.")); OnDrawStyleChange("Frames"); Subscribe(topic_->GetValue()); diff --git a/plugins/RobotImage.h b/plugins/RobotImage.h new file mode 100644 index 0000000..f7d50df --- /dev/null +++ b/plugins/RobotImage.h @@ -0,0 +1,243 @@ + +#ifndef PUBVIZ_PLUGIN_ROBOT_IMAGE_H +#define PUBVIZ_PLUGIN_ROBOT_IMAGE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GLEW_STATIC +#include + +#ifndef _WIN32 +#include +#include +#include +#include +#endif + +#include "../Plugin.h" +#include "../properties.h" + +class RobotImagePlugin: public pubviz::Plugin +{ + FloatProperty* alpha_; + ColorProperty* color_; + BooleanProperty* show_outline_; + + FloatProperty *width_, *length_, *x_offset_; + + FileProperty* image_; + + unsigned int texture_ = -1; + + void OnImageChange(std::string file) + { + if (texture_ != -1) + { + glDeleteTextures(1, &texture_); + } + + int width = 10; + int height = 10; + + // make the color buffer + std::vector pixels; + + FREE_IMAGE_FORMAT imageFormat = FreeImage_GetFileType( file.c_str() ); + + if ( imageFormat == FIF_UNKNOWN ) + { imageFormat = FreeImage_GetFIFFromFilename( file.c_str() ); } + + FIBITMAP* bits = FreeImage_Load( imageFormat, file.c_str() ); + if (bits) + { + // Convert to 32bit + FIBITMAP* bits32 = FreeImage_ConvertTo32Bits( bits ); + FreeImage_Unload( bits ); + + auto rotated = FreeImage_Rotate(bits32, -90); + FreeImage_Unload(bits32); + const uint32_t* data = (const uint32_t*)FreeImage_GetBits( rotated ); + width = FreeImage_GetWidth( rotated ); + height = FreeImage_GetHeight( rotated ); + pixels.resize(width*height); + for (int i = 0; i < pixels.size(); i++) + { + pixels[i] = data[i]; + } + + FreeImage_Unload( rotated ); + } + else + { + printf("file '%s' not found\n", file.c_str()); + pixels.resize(width*height); + for (int i = 0; i < pixels.size(); i++) + { + pixels[i] = 0xffffffff; + } + } + +#ifdef FREEIMAGE_BIGENDIAN + GLenum format = GL_RGBA; +#else + GLenum format = GL_BGRA; +#endif + + glGenTextures(1, &texture_); + + glBindTexture(GL_TEXTURE_2D, texture_); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA, + width, + height, + 0, + format, + GL_UNSIGNED_BYTE, + pixels.data()); + + glBindTexture(GL_TEXTURE_2D, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + } + +public: + + RobotImagePlugin() + { + // dont use pubsub here + } + + virtual ~RobotImagePlugin() + { + if (texture_ != -1) + { + glDeleteTextures(1, &texture_); + } + } + + // Clear out any historical data so the view gets cleared + virtual void Clear() + { + + } + + virtual void Update() + { + + } + + virtual void Render() + { + double width = length_->GetValue(); + double height = width_->GetValue(); + double x_offset = x_offset_->GetValue(); + + Vec3d pts[4]; + pts[0] = Vec3d(-width/2 + x_offset, -height/2, 0); + pts[1] = Vec3d(-width/2 + x_offset, height/2, 0); + pts[2] = Vec3d(width/2 + x_offset, height/2, 0); + pts[3] = Vec3d(width/2 + x_offset, -height/2, 0); + + // transform to view frame + for (int i = 0; i < 4; i++) + { + GetCanvas()->TransformToView(OpenGLCanvas::Vehicle, pts[i]); + } + + // draw the bounds of the costmap + if (show_outline_->GetValue()) + { + glLineWidth(4.0f); + glBegin(GL_LINE_STRIP); + + glColor3f(0, 0, 0); + + glVertex2f(pts[0].x, pts[0].y); + glVertex2f(pts[1].x, pts[1].y); + glVertex2f(pts[2].x, pts[2].y); + glVertex2f(pts[3].x, pts[3].y); + glVertex2f(pts[0].x, pts[0].y); + + glEnd(); + } + + glEnable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0); + + // Now draw the costmap itself + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture_); + glBegin(GL_TRIANGLES); + + glColor4f(1.0f, 1.0f, 1.0f, alpha_->GetValue() ); + + glTexCoord2d(0, 0); + glVertex2f(pts[0].x, pts[0].y); + glTexCoord2d(1.0, 0); + glVertex2f(pts[3].x, pts[3].y); + glTexCoord2d(1.0, 1.0); + glVertex2f(pts[2].x, pts[2].y); + + glTexCoord2d(0, 0); + glVertex2f(pts[0].x, pts[0].y); + glTexCoord2d(1.0, 1.0); + glVertex2f(pts[2].x, pts[2].y); + glTexCoord2d(0, 1.0); + glVertex2f(pts[1].x, pts[1].y); + + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + glDisable(GL_ALPHA_TEST); + glDisable(GL_BLEND); + } + + virtual void Initialize(Gwen::Controls::Properties* tree) + { + // add any properties + alpha_ = AddFloatProperty(tree, "Alpha", 1.0, 0.0, 1.0, 0.1, "Image transparency."); + + image_ = AddFileProperty(tree, "Image", ""); + image_->onChange = std::bind(&RobotImagePlugin::OnImageChange, this, std::placeholders::_1); + + show_outline_ = AddBooleanProperty(tree, "Show Outline", true, "If true, draw an outline around the costmap."); + + length_ = AddFloatProperty(tree, "Length", 1.0, 0.0, 20.0, 0.1, "Width of robot."); + width_ = AddFloatProperty(tree, "Width", 1.0, 0.0, 20.0, 0.1, "Width of robot"); + x_offset_ = AddFloatProperty(tree, "X Offset", 0.0, 0.0, 20.0, 0.1, "X offset from center."); + + OnImageChange(image_->GetValue()); + } + + std::string GetTitle() override + { + return "Robot Image"; + } +}; + +REGISTER_PLUGIN("robot_image", RobotImagePlugin) + +#endif diff --git a/properties.h b/properties.h index 1da1c37..8cbe0a9 100644 --- a/properties.h +++ b/properties.h @@ -3,11 +3,12 @@ #define PUBVIZ_PROPERTIES_H #include +#include #include #include #include #include -#include +#include #include #include @@ -88,6 +89,54 @@ class BooleanProperty: public PropertyBase std::function onChange; }; +class ButtonProperty: public PropertyBase +{ + Gwen::Controls::Property::Button* property_; + + void OnChange(Gwen::Controls::Base* prop) + { + if (onChange) + { + onChange(); + } + } + +public: + + ButtonProperty(Gwen::Controls::Properties* tree, const std::string& name, const std::string& description = "") + { + property_ = new Gwen::Controls::Property::Button(tree); + auto item = tree->Add("", property_, name); + item->onChange.Add(this, &ButtonProperty::OnChange); + if (description.length()) + { + item->SetToolTip(description); + } + } + + virtual std::string Serialize() + { + return ""; + } + + virtual void Deserialize(const std::string& str) + { + + } + + void Hide() + { + property_->GetParent()->Hide(); + } + + void Show() + { + property_->GetParent()->Show(); + } + + std::function onChange; +}; + class NumberProperty: public PropertyBase { Gwen::Controls::Property::Numeric* property_; @@ -224,8 +273,8 @@ class FloatProperty: public PropertyBase std::function onChange; }; -extern std::map> _topics; -extern std::map _found_topics; +extern std::map> _published_topics; +extern std::map> _subscribed_topics; class TopicProperty: public PropertyBase { @@ -234,6 +283,7 @@ class TopicProperty: public PropertyBase std::string value_; std::string message_type_; + bool published_; void cbOnChange(Gwen::Controls::Base* prop) { @@ -262,10 +312,11 @@ class TopicProperty: public PropertyBase // populate the topic list topic_list_->Clear(); int added_count = 0; + auto& topics = published_ ? _published_topics : _subscribed_topics; if (message_type_.length()) { - auto list = _topics.find(message_type_); - if (list != _topics.end()) + auto list = topics.find(message_type_); + if (list != topics.end()) { for (const auto& topic: list->second) { @@ -276,10 +327,13 @@ class TopicProperty: public PropertyBase } else { - for (const auto& topic: _found_topics) + for (const auto& list: topics) { - topic_list_->AddItem(topic.first, topic.first); - added_count++; + for (const auto& topic: list.second) + { + topic_list_->AddItem(topic, topic); + added_count++; + } } } if (added_count == 0) @@ -305,9 +359,10 @@ class TopicProperty: public PropertyBase public: - TopicProperty(Gwen::Controls::Properties* tree, const std::string& name, std::string topic = "", std::string description = "", std::string type = "") + TopicProperty(Gwen::Controls::Properties* tree, const std::string& name, std::string topic = "", std::string description = "", std::string type = "", bool published=true) { message_type_ = type; + published_ = published; property_ = new Gwen::Controls::Property::Text(tree->GetParent()); auto item = tree->Add(name, property_, topic); @@ -439,6 +494,71 @@ class StringProperty: public PropertyBase std::function onChange; }; +class FileProperty: public PropertyBase +{ + Gwen::Controls::Property::Text* property_; + + std::string value_; + + void cbOnChange(Gwen::Controls::Base* prop) + { + property_->Redraw(); + value_ = property_->GetPropertyValue().c_str(); + if (onChange) + { + onChange(value_); + } + } + +public: + + FileProperty(Gwen::Controls::Properties* tree, const std::string& name, std::string topic = "", const std::string& description = "") + { + property_ = new Gwen::Controls::Property::File(tree); + auto item = tree->Add(name, property_, topic); + item->onChange.Add(this, &FileProperty::cbOnChange); + if (description.length()) + { + item->SetToolTip(description); + } + value_ = topic; + } + + void SetValue(const std::string& val) + { + value_ = val; + property_->SetPropertyValue(val, true); + } + + inline const std::string& GetValue() + { + return value_; + } + + virtual std::string Serialize() + { + return value_; + } + + virtual void Deserialize(const std::string& str) + { + value_ = str; + property_->SetPropertyValue(str, true); + } + + void Hide() + { + property_->GetParent()->Hide(); + } + + void Show() + { + property_->GetParent()->Show(); + } + + std::function onChange; +}; + class EnumProperty: public PropertyBase { Gwen::Controls::Property::ComboBox* property_; diff --git a/pubviz_2d.png b/pubviz_2d.png new file mode 100644 index 0000000..4c3fdb4 Binary files /dev/null and b/pubviz_2d.png differ diff --git a/pubviz_3d.png b/pubviz_3d.png new file mode 100644 index 0000000..f769224 Binary files /dev/null and b/pubviz_3d.png differ diff --git a/pubviz_screenshot.png b/pubviz_screenshot.png deleted file mode 100644 index 2f62dd4..0000000 Binary files a/pubviz_screenshot.png and /dev/null differ diff --git a/tools/pubviz.cpp b/tools/pubviz.cpp index ea7b6f5..c31828c 100644 --- a/tools/pubviz.cpp +++ b/tools/pubviz.cpp @@ -40,7 +40,6 @@ int main(int argc, char** args) { ppUnit->LoadConfig(args[1]); } - // Wait for exit, use this instead of spin while (app.Okay()) { @@ -48,7 +47,7 @@ int main(int argc, char** args) { break; } - + //if (!window->NeedsRedraw()) { // If we dont need a redraw, sleep until we get new input diff --git a/tools/sackviz.cpp b/tools/sackviz.cpp index e2e518a..a1ac801 100644 --- a/tools/sackviz.cpp +++ b/tools/sackviz.cpp @@ -32,6 +32,7 @@ int main(int argc, char** args) //app.SetDefaultFont("Open Sans", 10); auto control = new SackViz(app.AddWindow("Sackviz", 700, 500)); + control->GetSkin()->GetRender()->EnableVSync(true); if (argc > 1) { control->Open(args[1]);