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,
"json-save": true,
"json-save-directory": "local/scans",
"barcode": "",
"barcode": "014100085980",
"capture-device": "/dev/video0"
},
"api":
@ -86,12 +86,13 @@
{
"main-button-y": -0.75,
"main-button-single-x": 0.0,
"main-button-double-x": 0.65,
"main-button-double-x": 0.6,
"main-button-scale": 0.25,
"camera-button-label": "scan",
"inventory-button-label": "inventory",
"arrow-button-location": [0.75, 0.0],
"arrow-button-scale": 0.1,
"arrow-button-label": "arrow"
"arrow-button-scale": 0.2,
"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:
template<typename Container>
auto current(Container& container)
auto current(Container& container) const
{
auto location = container.begin();
if (location != container.end())
@ -39,35 +39,49 @@ public:
}
template<typename Container>
auto next(const Container& container)
void next(const Container& container)
{
offset = ++offset % container.size();
}
template<typename Container>
auto previous(const Container& container)
void previous(const Container& container)
{
offset = sb::mod(--offset, container.size());
}
template<typename Container>
auto increment(const Container& container, int amount)
void increment(const Container& container, int amount)
{
offset = sb::mod(offset + amount, container.size());
}
template<typename Container>
auto beginning()
void beginning()
{
offset = 0;
}
template<typename Container>
auto end(const Container& container)
void end(const Container& container)
{
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

View File

@ -74,25 +74,42 @@ std::string Item::full_name() const
void Item::texture(sb::Texture& texture, const std::string& name)
{
image.texture(texture, name);
item_view.texture(texture, name);
}
sb::Texture& Item::current_texture()
{
return carousel.current(image.textures())->second;
return carousel.current(item_view.textures())->second;
}
void Item::next_texture()
{
carousel.next(image.textures());
carousel.next(item_view.textures());
}
void Item::previous_texture()
{
carousel.previous(image.textures());
carousel.previous(item_view.textures());
}
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:
nlohmann::json json = {};
Plane image;
Plane item_view;
Carousel carousel;
std::string item_brand_name = "", item_product_name = "", item_upc = "";
void text_property(const std::string&, std::string&, const std::string&);
@ -60,6 +60,9 @@ public:
void next_texture();
void previous_texture();
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);
}
/* Set the transformation matrix. */
/* Get the model's transformation matrix. */
const glm::mat4& Model::transformation() const
{
return model_transformation;
}
/* Set the transformation matrix. */
void Model::transform(const glm::mat4& transformation)
/* Set the model's transformation matrix. */
void Model::transformation(const glm::mat4& transformation)
{
model_transformation = transformation;
}

View File

@ -35,7 +35,7 @@ private:
inline static const std::string DEFAULT_TEXTURE_NAME = "default";
std::map<std::string, sb::Texture> model_textures;
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:
@ -56,7 +56,7 @@ public:
void texture(const sb::Texture&, const std::string&);
void texture(const sb::Texture&);
const glm::mat4& transformation() const;
void transform(const glm::mat4&);
void transformation(const glm::mat4&);
std::size_t size();
operator glm::mat4() const;

View File

@ -243,8 +243,18 @@ void Pudding::load_pads()
}
nlohmann::json interface = get_configuration()["interface"];
camera_button.texture(labels["scan"]);
camera_button.transform({interface["main-button-single-x"], interface["main-button-y"]},
interface["main-button-scale"], window_box().aspect());
camera_button.translation({interface["main-button-single-x"], interface["main-button-y"]});
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
@ -325,18 +335,45 @@ void Pudding::respond(SDL_Event& event)
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
};
/* Check for collision with the camera button if camera view is open, otherwise check for collision with main viewport. */
if ((!capture.isOpened() && camera_button.collide(ndc)) ||
(capture.isOpened() && get_display().ndc_subsection(main_viewport).collide(ndc)))
bool over_camera_button = !capture.isOpened() && !item_display_active() && camera_button.collide(ndc),
over_inventory_button = items.size() > 0 && !item_display_active() && !capture.isOpened() && inventory_button.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())
{
SDL_SetCursor(poke.get());
}
/* Respond to a click */
if (event.type == SDL_MOUSEBUTTONDOWN)
{
/* Reset cursor to default arrow */
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())
@ -381,8 +418,14 @@ void Pudding::add_item(const std::string& upc)
if (item.texture_count() > 0)
{
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);
/* 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
{
@ -818,7 +861,7 @@ void Pudding::update()
/* shrink viewport if item texture or camera will be displayed */
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 */
main_viewport = viewport;
@ -912,9 +955,16 @@ void Pudding::update()
}
glViewport(viewport);
current_item().current_texture().bind();
plane.enable();
current_item().view().enable();
/* 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 */
if (capture.isOpened())
@ -932,10 +982,12 @@ void Pudding::update()
{
/* Draw the camera button if neither the camera or inventory is displayed */
glUseProgram(flat_program);
glUniformMatrix4fv(uniform["flat"]["transformation"], 1, GL_FALSE, &camera_button.transformation()[0][0]);
camera_button.texture().bind();
plane.enable();
glDrawArrays(GL_TRIANGLES, 0, camera_button.attributes("position")->count());
camera_button.draw(uniform["flat"]["transformation"]);
/* And the inventory button if there are items scanned into the inventory */
if (items.size() > 0)
{
inventory_button.draw(uniform["flat"]["transformation"]);
}
}
SDL_GL_SwapWindow(get_window());
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
* to launch an arbitrary user function. It can be sized and placed by setting the offset and scale values. The offset
* 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). * The texture is the graphic that displays in the Pad location. The callback must be a function that
* doesn't return a value or accept any arguments. */
Pad::Pad(sb::Texture texture, glm::vec2 offset, float scale, float ratio, std::function<void()> on_connect, float rotation)
/* 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 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 (1.0, 1.0).
* The texture is the graphic that displays in the Pad location. The callback must be a function that doesn't return a value or
* accept any arguments. */
Pad::Pad(sb::Texture texture, glm::vec2 translation, float scale, float ratio, std::function<void()> on_connect, float rotation)
{
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);
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
* 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
* value because the aspect ratio will determine how much each axis is scaled. If the aspect ratio is above one, the
* 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
* 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)
/* Set angle in radians the pad will be rotated. The pad will be rotated around its center. The collision box will not
* change, so the box will not contain the entire pad if the angle is not a multiple of pi/2. The pad's transformation
* matrix will automatically be set to incorporate this rotation transformation. */
void Pad::rotation(float angle)
{
glm::vec3 scale_components { scale, scale, 1 };
if (ratio > 1.0f)
rotation_angle = angle;
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});
box.center(offset);
std::cout << "pad box is " << box << std::endl;
Model::transform(glm::translate(glm::vec3{offset.x, offset.y, 0}) * glm::scale(scale_components) *
glm::rotate(rotation, glm::vec3{0.0f, 0.0f, 1.0f}));
collision_box.size(2.0f * glm::vec2{scale.x, scale.y}, true);
collision_box.center(translation_vector);
Model::transformation(glm::translate(glm::vec3{translation_vector.x, translation_vector.y, 0.0f}) *
glm::scale(scale) * glm::rotate(rotation_angle, ROTATION_AXIS));
}
/* 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);
}
/* Returns true if the point at position collides with the box containing the pad object, which is the box the pad
* object fits inside after the transform is applied. */
/* Returns true if the point at position collides with the pad's collision box. */
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)

View File

@ -7,8 +7,11 @@
// GUNKISS \\ \ \
//_________________________\\ `---------------------------------------------------------------*/
#ifndef Pudding_h_
#define Pudding_h_
#ifndef PUDDING_H_
#define PUDDING_H_
/* Needed for functions in glm/gtx/ */
#define GLM_ENABLE_EXPERIMENTAL
#include <stdlib.h>
#include <string>
@ -24,6 +27,7 @@
#include "sdl2-gfx/SDL2_gfxPrimitives.h"
#include "json/json.hpp"
#include "glm/glm.hpp"
#include "glm/gtx/matrix_decompose.hpp"
#include "opencv2/core.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/highgui.hpp"
@ -196,17 +200,25 @@ class Pad : public Plane
private:
inline static const glm::vec3 ROTATION_AXIS {0.0f, 0.0f, 1.0f};
using callback = std::function<void()>;
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:
Pad() {};
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);
bool collide(const glm::vec2&) const;
void draw(GLuint);
};
@ -267,7 +279,7 @@ private:
sb::VBO vbo;
std::map<std::string, sb::Texture> labels;
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_gl_context();