diff --git a/Makefile b/Makefile index 32a954b..64ea454 100644 --- a/Makefile +++ b/Makefile @@ -44,9 +44,9 @@ CREATE_FONT_SYMLINK := ln -nsf $(SB_DIR)"BPmono.ttf" . SDL_CFLAGS = $(shell $(SDLCONFIG) --cflags) SDL_LFLAGS := $(shell $(SDLCONFIG) --libs) SB_H_FILES := $(wildcard $(addprefix $(SB_SRC_DIR),*.hpp)) -SB_O_FILES := $(filter-out $(addprefix $(SB_SRC_DIR),filesystem.o),$(SB_H_FILES:.hpp=.o)) +SB_O_FILES := $(filter-out $(addprefix $(SB_SRC_DIR),filesystem.o utility.o),$(SB_H_FILES:.hpp=.o)) SRC_H_FILES := $(wildcard $(addprefix $(SRC_DIR),*.hpp)) -SRC_O_FILES := $(SRC_H_FILES:.hpp=.o) +SRC_O_FILES := $(filter-out $(addprefix $(SRC_DIR),Carousel.o),$(SRC_H_FILES:.hpp=.o)) ##################################################################### # Targets for building [SPACE BOX], dependencies and project source # @@ -69,14 +69,14 @@ $(SB_SRC_DIR)Delegate.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Input.hpp) $(SB_SRC_DIR)Display.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Box.hpp Configuration.hpp Delegate.hpp Log.hpp) $(SB_SRC_DIR)Box.o : $(addprefix $(SB_SRC_DIR),extension.hpp Segment.hpp) $(SB_SRC_DIR)Segment.o : $(addprefix $(SB_SRC_DIR),extension.hpp Box.hpp) -$(SB_SRC_DIR)Pixels.o : $(addprefix $(SB_SRC_DIR),Box.hpp extension.hpp Log.hpp) +$(SB_SRC_DIR)Pixels.o : $(addprefix $(SB_SRC_DIR),Box.hpp extension.hpp Log.hpp utility.hpp) $(SB_SRC_DIR)Audio.o : $(addprefix $(SB_SRC_DIR),Node.hpp Display.hpp Configuration.hpp Box.hpp filesystem.hpp extension.hpp) $(SB_SRC_DIR)GLObject.o : $(addprefix $(SB_SRC_DIR),Log.hpp) $(SB_SRC_DIR)Texture.o : $(addprefix $(SB_SRC_DIR),GLObject.hpp filesystem.hpp Log.hpp) $(SB_SRC_DIR)VBO.o : $(addprefix $(SB_SRC_DIR),Log.hpp GLObject.hpp Attributes.hpp extension.hpp) $(SB_SRC_DIR)Attributes.o : $(addprefix $(SB_SRC_DIR),Log.hpp extension.hpp) -$(SRC_DIR)Model.o : $(addprefix $(SB_SRC_DIR),extension.hpp Attributes.hpp Texture.hpp) -$(SRC_DIR)Item.o : $(addprefix $(SB_SRC_DIR),extension.hpp Node.hpp Texture.hpp Log.hpp) $(SRC_DIR)Model.hpp +$(SRC_DIR)Model.o : $(addprefix $(SB_SRC_DIR),extension.hpp Attributes.hpp Texture.hpp) $(addprefix $(SRC_DIR),Carousel.hpp) +$(SRC_DIR)Item.o : $(addprefix $(SB_SRC_DIR),Texture.hpp Log.hpp) $(addprefix $(SRC_DIR),Model.hpp Carousel.hpp) $(SRC_DIR)Pudding.o : $(SRC_H_FILES) $(SB_H_FILES) %.o : %.cpp %.hpp $(CPPC) $(CPP_FLAGS) $< -c -o $@ diff --git a/config.json b/config.json index 2b6a24c..b7f3df0 100644 --- a/config.json +++ b/config.json @@ -47,7 +47,7 @@ "enabled": true, "json-save": true, "json-save-directory": "local/scans", - "barcode": "8410076472953", + "barcode": "400063314395", "capture-device": "/dev/video0" }, "api": diff --git a/lib/sb b/lib/sb index 2c0517b..dd736f3 160000 --- a/lib/sb +++ b/lib/sb @@ -1 +1 @@ -Subproject commit 2c0517b9e28ab2be46c1402bab2dafffd24c6637 +Subproject commit dd736f341fb49380a4d33d14859c7632853d06e0 diff --git a/src/Carousel.hpp b/src/Carousel.hpp new file mode 100644 index 0000000..6ba5d93 --- /dev/null +++ b/src/Carousel.hpp @@ -0,0 +1,73 @@ +/* _______________ ,----------------------------------------------------------------. + //`````````````\\ \ \ + //~~~~~~~~~~~~~~~\\ \ by @ohsqueezy & @sleepin \ + //=================\\ \ [ohsqueezy.itch.io] [sleepin.itch.io] \ + // \\ \ \ + // \\ \ code released under the zlib license [git.nugget.fun/pudding] \ + // ☆ GUNKISS ☆ \\ \ \ +//_________________________\\ `---------------------------------------------------------------*/ + +#ifndef CAROUSEL_H_ +#define CAROUSEL_H_ + +#include +#include +#include "utility.hpp" + +class Carousel +{ + +private: + + std::uint32_t offset = 0; + +public: + + template + auto current(Container& container) + { + auto location = container.begin(); + if (location != container.end()) + { + std::advance(location, offset); + return location; + } + else + { + throw std::out_of_range("Container is empty"); + } + } + + template + auto next(const Container& container) + { + offset = ++offset % container.size(); + } + + template + auto previous(const Container& container) + { + offset = sb::mod(--offset, container.size()); + } + + template + auto increment(const Container& container, int amount) + { + offset = sb::mod(offset + amount, container.size()); + } + + template + auto beginning() + { + offset = 0; + } + + template + auto end(const Container& container) + { + offset = container.size() - 1; + } + +}; + +#endif diff --git a/src/Item.cpp b/src/Item.cpp index a01b1f9..678a1fc 100644 --- a/src/Item.cpp +++ b/src/Item.cpp @@ -9,85 +9,91 @@ #include "Item.hpp" -Item::Item(Node* parent) : Node(parent) {}; +Item::Item() {}; -void Item::set_text_property(const std::string& value, std::string& property, const std::string& property_name) +void Item::text_property(const std::string& value, std::string& property, const std::string& property_name) { if (property == "") { if (value != "") { property = value; - sb::Log::log("set " + property_name + " to " + property + " in " + get_full_name()); + sb::Log::log("set " + property_name + " to " + property + " in " + full_name()); } else { - sb::Log::log("empty string passed, not setting " + property_name + " in " + get_full_name(), sb::Log::DEBUG); + sb::Log::log("empty string passed, not setting " + property_name + " in " + full_name(), sb::Log::DEBUG); } } else { - sb::Log::log(property_name + " already set to " + property + " in " + get_full_name() + ", not setting", sb::Log::DEBUG); + sb::Log::log(property_name + " already set to " + property + " in " + full_name() + ", not setting", sb::Log::DEBUG); } } -void Item::add_image_texture(sb::Texture texture) +void Item::brand_name(const std::string& name) { - image_textures.push_back(texture); + text_property(name, item_brand_name, "brand name"); } -const std::vector& Item::get_image_textures() const +const std::string& Item::brand_name() const { - return image_textures; + return item_brand_name; } -const sb::Texture& Item::get_active_image_texture() const +void Item::product_name(const std::string& name) { - return get_image_textures()[current_image_index]; + text_property(name, item_product_name, "product name"); } -void Item::set_brand_name(const std::string& name) +const std::string& Item::product_name() const { - set_text_property(name, brand_name, "brand name"); + return item_product_name; } -const std::string& Item::get_brand_name() const +void Item::upc(const std::string& upc) { - return brand_name; + text_property(upc, item_upc, "UPC"); } -void Item::set_product_name(const std::string& name) +const std::string& Item::upc() const { - set_text_property(name, product_name, "product name"); + return item_upc; } -const std::string& Item::get_product_name() const +std::string Item::full_name() const { - return product_name; -} - -void Item::set_upc(const std::string& upc) -{ - set_text_property(upc, this->upc, "UPC"); -} - -const std::string& Item::get_upc() const -{ - return upc; -} - -std::string Item::get_full_name() const -{ - std::string name = get_brand_name(); + std::string name = brand_name(); if (name != "") { name += " "; } - name += get_product_name(); + name += product_name(); return name; } -void Item::increment_image_index(int increment) +void Item::texture(sb::Texture& texture, const std::string& name) { - current_image_index = sb::mod(current_image_index + increment, static_cast(get_image_textures().size())); + image.texture(texture, name); +} + +sb::Texture& Item::current_texture() +{ + // return image.texture().begin()->second; + return carousel.current(image.texture())->second; +} + +void Item::next_texture() +{ + carousel.next(image.texture()); +} + +void Item::previous_texture() +{ + carousel.previous(image.texture()); +} + +std::size_t Item::texture_count() +{ + return image.texture().size(); } diff --git a/src/Item.hpp b/src/Item.hpp index f72ace8..b54c24e 100644 --- a/src/Item.hpp +++ b/src/Item.hpp @@ -24,23 +24,21 @@ #include #include #include "json/json.hpp" -#include "extension.hpp" -#include "Node.hpp" #include "Texture.hpp" #include "Log.hpp" #include "Model.hpp" +#include "Carousel.hpp" -class Item : public Node +class Item { private: nlohmann::json json = {}; - // Plane image; - std::vector image_textures; - std::string brand_name = "", product_name = "", upc = ""; - int current_image_index = 0; - void set_text_property(const std::string&, std::string&, const std::string&); + Plane image; + Carousel carousel; + std::string item_brand_name = "", item_product_name = "", item_upc = ""; + void text_property(const std::string&, std::string&, const std::string&); /* * add properties: ingredients, protein weight, nutrition grade, popularity, "serving unit", keywords, @@ -49,18 +47,19 @@ private: public: - Item(Node*); - void add_image_texture(sb::Texture); - const std::vector& get_image_textures() const; - const sb::Texture& get_active_image_texture() const; - void set_brand_name(const std::string&); - const std::string& get_brand_name() const; - void set_product_name(const std::string&); - const std::string& get_product_name() const; - void set_upc(const std::string&); - const std::string& get_upc() const; - std::string get_full_name() const; - void increment_image_index(int = 1); + Item(); + void texture(sb::Texture&, const std::string&); + void brand_name(const std::string&); + const std::string& brand_name() const; + void product_name(const std::string&); + const std::string& product_name() const; + void upc(const std::string&); + const std::string& upc() const; + std::string full_name() const; + sb::Texture& current_texture(); + void next_texture(); + void previous_texture(); + std::size_t texture_count(); }; diff --git a/src/Model.cpp b/src/Model.cpp index fb45400..df9c1f5 100644 --- a/src/Model.cpp +++ b/src/Model.cpp @@ -18,7 +18,7 @@ Model::Model(const std::map>& attri { for (auto attributes : attributes_pack) { - this->attributes(attributes.first, attributes.second); + this->attributes(attributes.second, attributes.first); } } @@ -29,7 +29,7 @@ Model::Model(const std::map& attributes_pack) { for (auto attributes : attributes_pack) { - this->attributes(attributes.first, attributes.second); + this->attributes(attributes.second, attributes.first); } } @@ -39,7 +39,7 @@ Model::Model(const std::initializer_list& names) { for (const std::string& name : names) { - this->attributes(name, sb::Attributes()); + this->attributes(sb::Attributes(), name); } } @@ -67,20 +67,20 @@ std::shared_ptr& Model::operator[](const std::string& name) auto element = model_attributes.find(name); if (element == model_attributes.end()) { - attributes(name, sb::Attributes{}); + attributes(sb::Attributes{}, name); } return model_attributes[name]; } /* Assign name to attributes, copy and wrap in a shared pointer. The model can share * ownership of the created attribute memory with callers that request it. */ -void Model::attributes(const std::string& name, const sb::Attributes& attributes) +void Model::attributes(const sb::Attributes& attributes, const std::string& name) { - this->attributes(name, std::make_shared(attributes)); + this->attributes(std::make_shared(attributes), name); } /* Assign name to attributes and share ownership. */ -void Model::attributes(const std::string& name, const std::shared_ptr& attributes) +void Model::attributes(const std::shared_ptr& attributes, const std::string& name) { model_attributes[name] = attributes; } @@ -103,6 +103,12 @@ void Model::disable() } } +/* Return a reference to the texture container. */ +std::map& Model::texture() +{ + return model_texture; +} + /* Get the texture at name. This can be used to read the texture memory, share ownership of it, or * anything else a Texture object can be used for with direct calls to GL functions. */ sb::Texture& Model::texture(const std::string& name) @@ -111,7 +117,7 @@ sb::Texture& Model::texture(const std::string& name) } /* Assign name to texture and share ownership. */ -void Model::texture(const std::string& name, const sb::Texture& texture) +void Model::texture(const sb::Texture& texture, const std::string& name) { model_texture[name] = texture; } @@ -143,21 +149,19 @@ Model::operator glm::mat4() const * start over from the beginning. */ void Background::next() { - offset = ++offset % model_texture.size(); + carousel.next(model_texture); } -/* Return the currently active tile texture. */ -const sb::Texture& Background::current() const +/* Return the currently active texture. */ +sb::Texture& Background::current() { - auto position = model_texture.begin(); - std::advance(position, offset); - return position->second; + return carousel.current(model_texture)->second; } CameraView::CameraView() : Plane() { - texture("front", sb::Texture()); - texture("back", sb::Texture()); + texture(sb::Texture(), "front"); + texture(sb::Texture(), "back"); } void CameraView::generate(const glm::vec2& size) diff --git a/src/Model.hpp b/src/Model.hpp index 2f83e0a..d7b5a23 100644 --- a/src/Model.hpp +++ b/src/Model.hpp @@ -25,6 +25,7 @@ #include "glm/glm.hpp" #include "Attributes.hpp" #include "Texture.hpp" +#include "Carousel.hpp" class Model { @@ -43,13 +44,14 @@ public: Model(const std::initializer_list&); std::map>& attributes(); std::shared_ptr& attributes(const std::string&); - void attributes(const std::string&, const sb::Attributes&); - void attributes(const std::string&, const std::shared_ptr&); + void attributes(const sb::Attributes&, const std::string&); + void attributes(const std::shared_ptr&, const std::string&); std::shared_ptr& operator[](const std::string&); void enable(); void disable(); + std::map& texture(); sb::Texture& texture(const std::string&); - void texture(const std::string&, const sb::Texture&); + void texture(const sb::Texture&, const std::string&); void transformation(const glm::mat4&); std::size_t size(); operator glm::mat4() const; @@ -74,19 +76,19 @@ public: }; -/* Plane that stores multiple textures which can be cycled through and looped. Only one texture is - * active at a time, returned by Background::current. Cycle only goes forward, using Background::next. */ +/* A Plane that contains a Carousel for cycling through active textures. Only one texture is + * active at a time, returned by Background::current. Carousel only goes forward, using Background::next. */ class Background : public Plane { private: - std::uint8_t offset = 0; + Carousel carousel; public: void next(); - const sb::Texture& current() const; + sb::Texture& current(); }; diff --git a/src/Pudding.cpp b/src/Pudding.cpp index 66fe6ba..8ad409a 100644 --- a/src/Pudding.cpp +++ b/src/Pudding.cpp @@ -220,7 +220,7 @@ void Pudding::load_tiles() { sb::Texture texture = sb::Texture(path); texture.load(); - background.texture(path, texture); + background.texture(texture, path); } } @@ -255,24 +255,24 @@ void Pudding::respond(SDL_Event& event) { if (get_delegate().compare(event, "up")) { - increment_item_index(); + item_carousel.next(items); } else if (get_delegate().compare(event, "right")) { if (items.size() > 0) { - current_item().increment_image_index(); + current_item().next_texture(); } } else if (get_delegate().compare(event, "down")) { - increment_item_index(-1); + item_carousel.previous(items); } else if (get_delegate().compare(event, "left")) { if (items.size() > 0) { - current_item().increment_image_index(-1); + current_item().previous_texture(); } } else if (get_delegate().compare(event, "toggle-camera")) @@ -309,8 +309,8 @@ void Pudding::respond(SDL_Event& event) */ void Pudding::add_item(const std::string& upc) { - Item item(this); - item.set_upc(upc); + Item item; + item.upc(upc); if (get_configuration()["api"]["open-food-enabled"]) { incorporate_open_food_api(item); @@ -327,11 +327,11 @@ void Pudding::add_item(const std::string& upc) { incorporate_best_buy_api(item); } - if (item.get_image_textures().size() > 0) + if (item.texture_count() > 0) { items.push_back(item); /* set item index to end so newest item will display */ - current_item_index = items.size() - 1; + item_carousel.end(items); } else { @@ -346,7 +346,7 @@ void Pudding::add_item(const std::string& upc) void Pudding::incorporate_open_food_api(Item& item) { sb::Log::log("checking Open Food API"); - nlohmann::json json = json_from_url(OPEN_FOOD_API_URL + item.get_upc()); + nlohmann::json json = json_from_url(OPEN_FOOD_API_URL + item.upc()); /* test that should determine if an Open Food API response is not empty */ if (json.value("status", 0) && json.contains("product")) { @@ -356,11 +356,11 @@ void Pudding::incorporate_open_food_api(Item& item) sb::Texture texture = texture_from_image_url(url); if (texture.generated()) { - item.add_image_texture(texture); + item.texture(texture, url); } } - item.set_brand_name(json["product"].value("brands", "")); - item.set_product_name(json["product"].value("product_name", "")); + item.brand_name(json["product"].value("brands", "")); + item.product_name(json["product"].value("product_name", "")); save_item_json(json, item, "Open_Food_API"); } else @@ -376,7 +376,7 @@ void Pudding::incorporate_nutronix_api(Item& item) sb::Log::log("checking Nutronix API"); /* Nutronix requires API keys in headers for validation */ nlohmann::json json = json_from_url( - NUTRONIX_API_URL + item.get_upc(), { + NUTRONIX_API_URL + item.upc(), { "x-app-id: " + get_configuration()["api"]["nutronix-app-id"].get(), "x-app-key: " + get_configuration()["api"]["nutronix-app-key"].get() }); @@ -391,11 +391,11 @@ void Pudding::incorporate_nutronix_api(Item& item) sb::Texture texture = texture_from_image_url(url); if (texture.generated()) { - item.add_image_texture(texture); + item.texture(texture, url); } } - item.set_brand_name(food.value("brand_name", "")); - item.set_product_name(food.value("food_name", "")); + item.brand_name(food.value("brand_name", "")); + item.product_name(food.value("food_name", "")); save_item_json(json, item, "Nutronix_API"); } else @@ -411,7 +411,7 @@ void Pudding::incorporate_edamam_api(Item& item) sb::Log::log("checking Edamam API"); /* build API url by concatenating relevant values into query string */ std::stringstream url; - url << "https://api.edamam.com/api/food-database/v2/parser?upc=" << item.get_upc() << "&app_id=" << + url << "https://api.edamam.com/api/food-database/v2/parser?upc=" << item.upc() << "&app_id=" << get_configuration()["api"]["edamam-app-id"].get() << "&app_key=" << get_configuration()["api"]["edamam-app-key"].get(); nlohmann::json json = json_from_url(url.str()); @@ -425,9 +425,9 @@ void Pudding::incorporate_edamam_api(Item& item) sb::Texture texture = texture_from_image_url(url); if (texture.generated()) { - item.add_image_texture(texture); + item.texture(texture, url); } - item.set_product_name(food.value("label", "")); + item.product_name(food.value("label", "")); } save_item_json(json, item, "Edamam_API"); } @@ -444,7 +444,7 @@ void Pudding::incorporate_best_buy_api(Item& item) sb::Log::log("checking Best Buy API"); /* build API url by concatenating relevant values into query string */ std::stringstream url; - url << "https://api.bestbuy.com/v1/products(upc=" << item.get_upc() << ")?format=json&apiKey=" << + url << "https://api.bestbuy.com/v1/products(upc=" << item.upc() << ")?format=json&apiKey=" << get_configuration()["api"]["best-buy-api-key"].get(); nlohmann::json json = json_from_url(url.str()); /* test that should determine if a Best Buy response has a result */ @@ -460,11 +460,11 @@ void Pudding::incorporate_best_buy_api(Item& item) sb::Texture texture = texture_from_image_url(url); if (texture.generated()) { - item.add_image_texture(texture); + item.texture(texture, url); } } } - item.set_product_name(product.value("name", "")); + item.product_name(product.value("name", "")); save_item_json(json, item, "Best_Buy_API"); } else @@ -486,16 +486,16 @@ void Pudding::save_item_json(const nlohmann::json& json, const Item& item, const fs::create_directories(path); } std::string prefix = api_name; - if (item.get_full_name() != "") + if (item.full_name() != "") { - prefix += "_" + item.get_full_name(); + prefix += "_" + item.full_name(); } else { prefix += "_Unknown"; } std::replace_if(prefix.begin(), prefix.end(), [](char c) { return !std::isalnum(c); }, '_'); - path /= prefix + "_" + item.get_upc() + ".json"; + path /= prefix + "_" + item.upc() + ".json"; std::ofstream out(path); out << std::setw(4) << json << std::endl; sb::Log::log("Saved JSON to " + path.string()); @@ -612,18 +612,12 @@ void Pudding::destroy_texture(GLuint* texture_id) glDeleteTextures(1, texture_id); } -/* Change the currently selected item */ -void Pudding::increment_item_index(int increment) -{ - current_item_index = sb::mod(current_item_index + increment, static_cast(items.size())); -} - /* Return the item currently selected in the inventory */ Item& Pudding::current_item() { try { - return items.at(current_item_index); + return *item_carousel.current(items); } catch (const std::out_of_range& exception) { @@ -774,7 +768,7 @@ void Pudding::update() pudding_model.attributes("uv")->enable(); glUniform1i(uniform["mvp"]["pudding texture"], 0); glActiveTexture(GL_TEXTURE0); - current_item().get_active_image_texture().bind(); + current_item().current_texture().bind(); } /* draw pudding model */ glEnable(GL_DEPTH_TEST); @@ -814,7 +808,7 @@ void Pudding::update() viewport_box.left(viewport_box.cx(), true); } glViewport(viewport_box.left(), viewport_box.bottom(), viewport_box.width(), viewport_box.height()); - current_item().get_active_image_texture().bind(); + current_item().current_texture().bind(); plane.enable(); /* draws rectangle vertices and rectangle texture using UV coords */ glDrawArrays(GL_TRIANGLES, 0, plane.attributes("position")->count()); diff --git a/src/Pudding.hpp b/src/Pudding.hpp index 83e175c..0ef522d 100644 --- a/src/Pudding.hpp +++ b/src/Pudding.hpp @@ -40,6 +40,7 @@ #include "VBO.hpp" #include "Item.hpp" #include "Model.hpp" +#include "utility.hpp" class Pudding : public Game { @@ -78,7 +79,8 @@ private: const glm::vec3 PUDDING_YELLOW = glm::vec3(0.878f, 0.859f, 0.122f); std::string current_barcode, previous_barcode, current_config_barcode, current_camera_barcode; std::vector items; - int current_item_index = 0, effect_id = EFFECT_NONE, pudding_triangle_vertex_count = 0, pudding_fan_vertex_count = 0; + Carousel item_carousel; + int effect_id = EFFECT_NONE, pudding_triangle_vertex_count = 0, pudding_fan_vertex_count = 0; cv::VideoCapture capture; zbar::ImageScanner image_scanner; std::map> uniform; @@ -115,7 +117,6 @@ public: Pudding(); void respond(SDL_Event&); void add_item(const std::string&); - void increment_item_index(int = 1); Item& current_item(); void update(); virtual std::string class_name() const { return "Pudding"; }