multiple buttons wip
This commit is contained in:
parent
e0b1d1fd53
commit
16cc5406cf
|
@ -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
2
lib/sb
|
@ -1 +1 @@
|
|||
Subproject commit 54cf01246b0e5ec81ba5b9158248bca7492823db
|
||||
Subproject commit 660865b2f21b61f145f351514e51b26e3f0b68e9
|
|
@ -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
|
||||
|
|
27
src/Item.cpp
27
src/Item.cpp
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
178
src/Pudding.cpp
178
src/Pudding.cpp
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue