multiple buttons wip

This commit is contained in:
frank 2021-11-16 23:21:24 -05:00
parent e0b1d1fd53
commit 16cc5406cf
9 changed files with 210 additions and 71 deletions

View File

@ -48,7 +48,7 @@
"enabled": true, "enabled": true,
"json-save": true, "json-save": true,
"json-save-directory": "local/scans", "json-save-directory": "local/scans",
"barcode": "", "barcode": "014100085980",
"capture-device": "/dev/video0" "capture-device": "/dev/video0"
}, },
"api": "api":
@ -86,12 +86,13 @@
{ {
"main-button-y": -0.75, "main-button-y": -0.75,
"main-button-single-x": 0.0, "main-button-single-x": 0.0,
"main-button-double-x": 0.65, "main-button-double-x": 0.6,
"main-button-scale": 0.25, "main-button-scale": 0.25,
"camera-button-label": "scan", "camera-button-label": "scan",
"inventory-button-label": "inventory", "inventory-button-label": "inventory",
"arrow-button-location": [0.75, 0.0], "arrow-button-location": [0.75, 0.0],
"arrow-button-scale": 0.1, "arrow-button-scale": 0.2,
"arrow-button-label": "arrow" "arrow-button-label": "arrow",
"pop-up-viewport-height": 0.6
} }
} }

2
lib/sb

@ -1 +1 @@
Subproject commit 54cf01246b0e5ec81ba5b9158248bca7492823db Subproject commit 660865b2f21b61f145f351514e51b26e3f0b68e9

View File

@ -24,7 +24,7 @@ private:
public: public:
template<typename Container> template<typename Container>
auto current(Container& container) auto current(Container& container) const
{ {
auto location = container.begin(); auto location = container.begin();
if (location != container.end()) if (location != container.end())
@ -39,35 +39,49 @@ public:
} }
template<typename Container> template<typename Container>
auto next(const Container& container) void next(const Container& container)
{ {
offset = ++offset % container.size(); offset = ++offset % container.size();
} }
template<typename Container> template<typename Container>
auto previous(const Container& container) void previous(const Container& container)
{ {
offset = sb::mod(--offset, container.size()); offset = sb::mod(--offset, container.size());
} }
template<typename Container> template<typename Container>
auto increment(const Container& container, int amount) void increment(const Container& container, int amount)
{ {
offset = sb::mod(offset + amount, container.size()); offset = sb::mod(offset + amount, container.size());
} }
template<typename Container> void beginning()
auto beginning()
{ {
offset = 0; offset = 0;
} }
template<typename Container> template<typename Container>
auto end(const Container& container) void end(const Container& container)
{ {
offset = container.size() - 1; offset = container.size() - 1;
} }
/* Return true if the carousel currently points to the location at the beginning of the container. */
bool at_beginning() const
{
return offset == 0;
}
/* Return true if the carousel currently points to the location at the end of the container. */
template<typename Container>
bool at_end(const Container& container) const
{
auto location = container.begin();
std::advance(location, offset);
return location == container.end();
}
}; };
#endif #endif

View File

@ -74,25 +74,42 @@ std::string Item::full_name() const
void Item::texture(sb::Texture& texture, const std::string& name) void Item::texture(sb::Texture& texture, const std::string& name)
{ {
image.texture(texture, name); item_view.texture(texture, name);
} }
sb::Texture& Item::current_texture() sb::Texture& Item::current_texture()
{ {
return carousel.current(image.textures())->second; return carousel.current(item_view.textures())->second;
} }
void Item::next_texture() void Item::next_texture()
{ {
carousel.next(image.textures()); carousel.next(item_view.textures());
} }
void Item::previous_texture() void Item::previous_texture()
{ {
carousel.previous(image.textures()); carousel.previous(item_view.textures());
} }
std::size_t Item::texture_count() std::size_t Item::texture_count()
{ {
return image.textures().size(); return item_view.textures().size();
}
Plane& Item::view()
{
return item_view;
}
/* Return true if the current texture is the first texture added. */
bool Item::at_first() const
{
return carousel.at_beginning();
}
/* Return true if the current texture is the last texture added. */
bool Item::at_last()
{
return carousel.at_end(item_view.textures());
} }

View File

@ -35,7 +35,7 @@ class Item
private: private:
nlohmann::json json = {}; nlohmann::json json = {};
Plane image; Plane item_view;
Carousel carousel; Carousel carousel;
std::string item_brand_name = "", item_product_name = "", item_upc = ""; std::string item_brand_name = "", item_product_name = "", item_upc = "";
void text_property(const std::string&, std::string&, const std::string&); void text_property(const std::string&, std::string&, const std::string&);
@ -60,6 +60,9 @@ public:
void next_texture(); void next_texture();
void previous_texture(); void previous_texture();
std::size_t texture_count(); std::size_t texture_count();
Plane& view();
bool at_first() const;
bool at_last();
}; };

View File

@ -137,14 +137,14 @@ void Model::texture(const sb::Texture& texture)
this->texture(texture, DEFAULT_TEXTURE_NAME); this->texture(texture, DEFAULT_TEXTURE_NAME);
} }
/* Set the transformation matrix. */ /* Get the model's transformation matrix. */
const glm::mat4& Model::transformation() const const glm::mat4& Model::transformation() const
{ {
return model_transformation; return model_transformation;
} }
/* Set the transformation matrix. */ /* Set the model's transformation matrix. */
void Model::transform(const glm::mat4& transformation) void Model::transformation(const glm::mat4& transformation)
{ {
model_transformation = transformation; model_transformation = transformation;
} }

View File

@ -35,7 +35,7 @@ private:
inline static const std::string DEFAULT_TEXTURE_NAME = "default"; inline static const std::string DEFAULT_TEXTURE_NAME = "default";
std::map<std::string, sb::Texture> model_textures; std::map<std::string, sb::Texture> model_textures;
std::map<std::string, std::shared_ptr<sb::Attributes>> model_attributes; std::map<std::string, std::shared_ptr<sb::Attributes>> model_attributes;
glm::mat4 model_transformation = glm::mat4(1); glm::mat4 model_transformation {1.0f};
public: public:
@ -56,7 +56,7 @@ public:
void texture(const sb::Texture&, const std::string&); void texture(const sb::Texture&, const std::string&);
void texture(const sb::Texture&); void texture(const sb::Texture&);
const glm::mat4& transformation() const; const glm::mat4& transformation() const;
void transform(const glm::mat4&); void transformation(const glm::mat4&);
std::size_t size(); std::size_t size();
operator glm::mat4() const; operator glm::mat4() const;

View File

@ -243,8 +243,18 @@ void Pudding::load_pads()
} }
nlohmann::json interface = get_configuration()["interface"]; nlohmann::json interface = get_configuration()["interface"];
camera_button.texture(labels["scan"]); camera_button.texture(labels["scan"]);
camera_button.transform({interface["main-button-single-x"], interface["main-button-y"]}, camera_button.translation({interface["main-button-single-x"], interface["main-button-y"]});
interface["main-button-scale"], window_box().aspect()); camera_button.scale(interface["main-button-scale"], window_box().aspect());
inventory_button.texture(labels["inventory"]);
inventory_button.translation({interface["main-button-double-x"], interface["main-button-y"]});
inventory_button.scale(interface["main-button-scale"], window_box().aspect());
previous_button.texture(labels["arrow"]);
previous_button.translation(glm::vec2({-1, 1}) * interface["arrow-button-location"].get<glm::vec2>());
previous_button.scale(interface["arrow-button-scale"], window_box().aspect());
next_button.texture(labels["arrow"]);
next_button.translation(interface["arrow-button-location"]);
next_button.scale(interface["arrow-button-scale"], window_box().aspect());
next_button.rotation(glm::radians(180.0f));
} }
/* Try to create cv::VideoCapture object using device ID #0. If successful, this will create GL texture IDs and storage /* Try to create cv::VideoCapture object using device ID #0. If successful, this will create GL texture IDs and storage
@ -325,18 +335,45 @@ void Pudding::respond(SDL_Event& event)
glm::vec2 ndc { glm::vec2 ndc {
float(mouse.x) / window_box().width() * 2.0f - 1.0f, (1.0f - float(mouse.y) / window_box().height()) * 2.0f - 1.0f float(mouse.x) / window_box().width() * 2.0f - 1.0f, (1.0f - float(mouse.y) / window_box().height()) * 2.0f - 1.0f
}; };
/* Check for collision with the camera button if camera view is open, otherwise check for collision with main viewport. */ bool over_camera_button = !capture.isOpened() && !item_display_active() && camera_button.collide(ndc),
if ((!capture.isOpened() && camera_button.collide(ndc)) || over_inventory_button = items.size() > 0 && !item_display_active() && !capture.isOpened() && inventory_button.collide(ndc),
(capture.isOpened() && get_display().ndc_subsection(main_viewport).collide(ndc))) over_close_area = (capture.isOpened() || item_display_active()) && get_display().ndc_subsection(main_viewport).collide(ndc);
/* Check for collisions with anything clickable */
if (over_camera_button || over_inventory_button || over_close_area)
{ {
/* Set cursor to pokey finger */
if (SDL_GetCursor() != poke.get()) if (SDL_GetCursor() != poke.get())
{ {
SDL_SetCursor(poke.get()); SDL_SetCursor(poke.get());
} }
/* Respond to a click */
if (event.type == SDL_MOUSEBUTTONDOWN) if (event.type == SDL_MOUSEBUTTONDOWN)
{ {
/* Reset cursor to default arrow */
SDL_SetCursor(SDL_GetDefaultCursor()); SDL_SetCursor(SDL_GetDefaultCursor());
camera_switch.toggle(); if (over_camera_button)
{
camera_switch.connect();
}
else if (over_inventory_button)
{
show_item = true;
Box viewport = sb::Display::ndc;
/* Drag viewport completely closed to the bottom of the screen */
viewport.top(viewport.bottom(), true);
nlohmann::json interface = get_configuration()["interface"];
/* Drag viewport back up the height of the pop-up window */
viewport.drag_top(interface["pop-up-viewport-height"]);
/* Get the viewport in pixel resolution to size the buttons to be square inside the viewport */
Box pixel_resolution = get_display().ndc_to_pixel(viewport);
next_button.scale(interface["arrow-button-scale"], pixel_resolution.aspect());
previous_button.scale(interface["arrow-button-scale"], pixel_resolution.aspect());
}
else if (over_close_area)
{
camera_switch.disconnect();
show_item = false;
}
} }
} }
else if (SDL_GetCursor() == poke.get()) else if (SDL_GetCursor() == poke.get())
@ -381,8 +418,14 @@ void Pudding::add_item(const std::string& upc)
if (item.texture_count() > 0) if (item.texture_count() > 0)
{ {
items.push_back(item); items.push_back(item);
/* set item index to end so newest item will display */ /* Set item index to end so newest item will display. */
item_carousel.end(items); item_carousel.end(items);
/* Move the camera button away from center to make room for inventory button if this is the first item added. */
if (items.size() == 1)
{
const nlohmann::json& interface = get_configuration()["interface"];
camera_button.translation({-1.0f * interface["main-button-double-x"].get<float>(), interface["main-button-y"]});
}
} }
else else
{ {
@ -818,7 +861,7 @@ void Pudding::update()
/* shrink viewport if item texture or camera will be displayed */ /* shrink viewport if item texture or camera will be displayed */
if (item_display_active() || capture.isOpened()) if (item_display_active() || capture.isOpened())
{ {
viewport.drag_bottom(0.3f * viewport.height()); viewport.drag_bottom(0.5f * get_configuration()["interface"]["pop-up-viewport-height"].get<float>() * viewport.height());
} }
/* Save the main viewport dimensions */ /* Save the main viewport dimensions */
main_viewport = viewport; main_viewport = viewport;
@ -912,9 +955,16 @@ void Pudding::update()
} }
glViewport(viewport); glViewport(viewport);
current_item().current_texture().bind(); current_item().current_texture().bind();
plane.enable(); current_item().view().enable();
/* draws rectangle vertices and rectangle texture using UV coords */ /* draws rectangle vertices and rectangle texture using UV coords */
glDrawArrays(GL_TRIANGLES, 0, plane.attributes("position")->count()); glDrawArrays(GL_TRIANGLES, 0, current_item().view().attributes("position")->count());
current_item().view().disable();
/* Draw arrows for cycling through items in inventory */
if (items.size() > 1 || current_item().texture_count() > 1)
{
next_button.draw(uniform["flat"]["transformation"]);
previous_button.draw(uniform["flat"]["transformation"]);
}
} }
/* draw the camera if the camera has been opened */ /* draw the camera if the camera has been opened */
if (capture.isOpened()) if (capture.isOpened())
@ -932,10 +982,12 @@ void Pudding::update()
{ {
/* Draw the camera button if neither the camera or inventory is displayed */ /* Draw the camera button if neither the camera or inventory is displayed */
glUseProgram(flat_program); glUseProgram(flat_program);
glUniformMatrix4fv(uniform["flat"]["transformation"], 1, GL_FALSE, &camera_button.transformation()[0][0]); camera_button.draw(uniform["flat"]["transformation"]);
camera_button.texture().bind(); /* And the inventory button if there are items scanned into the inventory */
plane.enable(); if (items.size() > 0)
glDrawArrays(GL_TRIANGLES, 0, camera_button.attributes("position")->count()); {
inventory_button.draw(uniform["flat"]["transformation"]);
}
} }
SDL_GL_SwapWindow(get_window()); SDL_GL_SwapWindow(get_window());
sb::Log::gl_errors("at end of update"); sb::Log::gl_errors("at end of update");
@ -947,42 +999,74 @@ void Pudding::update()
} }
} }
/* Construct a Pad using a texture, an offset, a scale, and a callback function. A Pad is a Plane which can be clicked /* Construct a Pad using a texture, a translation, a scale, and a callback function. A Pad is a Plane which can be clicked
* to launch an arbitrary user function. It can be sized and placed by setting the offset and scale values. The offset * to launch an arbitrary user function. It can be sized and placed by setting the translation and scale values. The translation
* is relative to (0.0, 0.0), and the scale is relative to the Plane, which has opposite corners at (-1.0, -1.0) and * is relative to (0.0, 0.0), and the scale is relative to the Plane, which has opposite corners at (-1.0, -1.0) and (1.0, 1.0).
* (1.0, 1.0). * The texture is the graphic that displays in the Pad location. The callback must be a function that * The texture is the graphic that displays in the Pad location. The callback must be a function that doesn't return a value or
* doesn't return a value or accept any arguments. */ * accept any arguments. */
Pad::Pad(sb::Texture texture, glm::vec2 offset, float scale, float ratio, std::function<void()> on_connect, float rotation) Pad::Pad(sb::Texture texture, glm::vec2 translation, float scale, float ratio, std::function<void()> on_connect, float rotation)
{ {
this->texture(texture); this->texture(texture);
transform(offset, scale, ratio, rotation); this->translation(translation);
this->scale(scale, ratio);
if (rotation)
{
this->rotation(rotation);
}
this->on_connect(on_connect); this->on_connect(on_connect);
box.invert_y(true); collision_box.invert_y(true);
} }
/* Set the Pad's transformation matrix based on an offset, a scale, an aspect ratio, and a rotation. The offset is the /* Set angle in radians the pad will be rotated. The pad will be rotated around its center. The collision box will not
* amount it will be shifted in the (x, y) plane. The scale is a value relative to the (x, y) plane, and it is a single * change, so the box will not contain the entire pad if the angle is not a multiple of pi/2. The pad's transformation
* value because the aspect ratio will determine how much each axis is scaled. If the aspect ratio is above one, the * matrix will automatically be set to incorporate this rotation transformation. */
* x-axis's scale will be divided by the ratio. If the aspect ratio is below one, the y-axis's scale will be multiplied void Pad::rotation(float angle)
* by the aspect ratio. If the aspect ratio of the window is given, this will force the Pad to display as a square, and
* the ratio will be relative to the shorter axis. The rotation is an angle in radians to rotate the pad object around
* its center after it has been offset and scaled. */
void Pad::transform(glm::vec2 offset, float scale, float ratio, float rotation)
{ {
glm::vec3 scale_components { scale, scale, 1 }; rotation_angle = angle;
if (ratio > 1.0f) transform();
}
/* Set the scale using a factor and ratio that will transform the pad in the X and Y dimensions. The ratio will determine
* how much each axis is scaled. If the ratio is above one, the X-axis's scale will be divided by the ratio. If the ratio
* is below one, the Y-axis's scale will be multiplied by the aspect ratio. If the aspect ratio of the window is given,
* this will force the pad to display as a square, and the ratio will be relative to the shorter axis. The collision box
* will be scaled by the same factors. The pad's transformation matrix will automatically be set to incorporate this
* scale transformation. */
void Pad::scale(float factor, float ratio)
{
scale_factor = factor;
scale_ratio = ratio;
transform();
}
/* Set a translation for the pad object in the X and Y dimension using a 2d vector. The collision box will be moved by the
* same translation. The pad's transformation matrix will automatically be set to incorporate this translation
* transformation. */
void Pad::translation(const glm::vec2& translation)
{
translation_vector = translation;
transform();
}
/* Set the transformation matrix for the pad object by applying the scale to the translation and the rotation to the
* resulting matrix, meaning the transformations will be applied to the pad in the order of: translate, scale, and
* rotate. The collision box will be scaled and moved to fit around the position coordinates that would result from
* applying this transformation to the position coordinates. */
void Pad::transform()
{
glm::vec3 scale { scale_factor, scale_factor, 1.0f };
if (scale_ratio > 1.0f)
{ {
scale_components.x /= ratio; scale.x /= scale_ratio;
} }
else if (ratio < 1.0f) else if (scale_ratio < 1.0f)
{ {
scale_components.y *= ratio; scale.y *= scale_ratio;
} }
box.size({scale_components.x * 2, scale_components.y * 2}); collision_box.size(2.0f * glm::vec2{scale.x, scale.y}, true);
box.center(offset); collision_box.center(translation_vector);
std::cout << "pad box is " << box << std::endl; Model::transformation(glm::translate(glm::vec3{translation_vector.x, translation_vector.y, 0.0f}) *
Model::transform(glm::translate(glm::vec3{offset.x, offset.y, 0}) * glm::scale(scale_components) * glm::scale(scale) * glm::rotate(rotation_angle, ROTATION_AXIS));
glm::rotate(rotation, glm::vec3{0.0f, 0.0f, 1.0f}));
} }
/* Set the function that will run when a pad object is clicked. */ /* Set the function that will run when a pad object is clicked. */
@ -991,11 +1075,19 @@ void Pad::on_connect(std::function<void()> on_connect)
connection.on_connect(on_connect); connection.on_connect(on_connect);
} }
/* Returns true if the point at position collides with the box containing the pad object, which is the box the pad /* Returns true if the point at position collides with the pad's collision box. */
* object fits inside after the transform is applied. */
bool Pad::collide(const glm::vec2& position) const bool Pad::collide(const glm::vec2& position) const
{ {
return box.collide(position); return collision_box.collide(position);
}
void Pad::draw(GLuint uniform_id)
{
glUniformMatrix4fv(uniform_id, 1, GL_FALSE, &transformation()[0][0]);
texture().bind();
enable();
glDrawArrays(GL_TRIANGLES, 0, attributes("position")->count());
disable();
} }
void glViewport(Box box) void glViewport(Box box)

View File

@ -7,8 +7,11 @@
// GUNKISS \\ \ \ // GUNKISS \\ \ \
//_________________________\\ `---------------------------------------------------------------*/ //_________________________\\ `---------------------------------------------------------------*/
#ifndef Pudding_h_ #ifndef PUDDING_H_
#define Pudding_h_ #define PUDDING_H_
/* Needed for functions in glm/gtx/ */
#define GLM_ENABLE_EXPERIMENTAL
#include <stdlib.h> #include <stdlib.h>
#include <string> #include <string>
@ -24,6 +27,7 @@
#include "sdl2-gfx/SDL2_gfxPrimitives.h" #include "sdl2-gfx/SDL2_gfxPrimitives.h"
#include "json/json.hpp" #include "json/json.hpp"
#include "glm/glm.hpp" #include "glm/glm.hpp"
#include "glm/gtx/matrix_decompose.hpp"
#include "opencv2/core.hpp" #include "opencv2/core.hpp"
#include "opencv2/videoio.hpp" #include "opencv2/videoio.hpp"
#include "opencv2/highgui.hpp" #include "opencv2/highgui.hpp"
@ -196,17 +200,25 @@ class Pad : public Plane
private: private:
inline static const glm::vec3 ROTATION_AXIS {0.0f, 0.0f, 1.0f};
using callback = std::function<void()>; using callback = std::function<void()>;
Connection<> connection; Connection<> connection;
Box box; Box collision_box;
float rotation_angle = 0.0f, scale_factor = 0.0f, scale_ratio = 1.0f;
glm::vec2 translation_vector {0.0f, 0.0f};
void transform();
public: public:
Pad() {}; Pad() {};
Pad(sb::Texture, glm::vec2, float, float, callback, float = 0.0f); Pad(sb::Texture, glm::vec2, float, float, callback, float = 0.0f);
void transform(glm::vec2, float, float = 1.0f, float = 0.0f); void rotation(float);
void scale(float, float = 1.0f);
void translation(const glm::vec2&);
void on_connect(callback); void on_connect(callback);
bool collide(const glm::vec2&) const; bool collide(const glm::vec2&) const;
void draw(GLuint);
}; };
@ -267,7 +279,7 @@ private:
sb::VBO vbo; sb::VBO vbo;
std::map<std::string, sb::Texture> labels; std::map<std::string, sb::Texture> labels;
Pad camera_button, previous_button, next_button, inventory_button; Pad camera_button, previous_button, next_button, inventory_button;
Box viewport, main_viewport; Box viewport, main_viewport, pop_up_viewport;
void load_pudding_model(float, float, int, int = 1, float = -1.0f, float = 1.0f, float = 0.3f); void load_pudding_model(float, float, int, int = 1, float = -1.0f, float = 1.0f, float = 0.3f);
void load_gl_context(); void load_gl_context();