mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-11 06:09:08 -07:00
Move plugins to external folder
This commit is contained in:
353
applications/external/solitaire/common/card.c
vendored
Normal file
353
applications/external/solitaire/common/card.c
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
#include "card.h"
|
||||
#include "dml.h"
|
||||
#include "ui.h"
|
||||
|
||||
#define CARD_DRAW_X_START 108
|
||||
#define CARD_DRAW_Y_START 38
|
||||
#define CARD_DRAW_X_SPACE 10
|
||||
#define CARD_DRAW_Y_SPACE 8
|
||||
#define CARD_DRAW_X_OFFSET 4
|
||||
#define CARD_DRAW_FIRST_ROW_LENGTH 7
|
||||
|
||||
uint8_t pips[4][3] = {
|
||||
{21, 10, 7}, //spades
|
||||
{7, 10, 7}, //hearts
|
||||
{0, 10, 7}, //diamonds
|
||||
{14, 10, 7}, //clubs
|
||||
};
|
||||
uint8_t letters[13][3] = {
|
||||
{0, 0, 5},
|
||||
{5, 0, 5},
|
||||
{10, 0, 5},
|
||||
{15, 0, 5},
|
||||
{20, 0, 5},
|
||||
{25, 0, 5},
|
||||
{30, 0, 5},
|
||||
{0, 5, 5},
|
||||
{5, 5, 5},
|
||||
{10, 5, 5},
|
||||
{15, 5, 5},
|
||||
{20, 5, 5},
|
||||
{25, 5, 5},
|
||||
};
|
||||
|
||||
//region Player card positions
|
||||
uint8_t playerCardPositions[22][4] = {
|
||||
//first row
|
||||
{108, 38},
|
||||
{98, 38},
|
||||
{88, 38},
|
||||
{78, 38},
|
||||
{68, 38},
|
||||
{58, 38},
|
||||
{48, 38},
|
||||
{38, 38},
|
||||
//second row
|
||||
{104, 26},
|
||||
{94, 26},
|
||||
{84, 26},
|
||||
{74, 26},
|
||||
{64, 26},
|
||||
{54, 26},
|
||||
{44, 26},
|
||||
//third row
|
||||
{99, 14},
|
||||
{89, 14},
|
||||
{79, 14},
|
||||
{69, 14},
|
||||
{59, 14},
|
||||
{49, 14},
|
||||
};
|
||||
//endregion
|
||||
Icon* card_graphics = NULL;
|
||||
|
||||
void set_card_graphics(const Icon* graphics) {
|
||||
card_graphics = (Icon*)graphics;
|
||||
}
|
||||
|
||||
void draw_card_at_colored(
|
||||
int8_t pos_x,
|
||||
int8_t pos_y,
|
||||
uint8_t pip,
|
||||
uint8_t character,
|
||||
bool inverted,
|
||||
Canvas* const canvas) {
|
||||
DrawMode primary = inverted ? Black : White;
|
||||
DrawMode secondary = inverted ? White : Black;
|
||||
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, primary);
|
||||
draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black);
|
||||
|
||||
uint8_t* drawInfo = pips[pip];
|
||||
uint8_t px = drawInfo[0], py = drawInfo[1], s = drawInfo[2];
|
||||
|
||||
uint8_t left = pos_x + 2;
|
||||
uint8_t right = (pos_x + CARD_WIDTH - s - 2);
|
||||
uint8_t top = pos_y + 2;
|
||||
uint8_t bottom = (pos_y + CARD_HEIGHT - s - 2);
|
||||
|
||||
draw_icon_clip(canvas, card_graphics, right, top, px, py, s, s, secondary);
|
||||
draw_icon_clip_flipped(canvas, card_graphics, left, bottom, px, py, s, s, secondary);
|
||||
|
||||
drawInfo = letters[character];
|
||||
px = drawInfo[0], py = drawInfo[1], s = drawInfo[2];
|
||||
left = pos_x + 2;
|
||||
right = (pos_x + CARD_WIDTH - s - 2);
|
||||
top = pos_y + 2;
|
||||
bottom = (pos_y + CARD_HEIGHT - s - 2);
|
||||
|
||||
draw_icon_clip(canvas, card_graphics, left, top + 1, px, py, s, s, secondary);
|
||||
draw_icon_clip_flipped(canvas, card_graphics, right, bottom - 1, px, py, s, s, secondary);
|
||||
}
|
||||
|
||||
void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas* const canvas) {
|
||||
draw_card_at_colored(pos_x, pos_y, pip, character, false, canvas);
|
||||
}
|
||||
|
||||
void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas) {
|
||||
for(int i = count - 1; i >= 0; i--) {
|
||||
draw_card_at(
|
||||
playerCardPositions[i][0],
|
||||
playerCardPositions[i][1],
|
||||
cards[i].pip,
|
||||
cards[i].character,
|
||||
canvas);
|
||||
}
|
||||
}
|
||||
|
||||
Vector card_pos_at_index(uint8_t index) {
|
||||
return (Vector){playerCardPositions[index][0], playerCardPositions[index][1]};
|
||||
}
|
||||
|
||||
void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas* const canvas) {
|
||||
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, White);
|
||||
draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black);
|
||||
|
||||
draw_icon_clip(canvas, card_graphics, pos_x + 1, pos_y + 1, 35, 0, 15, 21, Black);
|
||||
}
|
||||
|
||||
void generate_deck(Deck* deck_ptr, uint8_t deck_count) {
|
||||
uint16_t counter = 0;
|
||||
if(deck_ptr->cards != NULL) {
|
||||
free(deck_ptr->cards);
|
||||
}
|
||||
|
||||
deck_ptr->deck_count = deck_count;
|
||||
deck_ptr->card_count = deck_count * 52;
|
||||
deck_ptr->cards = malloc(sizeof(Card) * deck_ptr->card_count);
|
||||
|
||||
for(uint8_t deck = 0; deck < deck_count; deck++) {
|
||||
for(uint8_t pip = 0; pip < 4; pip++) {
|
||||
for(uint8_t label = 0; label < 13; label++) {
|
||||
deck_ptr->cards[counter] = (Card){pip, label, false, false};
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void shuffle_deck(Deck* deck_ptr) {
|
||||
srand(DWT->CYCCNT);
|
||||
deck_ptr->index = 0;
|
||||
int max = deck_ptr->deck_count * 52;
|
||||
for(int i = 0; i < max; i++) {
|
||||
int r = i + (rand() % (max - i));
|
||||
Card tmp = deck_ptr->cards[i];
|
||||
deck_ptr->cards[i] = deck_ptr->cards[r];
|
||||
deck_ptr->cards[r] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t hand_count(const Card* cards, uint8_t count) {
|
||||
uint8_t aceCount = 0;
|
||||
uint8_t score = 0;
|
||||
|
||||
for(uint8_t i = 0; i < count; i++) {
|
||||
if(cards[i].character == 12)
|
||||
aceCount++;
|
||||
else {
|
||||
if(cards[i].character > 8)
|
||||
score += 10;
|
||||
else
|
||||
score += cards[i].character + 2;
|
||||
}
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < aceCount; i++) {
|
||||
if((score + 11) <= 21)
|
||||
score += 11;
|
||||
else
|
||||
score++;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
void draw_card_animation(
|
||||
Card animatingCard,
|
||||
Vector from,
|
||||
Vector control,
|
||||
Vector to,
|
||||
float t,
|
||||
bool extra_margin,
|
||||
Canvas* const canvas) {
|
||||
float time = t;
|
||||
if(extra_margin) {
|
||||
time += 0.2;
|
||||
}
|
||||
|
||||
Vector currentPos = quadratic_2d(from, control, to, time);
|
||||
if(t > 1) {
|
||||
draw_card_at(
|
||||
currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, canvas);
|
||||
} else {
|
||||
if(t < 0.5)
|
||||
draw_card_back_at(currentPos.x, currentPos.y, canvas);
|
||||
else
|
||||
draw_card_at(
|
||||
currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, canvas);
|
||||
}
|
||||
}
|
||||
|
||||
void init_hand(Hand* hand_ptr, uint8_t count) {
|
||||
hand_ptr->cards = malloc(sizeof(Card) * count);
|
||||
hand_ptr->index = 0;
|
||||
hand_ptr->max = count;
|
||||
}
|
||||
|
||||
void free_hand(Hand* hand_ptr) {
|
||||
FURI_LOG_D("CARD", "Freeing hand");
|
||||
free(hand_ptr->cards);
|
||||
}
|
||||
|
||||
void add_to_hand(Hand* hand_ptr, Card card) {
|
||||
FURI_LOG_D("CARD", "Adding to hand");
|
||||
if(hand_ptr->index < hand_ptr->max) {
|
||||
hand_ptr->cards[hand_ptr->index] = card;
|
||||
hand_ptr->index++;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas* const canvas) {
|
||||
if(highlighted) {
|
||||
draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black);
|
||||
draw_rounded_box_frame(
|
||||
canvas, pos_x + 2, pos_y + 2, CARD_WIDTH - 4, CARD_HEIGHT - 4, White);
|
||||
} else {
|
||||
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black);
|
||||
draw_rounded_box_frame(
|
||||
canvas, pos_x + 2, pos_y + 2, CARD_WIDTH - 4, CARD_HEIGHT - 4, White);
|
||||
}
|
||||
}
|
||||
|
||||
int first_non_flipped_card(Hand hand) {
|
||||
for(int i = 0; i < hand.index; i++) {
|
||||
if(!hand.cards[i].flipped) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return hand.index;
|
||||
}
|
||||
|
||||
void draw_hand_column(
|
||||
Hand hand,
|
||||
int16_t pos_x,
|
||||
int16_t pos_y,
|
||||
int8_t highlight,
|
||||
Canvas* const canvas) {
|
||||
if(hand.index == 0) {
|
||||
draw_card_space(pos_x, pos_y, highlight > 0, canvas);
|
||||
if(highlight == 0)
|
||||
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Inverse);
|
||||
return;
|
||||
}
|
||||
|
||||
int loopEnd = hand.index;
|
||||
int hStart = max(loopEnd - 4, 0);
|
||||
int pos = 0;
|
||||
int first = first_non_flipped_card(hand);
|
||||
bool wastop = false;
|
||||
if(first >= 0 && first <= hStart && highlight != first) {
|
||||
if(first > 0) {
|
||||
draw_card_back_at(pos_x, pos_y + pos, canvas);
|
||||
pos += 4;
|
||||
hStart++;
|
||||
wastop = true;
|
||||
}
|
||||
draw_card_at_colored(
|
||||
pos_x, pos_y + pos, hand.cards[first].pip, hand.cards[first].character, false, canvas);
|
||||
pos += 8;
|
||||
hStart++;
|
||||
}
|
||||
if(hStart > highlight && highlight >= 0) {
|
||||
if(!wastop && first > 0) {
|
||||
draw_card_back_at(pos_x, pos_y + pos, canvas);
|
||||
pos += 4;
|
||||
hStart++;
|
||||
}
|
||||
draw_card_at_colored(
|
||||
pos_x,
|
||||
pos_y + pos,
|
||||
hand.cards[highlight].pip,
|
||||
hand.cards[highlight].character,
|
||||
true,
|
||||
canvas);
|
||||
pos += 8;
|
||||
hStart++;
|
||||
}
|
||||
for(int i = hStart; i < loopEnd; i++, pos += 4) {
|
||||
if(hand.cards[i].flipped) {
|
||||
draw_card_back_at(pos_x, pos_y + pos, canvas);
|
||||
if(i == highlight)
|
||||
draw_rounded_box(
|
||||
canvas, pos_x + 1, pos_y + pos + 1, CARD_WIDTH - 2, CARD_HEIGHT - 2, Inverse);
|
||||
} else {
|
||||
draw_card_at_colored(
|
||||
pos_x,
|
||||
pos_y + pos,
|
||||
hand.cards[i].pip,
|
||||
hand.cards[i].character,
|
||||
(i == highlight),
|
||||
canvas);
|
||||
if(i == highlight || i == first) pos += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Card remove_from_deck(uint16_t index, Deck* deck) {
|
||||
FURI_LOG_D("CARD", "Removing from deck");
|
||||
Card result = {0, 0, true, false};
|
||||
if(deck->card_count > 0) {
|
||||
deck->card_count--;
|
||||
for(int i = 0, curr_index = 0; i <= deck->card_count; i++) {
|
||||
if(i != index) {
|
||||
deck->cards[curr_index] = deck->cards[i];
|
||||
curr_index++;
|
||||
} else {
|
||||
result = deck->cards[i];
|
||||
}
|
||||
}
|
||||
if(deck->index >= 0) {
|
||||
deck->index--;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void extract_hand_region(Hand* hand, Hand* to, uint8_t start_index) {
|
||||
FURI_LOG_D("CARD", "Extracting hand region");
|
||||
if(start_index >= hand->index) return;
|
||||
|
||||
for(uint8_t i = start_index; i < hand->index; i++) {
|
||||
add_to_hand(to, hand->cards[i]);
|
||||
}
|
||||
hand->index = start_index;
|
||||
}
|
||||
|
||||
void add_hand_region(Hand* to, Hand* from) {
|
||||
FURI_LOG_D("CARD", "Adding hand region");
|
||||
if((to->index + from->index) <= to->max) {
|
||||
for(int i = 0; i < from->index; i++) {
|
||||
add_to_hand(to, from->cards[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
192
applications/external/solitaire/common/card.h
vendored
Normal file
192
applications/external/solitaire/common/card.h
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include "dml.h"
|
||||
|
||||
#define CARD_HEIGHT 23
|
||||
#define CARD_HALF_HEIGHT 11
|
||||
#define CARD_WIDTH 17
|
||||
#define CARD_HALF_WIDTH 8
|
||||
|
||||
//region types
|
||||
typedef struct {
|
||||
uint8_t pip; //Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs
|
||||
uint8_t character; //Card letter [0-12], 0 means 2, 12 is Ace
|
||||
bool disabled;
|
||||
bool flipped;
|
||||
} Card;
|
||||
|
||||
typedef struct {
|
||||
uint8_t deck_count; //Number of decks used
|
||||
Card* cards; //Cards in the deck
|
||||
int card_count;
|
||||
int index; //Card index (to know where we at in the deck)
|
||||
} Deck;
|
||||
|
||||
typedef struct {
|
||||
Card* cards; //Cards in the deck
|
||||
uint8_t index; //Current index
|
||||
uint8_t max; //How many cards we want to store
|
||||
} Hand;
|
||||
//endregion
|
||||
|
||||
void set_card_graphics(const Icon* graphics);
|
||||
|
||||
/**
|
||||
* Gets card coordinates at the index (range: 0-20).
|
||||
*
|
||||
* @param index Index to check 0-20
|
||||
* @return Position of the card
|
||||
*/
|
||||
Vector card_pos_at_index(uint8_t index);
|
||||
|
||||
/**
|
||||
* Draws card at a given coordinate (top-left corner)
|
||||
*
|
||||
* @param pos_x X position
|
||||
* @param pos_y Y position
|
||||
* @param pip Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs
|
||||
* @param character Letter [0-12] 0 is 2, 12 is A
|
||||
* @param canvas Pointer to Flipper's canvas object
|
||||
*/
|
||||
void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas* const canvas);
|
||||
|
||||
/**
|
||||
* Draws card at a given coordinate (top-left corner)
|
||||
*
|
||||
* @param pos_x X position
|
||||
* @param pos_y Y position
|
||||
* @param pip Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs
|
||||
* @param character Letter [0-12] 0 is 2, 12 is A
|
||||
* @param inverted Invert colors
|
||||
* @param canvas Pointer to Flipper's canvas object
|
||||
*/
|
||||
void draw_card_at_colored(
|
||||
int8_t pos_x,
|
||||
int8_t pos_y,
|
||||
uint8_t pip,
|
||||
uint8_t character,
|
||||
bool inverted,
|
||||
Canvas* const canvas);
|
||||
|
||||
/**
|
||||
* Draws 'count' cards at the bottom right corner
|
||||
*
|
||||
* @param cards List of cards
|
||||
* @param count Count of cards
|
||||
* @param canvas Pointer to Flipper's canvas object
|
||||
*/
|
||||
void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas);
|
||||
|
||||
/**
|
||||
* Draws card back at a given coordinate (top-left corner)
|
||||
*
|
||||
* @param pos_x X coordinate
|
||||
* @param pos_y Y coordinate
|
||||
* @param canvas Pointer to Flipper's canvas object
|
||||
*/
|
||||
void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas* const canvas);
|
||||
|
||||
/**
|
||||
* Generates the deck
|
||||
*
|
||||
* @param deck_ptr Pointer to the deck
|
||||
* @param deck_count Number of decks
|
||||
*/
|
||||
void generate_deck(Deck* deck_ptr, uint8_t deck_count);
|
||||
|
||||
/**
|
||||
* Shuffles the deck
|
||||
*
|
||||
* @param deck_ptr Pointer to the deck
|
||||
*/
|
||||
void shuffle_deck(Deck* deck_ptr);
|
||||
|
||||
/**
|
||||
* Calculates the hand count for blackjack
|
||||
*
|
||||
* @param cards List of cards
|
||||
* @param count Count of cards
|
||||
* @return Hand value
|
||||
*/
|
||||
uint8_t hand_count(const Card* cards, uint8_t count);
|
||||
|
||||
/**
|
||||
* Draws card animation
|
||||
*
|
||||
* @param animatingCard Card to animate
|
||||
* @param from Starting position
|
||||
* @param control Quadratic lerp control point
|
||||
* @param to End point
|
||||
* @param t Current time (0-1)
|
||||
* @param extra_margin Use extra margin at the end (arrives 0.2 unit before the end so it can stay there a bit)
|
||||
* @param canvas Pointer to Flipper's canvas object
|
||||
*/
|
||||
void draw_card_animation(
|
||||
Card animatingCard,
|
||||
Vector from,
|
||||
Vector control,
|
||||
Vector to,
|
||||
float t,
|
||||
bool extra_margin,
|
||||
Canvas* const canvas);
|
||||
|
||||
/**
|
||||
* Init hand pointer
|
||||
* @param hand_ptr Pointer to hand
|
||||
* @param count Number of cards we want to store
|
||||
*/
|
||||
void init_hand(Hand* hand_ptr, uint8_t count);
|
||||
|
||||
/**
|
||||
* Free hand resources
|
||||
* @param hand_ptr Pointer to hand
|
||||
*/
|
||||
void free_hand(Hand* hand_ptr);
|
||||
|
||||
/**
|
||||
* Add card to hand
|
||||
* @param hand_ptr Pointer to hand
|
||||
* @param card Card to add
|
||||
*/
|
||||
void add_to_hand(Hand* hand_ptr, Card card);
|
||||
|
||||
/**
|
||||
* Draw card placement position at coordinate
|
||||
* @param pos_x X coordinate
|
||||
* @param pos_y Y coordinate
|
||||
* @param highlighted Apply highlight effect
|
||||
* @param canvas Canvas object
|
||||
*/
|
||||
void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas* const canvas);
|
||||
|
||||
/**
|
||||
* Draws a column of card, displaying the last [max_cards] cards on the list
|
||||
* @param hand Hand object
|
||||
* @param pos_x X coordinate to draw
|
||||
* @param pos_y Y coordinate to draw
|
||||
* @param highlight Index to highlight, negative means no highlight
|
||||
* @param canvas Canvas object
|
||||
*/
|
||||
void draw_hand_column(
|
||||
Hand hand,
|
||||
int16_t pos_x,
|
||||
int16_t pos_y,
|
||||
int8_t highlight,
|
||||
Canvas* const canvas);
|
||||
|
||||
/**
|
||||
* Removes a card from the deck (Be aware, if you remove the first item, the deck index will be at -1 so you have to handle that)
|
||||
* @param index Index to remove
|
||||
* @param deck Deck reference
|
||||
* @return The removed card
|
||||
*/
|
||||
Card remove_from_deck(uint16_t index, Deck* deck);
|
||||
|
||||
int first_non_flipped_card(Hand hand);
|
||||
|
||||
void extract_hand_region(Hand* hand, Hand* to, uint8_t start_index);
|
||||
|
||||
void add_hand_region(Hand* to, Hand* from);
|
||||
53
applications/external/solitaire/common/dml.c
vendored
Normal file
53
applications/external/solitaire/common/dml.c
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "dml.h"
|
||||
#include <math.h>
|
||||
|
||||
float lerp(float v0, float v1, float t) {
|
||||
if(t > 1) return v1;
|
||||
return (1 - t) * v0 + t * v1;
|
||||
}
|
||||
|
||||
Vector lerp_2d(Vector start, Vector end, float t) {
|
||||
return (Vector){
|
||||
lerp(start.x, end.x, t),
|
||||
lerp(start.y, end.y, t),
|
||||
};
|
||||
}
|
||||
|
||||
Vector quadratic_2d(Vector start, Vector control, Vector end, float t) {
|
||||
return lerp_2d(lerp_2d(start, control, t), lerp_2d(control, end, t), t);
|
||||
}
|
||||
|
||||
Vector vector_add(Vector a, Vector b) {
|
||||
return (Vector){a.x + b.x, a.y + b.y};
|
||||
}
|
||||
|
||||
Vector vector_sub(Vector a, Vector b) {
|
||||
return (Vector){a.x - b.x, a.y - b.y};
|
||||
}
|
||||
|
||||
Vector vector_mul_components(Vector a, Vector b) {
|
||||
return (Vector){a.x * b.x, a.y * b.y};
|
||||
}
|
||||
|
||||
Vector vector_div_components(Vector a, Vector b) {
|
||||
return (Vector){a.x / b.x, a.y / b.y};
|
||||
}
|
||||
|
||||
Vector vector_normalized(Vector a) {
|
||||
float length = vector_magnitude(a);
|
||||
return (Vector){a.x / length, a.y / length};
|
||||
}
|
||||
|
||||
float vector_magnitude(Vector a) {
|
||||
return sqrt(a.x * a.x + a.y * a.y);
|
||||
}
|
||||
|
||||
float vector_distance(Vector a, Vector b) {
|
||||
return vector_magnitude(vector_sub(a, b));
|
||||
}
|
||||
|
||||
float vector_dot(Vector a, Vector b) {
|
||||
Vector _a = vector_normalized(a);
|
||||
Vector _b = vector_normalized(b);
|
||||
return _a.x * _b.x + _a.y * _b.y;
|
||||
}
|
||||
116
applications/external/solitaire/common/dml.h
vendored
Normal file
116
applications/external/solitaire/common/dml.h
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
//
|
||||
// Doofy's Math library
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef struct {
|
||||
float x;
|
||||
float y;
|
||||
} Vector;
|
||||
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define abs(x) ((x) > 0 ? (x) : -(x))
|
||||
|
||||
/**
|
||||
* Lerp function
|
||||
*
|
||||
* @param v0 Start value
|
||||
* @param v1 End value
|
||||
* @param t Time (0-1 range)
|
||||
* @return Point between v0-v1 at a given time
|
||||
*/
|
||||
float lerp(float v0, float v1, float t);
|
||||
|
||||
/**
|
||||
* 2D lerp function
|
||||
*
|
||||
* @param start Start vector
|
||||
* @param end End vector
|
||||
* @param t Time (0-1 range)
|
||||
* @return 2d Vector between start and end at time
|
||||
*/
|
||||
Vector lerp_2d(Vector start, Vector end, float t);
|
||||
|
||||
/**
|
||||
* Quadratic lerp function
|
||||
*
|
||||
* @param start Start vector
|
||||
* @param control Control point
|
||||
* @param end End vector
|
||||
* @param t Time (0-1 range)
|
||||
* @return 2d Vector at time
|
||||
*/
|
||||
Vector quadratic_2d(Vector start, Vector control, Vector end, float t);
|
||||
|
||||
/**
|
||||
* Add vector components together
|
||||
*
|
||||
* @param a First vector
|
||||
* @param b Second vector
|
||||
* @return Resulting vector
|
||||
*/
|
||||
Vector vector_add(Vector a, Vector b);
|
||||
|
||||
/**
|
||||
* Subtract vector components together
|
||||
*
|
||||
* @param a First vector
|
||||
* @param b Second vector
|
||||
* @return Resulting vector
|
||||
*/
|
||||
Vector vector_sub(Vector a, Vector b);
|
||||
|
||||
/**
|
||||
* Multiplying vector components together
|
||||
*
|
||||
* @param a First vector
|
||||
* @param b Second vector
|
||||
* @return Resulting vector
|
||||
*/
|
||||
Vector vector_mul_components(Vector a, Vector b);
|
||||
|
||||
/**
|
||||
* Dividing vector components
|
||||
*
|
||||
* @param a First vector
|
||||
* @param b Second vector
|
||||
* @return Resulting vector
|
||||
*/
|
||||
Vector vector_div_components(Vector a, Vector b);
|
||||
|
||||
/**
|
||||
* Calculating Vector length
|
||||
*
|
||||
* @param a Direction vector
|
||||
* @return Length of the vector
|
||||
*/
|
||||
float vector_magnitude(Vector a);
|
||||
|
||||
/**
|
||||
* Get a normalized vector (length of 1)
|
||||
*
|
||||
* @param a Direction vector
|
||||
* @return Normalized vector
|
||||
*/
|
||||
Vector vector_normalized(Vector a);
|
||||
|
||||
/**
|
||||
* Calculate two vector's distance
|
||||
*
|
||||
* @param a First vector
|
||||
* @param b Second vector
|
||||
* @return Distance between vectors
|
||||
*/
|
||||
float vector_distance(Vector a, Vector b);
|
||||
|
||||
/**
|
||||
* Calculate the dot product of the vectors.
|
||||
* No need to normalize, it will do it
|
||||
*
|
||||
* @param a First vector
|
||||
* @param b Second vector
|
||||
* @return value from -1 to 1
|
||||
*/
|
||||
float vector_dot(Vector a, Vector b);
|
||||
103
applications/external/solitaire/common/menu.c
vendored
Normal file
103
applications/external/solitaire/common/menu.c
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "menu.h"
|
||||
|
||||
void add_menu(Menu* menu, const char* name, void (*callback)(void*)) {
|
||||
MenuItem* items = menu->items;
|
||||
|
||||
menu->items = malloc(sizeof(MenuItem) * (menu->menu_count + 1));
|
||||
for(uint8_t i = 0; i < menu->menu_count; i++) {
|
||||
menu->items[i] = items[i];
|
||||
}
|
||||
free(items);
|
||||
|
||||
menu->items[menu->menu_count] = (MenuItem){name, true, callback};
|
||||
menu->menu_count++;
|
||||
}
|
||||
|
||||
void free_menu(Menu* menu) {
|
||||
free(menu->items);
|
||||
free(menu);
|
||||
}
|
||||
|
||||
void set_menu_state(Menu* menu, uint8_t index, bool state) {
|
||||
if(menu->menu_count > index) {
|
||||
menu->items[index].enabled = state;
|
||||
}
|
||||
if(!state && menu->current_menu == index) move_menu(menu, 1);
|
||||
}
|
||||
|
||||
void move_menu(Menu* menu, int8_t direction) {
|
||||
if(!menu->enabled) return;
|
||||
int max = menu->menu_count;
|
||||
for(int8_t i = 0; i < max; i++) {
|
||||
FURI_LOG_D(
|
||||
"MENU",
|
||||
"Iteration %i, current %i, direction %i, state %i",
|
||||
i,
|
||||
menu->current_menu,
|
||||
direction,
|
||||
menu->items[menu->current_menu].enabled ? 1 : 0);
|
||||
if(direction < 0 && menu->current_menu == 0) {
|
||||
menu->current_menu = menu->menu_count - 1;
|
||||
} else {
|
||||
menu->current_menu = (menu->current_menu + direction) % menu->menu_count;
|
||||
}
|
||||
FURI_LOG_D(
|
||||
"MENU",
|
||||
"After process current %i, direction %i, state %i",
|
||||
menu->current_menu,
|
||||
direction,
|
||||
menu->items[menu->current_menu].enabled ? 1 : 0);
|
||||
if(menu->items[menu->current_menu].enabled) {
|
||||
FURI_LOG_D("MENU", "Next menu %i", menu->current_menu);
|
||||
return;
|
||||
}
|
||||
}
|
||||
FURI_LOG_D("MENU", "Not found, setting false");
|
||||
menu->enabled = false;
|
||||
}
|
||||
|
||||
void activate_menu(Menu* menu, void* state) {
|
||||
if(!menu->enabled) return;
|
||||
menu->items[menu->current_menu].callback(state);
|
||||
}
|
||||
|
||||
void render_menu(Menu* menu, Canvas* canvas, uint8_t pos_x, uint8_t pos_y) {
|
||||
if(!menu->enabled) return;
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_rbox(canvas, pos_x, pos_y, menu->menu_width + 2, 10, 2);
|
||||
|
||||
uint8_t w = pos_x + menu->menu_width;
|
||||
uint8_t h = pos_y + 10;
|
||||
uint8_t p1x = pos_x + 2;
|
||||
uint8_t p2x = pos_x + menu->menu_width - 2;
|
||||
uint8_t p1y = pos_y + 2;
|
||||
uint8_t p2y = pos_y + 8;
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_line(canvas, p1x, pos_y, p2x, pos_y);
|
||||
canvas_draw_line(canvas, p1x, h, p2x, h);
|
||||
canvas_draw_line(canvas, pos_x, p1y, pos_x, p2y);
|
||||
canvas_draw_line(canvas, w, p1y, w, p2y);
|
||||
canvas_draw_dot(canvas, pos_x + 1, pos_y + 1);
|
||||
canvas_draw_dot(canvas, w - 1, pos_y + 1);
|
||||
canvas_draw_dot(canvas, w - 1, h - 1);
|
||||
canvas_draw_dot(canvas, pos_x + 1, h - 1);
|
||||
|
||||
// canvas_draw_rbox(canvas, pos_x, pos_y, menu->menu_width + 2, 10, 2);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
pos_x + menu->menu_width / 2,
|
||||
pos_y + 6,
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
menu->items[menu->current_menu].name);
|
||||
//9*5
|
||||
int center = pos_x + menu->menu_width / 2;
|
||||
for(uint8_t i = 0; i < 4; i++) {
|
||||
for(int8_t j = -i; j <= i; j++) {
|
||||
canvas_draw_dot(canvas, center + j, pos_y - 4 + i);
|
||||
canvas_draw_dot(canvas, center + j, pos_y + 14 - i);
|
||||
}
|
||||
}
|
||||
}
|
||||
77
applications/external/solitaire/common/menu.h
vendored
Normal file
77
applications/external/solitaire/common/menu.h
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
|
||||
typedef struct {
|
||||
const char* name; //Name of the menu
|
||||
bool enabled; //Is the menu item enabled (it will not render, you cannot select it)
|
||||
|
||||
void (*callback)(
|
||||
void* state); //Callback for when the activate_menu is called while this menu is selected
|
||||
} MenuItem;
|
||||
|
||||
typedef struct {
|
||||
MenuItem* items; //list of menu items
|
||||
uint8_t menu_count; //count of menu items (do not change)
|
||||
uint8_t current_menu; //currently selected menu item
|
||||
uint8_t menu_width; //width of the menu
|
||||
bool enabled; //is the menu enabled (it will not render and accept events when disabled)
|
||||
} Menu;
|
||||
|
||||
/**
|
||||
* Cleans up the pointers used by the menu
|
||||
*
|
||||
* @param menu Pointer of the menu to clean up
|
||||
*/
|
||||
void free_menu(Menu* menu);
|
||||
|
||||
/**
|
||||
* Add a new menu item
|
||||
*
|
||||
* @param menu Pointer of the menu
|
||||
* @param name Name of the menu item
|
||||
* @param callback Callback called on activation
|
||||
*/
|
||||
void add_menu(Menu* menu, const char* name, void (*callback)(void*));
|
||||
|
||||
/**
|
||||
* Setting menu item to be enabled/disabled
|
||||
*
|
||||
* @param menu Pointer of the menu
|
||||
* @param index Menu index to set
|
||||
* @param state Enabled (true), Disabled(false)
|
||||
*/
|
||||
void set_menu_state(Menu* menu, uint8_t index, bool state);
|
||||
|
||||
/**
|
||||
* Moves selection up or down
|
||||
*
|
||||
* @param menu Pointer of the menu
|
||||
* @param direction Direction to move -1 down, 1 up
|
||||
*/
|
||||
void move_menu(Menu* menu, int8_t direction);
|
||||
|
||||
/**
|
||||
* Triggers the current menu callback
|
||||
*
|
||||
* @param menu Pointer of the menu
|
||||
* @param state Usually your application state
|
||||
*/
|
||||
void activate_menu(Menu* menu, void* state);
|
||||
|
||||
/**
|
||||
* Renders the menu at a coordinate (call it in your render function).
|
||||
*
|
||||
* Keep in mind that Flipper has a 128x64 pixel screen resolution and the coordinate
|
||||
* you give is the menu's rectangle top-left corner (arrows not included).
|
||||
* The rectangle height is 10 px, the arrows have a 4 pixel height. Space needed is 18px.
|
||||
* The width of the menu can be configured in the menu object.
|
||||
*
|
||||
*
|
||||
* @param menu Pointer of the menu
|
||||
* @param canvas Flippers Canvas pointer
|
||||
* @param pos_x X position to draw
|
||||
* @param pos_y Y position to draw
|
||||
*/
|
||||
void render_menu(Menu* menu, Canvas* canvas, uint8_t pos_x, uint8_t pos_y);
|
||||
69
applications/external/solitaire/common/queue.c
vendored
Normal file
69
applications/external/solitaire/common/queue.c
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "queue.h"
|
||||
|
||||
void render_queue(const QueueState* queue_state, const void* app_state, Canvas* const canvas) {
|
||||
if(queue_state->current != NULL && queue_state->current->render != NULL)
|
||||
((QueueItem*)queue_state->current)->render(app_state, canvas);
|
||||
}
|
||||
|
||||
bool run_queue(QueueState* queue_state, void* app_state) {
|
||||
if(queue_state->current != NULL) {
|
||||
queue_state->running = true;
|
||||
if((furi_get_tick() - queue_state->start) >= queue_state->current->duration)
|
||||
dequeue(queue_state, app_state);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void dequeue(QueueState* queue_state, void* app_state) {
|
||||
((QueueItem*)queue_state->current)->callback(app_state);
|
||||
QueueItem* f = queue_state->current;
|
||||
queue_state->current = f->next;
|
||||
free(f);
|
||||
if(queue_state->current != NULL) {
|
||||
if(queue_state->current->start != NULL) queue_state->current->start(app_state);
|
||||
queue_state->start = furi_get_tick();
|
||||
} else {
|
||||
queue_state->running = false;
|
||||
}
|
||||
}
|
||||
|
||||
void queue_clear(QueueState* queue_state) {
|
||||
queue_state->running = false;
|
||||
QueueItem* curr = queue_state->current;
|
||||
while(curr != NULL) {
|
||||
QueueItem* f = curr;
|
||||
curr = curr->next;
|
||||
free(f);
|
||||
}
|
||||
}
|
||||
|
||||
void enqueue(
|
||||
QueueState* queue_state,
|
||||
void* app_state,
|
||||
void (*done)(void* state),
|
||||
void (*start)(void* state),
|
||||
void (*render)(const void* state, Canvas* const canvas),
|
||||
uint32_t duration) {
|
||||
QueueItem* next;
|
||||
if(queue_state->current == NULL) {
|
||||
queue_state->start = furi_get_tick();
|
||||
queue_state->current = malloc(sizeof(QueueItem));
|
||||
next = queue_state->current;
|
||||
if(next->start != NULL) next->start(app_state);
|
||||
|
||||
} else {
|
||||
next = queue_state->current;
|
||||
while(next->next != NULL) {
|
||||
next = (QueueItem*)(next->next);
|
||||
}
|
||||
next->next = malloc(sizeof(QueueItem));
|
||||
next = next->next;
|
||||
}
|
||||
next->callback = done;
|
||||
next->render = render;
|
||||
next->start = start;
|
||||
next->duration = duration;
|
||||
next->next = NULL;
|
||||
}
|
||||
70
applications/external/solitaire/common/queue.h
vendored
Normal file
70
applications/external/solitaire/common/queue.h
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <furi.h>
|
||||
|
||||
typedef struct {
|
||||
void (*callback)(void* state); //Callback for when the item is dequeued
|
||||
void (*render)(
|
||||
const void* state,
|
||||
Canvas* const canvas); //Callback for the rendering loop while this item is running
|
||||
void (*start)(void* state); //Callback when this item is started running
|
||||
void* next; //Pointer to the next item
|
||||
uint32_t duration; //duration of the item
|
||||
} QueueItem;
|
||||
|
||||
typedef struct {
|
||||
unsigned int start; //current queue item start time
|
||||
QueueItem* current; //current queue item
|
||||
bool running; //is the queue running
|
||||
} QueueState;
|
||||
|
||||
/**
|
||||
* Enqueue a new item.
|
||||
*
|
||||
* @param queue_state The queue state pointer
|
||||
* @param app_state Your app state
|
||||
* @param done Callback for dequeue event
|
||||
* @param start Callback for when the item is activated
|
||||
* @param render Callback to render loop if needed
|
||||
* @param duration Length of the item
|
||||
*/
|
||||
void enqueue(
|
||||
QueueState* queue_state,
|
||||
void* app_state,
|
||||
void (*done)(void* state),
|
||||
void (*start)(void* state),
|
||||
void (*render)(const void* state, Canvas* const canvas),
|
||||
uint32_t duration);
|
||||
/**
|
||||
* Clears all queue items
|
||||
*
|
||||
* @param queue_state The queue state pointer
|
||||
*/
|
||||
void queue_clear(QueueState* queue_state);
|
||||
|
||||
/**
|
||||
* Dequeues the active queue item. Usually you don't need to call it directly.
|
||||
*
|
||||
* @param queue_state The queue state pointer
|
||||
* @param app_state Your application state
|
||||
*/
|
||||
void dequeue(QueueState* queue_state, void* app_state);
|
||||
|
||||
/**
|
||||
* Runs the queue logic (place it in your tick function)
|
||||
*
|
||||
* @param queue_state The queue state pointer
|
||||
* @param app_state Your application state
|
||||
* @return FALSE when there is nothing to run, TRUE otherwise
|
||||
*/
|
||||
bool run_queue(QueueState* queue_state, void* app_state);
|
||||
|
||||
/**
|
||||
* Calls the currently active queue items render callback (if there is any)
|
||||
*
|
||||
* @param queue_state The queue state pointer
|
||||
* @param app_state Your application state
|
||||
* @param canvas Pointer to Flipper's canvas object
|
||||
*/
|
||||
void render_queue(const QueueState* queue_state, const void* app_state, Canvas* const canvas);
|
||||
257
applications/external/solitaire/common/ui.c
vendored
Normal file
257
applications/external/solitaire/common/ui.c
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
#include "ui.h"
|
||||
#include <gui/canvas_i.h>
|
||||
#include <u8g2_glue.h>
|
||||
#include <gui/icon_animation_i.h>
|
||||
#include <gui/icon.h>
|
||||
#include <gui/icon_i.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
TileMap* tileMap;
|
||||
uint8_t tileMapCount = 0;
|
||||
|
||||
void ui_cleanup() {
|
||||
if(tileMap != NULL) {
|
||||
for(uint8_t i = 0; i < tileMapCount; i++) {
|
||||
if(tileMap[i].data != NULL) free(tileMap[i].data);
|
||||
}
|
||||
free(tileMap);
|
||||
}
|
||||
}
|
||||
|
||||
void add_new_tilemap(uint8_t* data, unsigned long iconId) {
|
||||
TileMap* old = tileMap;
|
||||
tileMapCount++;
|
||||
tileMap = malloc(sizeof(TileMap) * tileMapCount);
|
||||
if(tileMapCount > 1) {
|
||||
for(uint8_t i = 0; i < tileMapCount; i++) tileMap[i] = old[i];
|
||||
}
|
||||
tileMap[tileMapCount - 1] = (TileMap){data, iconId};
|
||||
}
|
||||
|
||||
uint8_t* get_tilemap(unsigned long icon_id) {
|
||||
for(uint8_t i = 0; i < tileMapCount; i++) {
|
||||
if(tileMap[i].iconId == icon_id) return tileMap[i].data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t pixel_index(uint8_t x, uint8_t y) {
|
||||
return y * SCREEN_WIDTH + x;
|
||||
}
|
||||
|
||||
bool in_screen(int16_t x, int16_t y) {
|
||||
return x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT;
|
||||
}
|
||||
|
||||
unsigned flipBit(uint8_t x, uint8_t bit) {
|
||||
return x ^ (1 << bit);
|
||||
}
|
||||
|
||||
unsigned setBit(uint8_t x, uint8_t bit) {
|
||||
return x | (1 << bit);
|
||||
}
|
||||
|
||||
unsigned unsetBit(uint8_t x, uint8_t bit) {
|
||||
return x & ~(1 << bit);
|
||||
}
|
||||
|
||||
bool test_pixel(uint8_t* data, uint8_t x, uint8_t y, uint8_t w) {
|
||||
uint8_t current_bit = (y % 8);
|
||||
uint8_t current_row = ((y - current_bit) / 8);
|
||||
uint8_t current_value = data[current_row * w + x];
|
||||
return current_value & (1 << current_bit);
|
||||
}
|
||||
|
||||
uint8_t* get_buffer(Canvas* const canvas) {
|
||||
return canvas->fb.tile_buf_ptr;
|
||||
// return canvas_get_buffer(canvas);
|
||||
}
|
||||
uint8_t* make_buffer() {
|
||||
return malloc(sizeof(uint8_t) * 8 * 128);
|
||||
}
|
||||
void clone_buffer(uint8_t* canvas, uint8_t* data) {
|
||||
for(int i = 0; i < 1024; i++) {
|
||||
data[i] = canvas[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool read_pixel(Canvas* const canvas, int16_t x, int16_t y) {
|
||||
if(in_screen(x, y)) {
|
||||
return test_pixel(get_buffer(canvas), x, y, SCREEN_WIDTH);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void set_pixel(Canvas* const canvas, int16_t x, int16_t y, DrawMode draw_mode) {
|
||||
if(in_screen(x, y)) {
|
||||
uint8_t current_bit = (y % 8);
|
||||
uint8_t current_row = ((y - current_bit) / 8);
|
||||
uint32_t i = pixel_index(x, current_row);
|
||||
uint8_t* buffer = get_buffer(canvas);
|
||||
|
||||
uint8_t current_value = buffer[i];
|
||||
if(draw_mode == Inverse) {
|
||||
buffer[i] = flipBit(current_value, current_bit);
|
||||
} else {
|
||||
if(draw_mode == White) {
|
||||
buffer[i] = unsetBit(current_value, current_bit);
|
||||
} else {
|
||||
buffer[i] = setBit(current_value, current_bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_line(
|
||||
Canvas* const canvas,
|
||||
int16_t x1,
|
||||
int16_t y1,
|
||||
int16_t x2,
|
||||
int16_t y2,
|
||||
DrawMode draw_mode) {
|
||||
for(int16_t x = x2; x >= x1; x--) {
|
||||
for(int16_t y = y2; y >= y1; y--) {
|
||||
set_pixel(canvas, x, y, draw_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_rounded_box_frame(
|
||||
Canvas* const canvas,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t w,
|
||||
uint8_t h,
|
||||
DrawMode draw_mode) {
|
||||
int16_t xMinCorner = x + 1;
|
||||
int16_t xMax = x + w - 1;
|
||||
int16_t xMaxCorner = x + w - 2;
|
||||
int16_t yMinCorner = y + 1;
|
||||
int16_t yMax = y + h - 1;
|
||||
int16_t yMaxCorner = y + h - 2;
|
||||
draw_line(canvas, xMinCorner, y, xMaxCorner, y, draw_mode);
|
||||
draw_line(canvas, xMinCorner, yMax, xMaxCorner, yMax, draw_mode);
|
||||
draw_line(canvas, x, yMinCorner, x, yMaxCorner, draw_mode);
|
||||
draw_line(canvas, xMax, yMinCorner, xMax, yMaxCorner, draw_mode);
|
||||
}
|
||||
|
||||
void draw_rounded_box(
|
||||
Canvas* const canvas,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t w,
|
||||
uint8_t h,
|
||||
DrawMode draw_mode) {
|
||||
for(int16_t o = w - 2; o >= 1; o--) {
|
||||
for(int16_t p = h - 2; p >= 1; p--) {
|
||||
set_pixel(canvas, x + o, y + p, draw_mode);
|
||||
}
|
||||
}
|
||||
draw_rounded_box_frame(canvas, x, y, w, h, draw_mode);
|
||||
}
|
||||
|
||||
void invert_shape(Canvas* const canvas, uint8_t* data, int16_t x, int16_t y, uint8_t w, uint8_t h) {
|
||||
draw_pixels(canvas, data, x, y, w, h, Inverse);
|
||||
}
|
||||
|
||||
void draw_pixels(
|
||||
Canvas* const canvas,
|
||||
uint8_t* data,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t w,
|
||||
uint8_t h,
|
||||
DrawMode drawMode) {
|
||||
for(int8_t o = 0; o < w; o++) {
|
||||
for(int8_t p = 0; p < h; p++) {
|
||||
if(in_screen(o + x, p + y) && data[p * w + o] == 1)
|
||||
set_pixel(canvas, o + x, p + y, drawMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_rectangle(
|
||||
Canvas* const canvas,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t w,
|
||||
uint8_t h,
|
||||
DrawMode drawMode) {
|
||||
for(int8_t o = 0; o < w; o++) {
|
||||
for(int8_t p = 0; p < h; p++) {
|
||||
if(in_screen(o + x, p + y)) {
|
||||
set_pixel(canvas, o + x, p + y, drawMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void invert_rectangle(Canvas* const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h) {
|
||||
draw_rectangle(canvas, x, y, w, h, Inverse);
|
||||
}
|
||||
|
||||
uint8_t* image_data(Canvas* const canvas, const Icon* icon) {
|
||||
uint8_t* data = malloc(sizeof(uint8_t) * 8 * 128);
|
||||
uint8_t* screen = canvas->fb.tile_buf_ptr;
|
||||
canvas->fb.tile_buf_ptr = data;
|
||||
canvas_draw_icon(canvas, 0, 0, icon);
|
||||
canvas->fb.tile_buf_ptr = screen;
|
||||
return data;
|
||||
}
|
||||
|
||||
uint8_t* getOrAddIconData(Canvas* const canvas, const Icon* icon) {
|
||||
uint8_t* icon_data = get_tilemap((unsigned long)icon);
|
||||
if(icon_data == NULL) {
|
||||
icon_data = image_data(canvas, icon);
|
||||
add_new_tilemap(icon_data, (unsigned long)icon);
|
||||
}
|
||||
return icon_data;
|
||||
}
|
||||
|
||||
void draw_icon_clip(
|
||||
Canvas* const canvas,
|
||||
const Icon* icon,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t left,
|
||||
uint8_t top,
|
||||
uint8_t w,
|
||||
uint8_t h,
|
||||
DrawMode drawMode) {
|
||||
uint8_t* icon_data = getOrAddIconData(canvas, icon);
|
||||
|
||||
for(int i = 0; i < w; i++) {
|
||||
for(int j = 0; j < h; j++) {
|
||||
bool on = test_pixel(icon_data, left + i, top + j, SCREEN_WIDTH);
|
||||
if(drawMode == Filled) {
|
||||
set_pixel(canvas, x + i, y + j, on ? Black : White);
|
||||
} else if(on)
|
||||
set_pixel(canvas, x + i, y + j, drawMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_icon_clip_flipped(
|
||||
Canvas* const canvas,
|
||||
const Icon* icon,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t left,
|
||||
uint8_t top,
|
||||
uint8_t w,
|
||||
uint8_t h,
|
||||
DrawMode drawMode) {
|
||||
uint8_t* icon_data = getOrAddIconData(canvas, icon);
|
||||
|
||||
for(int i = 0; i < w; i++) {
|
||||
for(int j = 0; j < h; j++) {
|
||||
bool on = test_pixel(icon_data, left + i, top + j, SCREEN_WIDTH);
|
||||
|
||||
if(drawMode == Filled) {
|
||||
set_pixel(canvas, x + w - i - 1, y + h - j - 1, on ? Black : White);
|
||||
} else if(on)
|
||||
set_pixel(canvas, x + w - i - 1, y + h - j - 1, drawMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
105
applications/external/solitaire/common/ui.h
vendored
Normal file
105
applications/external/solitaire/common/ui.h
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <gui/canvas.h>
|
||||
|
||||
#define SCREEN_WIDTH 128
|
||||
#define SCREEN_HEIGHT 64
|
||||
|
||||
typedef enum {
|
||||
Black,
|
||||
White,
|
||||
Inverse,
|
||||
Filled //Currently only for Icon clip drawing
|
||||
} DrawMode;
|
||||
|
||||
// size is the screen size
|
||||
|
||||
typedef struct {
|
||||
uint8_t* data;
|
||||
unsigned long iconId;
|
||||
} TileMap;
|
||||
|
||||
bool test_pixel(uint8_t* data, uint8_t x, uint8_t y, uint8_t w);
|
||||
|
||||
uint8_t* image_data(Canvas* const canvas, const Icon* icon);
|
||||
|
||||
uint32_t pixel_index(uint8_t x, uint8_t y);
|
||||
|
||||
void draw_icon_clip(
|
||||
Canvas* const canvas,
|
||||
const Icon* icon,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t left,
|
||||
uint8_t top,
|
||||
uint8_t w,
|
||||
uint8_t h,
|
||||
DrawMode drawMode);
|
||||
|
||||
void draw_icon_clip_flipped(
|
||||
Canvas* const canvas,
|
||||
const Icon* icon,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t left,
|
||||
uint8_t top,
|
||||
uint8_t w,
|
||||
uint8_t h,
|
||||
DrawMode drawMode);
|
||||
|
||||
void draw_rounded_box(
|
||||
Canvas* const canvas,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t w,
|
||||
uint8_t h,
|
||||
DrawMode drawMode);
|
||||
|
||||
void draw_rounded_box_frame(
|
||||
Canvas* const canvas,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t w,
|
||||
uint8_t h,
|
||||
DrawMode drawMode);
|
||||
|
||||
void draw_rectangle(
|
||||
Canvas* const canvas,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t w,
|
||||
uint8_t h,
|
||||
DrawMode drawMode);
|
||||
|
||||
void invert_rectangle(Canvas* const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h);
|
||||
|
||||
void invert_shape(Canvas* const canvas, uint8_t* data, int16_t x, int16_t y, uint8_t w, uint8_t h);
|
||||
|
||||
void draw_pixels(
|
||||
Canvas* const canvas,
|
||||
uint8_t* data,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t w,
|
||||
uint8_t h,
|
||||
DrawMode drawMode);
|
||||
|
||||
bool read_pixel(Canvas* const canvas, int16_t x, int16_t y);
|
||||
|
||||
void set_pixel(Canvas* const canvas, int16_t x, int16_t y, DrawMode draw_mode);
|
||||
|
||||
void draw_line(
|
||||
Canvas* const canvas,
|
||||
int16_t x1,
|
||||
int16_t y1,
|
||||
int16_t x2,
|
||||
int16_t y2,
|
||||
DrawMode draw_mode);
|
||||
|
||||
bool in_screen(int16_t x, int16_t y);
|
||||
|
||||
void ui_cleanup();
|
||||
uint8_t* get_buffer(Canvas* const canvas);
|
||||
uint8_t* make_buffer();
|
||||
void clone_buffer(uint8_t* canvas, uint8_t* data);
|
||||
Reference in New Issue
Block a user