Add interactive mathematics for elementary review and four secondary-school years.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
pepaskopec-blip 2026-10-06 14:49:25 +02:00
commit 0e24e8c649
38 changed files with 9507 additions and 11 deletions

View file

@ -20,6 +20,7 @@
#define HW_SUBJ 2 /* index of "Technické vybavení" */
#define CZ_SUBJ 3 /* index of "Český jazyk a literatura" */
#define ON_SUBJ 4 /* index of "Občanská nauka" */
#define MATH_SUBJ 6 /* index of "Matematika" */
#define PHY_SUBJ 7 /* index of "Fyzika" */
#define SCI_SUBJ 8 /* index of "Základy Přírodopisných věd" */
#define CSS_FILE "style.css"
@ -67,6 +68,11 @@ extern char *app_progress_fyz;
extern char *app_progress_fyz2;
extern char *app_progress_fyz3;
extern char *app_progress_fyz4;
extern char *app_progress_mat0;
extern char *app_progress_mat;
extern char *app_progress_mat2;
extern char *app_progress_mat3;
extern char *app_progress_mat4;
#define PROGRESS_DIR app_progress_dir
#define PROGRESS_U1 app_progress_u1
#define PROGRESS_U2 app_progress_u2
@ -146,6 +152,7 @@ extern char *app_progress_fyz4;
#define LIT_N 8
#define SCI_N 8
#define FYZ_N 8
#define MATH_N 8
#define HW_SLIDES 3
#define HW2_SLIDES 6
#define HW3_SLIDES 2
@ -345,6 +352,11 @@ typedef struct {
int n_options;
int correct;
} ChoiceQ;
typedef struct {
const char *prompt;
const char *answer;
const char *hint;
} MathItem;
typedef struct {
const ChoiceQ *qs;
int n;
@ -979,6 +991,37 @@ extern NetLesson fyz_lessons[FYZ_N];
extern NetLesson fyz2_lessons[FYZ_N];
extern NetLesson fyz3_lessons[FYZ_N];
extern NetLesson fyz4_lessons[FYZ_N];
gboolean math_answer_ok(const char *spec, const char *user);
gboolean math_is_draw(const char *spec);
int math_draw_need(const char *spec);
int math_draw_fixed(const char *spec, int *xs, int *ys, int cap);
gboolean math_draw_ok(const char *spec, const int *xs, const int *ys, int n);
void math_load_progress(void);
void math_rail_theme_reset(void);
void math_lessons_apply_lang(void);
void progress_for_math(ProgressSum *out);
GtkWidget *math_unit_page(int year, NetLesson *L, const char *title_key,
const char *sub_key, const NetSlide *slides,
guint n_slides);
GtkWidget *math_practice_page(int year, int lesson_id, const char *back_page,
const char *title_key, const char *heading_key,
const MathItem *items, int n);
GtkWidget *build_matyears_page(void);
GtkWidget *build_mat0map_page(void);
GtkWidget *build_matmap_page(void);
GtkWidget *build_mat2map_page(void);
GtkWidget *build_mat3map_page(void);
GtkWidget *build_mat4map_page(void);
void add_mat0_pages(GtkStack *stack);
void add_mat_pages(GtkStack *stack);
void add_mat2_pages(GtkStack *stack);
void add_mat3_pages(GtkStack *stack);
void add_mat4_pages(GtkStack *stack);
extern NetLesson mat0_lessons[MATH_N];
extern NetLesson mat_lessons[MATH_N];
extern NetLesson mat2_lessons[MATH_N];
extern NetLesson mat3_lessons[MATH_N];
extern NetLesson mat4_lessons[MATH_N];
void hw_lessons_apply_lang(void);
GtkWidget *build_hwyears_page(void);
GtkWidget *build_hwmap_page(void);
@ -1068,6 +1111,8 @@ void draw_civics_icon(GtkDrawingArea *area, cairo_t *cr,
int width, int height, gpointer data);
void draw_science_icon(GtkDrawingArea *area, cairo_t *cr,
int width, int height, gpointer data);
void draw_math_icon(GtkDrawingArea *area, cairo_t *cr,
int width, int height, gpointer data);
void draw_physics_icon(GtkDrawingArea *area, cairo_t *cr,
int width, int height, gpointer data);
void draw_czech_flag(GtkDrawingArea *area, cairo_t *cr,

View file

@ -90,12 +90,12 @@ const TrEntry tr_ui[] = {
"Cvičení, nápovědy a vokabeltraining. Aplikace si pamatuje, kde jste skončili.",
"Exercises, hints and vocabulary drills. The app remembers where you left off."},
{"welcome_feat1_link", "Začít procvičovat", "Start practicing"},
{"welcome_feat2_title", "Sedm otevřených předmětů", "Seven open subjects"},
{"welcome_feat2_title", "Osm otevřených předmětů", "Eight open subjects"},
{"welcome_feat2_body",
"Deutsch, sítě, hardware, čeština, občanská nauka, fyzika a přírodní vědy. Ostatní předměty čekají na svůj obsah.",
"German, networks, hardware, Czech, civics, physics and natural sciences. The other subjects are still locked."},
"Deutsch, sítě, hardware, čeština, občanská nauka, matematika, fyzika a přírodní vědy. Ostatní předměty čekají na svůj obsah.",
"German, networks, hardware, Czech, civics, mathematics, physics and natural sciences. The other subjects are still locked."},
{"welcome_feat2_link", "Jaké předměty jsou otevřené?", "Which subjects are open?"},
{"welcome_stat_value", "7", "7"},
{"welcome_stat_value", "8", "8"},
{"welcome_stat_label", "otevřené\npředměty", "open\nsubjects"},
{"continue", "Pokračuj", "Continue"},
{"roadmap_title", "Učební plán", "Learning path"},
@ -1619,6 +1619,7 @@ const TrEntry tr_ui[] = {
{"lit_fuks_plot_title", "Sestavte děj", "Order the plot"},
{"lit_fuks_plot_sub", "Přetáhněte části příběhu do správného pořadí.",
"Drag the story parts into the correct order."},
#include "math_i18n.inc"
{NULL, NULL, NULL}
};
@ -2603,6 +2604,7 @@ void on4_lessons_apply_lang(void);
void lit_lessons_apply_lang(void);
void sci_lessons_apply_lang(void);
void fyz_lessons_apply_lang(void);
void math_lessons_apply_lang(void);
void net_rail_theme_reset(void);
void apply_language(void) {
@ -2624,5 +2626,6 @@ void apply_language(void) {
lit_lessons_apply_lang();
sci_lessons_apply_lang();
fyz_lessons_apply_lang();
math_lessons_apply_lang();
search_apply_lang();
}

View file

@ -261,6 +261,34 @@ void draw_physics_icon(GtkDrawingArea *area, cairo_t *cr,
cairo_stroke(cr);
}
/* Axes and a rising curve for Matematika. */
void draw_math_icon(GtkDrawingArea *area, cairo_t *cr,
int width, int height, gpointer data) {
const double s = MIN(width, height);
const double left = width * 0.22;
const double right = width * 0.82;
const double bottom = height * 0.78;
const double top = height * 0.22;
const Rgb c = mix_rgb(color_from_hex(app_theme.on_accent),
color_from_hex(app_theme.accent2), 0.22);
(void)area;
(void)data;
cairo_set_source_rgb(cr, c.r, c.g, c.b);
cairo_set_line_width(cr, MAX(1.6, s * 0.07));
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
cairo_move_to(cr, left, top);
cairo_line_to(cr, left, bottom);
cairo_line_to(cr, right, bottom);
cairo_stroke(cr);
cairo_move_to(cr, left + s * 0.06, bottom - s * 0.08);
cairo_curve_to(cr, left + s * 0.28, bottom - s * 0.02,
width * 0.48, height * 0.62,
right - s * 0.04, top + s * 0.06);
cairo_stroke(cr);
}
/* Open book glyph, painted in the given colour. */
static void book_paint(cairo_t *cr, int width, int height, Rgb c) {
const double w = width;

View file

@ -390,6 +390,9 @@ static gboolean run_smoke_test(gpointer data) {
"czechmap",
"lityears", "litmap", "lit2map", "lit3map", "lit4map",
"scimap", "chemmap", "biomap",
"matyears", "mat0map", "matmap", "mat2map", "mat3map", "mat4map",
"mat0unit1", "mat0ex1", "matunit1", "matex1",
"mat4unit8", "mat4ex8",
"fyzyears", "fyzmap", "fyz2map", "fyz3map", "fyz4map",
"fyzunit1", "fyzex1", "fyzunit8", "fyzex8",
"fyz2unit1", "fyz2ex1", "fyz3unit1", "fyz4unit1",
@ -532,6 +535,17 @@ void activate(GtkApplication *app, gpointer user_data) {
add_on3_pages(main_stack);
gtk_stack_add_named(main_stack, build_on4map_page(), "on4map");
add_on4_pages(main_stack);
gtk_stack_add_named(main_stack, build_matyears_page(), "matyears");
gtk_stack_add_named(main_stack, build_mat0map_page(), "mat0map");
add_mat0_pages(main_stack);
gtk_stack_add_named(main_stack, build_matmap_page(), "matmap");
add_mat_pages(main_stack);
gtk_stack_add_named(main_stack, build_mat2map_page(), "mat2map");
add_mat2_pages(main_stack);
gtk_stack_add_named(main_stack, build_mat3map_page(), "mat3map");
add_mat3_pages(main_stack);
gtk_stack_add_named(main_stack, build_mat4map_page(), "mat4map");
add_mat4_pages(main_stack);
gtk_stack_add_named(main_stack, build_fyzyears_page(), "fyzyears");
gtk_stack_add_named(main_stack, build_fyzmap_page(), "fyzmap");
add_fyz_pages(main_stack);

469
src/mat_year0.c Normal file
View file

@ -0,0 +1,469 @@
#include "graduately.h"
NetLesson mat0_lessons[MATH_N] = {
{ .n_slides = 3, .unit_page = "mat0unit1", .ex_page = "mat0ex1" },
{ .n_slides = 3, .unit_page = "mat0unit2", .ex_page = "mat0ex2" },
{ .n_slides = 3, .unit_page = "mat0unit3", .ex_page = "mat0ex3" },
{ .n_slides = 3, .unit_page = "mat0unit4", .ex_page = "mat0ex4" },
{ .n_slides = 3, .unit_page = "mat0unit5", .ex_page = "mat0ex5" },
{ .n_slides = 3, .unit_page = "mat0unit6", .ex_page = "mat0ex6" },
{ .n_slides = 3, .unit_page = "mat0unit7", .ex_page = "mat0ex7" },
{ .n_slides = 3, .unit_page = "mat0unit8", .ex_page = "mat0ex8" },
};
static GtkWidget *build_mat0_unit1_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Pravidlo", "Co se počítá dřív",
"Tip: násobení a dělení jsou stejně silné a jdou zleva doprava.",
{
"Nejprve se vyřeší závorky, zevnitř ven.",
"Pak jsou mocniny a odmocniny.",
"Násobení a dělení mají přednost před sčítáním a odčítáním.",
"Sčítání a odčítání se také provádějí zleva doprava.",
NULL,
},
},
{
"2 / 3 • Bez závorek", "Násobení má přednost",
"Tip: 2 + 3 · 4 není 20.",
{
"Ve výrazu 2 + 3 · 4 se nejdřív násobí: 3 · 4 = 12.",
"Teprve potom se přičtou dvě: 2 + 12 = 14.",
"Ve výrazu 20 − 6 / 2 se nejdřív dělí: 6 / 2 = 3.",
"Pak 20 − 3 = 17.",
NULL,
},
},
{
"3 / 3 • Závorky", "Závorka změní pořadí",
"Tip: závorka se chová jako jeden počet.",
{
"(8 − 3) · 2 začíná v závorce: 5 · 2 = 10.",
"3 · (4 + 1) je 3 · 5 = 15.",
"Bez závorky by 3 · 4 + 1 vyšlo 13.",
"Závorku proto pište vždy, když má sčítání předběhnout násobení.",
NULL,
},
},
};
return math_unit_page(0, &mat0_lessons[0], "mat0_unit1", "mat0_unit1_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat0_unit1_exercise_page(void) {
static const MathItem qs[] = {
{"Spočítejte 2 + 3 · 4.", "num:14", "Násobení má přednost: 3 · 4 = 12 a 2 + 12 = 14. Výsledek je 14."},
{"Spočítejte (8 − 3) · 2.", "num:10", "Nejdřív závorka: 8 − 3 = 5, potom 5 · 2 = 10. Výsledek je 10."},
{"Spočítejte 20 − 6 / 2.", "num:17", "Nejdřív dělení: 6 / 2 = 3, potom 20 − 3 = 17. Výsledek je 17."},
{"Spočítejte 3 · (4 + 1).", "num:15", "Nejdřív závorka: 4 + 1 = 5, potom 3 · 5 = 15. Výsledek je 15."},
};
return math_practice_page(0, 0, "mat0unit1",
"mat0_ex1_title", "mat0_quiz1_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat0_unit2_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Součet", "Společný jmenovatel",
"Tip: zlomek v základním tvaru má nesoudělný čitatel a jmenovatel.",
{
"Sčítat a odčítat lze zlomky se stejným jmenovatelem.",
"Jinak se jmenovatelé rozšíří na nejmenší společný násobek.",
"1/2 + 1/3 má společný jmenovatel 6: 3/6 + 2/6 = 5/6.",
"3/4 − 1/4 = 2/4 = 1/2.",
NULL,
},
},
{
"2 / 3 • Součin", "Čitatel s čitatelem",
"Tip: před násobením jde často krátit křížem.",
{
"Zlomky se násobí čitatel čitatelem a jmenovatel jmenovatelem.",
"2/3 · 3/5 = 6/15 = 2/5.",
"Krátit se smí jen čitatel se jmenovatelem, ne dvě horní čísla mezi sebou.",
"Smíšené číslo se před počtem převede na zlomek.",
NULL,
},
},
{
"3 / 3 • Podíl", "Dělit znamená násobit převráceným",
"Tip: převrácený zlomek k a/b je b/a.",
{
"Dělení zlomkem je násobení jeho převrácenou hodnotou.",
"(3/4) : (1/2) = 3/4 · 2/1 = 6/4 = 3/2.",
"Výsledek se uvede v základním tvaru.",
"Nulou se dělit nesmí, proto jmenovatel nesmí vyjít 0.",
NULL,
},
},
};
return math_unit_page(0, &mat0_lessons[1], "mat0_unit2", "mat0_unit2_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat0_unit2_exercise_page(void) {
static const MathItem qs[] = {
{"Spočítejte 1/2 + 1/3. Zlomek v základním tvaru.", "num:5/6", "Společný jmenovatel je 6: 3/6 + 2/6 = 5/6. Výsledek je 5/6."},
{"Spočítejte 3/4 − 1/4.", "num:1/2", "Jmenovatel už je stejný: 2/4 = 1/2. Výsledek je 1/2."},
{"Spočítejte 2/3 · 3/5.", "num:2/5", "6/15 se zkrátí třemi na 2/5. Výsledek je 2/5."},
{"Spočítejte (3/4) : (1/2).", "num:3/2", "Násobí se převráceným zlomkem: 3/4 · 2/1 = 3/2. Výsledek je 3/2."},
};
return math_practice_page(0, 1, "mat0unit2",
"mat0_ex2_title", "mat0_quiz2_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat0_unit3_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Zápis", "Čárka i tečka",
"Tip: 0,5 je totéž co 1/2.",
{
"Desetinná čárka odděluje celou část od desetin, setin a tisícin.",
"0,1 je jedna desetina, 0,01 je jedna setina.",
"Při sčítání se čárky píšou pod sebe.",
"2,5 + 1,5 = 4.",
NULL,
},
},
{
"2 / 3 • Násobení", "Čárka se posouvá",
"Tip: násobení deseti posune čárku o jedno místo doprava.",
{
"Počet desetinných míst součinu je součet počtů míst činitelů.",
"0,2 · 5 = 1, protože dvě desetiny pětkrát jsou jedna celá.",
"1,25 − 0,5 = 0,75, což je 3/4.",
"Výsledek lze psát desetinně i zlomkem.",
NULL,
},
},
{
"3 / 3 • Dělení", "Dělit desetinným číslem",
"Tip: dělitel se nejdřív upraví na celé číslo.",
{
"3,6 : 0,3 se rozšíří deseti na 36 : 3 = 12.",
"Čárka se v děliteli i děleném posune o stejný počet míst.",
"Dělení deseti posune čárku o jedno místo doleva.",
"Nulou se ani u desetinných čísel dělit nesmí.",
NULL,
},
},
};
return math_unit_page(0, &mat0_lessons[2], "mat0_unit3", "mat0_unit3_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat0_unit3_exercise_page(void) {
static const MathItem qs[] = {
{"Spočítejte 2,5 + 1,5.", "num:4", "Pět desetin a pět desetin je jedna celá, celkem 4. Výsledek je 4."},
{"Spočítejte 0,2 · 5.", "num:1", "Dvě desetiny pětkrát jsou 10 desetin, tedy 1. Výsledek je 1."},
{"Spočítejte 3,6 : 0,3.", "num:12", "Posun čárky dá 36 : 3 = 12. Výsledek je 12."},
{"Spočítejte 1,25 − 0,5.", "num:3/4", "1,25 − 0,50 = 0,75, tedy 3/4. Výsledek je 3/4."},
};
return math_practice_page(0, 2, "mat0unit3",
"mat0_ex3_title", "mat0_quiz3_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat0_unit4_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Základ", "Jedno procento je setina",
"Tip: pište jen číslo, znak procenta ne.",
{
"1 % ze základu je jedna setina základu.",
"p % ze základu z je (p/100) · z.",
"20 % ze 150 je 0,2 · 150 = 30.",
"Základ, procentová část a počet procent jsou tři veličiny.",
NULL,
},
},
{
"2 / 3 • Kolik procent", "Část dělená základem",
"Tip: výsledek 0,25 znamená 25 %.",
{
"Počet procent je (část / základ) · 100.",
"15 ze 60 je 15/60 = 1/4, tedy 25 %.",
"Do odpovědi se píše 25, ne 0,25 a ne 25 %.",
"Zlomek se na procenta násobí stem.",
NULL,
},
},
{
"3 / 3 • Sleva a základ", "Dopočítání celku",
"Tip: sleva 10 % nechá z ceny 90 %.",
{
"Po slevě 10 % zbývá 90 % původní ceny.",
"400 − 10 % je 0,9 · 400 = 360.",
"Když 20 % základu je 18, je 1 % rovno 18/20.",
"Celý základ je pak 100 takových dílů: 90.",
NULL,
},
},
};
return math_unit_page(0, &mat0_lessons[3], "mat0_unit4", "mat0_unit4_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat0_unit4_exercise_page(void) {
static const MathItem qs[] = {
{"Kolik je 20 % ze 150? Napište jen číslo.", "num:30", "20 % je 0,2 a 0,2 · 150 = 30. Výsledek je 30."},
{"15 je kolik procent z 60? Napište jen číslo, bez znaku %.", "num:25", "15/60 = 1/4 = 25 %. Výsledek je 25."},
{"Cena 400 se sníží o 10 %. Jaká bude nová cena?", "num:360", "Zůstane 90 %: 0,9 · 400 = 360. Výsledek je 360."},
{"20 % základu je 18. Jak velký je základ?", "num:90", "1 % je 18/20 = 0,9 a 100 % je 90. Výsledek je 90."},
};
return math_practice_page(0, 3, "mat0unit4",
"mat0_ex4_title", "mat0_quiz4_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat0_unit5_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Poměr", "Díly celku",
"Tip: poměr 2 : 3 má dohromady 5 dílů.",
{
"Poměr a : b říká, na kolik stejných dílů se celek rozdělí.",
"Počet dílů je a + b.",
"První část je a/(a + b) z celku.",
"Z 20 v poměru 2 : 3 připadá na první část 8.",
NULL,
},
},
{
"2 / 3 • Úměra", "Součin vnějších a vnitřních členů",
"Tip: 3 : 5 = x : 20 znamená 3/5 = x/20.",
{
"V úměře a : b = c : d platí a · d = b · c.",
"Z 3/5 = x/20 vyjde x = 3/5 · 20 = 12.",
"Stejný poměr lze rozšiřovat i krátit.",
"Neznámý člen se dopočítá jedním násobením a jedním dělením.",
NULL,
},
},
{
"3 / 3 • Měřítko", "Mapa a skutečnost",
"Tip: 1 : 1000 znamená, že 1 cm na mapě je 10 m ve skutečnosti.",
{
"Měřítko 1 : k zmenšuje: skutečnost je k-krát větší než plán.",
"5 cm na mapě 1 : 1000 je 5000 cm, tedy 50 m.",
"Měřítko 5 : 1 je zvětšení, obraz je pětkrát větší.",
"4 cm v měřítku 5 : 1 odpovídá obrazu 20 cm.",
NULL,
},
},
};
return math_unit_page(0, &mat0_lessons[4], "mat0_unit5", "mat0_unit5_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat0_unit5_exercise_page(void) {
static const MathItem qs[] = {
{"Číslo 20 rozdělte v poměru 2 : 3. Jak velká je první část?", "num:8", "Dílů je 5 a první část jsou 2 z nich: 2/5 · 20 = 8. Výsledek je 8."},
{"Na mapě 1 : 1000 jsou 5 cm. Kolik metrů je to ve skutečnosti?", "num:50", "5 cm · 1000 = 5000 cm = 50 m. Výsledek je 50."},
{"Platí 3 : 5 = x : 20. Kolik je x?", "num:12", "x = 3/5 · 20 = 12. Výsledek je 12."},
{"Úsečka 4 cm se zobrazí v měřítku 5 : 1. Jak dlouhý je obraz v centimetrech?", "num:20", "Měřítko 5 : 1 zvětšuje pětkrát: 4 · 5 = 20. Výsledek je 20."},
};
return math_practice_page(0, 4, "mat0unit5",
"mat0_ex5_title", "mat0_quiz5_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat0_unit6_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Číselná osa", "Vpravo roste, vlevo klesá",
"Tip: přičíst kladné číslo znamená posun doprava.",
{
"Kladná čísla leží vpravo od nuly, záporná vlevo.",
"−3 + 8 je posun z −3 o 8 kroků doprava, tedy na 5.",
"Odčítání záporného čísla je totéž co přičítání kladného.",
"6 − (−2) = 6 + 2 = 8.",
NULL,
},
},
{
"2 / 3 • Násobení", "Znaménka",
"Tip: dvě záporná znaménka dají kladný součin.",
{
"Součin kladného a záporného čísla je záporný.",
"Součin dvou záporných čísel je kladný.",
"(−4) · (−5) = 20.",
"Stejné pravidlo platí pro dělení.",
NULL,
},
},
{
"3 / 3 • Absolutní hodnota", "Vzdálenost od nuly",
"Tip: absolutní hodnota nikdy není záporná.",
{
"|a| je vzdálenost čísla a od nuly na ose.",
"|−7| = 7 a |7| = 7.",
"|0| = 0.",
"Absolutní hodnota smaže znaménko, ale číslo jinak nezmění.",
NULL,
},
},
};
return math_unit_page(0, &mat0_lessons[5], "mat0_unit6", "mat0_unit6_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat0_unit6_exercise_page(void) {
static const MathItem qs[] = {
{"Spočítejte −3 + 8.", "num:5", "Z −3 se jde o 8 doprava, na 5. Výsledek je 5."},
{"Spočítejte (−4) · (−5).", "num:20", "Součin dvou záporných čísel je kladný: 20. Výsledek je 20."},
{"Spočítejte |−7|.", "num:7", "Vzdálenost −7 od nuly je 7. Výsledek je 7."},
{"Spočítejte 6 − (−2).", "num:8", "Odčítat −2 znamená přičíst 2: 6 + 2 = 8. Výsledek je 8."},
};
return math_practice_page(0, 5, "mat0unit6",
"mat0_ex6_title", "mat0_quiz6_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat0_unit7_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Rovnováha", "Stejně na obě strany",
"Tip: co uděláte vlevo, udělejte i vpravo.",
{
"Rovnice říká, že levá strana se rovná pravé.",
"K oběma stranám lze přičíst stejné číslo.",
"Obě strany lze vynásobit stejným nenulovým číslem.",
"Cílem je osamostatnit neznámou.",
NULL,
},
},
{
"2 / 3 • Kroky", "Nejdřív číslo, pak koeficient",
"Tip: 2x − 4 = 10 se nejdřív zbaví čtyřky.",
{
"x + 5 = 12 se odečtením pěti změní na x = 7.",
"3x = 15 se vydělí třemi: x = 5.",
"2x − 4 = 10 dá po přičtení čtyř 2x = 14, tedy x = 7.",
"Zkouška dosadí výsledek do původní rovnice.",
NULL,
},
},
{
"3 / 3 • Dělení neznámé", "Zlomek s x",
"Tip: x/2 = 6 se násobí dvěma.",
{
"x/2 je totéž co (1/2) · x.",
"Násobení obou stran dvěma dá x = 12.",
"Násobit nulou se nesmí, rovnice by ztratila význam.",
"U lineární rovnice s nenulovým koeficientem je řešení jedno.",
NULL,
},
},
};
return math_unit_page(0, &mat0_lessons[6], "mat0_unit7", "mat0_unit7_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat0_unit7_exercise_page(void) {
static const MathItem qs[] = {
{"Vyřešte x + 5 = 12. Napište x.", "num:7", "Od obou stran se odečte 5. Výsledek je 7."},
{"Vyřešte 3x = 15. Napište x.", "num:5", "Obě strany se vydělí 3. Výsledek je 5."},
{"Vyřešte 2x − 4 = 10. Napište x.", "num:7", "2x = 14, takže x = 7. Výsledek je 7."},
{"Vyřešte x/2 = 6. Napište x.", "num:12", "Obě strany se násobí 2. Výsledek je 12."},
};
return math_practice_page(0, 6, "mat0unit7",
"mat0_ex7_title", "mat0_quiz7_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat0_unit8_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Obdélník", "Obvod a obsah",
"Tip: obvod je součet všech stran, obsah je součin dvou sousedních.",
{
"Obdélník a × b má obvod 2 · (a + b).",
"Obdélník 5 × 3 má obvod 2 · 8 = 16.",
"Obsah je a · b. Obdélník 6 × 4 má obsah 24.",
"Jednotky obvodu jsou délkové, jednotky obsahu čtvereční.",
NULL,
},
},
{
"2 / 3 • Trojúhelník", "Základna a výška",
"Tip: výška je kolmá na základnu, ne délka šikmé strany.",
{
"Součet vnitřních úhlů trojúhelníku je 180°.",
"Třetí úhel je 180° minus součet dvou známých.",
"Úhly 60° a 50° doplňuje 70°.",
"Obsah je (základna · výška) / 2. Pro 8 a 5 je to 20.",
NULL,
},
},
{
"3 / 3 • Čtverec", "Doplnění na mřížce",
"Tip: osa y míří nahoru. Klepnutím na bod ho odeberete.",
{
"Čtverec má čtyři shodné strany a čtyři pravé úhly.",
"Druhé dva vrcholy vzniknou posunutím strany o stejnou délku kolmo.",
"U vodorovné strany leží zbývající vrcholy přímo nad ní, nebo pod ní.",
"Na mřížce jsou platné jen oba vrcholy téže strany.",
NULL,
},
},
};
return math_unit_page(0, &mat0_lessons[7], "mat0_unit8", "mat0_unit8_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat0_unit8_exercise_page(void) {
static const MathItem qs[] = {
{"Obdélník má strany 5 a 3. Jaký je jeho obvod?", "num:16", "2 · (5 + 3) = 16. Výsledek je 16."},
{"Obdélník má strany 6 a 4. Jaký je jeho obsah?", "num:24", "Obsah je 6 · 4 = 24. Výsledek je 24."},
{"Trojúhelník má základnu 8 a výšku 5. Jaký je jeho obsah?", "num:20", "Obsah je (8 · 5) / 2 = 20. Výsledek je 20."},
{"Dva úhly trojúhelníku jsou 60° a 50°. Jak velký je třetí úhel ve stupních?", "num:70", "Součet je 180°, takže třetí úhel je 70°. Výsledek je 70."},
{"Na mřížce je strana čtverce od A = [2; 3] k B = [5; 3]. Doplňte zbývající dva vrcholy.", "square:2,3;5,3", "Strana má délku 3. Druhé dva vrcholy jsou [2; 6] a [5; 6], nebo [2; 0] a [5; 0]."},
};
return math_practice_page(0, 7, "mat0unit8",
"mat0_ex8_title", "mat0_quiz8_head",
qs, G_N_ELEMENTS(qs));
}
void add_mat0_pages(GtkStack *stack) {
typedef GtkWidget *(*MathBuilder)(void);
static const struct {
const char *unit_name;
const char *ex_name;
MathBuilder build_unit;
MathBuilder build_ex;
} pages[] = {
{"mat0unit1", "mat0ex1", build_mat0_unit1_page, build_mat0_unit1_exercise_page},
{"mat0unit2", "mat0ex2", build_mat0_unit2_page, build_mat0_unit2_exercise_page},
{"mat0unit3", "mat0ex3", build_mat0_unit3_page, build_mat0_unit3_exercise_page},
{"mat0unit4", "mat0ex4", build_mat0_unit4_page, build_mat0_unit4_exercise_page},
{"mat0unit5", "mat0ex5", build_mat0_unit5_page, build_mat0_unit5_exercise_page},
{"mat0unit6", "mat0ex6", build_mat0_unit6_page, build_mat0_unit6_exercise_page},
{"mat0unit7", "mat0ex7", build_mat0_unit7_page, build_mat0_unit7_exercise_page},
{"mat0unit8", "mat0ex8", build_mat0_unit8_page, build_mat0_unit8_exercise_page},
};
for (guint i = 0; i < G_N_ELEMENTS(pages); i++) {
gtk_stack_add_named(stack, pages[i].build_unit(), pages[i].unit_name);
gtk_stack_add_named(stack, pages[i].build_ex(), pages[i].ex_name);
}
}

468
src/mat_year1.c Normal file
View file

@ -0,0 +1,468 @@
#include "graduately.h"
NetLesson mat_lessons[MATH_N] = {
{ .n_slides = 3, .unit_page = "matunit1", .ex_page = "matex1" },
{ .n_slides = 3, .unit_page = "matunit2", .ex_page = "matex2" },
{ .n_slides = 3, .unit_page = "matunit3", .ex_page = "matex3" },
{ .n_slides = 3, .unit_page = "matunit4", .ex_page = "matex4" },
{ .n_slides = 3, .unit_page = "matunit5", .ex_page = "matex5" },
{ .n_slides = 3, .unit_page = "matunit6", .ex_page = "matex6" },
{ .n_slides = 3, .unit_page = "matunit7", .ex_page = "matex7" },
{ .n_slides = 3, .unit_page = "matunit8", .ex_page = "matex8" },
};
static GtkWidget *build_mat_unit1_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Dělitel", "Čísla, která dělí beze zbytku",
"Tip: prvočíslo má právě dva dělitele, jedničku a sebe.",
{
"Dělitel čísla a je celé číslo, kterým a dělí beze zbytku.",
"Číslo 12 má dělitele 1, 2, 3, 4, 6 a 12, tedy šest dělitelů.",
"Prvočíslo nelze rozložit na součin dvou menších celých čísel větších než 1.",
"Jednička prvočíslo není.",
NULL,
},
},
{
"2 / 3 • NSD a NSN", "Společný dělitel a násobek",
"Tip: NSD · NSN dvou čísel je jejich součin.",
{
"Největší společný dělitel je největší číslo, které dělí obě čísla.",
"NSD(24, 36) = 12.",
"Nejmenší společný násobek je nejmenší kladné číslo, které je násobkem obou.",
"NSN(6, 8) = 24.",
NULL,
},
},
{
"3 / 3 • Rozklad", "Součin mocnin prvočísel",
"Tip: společné prvočinitele berte v nejmenší mocnině pro NSD.",
{
"Každé celé číslo větší než 1 má jediný rozklad na prvočísla.",
"72 = 2³ · 3².",
"V NSD se bere nižší exponent, v NSN vyšší.",
"Rozklad usnadní krácení zlomků i hledání společného jmenovatele.",
NULL,
},
},
};
return math_unit_page(1, &mat_lessons[0], "mat_unit1", "mat_unit1_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat_unit1_exercise_page(void) {
static const MathItem qs[] = {
{"Jaký je největší společný dělitel čísel 24 a 36?", "num:12", "Společní dělitelé jsou 1, 2, 3, 4, 6 a 12. Výsledek je 12."},
{"Jaký je nejmenší společný násobek čísel 6 a 8?", "num:24", "Násobky 6 jsou 6, 12, 18, 24, … a 24 je první z nich dělitelné osmi. Výsledek je 24."},
{"Spočítejte 2³ · 3².", "num:72", "8 · 9 = 72. Výsledek je 72."},
{"Kolik kladných dělitelů má číslo 12?", "num:6", "Jsou to 1, 2, 3, 4, 6 a 12. Výsledek je 6."},
};
return math_practice_page(1, 0, "matunit1",
"mat_ex1_title", "mat_quiz1_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat_unit2_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Mocnina", "Opakované násobení",
"Tip: aⁿ je a násobené sebou n-krát.",
{
"2⁵ = 2 · 2 · 2 · 2 · 2 = 32.",
"10³ = 1000, tři nuly za jedničkou.",
"Každé číslo na první je ono samo a a⁰ = 1 pro a ≠ 0.",
"Záporný exponent znamená převrácenou hodnotu mocniny.",
NULL,
},
},
{
"2 / 3 • Pravidla", "Stejný základ",
"Tip: (aᵐ)ⁿ = aᵐ·ⁿ.",
{
"aᵐ · aⁿ = aᵐ⁺ⁿ.",
"aᵐ : aⁿ = aᵐ⁻ⁿ pro a ≠ 0.",
"(a · b)ⁿ = aⁿ · bⁿ.",
"(2³)² = 2⁶ = 64.",
NULL,
},
},
{
"3 / 3 • Odmocnina", "Druhá odmocnina",
"Tip: √a je nezáporné číslo, jehož druhá mocnina je a.",
{
"√81 = 9, protože 9² = 81.",
"√0 = 0 a √1 = 1.",
"Ze záporného čísla se v reálných číslech druhá odmocnina nedělá.",
"√(a²) = |a|, ne vždy a.",
NULL,
},
},
};
return math_unit_page(1, &mat_lessons[1], "mat_unit2", "mat_unit2_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat_unit2_exercise_page(void) {
static const MathItem qs[] = {
{"Spočítejte 2⁵.", "num:32", "Pět dvojkových činitelů je 32. Výsledek je 32."},
{"Spočítejte 10³.", "num:1000", "10 · 10 · 10 = 1000. Výsledek je 1000."},
{"Spočítejte √81.", "num:9", "9 · 9 = 81. Výsledek je 9."},
{"Spočítejte (2³)².", "num:64", "(8)² = 64, nebo 2⁶ = 64. Výsledek je 64."},
};
return math_practice_page(1, 1, "matunit2",
"mat_ex2_title", "mat_quiz2_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat_unit3_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Dosazení", "Písmeno je číslo",
"Tip: nejdřív závorky, potom mocniny a násobení.",
{
"Do výrazu se za proměnnou dosadí dané číslo.",
"2(x + 3) při x = 4 je 2 · 7 = 14.",
"3x − 2x + 5 se nejdřív sloučí na x + 5.",
"Při x = 2 je x + 5 = 7.",
NULL,
},
},
{
"2 / 3 • Vzorce", "Druhá mocnina dvojčlenu",
"Tip: (a + b)² není a² + b².",
{
"(a + b)² = a² + 2ab + b².",
"(a − b)² = a² − 2ab + b².",
"a² − b² = (a − b)(a + b).",
"Pro a = 3 a b = 1 je (a + b)² = 16.",
NULL,
},
},
{
"3 / 3 • Zkrácení", "Rozdíl čtverců",
"Tip: krátit lze jen pro x, která nejsou kořenem jmenovatele.",
{
"x² − 9 = (x − 3)(x + 3).",
"(x² − 9)/(x − 3) = x + 3 pro x ≠ 3.",
"Při x = 5 je hodnota 8.",
"Dosazení x = 3 by dělilo nulou, výraz tam není definován.",
NULL,
},
},
};
return math_unit_page(1, &mat_lessons[2], "mat_unit3", "mat_unit3_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat_unit3_exercise_page(void) {
static const MathItem qs[] = {
{"Do výrazu 2(x + 3) dosažte x = 4.", "num:14", "2 · (4 + 3) = 14. Výsledek je 14."},
{"Do výrazu (a + b)² dosažte a = 3 a b = 1.", "num:16", "4² = 16. Rozepsáno 9 + 6 + 1 = 16. Výsledek je 16."},
{"Do výrazu 3x − 2x + 5 dosažte x = 2.", "num:7", "Výraz je x + 5, při x = 2 je 7. Výsledek je 7."},
{"Do výrazu (x² − 9)/(x − 3) dosažte x = 5.", "num:8", "Pro x ≠ 3 je výraz roven x + 3, tedy 8. Výsledek je 8."},
};
return math_practice_page(1, 2, "matunit3",
"mat_ex3_title", "mat_quiz3_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat_unit4_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Převod", "Neznámé na jednu stranu",
"Tip: člen s x se převádí s opačným znaménkem.",
{
"2x + 1 = 9 se odečtením jedné změní na 2x = 8.",
"x = 4.",
"Členy s x patří obvykle vlevo, čísla vpravo.",
"Po každé úpravě zůstává rovnost zachovaná.",
NULL,
},
},
{
"2 / 3 • Obě strany", "Stejný člen vlevo i vpravo",
"Tip: 5x − 3 = 2x + 9 se zbaví 2x odečtením.",
{
"5x − 2x = 9 + 3.",
"3x = 12 a x = 4.",
"Zkouška: levá strana 5 · 4 − 3 = 17, pravá 8 + 9 = 17.",
"Když koeficient u x vyjde 0, rovnice buď nemá řešení, nebo jich má nekonečně mnoho.",
NULL,
},
},
{
"3 / 3 • Závorka a zlomek", "Nejdřív roznásobit",
"Tip: x/2 + 1 = 5 se nejdřív zbaví jedničky.",
{
"x/2 + 1 = 5 dá x/2 = 4, tedy x = 8.",
"3(x − 2) = 12 se vydělí třemi: x − 2 = 4.",
"Nebo se závorka roznásobí: 3x − 6 = 12.",
"Obě cesty dají x = 6.",
NULL,
},
},
};
return math_unit_page(1, &mat_lessons[3], "mat_unit4", "mat_unit4_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat_unit4_exercise_page(void) {
static const MathItem qs[] = {
{"Vyřešte 2x + 1 = 9. Napište x.", "num:4", "2x = 8, takže x = 4. Výsledek je 4."},
{"Vyřešte 5x − 3 = 2x + 9. Napište x.", "num:4", "3x = 12, takže x = 4. Výsledek je 4."},
{"Vyřešte x/2 + 1 = 5. Napište x.", "num:8", "x/2 = 4, takže x = 8. Výsledek je 8."},
{"Vyřešte 3(x − 2) = 12. Napište x.", "num:6", "x − 2 = 4, takže x = 6. Výsledek je 6."},
};
return math_practice_page(1, 3, "matunit4",
"mat_ex4_title", "mat_quiz4_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat_unit5_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Dráha", "Rychlost krát čas",
"Tip: jednotky času a rychlosti musí patřit k sobě.",
{
"Při stálé rychlosti je dráha s = v · t.",
"60 km/h po dobu 2,5 h urazí 150 km.",
"2,5 h je dvě a půl hodiny, ne dvě hodiny a padesát minut.",
"Z dráhy a času se rychlost dopočítá jako s/t.",
NULL,
},
},
{
"2 / 3 • Práce a směs", "Sčítají se výkony, ne časy",
"Tip: kdo práci udělá za 3 hodiny, udělá za hodinu třetinu.",
{
"První pracovník udělá za hodinu 1/6 práce, druhý 1/3.",
"Společně udělají 1/6 + 2/6 = 1/2 práce za hodinu, tedy celou za 2 hodiny.",
"U směsi se sčítá množství čisté látky.",
"2 kg dvacetiprocentní a 3 kg desetiprocentní směsi obsahují 0,4 + 0,3 = 0,7 kg, tedy 14 %.",
NULL,
},
},
{
"3 / 3 • Sleva", "Nová cena je část základu",
"Tip: po slevě 20 % zbývá 80 % původní ceny.",
{
"Když je nová cena 160 a sleva byla 20 %, je 160 rovno 80 % původní ceny.",
"Původní cena je 160 / 0,8 = 200.",
"Sleva v korunách je pak 40.",
"Nepleťte si slevu 20 % s tím, že se k nové ceně přičte 20 %.",
NULL,
},
},
};
return math_unit_page(1, &mat_lessons[4], "mat_unit5", "mat_unit5_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat_unit5_exercise_page(void) {
static const MathItem qs[] = {
{"Auto jede 2,5 hodiny rychlostí 60 km/h. Kolik kilometrů ujede?", "num:150", "s = v · t = 60 · 2,5 = 150. Výsledek je 150."},
{"První pracovník práci udělá za 6 hodin, druhý za 3 hodiny. Za kolik hodin ji udělají společně?", "num:2", "Za hodinu spolu udělají 1/2 práce, celou tedy za 2 hodiny. Výsledek je 2."},
{"Smíchají se 2 kg směsi s 20 % látky a 3 kg směsi s 10 % látky. Kolik procent látky má směs? Napište jen číslo.", "num:14", "Čisté látky je 0,7 kg z 5 kg, tedy 14 %. Výsledek je 14."},
{"Po slevě 20 % stojí zboží 160. Jaká byla původní cena?", "num:200", "160 je 80 % původní ceny, základ je 200. Výsledek je 200."},
};
return math_practice_page(1, 4, "matunit5",
"mat_ex5_title", "mat_quiz5_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat_unit6_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Trojúhelník", "Součet 180°",
"Tip: rovnoramenný trojúhelník má dvě ramena a dva úhly u základny shodné.",
{
"Součet vnitřních úhlů každého trojúhelníku je 180°.",
"V rovnoramenném trojúhelníku jsou úhly při základně shodné.",
"Když jsou úhly při základně 70°, je úhel proti základně 40°.",
"Rovnostranný trojúhelník má všechny strany shodné a každý úhel 60°.",
NULL,
},
},
{
"2 / 3 • Vnější úhel", "Součet dvou vzdálených vnitřních",
"Tip: vnější úhel a sousední vnitřní úhel dávají 180°.",
{
"Vnější úhel vznikne prodloužením jedné strany.",
"Je roven součtu dvou vnitřních úhlů, které s ním nesousedí.",
"Vnitřní úhly 40° a 60° dávají vnější úhel 100° u zbývajícího vrcholu.",
"Součet vnějšího úhlu a přilehlého vnitřního je 180°.",
NULL,
},
},
{
"3 / 3 • Mnohoúhelník", "Pravidelný šestiúhelník",
"Tip: součet vnitřních úhlů n-úhelníku je (n − 2) · 180°.",
{
"Trojúhelník má součet 180°, čtyřúhelník 360°.",
"Obecný vzorec je (n − 2) · 180°.",
"Pravidelný mnohoúhelník má všechny strany i úhly shodné.",
"Pravidelný šestiúhelník má vnitřní úhel 720° / 6 = 120°.",
NULL,
},
},
};
return math_unit_page(1, &mat_lessons[5], "mat_unit6", "mat_unit6_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat_unit6_exercise_page(void) {
static const MathItem qs[] = {
{"Vnější úhel trojúhelníku je součet vnitřních úhlů 40° a 60°. Jak je velký ve stupních?", "num:100", "Vnější úhel se rovná součtu dvou nesousedních vnitřních úhlů: 100°. Výsledek je 100."},
{"Rovnoramenný trojúhelník má úhly při základně 70°. Jak velký je úhel proti základně?", "num:40", "180° − 140° = 40°. Výsledek je 40."},
{"Rovnostranný trojúhelník má stranu 6. Jaký je jeho obvod?", "num:18", "Tři shodné strany: 3 · 6 = 18. Výsledek je 18."},
{"Jak velký je jeden vnitřní úhel pravidelného šestiúhelníku ve stupních?", "num:120", "Součet je (6 − 2) · 180° = 720° a jeden úhel je 120°. Výsledek je 120."},
};
return math_practice_page(1, 5, "matunit6",
"mat_ex6_title", "mat_quiz6_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat_unit7_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Kružnice", "Obvod a obsah",
"Tip: u výsledku s π nechte π ve výrazu a pište ho jako pi.",
{
"Kružnice s poloměrem r má obvod 2πr.",
"Obsah kruhu je πr².",
"Pro r = 7 je obvod 14π.",
"Pro r = 3 je obsah 9π.",
NULL,
},
},
{
"2 / 3 • Lichoběžník", "Dvě základny a výška",
"Tip: obsah je průměr základen krát výška.",
{
"Lichoběžník má právě jednu dvojici rovnoběžných stran, základny a a c.",
"Obsah je (a + c) · v / 2.",
"Základny 4 a 6 a výška 3 dávají obsah 15.",
"Výška je kolmá vzdálenost základen.",
NULL,
},
},
{
"3 / 3 • Pythagoras", "Pravoúhlý trojúhelník",
"Tip: přepona je nejdelší strana a leží proti pravému úhlu.",
{
"V pravoúhlém trojúhelníku je a² + b² = c².",
"c je přepona, a a b jsou odvěsny.",
"Trojúhelník 5, 12, 13 je pravoúhlý, protože 25 + 144 = 169.",
"Přepona je 13.",
NULL,
},
},
};
return math_unit_page(1, &mat_lessons[6], "mat_unit7", "mat_unit7_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat_unit7_exercise_page(void) {
static const MathItem qs[] = {
{"Jaký je obvod kružnice s poloměrem 7? Nechte výsledek v násobcích π.", "num:14*pi", "Obvod je 2πr = 14π. Výsledek je 14π."},
{"Jaký je obsah kruhu s poloměrem 3? Nechte výsledek v násobcích π.", "num:9*pi", "Obsah je πr² = 9π. Výsledek je 9π."},
{"Lichoběžník má základny 4 a 6 a výšku 3. Jaký je jeho obsah?", "num:15", "(4 + 6) · 3 / 2 = 15. Výsledek je 15."},
{"Pravoúhlý trojúhelník má odvěsny 5 a 12. Jak dlouhá je přepona?", "num:13", "5² + 12² = 25 + 144 = 169 = 13². Výsledek je 13."},
};
return math_practice_page(1, 6, "matunit7",
"mat_ex7_title", "mat_quiz7_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat_unit8_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Body", "Vzdálenost na mřížce",
"Tip: u svislé nebo vodorovné úsečky stačí rozdíl souřadnic.",
{
"Bod v rovině má souřadnice [x; y].",
"Vzdálenost [x₁; y₁] a [x₂; y₂] je √((x₂ − x₁)² + (y₂ − y₁)²).",
"A = [1; 1] a B = [5; 1] mají vzdálenost 4.",
"Osa y na naší mřížce míří nahoru.",
NULL,
},
},
{
"2 / 3 • SSS", "Trojúhelník ze tří stran",
"Tip: třetí vrchol je průsečík dvou kružnic.",
{
"Konstrukce SSS použije tři strany.",
"Z bodu A se rýsuje kružnice o poloměru |AC|, z bodu B o poloměru |BC|.",
"Kružnice se protnou ve dvou bodech, pokud trojúhelník existuje.",
"Trojúhelníková nerovnost: součet dvou stran je větší než třetí.",
NULL,
},
},
{
"3 / 3 • Příklad", "Strany 4, 3 a 5",
"Tip: 3² + 4² = 5², pravý úhel je u bodu A.",
{
"A = [1; 1], B = [5; 1], takže |AB| = 4.",
"Hledáme C tak, aby |AC| = 3 a |BC| = 5.",
"Na mřížce od 0 do 10 a od 0 do 8 leží jen C = [1; 4].",
"Druhý průsečík [1; −2] je pod mřížkou.",
NULL,
},
},
};
return math_unit_page(1, &mat_lessons[7], "mat_unit8", "mat_unit8_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat_unit8_exercise_page(void) {
static const MathItem qs[] = {
{"Jaká je vzdálenost bodů A = [1; 1] a B = [5; 1]?", "num:4", "Body mají stejné y, vzdálenost je 5 − 1 = 4. Výsledek je 4."},
{"Jaký je obsah pravoúhlého trojúhelníku s odvěsnami 3 a 4?", "num:6", "Obsah je (3 · 4) / 2 = 6. Výsledek je 6."},
{"Jaký je obvod trojúhelníku se stranami 3, 4 a 5?", "num:12", "3 + 4 + 5 = 12. Výsledek je 12."},
{"A = [1; 1] a B = [5; 1] jsou dané. Zakreslete C, pro které je |AC| = 3 a |BC| = 5.", "points:1,4;fix:1,1;fix:5,1", "Kružnice se na mřížce protnou v [1; 4]. Bod [1; −2] už na ní není."},
};
return math_practice_page(1, 7, "matunit8",
"mat_ex8_title", "mat_quiz8_head",
qs, G_N_ELEMENTS(qs));
}
void add_mat_pages(GtkStack *stack) {
typedef GtkWidget *(*MathBuilder)(void);
static const struct {
const char *unit_name;
const char *ex_name;
MathBuilder build_unit;
MathBuilder build_ex;
} pages[] = {
{"matunit1", "matex1", build_mat_unit1_page, build_mat_unit1_exercise_page},
{"matunit2", "matex2", build_mat_unit2_page, build_mat_unit2_exercise_page},
{"matunit3", "matex3", build_mat_unit3_page, build_mat_unit3_exercise_page},
{"matunit4", "matex4", build_mat_unit4_page, build_mat_unit4_exercise_page},
{"matunit5", "matex5", build_mat_unit5_page, build_mat_unit5_exercise_page},
{"matunit6", "matex6", build_mat_unit6_page, build_mat_unit6_exercise_page},
{"matunit7", "matex7", build_mat_unit7_page, build_mat_unit7_exercise_page},
{"matunit8", "matex8", build_mat_unit8_page, build_mat_unit8_exercise_page},
};
for (guint i = 0; i < G_N_ELEMENTS(pages); i++) {
gtk_stack_add_named(stack, pages[i].build_unit(), pages[i].unit_name);
gtk_stack_add_named(stack, pages[i].build_ex(), pages[i].ex_name);
}
}

468
src/mat_year2.c Normal file
View file

@ -0,0 +1,468 @@
#include "graduately.h"
NetLesson mat2_lessons[MATH_N] = {
{ .n_slides = 3, .unit_page = "mat2unit1", .ex_page = "mat2ex1" },
{ .n_slides = 3, .unit_page = "mat2unit2", .ex_page = "mat2ex2" },
{ .n_slides = 3, .unit_page = "mat2unit3", .ex_page = "mat2ex3" },
{ .n_slides = 3, .unit_page = "mat2unit4", .ex_page = "mat2ex4" },
{ .n_slides = 3, .unit_page = "mat2unit5", .ex_page = "mat2ex5" },
{ .n_slides = 3, .unit_page = "mat2unit6", .ex_page = "mat2ex6" },
{ .n_slides = 3, .unit_page = "mat2unit7", .ex_page = "mat2ex7" },
{ .n_slides = 3, .unit_page = "mat2unit8", .ex_page = "mat2ex8" },
};
static GtkWidget *build_mat2_unit1_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Předpis", "y = ax + b",
"Tip: a je směrnice, b je úsek na ose y.",
{
"Lineární funkce má předpis f(x) = ax + b.",
"Grafem je přímka.",
"b je hodnota f(0), tedy průsečík s osou y.",
"Pro f(x) = 2x − 3 je průsečík s osou y číslo −3.",
NULL,
},
},
{
"2 / 3 • Směrnice", "O kolik y vzroste",
"Tip: směrnice je podíl změny y a změny x.",
{
"Mezi body [x₁; y₁] a [x₂; y₂] je a = (y₂ − y₁)/(x₂ − x₁).",
"Body [1; 2] a [3; 8] dávají směrnici (8 − 2)/(3 − 1) = 3.",
"Kladná směrnice znamená růst, záporná pokles.",
"Nulová směrnice je vodorovná přímka.",
NULL,
},
},
{
"3 / 3 • Hodnota a kořen", "Dosazení a rovnice",
"Tip: nulový bod je x, pro které je f(x) = 0.",
{
"f(4) u funkce 2x − 3 je 8 − 3 = 5.",
"Rovnice 2x − 6 = 0 má řešení x = 3.",
"To je průsečík přímky y = 2x − 6 s osou x.",
"Lineární funkce s a ≠ 0 má právě jeden nulový bod.",
NULL,
},
},
};
return math_unit_page(2, &mat2_lessons[0], "mat2_unit1", "mat2_unit1_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat2_unit1_exercise_page(void) {
static const MathItem qs[] = {
{"Funkce f(x) = 2x − 3. Kolik je f(4)?", "num:5", "2 · 4 − 3 = 5. Výsledek je 5."},
{"Jaký je průsečík funkce f(x) = 2x − 3 s osou y?", "num:-3", "f(0) = −3. Výsledek je -3."},
{"Jaká je směrnice přímky body [1; 2] a [3; 8]?", "num:3", "(8 − 2)/(3 − 1) = 3. Výsledek je 3."},
{"Vyřešte 2x − 6 = 0. Napište x.", "num:3", "2x = 6, takže x = 3. Výsledek je 3."},
};
return math_practice_page(2, 0, "mat2unit1",
"mat2_ex1_title", "mat2_quiz1_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat2_unit2_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Dvojice", "Jedno x a jedno y",
"Tip: výsledek pište x;y, nejdřív x.",
{
"Soustava dvou lineárních rovnic hledá dvojici [x; y], která vyhovuje oběma.",
"Řešením je průsečík dvou přímek.",
"Sčítací metoda sečte rovnice tak, aby jedna neznámá zmizela.",
"Dosazovací metoda vyjádří jednu neznámou a dosadí ji do druhé rovnice.",
NULL,
},
},
{
"2 / 3 • Sčítání", "Opačné koeficienty",
"Tip: x + y = 10 a x − y = 2 se sčítají na 2x = 12.",
{
"Součet dá 2x = 12, tedy x = 6.",
"Z x + y = 10 je y = 4.",
"Zkouška ve druhé rovnici: 6 − 4 = 2.",
"Odečtení rovnic se použije, když má neznámá v obou stejný koeficient.",
NULL,
},
},
{
"3 / 3 • Odečtení", "Stejný koeficient",
"Tip: rovnice lze před odečtením násobit.",
{
"2x + y = 8 a x + y = 5 se odečtou na x = 3.",
"Pak y = 2.",
"3x + y = 10 a x + y = 6 dají 2x = 4, tedy x = 2 a y = 4.",
"x + y = 7 a x − y = 1 dají x = 4 a y = 3.",
NULL,
},
},
};
return math_unit_page(2, &mat2_lessons[1], "mat2_unit2", "mat2_unit2_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat2_unit2_exercise_page(void) {
static const MathItem qs[] = {
{"Vyřešte soustavu x + y = 10 a x − y = 2. Napište x a y oddělené středníkem.", "pair:6;4", "Sečtení dá 2x = 12, x = 6 a y = 4. Napište 6;4."},
{"Vyřešte soustavu 2x + y = 8 a x + y = 5. Napište x a y oddělené středníkem.", "pair:3;2", "Odečtení dá x = 3 a y = 2. Napište 3;2."},
{"Vyřešte soustavu x + y = 7 a x − y = 1. Napište x a y oddělené středníkem.", "pair:4;3", "Sečtení dá 2x = 8, x = 4 a y = 3. Napište 4;3."},
{"Vyřešte soustavu 3x + y = 10 a x + y = 6. Napište x a y oddělené středníkem.", "pair:2;4", "Odečtení dá 2x = 4, x = 2 a y = 4. Napište 2;4."},
};
return math_practice_page(2, 1, "mat2unit2",
"mat2_ex2_title", "mat2_quiz2_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat2_unit3_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Tvar", "ax² + bx + c = 0",
"Tip: kořeny pište oddělené středníkem, pořadí nehraje roli.",
{
"Kvadratická rovnice má a ≠ 0.",
"Kořeny rovnice x² − 5x + 6 = 0 jsou 2 a 3, protože (x − 2)(x − 3) = 0.",
"x² − 4 = 0 má kořeny −2 a 2.",
"Součin je nula, právě když je nula aspoň jeden činitel.",
NULL,
},
},
{
"2 / 3 • Diskriminant", "D = b² − 4ac",
"Tip: D > 0 znamená dva různé reálné kořeny.",
{
"Pro ax² + bx + c = 0 je D = b² − 4ac.",
"U x² − 2x − 3 je a = 1, b = −2, c = −3 a D = 4 + 12 = 16.",
"D = 0 znamená jeden dvojnásobný kořen.",
"D < 0 znamená, že v reálných číslech řešení není.",
NULL,
},
},
{
"3 / 3 • Vzorec", "Kořen je (−b ± √D) / (2a)",
"Tip: dvojnásobný kořen x² + 6x + 9 = 0 je −3.",
{
"x = (−b ± √D) / (2a).",
"x² + 6x + 9 = (x + 3)², takže kořen −3 je dvojnásobný.",
"U dvojnásobného kořene stačí napsat číslo jednou.",
"Zkouška dosadí kořen zpět do rovnice.",
NULL,
},
},
};
return math_unit_page(2, &mat2_lessons[2], "mat2_unit3", "mat2_unit3_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat2_unit3_exercise_page(void) {
static const MathItem qs[] = {
{"Najděte kořeny rovnice x² − 5x + 6 = 0. Oddělte je středníkem.", "set:2;3", "(x − 2)(x − 3) = 0. Napište 2;3."},
{"Najděte kořeny rovnice x² − 4 = 0. Oddělte je středníkem.", "set:-2;2", "x² = 4, takže x = −2 nebo x = 2. Napište -2;2."},
{"Jaký je diskriminant rovnice x² − 2x − 3 = 0?", "num:16", "D = (−2)² − 4 · 1 · (−3) = 4 + 12 = 16. Výsledek je 16."},
{"Jaký je dvojnásobný kořen rovnice x² + 6x + 9 = 0?", "num:-3", "(x + 3)² = 0, kořen je −3. Výsledek je -3."},
};
return math_practice_page(2, 2, "mat2unit3",
"mat2_ex3_title", "mat2_quiz3_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat2_unit4_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Obor", "Jmenovatel nesmí být nula",
"Tip: podmínku pište dřív, než krátíte.",
{
"Lomený výraz je podíl dvou mnohočlenů.",
"Není definován tam, kde je jmenovatel nula.",
"(x + 2)/(x − 2) nemá smysl pro x = 2.",
"Po krácení se vyloučené hodnoty do oboru nevracejí.",
NULL,
},
},
{
"2 / 3 • Krácení", "Rozdíl čtverců",
"Tip: x² − 1 = (x − 1)(x + 1).",
{
"(x² − 1)/(x − 1) = x + 1 pro x ≠ 1.",
"Při x = 4 je hodnota 5.",
"(x² − 4)/(x + 2) = x − 2 pro x ≠ −2.",
"Při x = 5 je hodnota 3.",
NULL,
},
},
{
"3 / 3 • Násobení", "Čitatel s čitatelem",
"Tip: x ve jmenovateli se zkrátí jen pro x ≠ 0.",
{
"Zlomky se násobí stejně jako číselné.",
"(2x/4) · (6/x) = 12x/(4x) = 3 pro x ≠ 0.",
"6x/(3x) = 2 pro x ≠ 0.",
"Než se zkrátí písmeno, ověří se, že není nula.",
NULL,
},
},
};
return math_unit_page(2, &mat2_lessons[3], "mat2_unit4", "mat2_unit4_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat2_unit4_exercise_page(void) {
static const MathItem qs[] = {
{"Do výrazu (x² − 1)/(x − 1) dosažte x = 4.", "num:5", "Pro x ≠ 1 je výraz x + 1, tedy 5. Výsledek je 5."},
{"Pro které x nemá výraz (x + 2)/(x − 2) smysl?", "num:2", "Jmenovatel x − 2 je nula právě při x = 2. Výsledek je 2."},
{"Upravte (2x/4) · (6/x) pro x ≠ 0.", "num:3", "12x/(4x) = 3. Výsledek je 3."},
{"Do výrazu (x² − 4)/(x + 2) dosažte x = 5.", "num:3", "Pro x ≠ −2 je výraz x − 2, tedy 3. Výsledek je 3."},
};
return math_practice_page(2, 3, "mat2unit4",
"mat2_ex4_title", "mat2_quiz4_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat2_unit5_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Odmocnina jako exponent", "a^(1/n)",
"Tip: a^(1/2) je druhá odmocnina z a.",
{
"Pro a > 0 je a^(1/n) n-tá odmocnina z a.",
"16^(1/2) = 4.",
"a^(m/n) je n-tá odmocnina z aᵐ, nebo m-tá mocnina odmocniny.",
"8^(2/3) = (8^(1/3))² = 2² = 4.",
NULL,
},
},
{
"2 / 3 • Stejný základ", "Exponenty se sčítají",
"Tip: 3² · 3³ = 3⁵.",
{
"aᵐ · aⁿ = aᵐ⁺ⁿ.",
"3² · 3³ = 3⁵ = 243.",
"(aᵐ)ⁿ = aᵐ·ⁿ.",
"Základ musí být při sčítání exponentů stejný.",
NULL,
},
},
{
"3 / 3 • Záporný exponent", "Převrácená hodnota",
"Tip: a⁻ⁿ = 1/aⁿ pro a ≠ 0.",
{
"10⁻² = 1/100.",
"10⁻¹ = 0,1 a 10⁰ = 1.",
"Záporný exponent nepřevrací znaménko základu, ale celou mocninu.",
"(−2)⁻² = 1/4, protože (−2)² = 4.",
NULL,
},
},
};
return math_unit_page(2, &mat2_lessons[4], "mat2_unit5", "mat2_unit5_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat2_unit5_exercise_page(void) {
static const MathItem qs[] = {
{"Spočítejte 16^(1/2).", "num:4", "Druhá odmocnina z 16 je 4. Výsledek je 4."},
{"Spočítejte 8^(2/3).", "num:4", "Třetí odmocnina z 8 je 2 a 2² = 4. Výsledek je 4."},
{"Spočítejte 3² · 3³.", "num:243", "3⁵ = 243. Výsledek je 243."},
{"Spočítejte 10⁻².", "num:1/100", "10⁻² = 1/10² = 1/100. Výsledek je 1/100."},
};
return math_practice_page(2, 4, "mat2unit5",
"mat2_ex5_title", "mat2_quiz5_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat2_unit6_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Poměry", "Odvěsny a přepona",
"Tip: sinus je protilehlá odvěsna ku přeponě.",
{
"V pravoúhlém trojúhelníku je sin α = protilehlá / přepona.",
"cos α = přilehlá / přepona.",
"tan α = protilehlá / přilehlá.",
"Přepona je strana proti pravému úhlu.",
NULL,
},
},
{
"2 / 3 • Tabulkové úhly", "30°, 45° a 60°",
"Tip: sinus 30° je 1/2.",
{
"sin 30° = 1/2 a cos 60° = 1/2.",
"sin 60° = cos 30° = √3/2.",
"sin 45° = cos 45° = √2/2.",
"tan 45° = 1.",
NULL,
},
},
{
"3 / 3 • Dopočet strany", "Přepona a úhel",
"Tip: proti úhlu 30° je polovina přepony.",
{
"Známe-li přeponu a úhel, protilehlá odvěsna je přepona · sin α.",
"Proti 30° při přeponě 10 je odvěsna 5.",
"Přilehlá odvěsna by byla 10 · cos 30°.",
"Tangens se hodí, když jsou známé dvě odvěsny a ne přepona.",
NULL,
},
},
};
return math_unit_page(2, &mat2_lessons[5], "mat2_unit6", "mat2_unit6_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat2_unit6_exercise_page(void) {
static const MathItem qs[] = {
{"Kolik je sin 30°?", "num:1/2", "Sinus 30° je 1/2. Výsledek je 1/2."},
{"Kolik je cos 60°?", "num:1/2", "Kosinus 60° je 1/2. Výsledek je 1/2."},
{"Kolik je tan 45°?", "num:1", "Protilehlá a přilehlá odvěsna jsou u 45° stejně dlouhé. Výsledek je 1."},
{"Přepona pravoúhlého trojúhelníku je 10 a jeden úhel je 30°. Jak dlouhá je odvěsna proti tomuto úhlu?", "num:5", "10 · sin 30° = 10 · 1/2 = 5. Výsledek je 5."},
};
return math_practice_page(2, 5, "mat2unit6",
"mat2_ex6_title", "mat2_quiz6_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat2_unit7_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Pythagoras", "a² + b² = c²",
"Tip: 8² + 15² = 64 + 225 = 289 = 17².",
{
"V pravoúhlém trojúhelníku se součet čtverců nad odvěsnami rovná čtverci nad přeponou.",
"Trojúhelník 8, 15, 17 je pravoúhlý a přepona je 17.",
"Čtverec nad přeponou trojúhelníku 3, 4, 5 má obsah 25.",
"Věta platí jen v pravoúhlém trojúhelníku.",
NULL,
},
},
{
"2 / 3 • Eukleides", "Úsek přepony",
"Tip: a² = c · a_c, kde a_c je úsek přepony přilehlý k odvěsně a.",
{
"Odvěsna je geometrický průměr přepony a svého úseku.",
"a² = c · a_c a b² = c · b_c.",
"Úseky a_c a b_c dají dohromady přeponu c.",
"U odvěsen 6 a 8 je přepona 10 a úsek u odvěsny 6 je 36/10 = 18/5.",
NULL,
},
},
{
"3 / 3 • Výška", "v² = a_c · b_c",
"Tip: také platí a · b = c · v.",
{
"Výška k přeponě je geometrický průměr obou úseků.",
"Z a · b = c · v plyne v = ab/c.",
"Pro odvěsny 6 a 8 a přeponu 10 je v = 48/10 = 24/5.",
"Obsah trojúhelníku je pak (c · v) / 2, stejně jako (a · b) / 2.",
NULL,
},
},
};
return math_unit_page(2, &mat2_lessons[6], "mat2_unit7", "mat2_unit7_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat2_unit7_exercise_page(void) {
static const MathItem qs[] = {
{"Pravoúhlý trojúhelník má odvěsny 8 a 15. Jak dlouhá je přepona?", "num:17", "64 + 225 = 289 = 17². Výsledek je 17."},
{"Jaký obsah má čtverec sestrojený nad přeponou pravoúhlého trojúhelníku se stranami 3, 4 a 5?", "num:25", "Přepona je 5 a 5² = 25. Výsledek je 25."},
{"Odvěsny jsou 6 a 8, přepona je 10. Jak dlouhý je úsek přepony přilehlý k odvěsně 6?", "num:18/5", "a² = c · a_c, takže a_c = 36/10 = 18/5. Výsledek je 18/5."},
{"Odvěsny jsou 6 a 8, přepona je 10. Jak dlouhá je výška k přeponě?", "num:24/5", "v = ab/c = 48/10 = 24/5. Výsledek je 24/5."},
};
return math_practice_page(2, 6, "mat2unit7",
"mat2_ex7_title", "mat2_quiz7_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat2_unit8_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Úhly", "Obvodový je polovina středového",
"Tip: oba úhly musí být nad stejným obloukem.",
{
"Středový úhel má vrchol ve středu kružnice.",
"Obvodový úhel má vrchol na kružnici.",
"Nad stejným obloukem je obvodový úhel polovinou středového.",
"Středový úhel 80° patří k obvodovému úhlu 40°.",
NULL,
},
},
{
"2 / 3 • Oblouk", "Část obvodu",
"Tip: délka oblouku je (α/360°) · 2πr.",
{
"Celý obvod je 2πr.",
"Oblouk 60° je šestina obvodu.",
"Při r = 6 je jeho délka 2π.",
"Výsledek nechte v násobcích π.",
NULL,
},
},
{
"3 / 3 • Thales", "Pravý úhel nad průměrem",
"Tip: vrchol pravého úhlu leží na kružnici s průměrem AB.",
{
"Thaletova věta: úhel nad průměrem je pravý.",
"Je-li AB průměr, je úhel ACB pravý pro každé C na kružnici různé od A a B.",
"Na mřížce má průměr od [2; 4] do [8; 4] střed [5; 4] a poloměr 3.",
"Vyhovují body [5; 7] a [5; 1].",
NULL,
},
},
};
return math_unit_page(2, &mat2_lessons[7], "mat2_unit8", "mat2_unit8_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat2_unit8_exercise_page(void) {
static const MathItem qs[] = {
{"Středový úhel je 80°. Jak velký je obvodový úhel nad stejným obloukem ve stupních?", "num:40", "Obvodový úhel je polovina středového. Výsledek je 40."},
{"Kružnice má poloměr 6. Jak dlouhý je oblouk 60°? Nechte π ve výsledku.", "num:2*pi", "(60/360) · 2π · 6 = 2π. Výsledek je 2π."},
{"Obvodový úhel je 35°. Jak velký je středový úhel nad stejným obloukem ve stupních?", "num:70", "Středový úhel je dvojnásobek obvodového. Výsledek je 70."},
{"AB je průměr od [2; 4] do [8; 4]. Zakreslete bod C na kružnici, ve kterém je úhel ACB pravý.", "right:2,4;8,4", "Thaletova kružnice má střed [5; 4] a poloměr 3. Na mřížce vyhovuje [5; 7] nebo [5; 1]."},
};
return math_practice_page(2, 7, "mat2unit8",
"mat2_ex8_title", "mat2_quiz8_head",
qs, G_N_ELEMENTS(qs));
}
void add_mat2_pages(GtkStack *stack) {
typedef GtkWidget *(*MathBuilder)(void);
static const struct {
const char *unit_name;
const char *ex_name;
MathBuilder build_unit;
MathBuilder build_ex;
} pages[] = {
{"mat2unit1", "mat2ex1", build_mat2_unit1_page, build_mat2_unit1_exercise_page},
{"mat2unit2", "mat2ex2", build_mat2_unit2_page, build_mat2_unit2_exercise_page},
{"mat2unit3", "mat2ex3", build_mat2_unit3_page, build_mat2_unit3_exercise_page},
{"mat2unit4", "mat2ex4", build_mat2_unit4_page, build_mat2_unit4_exercise_page},
{"mat2unit5", "mat2ex5", build_mat2_unit5_page, build_mat2_unit5_exercise_page},
{"mat2unit6", "mat2ex6", build_mat2_unit6_page, build_mat2_unit6_exercise_page},
{"mat2unit7", "mat2ex7", build_mat2_unit7_page, build_mat2_unit7_exercise_page},
{"mat2unit8", "mat2ex8", build_mat2_unit8_page, build_mat2_unit8_exercise_page},
};
for (guint i = 0; i < G_N_ELEMENTS(pages); i++) {
gtk_stack_add_named(stack, pages[i].build_unit(), pages[i].unit_name);
gtk_stack_add_named(stack, pages[i].build_ex(), pages[i].ex_name);
}
}

468
src/mat_year3.c Normal file
View file

@ -0,0 +1,468 @@
#include "graduately.h"
NetLesson mat3_lessons[MATH_N] = {
{ .n_slides = 3, .unit_page = "mat3unit1", .ex_page = "mat3ex1" },
{ .n_slides = 3, .unit_page = "mat3unit2", .ex_page = "mat3ex2" },
{ .n_slides = 3, .unit_page = "mat3unit3", .ex_page = "mat3ex3" },
{ .n_slides = 3, .unit_page = "mat3unit4", .ex_page = "mat3ex4" },
{ .n_slides = 3, .unit_page = "mat3unit5", .ex_page = "mat3ex5" },
{ .n_slides = 3, .unit_page = "mat3unit6", .ex_page = "mat3ex6" },
{ .n_slides = 3, .unit_page = "mat3unit7", .ex_page = "mat3ex7" },
{ .n_slides = 3, .unit_page = "mat3unit8", .ex_page = "mat3ex8" },
};
static GtkWidget *build_mat3_unit1_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Parabola", "f(x) = ax² + bx + c",
"Tip: pro a > 0 má parabola minimum, pro a < 0 maximum.",
{
"Grafem kvadratické funkce je parabola.",
"Průsečík s osou y je bod [0; c].",
"U f(x) = x² − 4x + 3 je f(0) = 3.",
"Osa paraboly je svislá.",
NULL,
},
},
{
"2 / 3 • Vrchol", "x = −b/(2a)",
"Tip: souřadnice vrcholu pište x;y.",
{
"x-ová souřadnice vrcholu je −b/(2a).",
"U x² − 4x + 3 je to −(−4)/2 = 2.",
"f(2) = 4 − 8 + 3 = −1.",
"Vrchol je tedy [2; −1].",
NULL,
},
},
{
"3 / 3 • Kořeny", "Průsečíky s osou x",
"Tip: kořeny jsou řešení f(x) = 0.",
{
"x² − 4x + 3 = (x − 1)(x − 3).",
"Kořeny jsou 1 a 3.",
"Leží symetricky kolem osy paraboly.",
"f(4) = 16 − 16 + 3 = 3, stejně jako f(0).",
NULL,
},
},
};
return math_unit_page(3, &mat3_lessons[0], "mat3_unit1", "mat3_unit1_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat3_unit1_exercise_page(void) {
static const MathItem qs[] = {
{"Najděte vrchol funkce f(x) = x² − 4x + 3. Napište x a y oddělené středníkem.", "pair:2;-1", "x = 4/2 = 2 a f(2) = −1. Napište 2;-1."},
{"Najděte kořeny funkce f(x) = x² − 4x + 3. Oddělte je středníkem.", "set:1;3", "(x − 1)(x − 3) = 0. Napište 1;3."},
{"Kolik je f(0) u funkce f(x) = x² − 4x + 3?", "num:3", "f(0) = 3, to je absolutní člen. Výsledek je 3."},
{"Kolik je f(4) u funkce f(x) = x² − 4x + 3?", "num:3", "16 − 16 + 3 = 3. Výsledek je 3."},
};
return math_practice_page(3, 0, "mat3unit1",
"mat3_ex1_title", "mat3_quiz1_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat3_unit2_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Definice", "Logaritmus je exponent",
"Tip: log_b(x) = y právě tehdy, když bʸ = x.",
{
"Základ logaritmu je kladný a různý od 1.",
"Argument logaritmu je kladný.",
"log₁₀(1000) = 3, protože 10³ = 1000.",
"log₂(32) = 5, protože 2⁵ = 32.",
NULL,
},
},
{
"2 / 3 • Rovnice", "Stejný základ",
"Tip: 2ˣ = 16 se přepíše jako 2ˣ = 2⁴.",
{
"Exponentialní rovnice se stejným základem porovná exponenty.",
"2ˣ = 16 má řešení x = 4.",
"log₃(81) = 4, protože 3⁴ = 81.",
"log_b(b) = 1 a log_b(1) = 0.",
NULL,
},
},
{
"3 / 3 • Pravidla", "Součin a podíl",
"Tip: logaritmus součinu je součet logaritmů.",
{
"log_b(xy) = log_b(x) + log_b(y).",
"log_b(x/y) = log_b(x) − log_b(y).",
"log_b(xⁿ) = n · log_b(x).",
"Těmito pravidly se argument zjednodušuje, ne základ.",
NULL,
},
},
};
return math_unit_page(3, &mat3_lessons[1], "mat3_unit2", "mat3_unit2_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat3_unit2_exercise_page(void) {
static const MathItem qs[] = {
{"Spočítejte logaritmus o základu 10 z 1000.", "num:3", "10³ = 1000, takže logaritmus je 3. Výsledek je 3."},
{"Spočítejte logaritmus o základu 2 z 32.", "num:5", "2⁵ = 32. Výsledek je 5."},
{"Vyřešte 2ˣ = 16. Napište x.", "num:4", "16 = 2⁴, takže x = 4. Výsledek je 4."},
{"Spočítejte logaritmus o základu 3 z 81.", "num:4", "3⁴ = 81. Výsledek je 4."},
};
return math_practice_page(3, 1, "mat3unit2",
"mat3_ex2_title", "mat3_quiz2_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat3_unit3_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Jednotková kružnice", "Úhel od kladné poloosy x",
"Tip: sinus je y-ová souřadnice, kosinus x-ová.",
{
"Bod na jednotkové kružnici pod úhlem α má souřadnice [cos α; sin α].",
"cos 0° = 1 a sin 0° = 0.",
"sin 90° = 1 a cos 90° = 0.",
"cos 180° = −1 a sin 180° = 0.",
NULL,
},
},
{
"2 / 3 • 45°", "Polovina pravého úhlu",
"Tip: sin 45° = cos 45° = √2/2.",
{
"Úhel 45° půlí čtvrtkružnici.",
"Souřadnice bodu splňují x = y a x² + y² = 1.",
"Kladné řešení je √2/2.",
"Odpověď lze psát jako sqrt(2)/2 nebo 1/sqrt(2).",
NULL,
},
},
{
"3 / 3 • Znaménka", "Čtyři kvadranty",
"Tip: v prvním kvadrantu jsou sinus i kosinus kladné.",
{
"Ve druhém kvadrantu je sinus kladný a kosinus záporný.",
"Ve třetím jsou oba záporné.",
"Ve čtvrtém je kosinus kladný a sinus záporný.",
"Tangens je sinus dělený kosinem a není definován při cos α = 0.",
NULL,
},
},
};
return math_unit_page(3, &mat3_lessons[2], "mat3_unit3", "mat3_unit3_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat3_unit3_exercise_page(void) {
static const MathItem qs[] = {
{"Kolik je sin 90°?", "num:1", "Bod [0; 1] má y-ovou souřadnici 1. Výsledek je 1."},
{"Kolik je cos 180°?", "num:-1", "Bod [−1; 0] má x-ovou souřadnici −1. Výsledek je -1."},
{"Kolik je sin 45°? Můžete psát sqrt(2)/2.", "num:sqrt(2)/2", "sin 45° = √2/2. Stejnou hodnotu má 1/√2."},
{"Kolik je cos 0°?", "num:1", "Úhel 0° je bod [1; 0]. Výsledek je 1."},
};
return math_practice_page(3, 2, "mat3unit3",
"mat3_ex3_title", "mat3_quiz3_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat3_unit4_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Sinová věta", "a/sin α = 2R",
"Tip: poměr strany a sinu protilehlého úhlu je pro všechny strany stejný.",
{
"a/sin α = b/sin β = c/sin γ = 2R.",
"R je poloměr kružnice opsané.",
"Když a = 10 a α = 30°, je 10 / (1/2) = 20 = 2R.",
"Poloměr R je 10.",
NULL,
},
},
{
"2 / 3 • Kosinová věta", "c² = a² + b² − 2ab cos γ",
"Tip: pro pravý úhel je cos 90° = 0 a věta přejde v Pythagora.",
{
"Kosinová věta dopočítá stranu proti známému úhlu.",
"Jsou-li dvě strany 2 a sevřený úhel 60°, je třetí strana také 2.",
"a² = 4 + 4 − 2 · 2 · 2 · cos 60° = 8 − 8 · 1/2 = 4.",
"Rovnostranný trojúhelník má všechny úhly 60°.",
NULL,
},
},
{
"3 / 3 • Obsah", "Dvě strany a sevřený úhel",
"Tip: S = (1/2) · b · c · sin α.",
{
"Obsah je polovina součinu dvou stran a sinu sevřeného úhlu.",
"(1/2) · 4 · 6 · sin 30° = 12 · 1/2 = 6.",
"Sinová věta také dopočítá další stranu: b = a · sin β / sin α.",
"Pro a = 8, α = 30° a β = 45° je b = 8√2.",
NULL,
},
},
};
return math_unit_page(3, &mat3_lessons[3], "mat3_unit4", "mat3_unit4_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat3_unit4_exercise_page(void) {
static const MathItem qs[] = {
{"Strana a = 10 leží proti úhlu 30°. Jaký je poloměr kružnice opsané?", "num:10", "a/sin 30° = 20 = 2R, takže R = 10. Výsledek je 10."},
{"Dvě strany trojúhelníku mají délku 2 a svírají úhel 60°. Jak dlouhá je třetí strana?", "num:2", "a² = 4 + 4 − 8 · 1/2 = 4, takže a = 2. Výsledek je 2."},
{"Dvě strany mají délky 4 a 6 a svírají úhel 30°. Jaký je obsah trojúhelníku?", "num:6", "(1/2) · 4 · 6 · 1/2 = 6. Výsledek je 6."},
{"Strana a = 8 je proti 30° a úhel β je 45°. Jak dlouhá je strana b? Sin 45° je √2/2.", "num:8*sqrt(2)", "b = 8 · sin 45° / sin 30° = 8 · (√2/2) / (1/2) = 8√2."},
};
return math_practice_page(3, 3, "mat3unit4",
"mat3_ex4_title", "mat3_quiz4_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat3_unit5_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Aritmetická", "Stálý rozdíl",
"Tip: aₙ = a₁ + (n − 1)d.",
{
"Aritmetická posloupnost přičítá stále stejný rozdíl d.",
"Pro a₁ = 3 a d = 2 je a₅ = 3 + 4 · 2 = 11.",
"Součet prvních n členů je sₙ = n/2 · (2a₁ + (n − 1)d).",
"s₅ = 5/2 · (6 + 8) = 35.",
NULL,
},
},
{
"2 / 3 • Geometrická", "Stálý kvocient",
"Tip: aₙ = a₁ · qⁿ⁻¹.",
{
"Geometrická posloupnost násobí stále stejným kvocientem q.",
"Pro a₁ = 2 a q = 3 je a₄ = 2 · 3³ = 54.",
"q = 1 znamená, že se členy nemění.",
"q = 0 je po prvním členu samá nula, pokud je první člen definován.",
NULL,
},
},
{
"3 / 3 • Nekonečná řada", "Součet pro |q| < 1",
"Tip: s = a₁ / (1 − q).",
{
"Nekonečná geometrická řada má součet jen pro |q| < 1.",
"Pro a₁ = 6 a q = 1/2 je s = 6 / (1/2) = 12.",
"Pro |q| ≥ 1 se součet členů neblíží ke konečnému číslu, kromě případu q = 1 a a₁ = 0.",
"Řada 6 + 3 + 3/2 + … se tedy blíží ke 12.",
NULL,
},
},
};
return math_unit_page(3, &mat3_lessons[4], "mat3_unit5", "mat3_unit5_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat3_unit5_exercise_page(void) {
static const MathItem qs[] = {
{"Aritmetická posloupnost má a₁ = 3 a d = 2. Kolik je a₅?", "num:11", "a₅ = 3 + 4 · 2 = 11. Výsledek je 11."},
{"Aritmetická posloupnost má a₁ = 3 a d = 2. Kolik je součet prvních pěti členů?", "num:35", "s₅ = 5/2 · (6 + 8) = 35. Výsledek je 35."},
{"Geometrická posloupnost má a₁ = 2 a q = 3. Kolik je a₄?", "num:54", "a₄ = 2 · 27 = 54. Výsledek je 54."},
{"Nekonečná geometrická řada má a₁ = 6 a q = 1/2. Jaký je její součet?", "num:12", "s = 6 / (1/2) = 12. Výsledek je 12."},
};
return math_practice_page(3, 4, "mat3unit5",
"mat3_ex5_title", "mat3_quiz5_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat3_unit6_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Permutace", "Pořadí všech prvků",
"Tip: n! = 1 · 2 · … · n a 0! = 1.",
{
"Permutace je uspořádání všech n různých prvků.",
"Počet je n!.",
"4! = 24 a 3! = 6.",
"Na prvním místě je n možností, na dalším n − 1, a tak dál.",
NULL,
},
},
{
"2 / 3 • Variace", "Pořadí vybraných prvků",
"Tip: variace bez opakování V(n, k) = n! / (n − k)!.",
{
"Variace vybírá k prvků z n a záleží na pořadí.",
"V(5, 2) = 5 · 4 = 20.",
"Opakování by dovolilo vybrat stejný prvek víckrát.",
"Permutace je variace, která vybírá všech n prvků.",
NULL,
},
},
{
"3 / 3 • Kombinace", "Pořadí nehraje roli",
"Tip: C(n, k) = n! / (k! · (n − k)!).",
{
"Kombinace vybírá k prvků a dvě vybrané skupiny se stejným obsahem jsou jedna.",
"C(5, 2) = 10.",
"C(n, k) = C(n, n − k).",
"Počet variací je k! krát větší než počet kombinací.",
NULL,
},
},
};
return math_unit_page(3, &mat3_lessons[5], "mat3_unit6", "mat3_unit6_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat3_unit6_exercise_page(void) {
static const MathItem qs[] = {
{"Kolik je permutací čtyř různých prvků?", "num:24", "4! = 24. Výsledek je 24."},
{"Kolik je kombinací 2 prvků z 5?", "num:10", "C(5, 2) = (5 · 4) / 2 = 10. Výsledek je 10."},
{"Kolik je variací 2 prvků z 5 bez opakování?", "num:20", "V(5, 2) = 5 · 4 = 20. Výsledek je 20."},
{"Kolik je 3!?", "num:6", "3 · 2 · 1 = 6. Výsledek je 6."},
};
return math_practice_page(3, 5, "mat3unit6",
"mat3_ex6_title", "mat3_quiz6_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat3_unit7_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Definice", "Příznivé ku všem",
"Tip: všechny výsledky musí být stejně možné.",
{
"Klasická pravděpodobnost je počet příznivých výsledků dělený počtem všech.",
"Hodnota leží od 0 do 1.",
"Jev jistý má pravděpodobnost 1, jev nemožný 0.",
"Na kostce je sudé číslo ve třech případech ze šesti, tedy 1/2.",
NULL,
},
},
{
"2 / 3 • Nezávislost", "Součin pravděpodobností",
"Tip: dvě mince mají čtyři stejně možné dvojice výsledků.",
{
"U dvou nezávislých mincí jsou možnosti PP, PO, OP a OO.",
"Dva panny jsou jedna možnost ze čtyř, tedy 1/4.",
"U dvou kostek je 36 stejně možných dvojic.",
"Součet 7 nastane u šesti dvojic: 1+6, 2+5, 3+4, 4+3, 5+2 a 6+1.",
NULL,
},
},
{
"3 / 3 • Karty", "Čtyři esa v balíčku 32 karet",
"Tip: mariášový balíček má 32 karet.",
{
"Pravděpodobnost esa je 4/32 = 1/8.",
"Tažení jedné karty považuje každou kartu za stejně možnou.",
"Doplňkový jev má pravděpodobnost 1 − p.",
"Výsledek se krátí do základního tvaru.",
NULL,
},
},
};
return math_unit_page(3, &mat3_lessons[6], "mat3_unit7", "mat3_unit7_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat3_unit7_exercise_page(void) {
static const MathItem qs[] = {
{"Jaká je pravděpodobnost sudého čísla při hodu hrací kostkou?", "num:1/2", "Sudá jsou 2, 4 a 6, tedy 3 ze 6, čili 1/2. Výsledek je 1/2."},
{"Jaká je pravděpodobnost, že na dvou mincích padnou dvě panny?", "num:1/4", "Ze čtyř dvojic je příznivá jedna. Výsledek je 1/4."},
{"V balíčku 32 karet jsou 4 esa. Jaká je pravděpodobnost, že tažená karta je eso?", "num:1/8", "4/32 = 1/8. Výsledek je 1/8."},
{"Jaká je pravděpodobnost, že součet na dvou hracích kostkách je 7?", "num:1/6", "Příznivých dvojic je 6 z 36, tedy 1/6. Výsledek je 1/6."},
};
return math_practice_page(3, 6, "mat3unit7",
"mat3_ex7_title", "mat3_quiz7_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat3_unit8_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Krychle", "Objem a povrch",
"Tip: krychle o hraně a má objem a³ a povrch 6a².",
{
"Objem je počet jednotkových krychlí, které se do tělesa vejdou.",
"Krychle o hraně 3 má objem 27.",
"Povrch je součet obsahů všech stěn: 6 · 9 = 54.",
"Jednotky objemu jsou krychlové, jednotky povrchu čtvereční.",
NULL,
},
},
{
"2 / 3 • Válec a koule", "Vzorce s π",
"Tip: výsledek nechte v násobcích π.",
{
"Objem válce je πr²v.",
"Pro r = 2 a v = 5 je objem 20π.",
"Objem koule je (4/3)πr³.",
"Pro r = 3 je objem (4/3)π · 27 = 36π.",
NULL,
},
},
{
"3 / 3 • Jehlan", "Třetina hranolu",
"Tip: objem jehlanu je (1/3) · obsah podstavy · výška.",
{
"Jehlan má stejný objem jako třetina hranolu se stejnou podstavou a výškou.",
"Podstava o obsahu 12 a výška 4 dávají objem 16.",
"Výška je kolmá vzdálenost vrcholu od roviny podstavy.",
"Stejný vztah třetiny platí i pro kužel vůči válci.",
NULL,
},
},
};
return math_unit_page(3, &mat3_lessons[7], "mat3_unit8", "mat3_unit8_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat3_unit8_exercise_page(void) {
static const MathItem qs[] = {
{"Krychle má hranu 3. Jaký je její objem?", "num:27", "3³ = 27. Výsledek je 27."},
{"Krychle má hranu 3. Jaký je její povrch?", "num:54", "Šest stěn o obsahu 9 dává 54. Výsledek je 54."},
{"Válec má poloměr 2 a výšku 5. Jaký je jeho objem? Nechte π ve výsledku.", "num:20*pi", "πr²v = π · 4 · 5 = 20π. Výsledek je 20π."},
{"Koule má poloměr 3. Jaký je její objem? Nechte π ve výsledku.", "num:36*pi", "(4/3)π · 27 = 36π. Výsledek je 36π."},
};
return math_practice_page(3, 7, "mat3unit8",
"mat3_ex8_title", "mat3_quiz8_head",
qs, G_N_ELEMENTS(qs));
}
void add_mat3_pages(GtkStack *stack) {
typedef GtkWidget *(*MathBuilder)(void);
static const struct {
const char *unit_name;
const char *ex_name;
MathBuilder build_unit;
MathBuilder build_ex;
} pages[] = {
{"mat3unit1", "mat3ex1", build_mat3_unit1_page, build_mat3_unit1_exercise_page},
{"mat3unit2", "mat3ex2", build_mat3_unit2_page, build_mat3_unit2_exercise_page},
{"mat3unit3", "mat3ex3", build_mat3_unit3_page, build_mat3_unit3_exercise_page},
{"mat3unit4", "mat3ex4", build_mat3_unit4_page, build_mat3_unit4_exercise_page},
{"mat3unit5", "mat3ex5", build_mat3_unit5_page, build_mat3_unit5_exercise_page},
{"mat3unit6", "mat3ex6", build_mat3_unit6_page, build_mat3_unit6_exercise_page},
{"mat3unit7", "mat3ex7", build_mat3_unit7_page, build_mat3_unit7_exercise_page},
{"mat3unit8", "mat3ex8", build_mat3_unit8_page, build_mat3_unit8_exercise_page},
};
for (guint i = 0; i < G_N_ELEMENTS(pages); i++) {
gtk_stack_add_named(stack, pages[i].build_unit(), pages[i].unit_name);
gtk_stack_add_named(stack, pages[i].build_ex(), pages[i].ex_name);
}
}

472
src/mat_year4.c Normal file
View file

@ -0,0 +1,472 @@
#include "graduately.h"
NetLesson mat4_lessons[MATH_N] = {
{ .n_slides = 3, .unit_page = "mat4unit1", .ex_page = "mat4ex1" },
{ .n_slides = 3, .unit_page = "mat4unit2", .ex_page = "mat4ex2" },
{ .n_slides = 3, .unit_page = "mat4unit3", .ex_page = "mat4ex3" },
{ .n_slides = 3, .unit_page = "mat4unit4", .ex_page = "mat4ex4" },
{ .n_slides = 3, .unit_page = "mat4unit5", .ex_page = "mat4ex5" },
{ .n_slides = 3, .unit_page = "mat4unit6", .ex_page = "mat4ex6" },
{ .n_slides = 3, .unit_page = "mat4unit7", .ex_page = "mat4ex7" },
{ .n_slides = 3, .unit_page = "mat4unit8", .ex_page = "mat4ex8" },
};
static GtkWidget *build_mat4_unit1_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Souřadnice", "Velikost a součet",
"Tip: velikost vektoru (a; b) je √(a² + b²).",
{
"Vektor má velikost a směr.",
"|(3; 4)| = 5, protože 9 + 16 = 25.",
"Vektory se sčítají po souřadnicích.",
"(1; 2) + (3; −1) = (4; 1).",
NULL,
},
},
{
"2 / 3 • Skalární součin", "Číslo, ne vektor",
"Tip: (a; b) · (c; d) = ac + bd.",
{
"Skalární součin dvou vektorů je číslo.",
"(1; 2) · (3; 4) = 3 + 8 = 11.",
"Nulový skalární součin znamená kolmé vektory.",
"Velikost lze psát jako odmocninu ze skalárního součinu vektoru se sebou.",
NULL,
},
},
{
"3 / 3 • Vzdálenost", "Velikost rozdílu",
"Tip: na mřížce osa y míří nahoru.",
{
"Vzdálenost bodů je velikost vektoru mezi nimi.",
"Od [1; 1] do [4; 5] je vektor (3; 4) a vzdálenost 5.",
"Koncový bod vektoru (3; 2) umístěného do [1; 1] je [4; 3].",
"Pořadí souřadnic je x a potom y.",
NULL,
},
},
};
return math_unit_page(4, &mat4_lessons[0], "mat4_unit1", "mat4_unit1_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat4_unit1_exercise_page(void) {
static const MathItem qs[] = {
{"Jaká je velikost vektoru (3; 4)?", "num:5", "√(9 + 16) = √25 = 5. Výsledek je 5."},
{"Sečtěte vektory (1; 2) a (3; −1). Napište souřadnice oddělené středníkem.", "pair:4;1", "(1 + 3; 2 + (−1)) = (4; 1). Napište 4;1."},
{"Spočítejte skalární součin vektorů (1; 2) a (3; 4).", "num:11", "1 · 3 + 2 · 4 = 11. Výsledek je 11."},
{"Jaká je vzdálenost bodů [1; 1] a [4; 5]?", "num:5", "Rozdíl je (3; 4) a jeho velikost je 5. Výsledek je 5."},
{"Z bodu A = [1; 1] naneste vektor (3; 2). Zakreslete koncový bod.", "points:4,3;fix:1,1", "Ke každé souřadnici se přičte složka vektoru: [1 + 3; 1 + 2] = [4; 3]."},
};
return math_practice_page(4, 0, "mat4unit1",
"mat4_ex1_title", "mat4_quiz1_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat4_unit2_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Směrnice", "y = ax + b",
"Tip: a = (y₂ − y₁)/(x₂ − x₁).",
{
"Body [1; 2] a [4; 8] určují směrnici (8 − 2)/(4 − 1) = 2.",
"Přímka procházející bodem [0; 1] se směrnicí 2 má rovnici y = 2x + 1.",
"Úsek na ose y je 1.",
"Svislá přímka směrnici tohoto tvaru nemá.",
NULL,
},
},
{
"2 / 3 • Parametr", "Bod a směrový vektor",
"Tip: X = A + t · u.",
{
"Parametrické vyjádření je x = x₀ + t · u₁, y = y₀ + t · u₂.",
"Přímka (1; 3) + t(2; 0) má při t = 2 bod [5; 3].",
"Každé t dá právě jeden bod přímky.",
"Směrový vektor (2; 0) je vodorovný.",
NULL,
},
},
{
"3 / 3 • Vzdálenost", "Bod od přímky",
"Tip: pro ax + by + c = 0 je vzdálenost |ax₀ + by₀ + c| / √(a² + b²).",
{
"Přímka 3x + 4y − 10 = 0 má a = 3, b = 4 a c = −10.",
"Vzdálenost počátku je |−10| / √(9 + 16) = 10/5 = 2.",
"Čitatel je absolutní hodnota, vzdálenost není záporná.",
"Čtvrtý vrchol rovnoběžníku k bodům A, B a C je B + C − A.",
NULL,
},
},
};
return math_unit_page(4, &mat4_lessons[1], "mat4_unit2", "mat4_unit2_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat4_unit2_exercise_page(void) {
static const MathItem qs[] = {
{"Jaká je směrnice přímky body [1; 2] a [4; 8]?", "num:2", "(8 − 2)/(4 − 1) = 2. Výsledek je 2."},
{"Přímka má rovnici y = 2x + 1. Jaký je její úsek na ose y?", "num:1", "Úsek je absolutní člen, tedy 1. Výsledek je 1."},
{"Bod přímky je (1; 3) + t(2; 0). Kam přímka dojde pro t = 2? Napište x a y středníkem.", "pair:5;3", "(1; 3) + 2 · (2; 0) = (5; 3). Napište 5;3."},
{"Jaká je vzdálenost bodu [0; 0] od přímky 3x + 4y − 10 = 0?", "num:2", "|−10| / √(9 + 16) = 10/5 = 2. Výsledek je 2."},
{"A = [1; 1], B = [4; 1] a C = [2; 3]. Zakreslete čtvrtý vrchol rovnoběžníku, ve kterém jsou AB a AC sousední strany.", "para:1,1;4,1;2,3", "D = B + C − A = [4 + 2 − 1; 1 + 3 − 1] = [5; 3]."},
};
return math_practice_page(4, 1, "mat4unit2",
"mat4_ex2_title", "mat4_quiz2_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat4_unit3_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Kružnice", "Střed a poloměr",
"Tip: (x − m)² + (y − n)² = r² má střed [m; n].",
{
"Kružnice je množina bodů stejně vzdálených od středu.",
"(x − 1)² + (y + 2)² = 9 má střed [1; −2].",
"y + 2 je totéž co y − (−2).",
"Poloměr je √9 = 3, ne 9.",
NULL,
},
},
{
"2 / 3 • Elipsa", "Dvě poloosy",
"Tip: ve tvaru x²/a² + y²/b² = 1 je a poloosa na ose x.",
{
"Elipsa v osové poloze se středem v počátku má rovnici x²/a² + y²/b² = 1.",
"U x²/25 + y²/9 = 1 je a = 5 a b = 3.",
"Delší poloosa je hlavní, kratší vedlejší.",
"Vrcholy na ose x jsou [±a; 0], na ose y [0; ±b].",
NULL,
},
},
{
"3 / 3 • Excentricita", "Vzdálenost ohniska od středu",
"Tip: pro a > b je e = √(a² − b²).",
{
"Lineární výstřednost e splňuje e² = a² − b², když je a hlavní poloosa.",
"√(25 − 9) = √16 = 4.",
"Ohniska elipsy jsou [±e; 0].",
"Součet vzdáleností bodu elipsy od obou ohnisek je 2a.",
NULL,
},
},
};
return math_unit_page(4, &mat4_lessons[2], "mat4_unit3", "mat4_unit3_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat4_unit3_exercise_page(void) {
static const MathItem qs[] = {
{"Kružnice má rovnici (x − 1)² + (y + 2)² = 9. Napište souřadnice středu jako x;y.", "pair:1;-2", "Střed je [1; −2]. Napište 1;-2."},
{"Jaký je poloměr kružnice (x − 1)² + (y + 2)² = 9?", "num:3", "Pravá strana je r², takže r = 3. Výsledek je 3."},
{"Elipsa má rovnici x²/25 + y²/9 = 1. Jak dlouhá je poloosa a na ose x?", "num:5", "a² = 25, takže a = 5. Výsledek je 5."},
{"Elipsa má rovnici x²/25 + y²/9 = 1. Jak dlouhá je poloosa b na ose y?", "num:3", "b² = 9, takže b = 3. Výsledek je 3."},
{"Elipsa má a = 5 a b = 3. Jaká je její lineární výstřednost e?", "num:4", "e = √(25 − 9) = √16 = 4. Výsledek je 4."},
};
return math_practice_page(4, 2, "mat4unit3",
"mat4_ex3_title", "mat4_quiz3_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat4_unit4_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Tvar", "a + bi",
"Tip: i² = −1. Reálnou a imaginární část pište oddělené středníkem.",
{
"Komplexní číslo a + bi má reálnou část a a imaginární část b.",
"i je imaginární jednotka a i² = −1.",
"Čísla se sčítají po složkách.",
"(2 + 3i) + (1 − i) = 3 + 2i.",
NULL,
},
},
{
"2 / 3 • Násobení", "Jako dvojčlen",
"Tip: (1 + i)(1 − i) je rozdíl čtverců.",
{
"(a + bi)(c + di) = ac + adi + bci + bdi².",
"bdi² = −bd, proto se imaginární jednotka ve výsledku nemusí objevit.",
"(1 + i)(1 − i) = 1 − (i)² = 1 − (−1) = 2.",
"Komplexní čísla lze násobit i v goniometrickém tvaru sčítáním argumentů.",
NULL,
},
},
{
"3 / 3 • Absolutní hodnota", "Vzdálenost od nuly",
"Tip: |a + bi| = √(a² + b²).",
{
"Absolutní hodnota je vzdálenost obrazu čísla od počátku Gaussovy roviny.",
"|3 + 4i| = 5.",
"|z| je vždy reálné nezáporné číslo.",
"i² = −1, i³ = −i a i⁴ = 1.",
NULL,
},
},
};
return math_unit_page(4, &mat4_lessons[3], "mat4_unit4", "mat4_unit4_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat4_unit4_exercise_page(void) {
static const MathItem qs[] = {
{"Sečtěte 2 + 3i a 1 − i. Napište reálnou a imaginární část středníkem.", "pair:3;2", "(2 + 1) + (3 − 1)i = 3 + 2i. Napište 3;2."},
{"Spočítejte (1 + i)(1 − i).", "num:2", "1 − i² = 1 − (−1) = 2. Výsledek je 2."},
{"Jaká je absolutní hodnota čísla 3 + 4i?", "num:5", "√(9 + 16) = 5. Výsledek je 5."},
{"Kolik je i²?", "num:-1", "Imaginární jednotka splňuje i² = −1. Výsledek je -1."},
};
return math_practice_page(4, 3, "mat4unit4",
"mat4_ex4_title", "mat4_quiz4_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat4_unit5_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Mocnina", "(xⁿ)' = n xⁿ⁻¹",
"Tip: derivace je směrnice tečny.",
{
"Derivace funkce v bodě je směrnice tečny grafu v tom bodě.",
"(x³)' = 3x². V bodě x = 2 je 3 · 4 = 12.",
"(5x²)' = 10x. V bodě x = 1 je 10.",
"Derivace konstanty je 0 a konstanta se vytýká.",
NULL,
},
},
{
"2 / 3 • Tečna", "Směrnice v bodě",
"Tip: u f(x) = x² je f'(x) = 2x.",
{
"Tečna ke grafu y = x² v bodě x = 1 má směrnici 2.",
"Rovnice tečny používá bod [1; 1] a tuto směrnici.",
"Kladná derivace znamená, že funkce v bodě roste.",
"Záporná derivace znamená, že funkce klesá.",
NULL,
},
},
{
"3 / 3 • Sinus", "Derivace sinu je kosinus",
"Tip: (sin x)' = cos x a cos 0 = 1.",
{
"Derivace funkce sin x je cos x.",
"Derivace funkce cos x je −sin x.",
"V bodě x = 0 je derivace sinu rovna cos 0 = 1.",
"Úhel v tomto vzorci je v radiánech, ale nula je nula v obou jednotkách.",
NULL,
},
},
};
return math_unit_page(4, &mat4_lessons[4], "mat4_unit5", "mat4_unit5_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat4_unit5_exercise_page(void) {
static const MathItem qs[] = {
{"Jaká je derivace funkce x³ v bodě x = 2?", "num:12", "(x³)' = 3x² a 3 · 4 = 12. Výsledek je 12."},
{"Jaká je derivace funkce 5x² v bodě x = 1?", "num:10", "(5x²)' = 10x a v jedničce je 10. Výsledek je 10."},
{"Jakou směrnici má tečna ke grafu y = x² v bodě x = 1?", "num:2", "Derivace 2x je v bodě 1 rovna 2. Výsledek je 2."},
{"Jaká je derivace funkce sin x v bodě x = 0? Derivace sinu je kosinus.", "num:1", "(sin x)' = cos x a cos 0 = 1. Výsledek je 1."},
};
return math_practice_page(4, 4, "mat4unit5",
"mat4_ex5_title", "mat4_quiz5_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat4_unit6_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Stacionární bod", "Derivace je nula",
"Tip: f'(x) = 0 je vodorovná tečna.",
{
"V lokálním extrému diferencovatelné funkce je derivace nulová.",
"U f(x) = x³ − 3x je f'(x) = 3x² − 3.",
"3(x² − 1) = 0 dává x = −1 a x = 1.",
"To jsou jediní kandidáti na lokální extrém.",
NULL,
},
},
{
"2 / 3 • Maximum a minimum", "Znaménko druhé derivace",
"Tip: f''(x) = 6x. Záporná druhá derivace znamená lokální maximum.",
{
"V x = −1 je f''(−1) = −6 < 0, takže jde o lokální maximum.",
"f(−1) = −1 + 3 = 2.",
"V x = 1 je f''(1) = 6 > 0, takže jde o lokální minimum.",
"f(1) = 1 − 3 = −2.",
NULL,
},
},
{
"3 / 3 • Jiný příklad", "Lineární derivace",
"Tip: g'(x) = 2x − 4 je nula při x = 2.",
{
"Kde je derivace nula, má graf vodorovnou tečnu.",
"Rovnice 2x − 4 = 0 má řešení x = 2.",
"Pro x < 2 je derivace 2x − 4 záporná, funkce klesá.",
"Pro x > 2 je derivace kladná, funkce roste.",
NULL,
},
},
};
return math_unit_page(4, &mat4_lessons[5], "mat4_unit6", "mat4_unit6_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat4_unit6_exercise_page(void) {
static const MathItem qs[] = {
{"Funkce f(x) = x³ − 3x. Ve kterém x má lokální maximum?", "num:-1", "f'(x) = 3x² − 3 = 0 pro x = ±1 a v −1 je maximum. Výsledek je -1."},
{"Funkce f(x) = x³ − 3x. Jaká je hodnota lokálního maxima?", "num:2", "f(−1) = −1 + 3 = 2. Výsledek je 2."},
{"Funkce f(x) = x³ − 3x. Ve kterém x má lokální minimum?", "num:1", "Druhá derivace 6x je v x = 1 kladná. Výsledek je 1."},
{"Derivace funkce je g'(x) = 2x − 4. Pro které x je derivace nulová?", "num:2", "2x − 4 = 0 dává x = 2. Výsledek je 2."},
};
return math_practice_page(4, 5, "mat4unit6",
"mat4_ex6_title", "mat4_quiz6_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat4_unit7_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Primitivní funkce", "Opačný postup k derivaci",
"Tip: primitivní funkce k xⁿ je xⁿ⁺¹/(n + 1) pro n ≠ −1.",
{
"Neurčitý integrál je množina primitivních funkcí.",
"Liší se o konstantu, protože derivace konstanty je nula.",
"Primitivní funkce k 2x je x².",
"Primitivní funkce k x² je x³/3.",
NULL,
},
},
{
"2 / 3 • Určitý integrál", "Newtonův vzorec",
"Tip: ∫ od a do b z f je F(b) − F(a).",
{
"Určitý integrál nezáporné funkce je obsah plochy pod grafem.",
"∫ od 0 do 3 z 2x dx = [x²] od 0 do 3 = 9.",
"∫ od 0 do 2 z x² dx = [x³/3] od 0 do 2 = 8/3.",
"Dolní mez 0 u těchto mocnin často vynuluje první člen.",
NULL,
},
},
{
"3 / 3 • Konstanta", "Násobek lze vytknout",
"Tip: integrál z c · f je c krát integrál z f.",
{
"∫ od 0 do 1 z 3x² dx = [x³] od 0 do 1 = 1.",
"∫ od 0 do 2 z 6x dx = [3x²] od 0 do 2 = 12.",
"Záporná funkce dává záporný integrál, nejde vždy o obsah beze znaménka.",
"Součet funkcí se integruje člen po členu.",
NULL,
},
},
};
return math_unit_page(4, &mat4_lessons[6], "mat4_unit7", "mat4_unit7_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat4_unit7_exercise_page(void) {
static const MathItem qs[] = {
{"Spočítejte integrál od 0 do 3 z funkce 2x.", "num:9", "Primitivní funkce je x² a 9 − 0 = 9. Výsledek je 9."},
{"Spočítejte integrál od 0 do 2 z funkce x².", "num:8/3", "Primitivní funkce je x³/3 a 8/3 − 0 = 8/3. Výsledek je 8/3."},
{"Spočítejte integrál od 0 do 1 z funkce 3x².", "num:1", "Primitivní funkce je x³ a 1 − 0 = 1. Výsledek je 1."},
{"Spočítejte integrál od 0 do 2 z funkce 6x.", "num:12", "Primitivní funkce je 3x² a 12 − 0 = 12. Výsledek je 12."},
};
return math_practice_page(4, 6, "mat4unit7",
"mat4_ex7_title", "mat4_quiz7_head",
qs, G_N_ELEMENTS(qs));
}
static GtkWidget *build_mat4_unit8_page(void) {
static const NetSlide slides[] = {
{
"1 / 3 • Číslo", "Logaritmus a zlomek",
"Tip: log₂(8) = 3, protože 2³ = 8.",
{
"Logaritmus vrací exponent.",
"1/2 + 1/3 = 5/6.",
"Společný jmenovatel se hledá i v maturitním příkladu stejně jako na základní škole.",
"Výsledek se krátí.",
NULL,
},
},
{
"2 / 3 • Počet a vektor", "Kombinace a velikost",
"Tip: C(6, 2) = 15 a |(3; 4)| = 5.",
{
"C(6, 2) = (6 · 5) / 2 = 15.",
"Pořadí vybrané dvojice se nepočítá dvakrát.",
"Velikost vektoru (3; 4) je přepona trojúhelníku 3-4-5.",
"Skalární součin by u kolmých vektorů vyšel nula.",
NULL,
},
},
{
"3 / 3 • Rovnice", "x² − 1 = 0",
"Tip: rozdíl čtverců (x − 1)(x + 1).",
{
"x² − 1 = 0 má kořeny −1 a 1.",
"Oba se napíšou, oddělené středníkem.",
"Diskriminant je 0 − 4 · 1 · (−1) = 4.",
"Zkouška: 1 − 1 = 0 a 1 − 1 = 0.",
NULL,
},
},
};
return math_unit_page(4, &mat4_lessons[7], "mat4_unit8", "mat4_unit8_sub",
slides, G_N_ELEMENTS(slides));
}
static GtkWidget *build_mat4_unit8_exercise_page(void) {
static const MathItem qs[] = {
{"Spočítejte logaritmus o základu 2 z 8.", "num:3", "2³ = 8. Výsledek je 3."},
{"Kolik je kombinací 2 prvků ze 6?", "num:15", "C(6, 2) = 15. Výsledek je 15."},
{"Jaká je velikost vektoru (3; 4)?", "num:5", "√(9 + 16) = 5. Výsledek je 5."},
{"Spočítejte 1/2 + 1/3.", "num:5/6", "3/6 + 2/6 = 5/6. Výsledek je 5/6."},
{"Najděte kořeny rovnice x² − 1 = 0. Oddělte je středníkem.", "set:-1;1", "(x − 1)(x + 1) = 0. Napište -1;1."},
};
return math_practice_page(4, 7, "mat4unit8",
"mat4_ex8_title", "mat4_quiz8_head",
qs, G_N_ELEMENTS(qs));
}
void add_mat4_pages(GtkStack *stack) {
typedef GtkWidget *(*MathBuilder)(void);
static const struct {
const char *unit_name;
const char *ex_name;
MathBuilder build_unit;
MathBuilder build_ex;
} pages[] = {
{"mat4unit1", "mat4ex1", build_mat4_unit1_page, build_mat4_unit1_exercise_page},
{"mat4unit2", "mat4ex2", build_mat4_unit2_page, build_mat4_unit2_exercise_page},
{"mat4unit3", "mat4ex3", build_mat4_unit3_page, build_mat4_unit3_exercise_page},
{"mat4unit4", "mat4ex4", build_mat4_unit4_page, build_mat4_unit4_exercise_page},
{"mat4unit5", "mat4ex5", build_mat4_unit5_page, build_mat4_unit5_exercise_page},
{"mat4unit6", "mat4ex6", build_mat4_unit6_page, build_mat4_unit6_exercise_page},
{"mat4unit7", "mat4ex7", build_mat4_unit7_page, build_mat4_unit7_exercise_page},
{"mat4unit8", "mat4ex8", build_mat4_unit8_page, build_mat4_unit8_exercise_page},
};
for (guint i = 0; i < G_N_ELEMENTS(pages); i++) {
gtk_stack_add_named(stack, pages[i].build_unit(), pages[i].unit_name);
gtk_stack_add_named(stack, pages[i].build_ex(), pages[i].ex_name);
}
}

955
src/matematika.c Normal file
View file

@ -0,0 +1,955 @@
#include "graduately.h"
#include <string.h>
/* Mathematics: elementary review plus four gymnasium years.
* Lessons are slides, then typed examples. A few lessons also
* ask for a point snapped to the grid. */
#define MATH_CELL 28.0
#define MATH_PAD 18.0
#define MATH_GX 10
#define MATH_GY 8
#define MATH_PTS 16
typedef struct {
const char *map_page;
const char *back_page;
const char *title_key;
const char *sub_key;
char **progress_path;
NetLesson *lessons;
const char *unit_keys[MATH_N];
const char *sub_keys[MATH_N];
GtkWidget *nodes[MATH_N];
int note_last_w;
} MathTrack;
typedef struct {
const char *spec;
int ux[MATH_PTS];
int uy[MATH_PTS];
int n;
int fx[8];
int fy[8];
int nf;
int need;
int checked;
int correct;
GtkWidget *area;
} MathBoard;
typedef struct {
const MathItem *items;
int n;
MathTrack *track;
int lesson_id;
GtkWidget *feedback;
GtkWidget **entries;
MathBoard **boards;
GtkWidget **hints;
} MathExCtx;
static MathTrack math_tracks[5];
static gboolean math_ready;
static char math_key_store[5][MATH_N][32];
static char math_sub_store[5][MATH_N][40];
static void math_ensure(void) {
static const char *prefixes[] = {"mat0", "mat", "mat2", "mat3", "mat4"};
static const char *maps[] = {
"mat0map", "matmap", "mat2map", "mat3map", "mat4map",
};
static const char *titles[] = {
"mat0_year", "mat_year1", "mat_year2", "mat_year3", "mat_year4",
};
static const char *subs[] = {
"mat0_sub", "mat_sub", "mat2_sub", "mat3_sub", "mat4_sub",
};
NetLesson *lessons[5] = {
mat0_lessons, mat_lessons, mat2_lessons, mat3_lessons, mat4_lessons,
};
char **paths[5] = {
&app_progress_mat0, &app_progress_mat, &app_progress_mat2,
&app_progress_mat3, &app_progress_mat4,
};
int y, i;
if (math_ready)
return;
for (y = 0; y < 5; y++) {
MathTrack *T = &math_tracks[y];
T->map_page = maps[y];
T->back_page = "matyears";
T->title_key = titles[y];
T->sub_key = subs[y];
T->progress_path = paths[y];
T->lessons = lessons[y];
T->note_last_w = -1;
for (i = 0; i < MATH_N; i++) {
g_snprintf(math_key_store[y][i], sizeof math_key_store[y][i],
"%s_unit%d", prefixes[y], i + 1);
g_snprintf(math_sub_store[y][i], sizeof math_sub_store[y][i],
"%s_unit%d_sub", prefixes[y], i + 1);
T->unit_keys[i] = math_key_store[y][i];
T->sub_keys[i] = math_sub_store[y][i];
}
}
math_ready = TRUE;
}
static void math_save(MathTrack *T) {
GKeyFile *kf;
gchar *data;
if (!T->progress_path || !*T->progress_path)
return;
kf = g_key_file_new();
for (int i = 0; i < MATH_N; i++) {
gchar key[8];
g_snprintf(key, sizeof key, "%d", i + 1);
g_key_file_set_boolean(kf, "done", key, T->lessons[i].done);
}
if (g_mkdir_with_parents(PROGRESS_DIR, 0755) != 0) {
g_key_file_free(kf);
return;
}
data = g_key_file_to_data(kf, NULL, NULL);
g_key_file_free(kf);
if (data) {
GError *err = NULL;
g_file_set_contents(*T->progress_path, data, -1, &err);
if (err)
g_error_free(err);
g_free(data);
}
}
static void math_load_one(MathTrack *T) {
GKeyFile *kf;
GError *err = NULL;
if (!T->progress_path || !*T->progress_path)
return;
kf = g_key_file_new();
if (!g_key_file_load_from_file(kf, *T->progress_path, G_KEY_FILE_NONE, &err)) {
if (err)
g_error_free(err);
g_key_file_free(kf);
return;
}
for (int i = 0; i < MATH_N; i++) {
gchar key[8];
g_snprintf(key, sizeof key, "%d", i + 1);
T->lessons[i].done = g_key_file_get_boolean(kf, "done", key, NULL);
}
g_key_file_free(kf);
}
void math_load_progress(void) {
math_ensure();
for (int y = 0; y < 5; y++)
math_load_one(&math_tracks[y]);
}
void math_rail_theme_reset(void) {
}
static void math_refresh(MathTrack *T) {
for (int i = 0; i < MATH_N; i++) {
GtkWidget *node = T->nodes[i];
GtkWidget *icon = T->lessons[i].done_icon;
if (!node)
continue;
if (T->lessons[i].done) {
gtk_widget_remove_css_class(node, "current");
gtk_widget_add_css_class(node, "done");
if (icon)
gtk_widget_set_visible(icon, TRUE);
} else {
gtk_widget_remove_css_class(node, "done");
gtk_widget_add_css_class(node, "current");
if (icon)
gtk_widget_set_visible(icon, FALSE);
}
}
}
static void math_mark_done(MathTrack *T, int lesson_id) {
if (lesson_id < 0 || lesson_id >= MATH_N)
return;
if (T->lessons[lesson_id].done)
return;
T->lessons[lesson_id].done = TRUE;
math_save(T);
math_refresh(T);
refresh_stats_ui();
}
void progress_for_math(ProgressSum *out) {
if (!out)
return;
math_ensure();
for (int y = 0; y < 5; y++) {
MathTrack *T = &math_tracks[y];
out->total_ex += MATH_N;
out->open_units += MATH_N;
for (int i = 0; i < MATH_N; i++) {
if (T->lessons[i].done) {
out->done_ex++;
out->done_units++;
}
}
}
}
static void math_open_unit(GtkButton *button, gpointer data) {
NetLesson *L = data;
(void)button;
if (!L)
return;
L->idx = 0;
if (L->stack) {
char name[16];
g_snprintf(name, sizeof name, "s%u", L->idx);
gtk_stack_set_visible_child_name(GTK_STACK(L->stack), name);
if (L->prev_btn)
gtk_widget_set_sensitive(L->prev_btn, FALSE);
if (L->next_btn)
gtk_button_set_label(GTK_BUTTON(L->next_btn), tr("net_slide_next"));
}
gtk_stack_set_visible_child_name(main_stack, L->unit_page);
}
static GtkWidget *math_lesson_button(MathTrack *T, int index) {
GtkWidget *btn;
GtkWidget *row;
GtkWidget *num;
GtkWidget *texts;
GtkWidget *title;
GtkWidget *sub;
GtkWidget *icon;
char *num_text;
btn = gtk_button_new();
gtk_widget_add_css_class(btn, "unit-node");
gtk_widget_add_css_class(btn, "current");
gtk_widget_set_can_focus(btn, FALSE);
gtk_widget_set_hexpand(btn, TRUE);
gtk_widget_set_size_request(btn, -1, 84);
row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 14);
gtk_widget_set_halign(row, GTK_ALIGN_START);
gtk_widget_set_valign(row, GTK_ALIGN_CENTER);
gtk_widget_set_margin_start(row, 18);
gtk_widget_set_margin_end(row, 18);
gtk_button_set_child(GTK_BUTTON(btn), row);
num_text = g_strdup_printf("%d", index + 1);
num = gtk_label_new(num_text);
g_free(num_text);
gtk_widget_add_css_class(num, "unit-number");
gtk_widget_set_valign(num, GTK_ALIGN_CENTER);
gtk_box_append(GTK_BOX(row), num);
texts = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
gtk_widget_set_halign(texts, GTK_ALIGN_START);
gtk_widget_set_hexpand(texts, TRUE);
gtk_box_append(GTK_BOX(row), texts);
title = gtk_label_new(NULL);
i18n_bind(title, T->unit_keys[index], 0);
gtk_widget_set_halign(title, GTK_ALIGN_START);
gtk_widget_add_css_class(title, "unit-name");
gtk_box_append(GTK_BOX(texts), title);
sub = gtk_label_new(NULL);
i18n_bind(sub, T->sub_keys[index], 0);
gtk_widget_set_halign(sub, GTK_ALIGN_START);
gtk_label_set_wrap(GTK_LABEL(sub), TRUE);
gtk_label_set_xalign(GTK_LABEL(sub), 0.0);
gtk_widget_add_css_class(sub, "hint");
gtk_box_append(GTK_BOX(texts), sub);
icon = icon_area_new(draw_check_icon, 0.1176, 0.1176, 0.1804, 18);
gtk_widget_set_visible(icon, FALSE);
gtk_widget_set_valign(icon, GTK_ALIGN_CENTER);
gtk_box_append(GTK_BOX(row), icon);
T->lessons[index].done_icon = icon;
T->nodes[index] = btn;
g_signal_connect(btn, "clicked", G_CALLBACK(math_open_unit),
&T->lessons[index]);
return btn;
}
static GtkWidget *math_build_list(MathTrack *T) {
GtkWidget *page;
GtkWidget *scroll;
GtkWidget *list;
math_ensure();
page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_hexpand(page, TRUE);
gtk_widget_set_vexpand(page, TRUE);
gtk_widget_set_margin_start(page, 32);
gtk_widget_set_margin_end(page, 32);
gtk_widget_set_margin_top(page, 24);
gtk_widget_set_margin_bottom(page, 24);
gtk_box_append(GTK_BOX(page),
top_bar(T->back_page, T->title_key, T->sub_key));
scroll = gtk_scrolled_window_new();
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_widget_set_vexpand(scroll, TRUE);
gtk_widget_set_margin_top(scroll, 18);
gtk_box_append(GTK_BOX(page), scroll);
list = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
gtk_widget_set_halign(list, GTK_ALIGN_CENTER);
gtk_widget_set_valign(list, GTK_ALIGN_START);
gtk_widget_set_size_request(list, 520, -1);
gtk_widget_set_margin_bottom(list, 12);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), list);
for (int i = 0; i < MATH_N; i++)
gtk_box_append(GTK_BOX(list), math_lesson_button(T, i));
math_refresh(T);
return page;
}
GtkWidget *build_mat0map_page(void) { return math_build_list(&math_tracks[0]); }
GtkWidget *build_matmap_page(void) { return math_build_list(&math_tracks[1]); }
GtkWidget *build_mat2map_page(void) { return math_build_list(&math_tracks[2]); }
GtkWidget *build_mat3map_page(void) { return math_build_list(&math_tracks[3]); }
GtkWidget *build_mat4map_page(void) { return math_build_list(&math_tracks[4]); }
static GtkWidget *math_year_button(const char *badge, const char *target,
const char *title_key, const char *sub_key) {
GtkWidget *btn;
GtkWidget *row;
GtkWidget *num;
GtkWidget *texts;
GtkWidget *title;
GtkWidget *sub;
btn = gtk_button_new();
gtk_widget_add_css_class(btn, "unit-node");
gtk_widget_add_css_class(btn, "current");
gtk_widget_set_can_focus(btn, FALSE);
gtk_widget_set_hexpand(btn, TRUE);
gtk_widget_set_size_request(btn, -1, 84);
row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 14);
gtk_widget_set_halign(row, GTK_ALIGN_START);
gtk_widget_set_valign(row, GTK_ALIGN_CENTER);
gtk_widget_set_margin_start(row, 18);
gtk_widget_set_margin_end(row, 18);
gtk_button_set_child(GTK_BUTTON(btn), row);
num = gtk_label_new(badge);
gtk_widget_add_css_class(num, "unit-number");
gtk_widget_set_valign(num, GTK_ALIGN_CENTER);
gtk_box_append(GTK_BOX(row), num);
texts = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
gtk_widget_set_halign(texts, GTK_ALIGN_START);
gtk_widget_set_hexpand(texts, TRUE);
gtk_box_append(GTK_BOX(row), texts);
title = gtk_label_new(NULL);
i18n_bind(title, title_key, 0);
gtk_widget_set_halign(title, GTK_ALIGN_START);
gtk_widget_add_css_class(title, "unit-name");
gtk_box_append(GTK_BOX(texts), title);
sub = gtk_label_new(NULL);
i18n_bind(sub, sub_key, 0);
gtk_widget_set_halign(sub, GTK_ALIGN_START);
gtk_label_set_wrap(GTK_LABEL(sub), TRUE);
gtk_label_set_xalign(GTK_LABEL(sub), 0.0);
gtk_widget_add_css_class(sub, "hint");
gtk_box_append(GTK_BOX(texts), sub);
g_object_set_data_full(G_OBJECT(btn), "target", g_strdup(target), g_free);
g_signal_connect(btn, "clicked", G_CALLBACK(on_nav_clicked), NULL);
return btn;
}
GtkWidget *build_matyears_page(void) {
GtkWidget *page;
GtkWidget *scroll;
GtkWidget *list;
static const char *badges[] = {"ZŠ", "1.", "2.", "3.", "4."};
static const char *targets[] = {
"mat0map", "matmap", "mat2map", "mat3map", "mat4map",
};
static const char *titles[] = {
"mat0_year", "mat_year1", "mat_year2", "mat_year3", "mat_year4",
};
static const char *subs[] = {
"mat0_sub", "mat_sub", "mat2_sub", "mat3_sub", "mat4_sub",
};
page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_hexpand(page, TRUE);
gtk_widget_set_vexpand(page, TRUE);
gtk_widget_set_margin_start(page, 32);
gtk_widget_set_margin_end(page, 32);
gtk_widget_set_margin_top(page, 24);
gtk_widget_set_margin_bottom(page, 24);
gtk_box_append(GTK_BOX(page),
top_bar("subjects", "Matematika", "mat_years_sub"));
scroll = gtk_scrolled_window_new();
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_widget_set_vexpand(scroll, TRUE);
gtk_widget_set_margin_top(scroll, 18);
gtk_box_append(GTK_BOX(page), scroll);
list = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
gtk_widget_set_halign(list, GTK_ALIGN_CENTER);
gtk_widget_set_valign(list, GTK_ALIGN_CENTER);
gtk_widget_set_size_request(list, 480, -1);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), list);
for (int y = 0; y < 5; y++)
gtk_box_append(GTK_BOX(list),
math_year_button(badges[y], targets[y], titles[y], subs[y]));
return page;
}
static void math_slide_apply(NetLesson *L) {
char name[16];
if (!L || !L->stack)
return;
g_snprintf(name, sizeof name, "s%u", L->idx);
gtk_stack_set_visible_child_name(GTK_STACK(L->stack), name);
if (L->prev_btn) {
gtk_button_set_label(GTK_BUTTON(L->prev_btn), tr("net_slide_prev"));
gtk_widget_set_sensitive(L->prev_btn, L->idx > 0);
}
if (L->next_btn) {
gtk_button_set_label(
GTK_BUTTON(L->next_btn),
L->idx + 1 >= L->n_slides ? tr("net_slide_start")
: tr("net_slide_next"));
}
}
static void math_slide_prev(GtkButton *button, gpointer data) {
NetLesson *L = data;
(void)button;
if (!L || L->idx == 0)
return;
L->idx--;
math_slide_apply(L);
}
static void math_slide_next(GtkButton *button, gpointer data) {
NetLesson *L = data;
(void)button;
if (!L)
return;
if (L->idx + 1 < L->n_slides) {
L->idx++;
math_slide_apply(L);
} else {
gtk_stack_set_visible_child_name(main_stack, L->ex_page);
}
}
static void math_rescale(MathTrack *T, int w) {
int body, head, kick;
if (w < 160)
w = 160;
body = w / 46;
if (body < 14)
body = 14;
if (body > 26)
body = 26;
head = body + 14;
if (head > 46)
head = 46;
kick = body - 3;
if (kick < 11)
kick = 11;
for (int i = 0; i < MATH_N; i++)
net_rescale_lesson_notes(&T->lessons[i], body, head, kick);
}
static gboolean math_note_tick(GtkWidget *w, GdkFrameClock *clock, gpointer data) {
MathTrack *T = data;
int width = gtk_widget_get_allocated_width(w);
(void)clock;
if (width != T->note_last_w) {
T->note_last_w = width;
math_rescale(T, width);
}
return G_SOURCE_CONTINUE;
}
GtkWidget *math_unit_page(int year, NetLesson *L, const char *title_key,
const char *sub_key, const NetSlide *slides,
guint n_slides) {
MathTrack *T;
GtkWidget *page;
GtkWidget *scroll;
GtkWidget *nav;
math_ensure();
if (year < 0 || year > 4)
year = 0;
T = &math_tracks[year];
net_lesson_notes_ensure(L);
net_notes_target = L;
L->n_slides = n_slides;
page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_margin_start(page, 28);
gtk_widget_set_margin_end(page, 28);
gtk_widget_set_margin_top(page, 20);
gtk_widget_set_margin_bottom(page, 20);
gtk_box_append(GTK_BOX(page), top_bar(T->map_page, title_key, sub_key));
scroll = gtk_scrolled_window_new();
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_widget_set_vexpand(scroll, TRUE);
gtk_widget_set_margin_top(scroll, 10);
gtk_box_append(GTK_BOX(page), scroll);
L->stack = gtk_stack_new();
gtk_stack_set_transition_type(GTK_STACK(L->stack),
GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), L->stack);
for (guint s = 0; s < n_slides; s++) {
GtkWidget *stage = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
GtkWidget *card;
char name[16];
gtk_widget_set_vexpand(stage, TRUE);
g_snprintf(name, sizeof name, "s%u", s);
gtk_stack_add_named(GTK_STACK(L->stack), stage, name);
card = gtk_box_new(GTK_ORIENTATION_VERTICAL, 16);
gtk_widget_add_css_class(card, "notes-card");
gtk_widget_set_valign(card, GTK_ALIGN_CENTER);
gtk_box_append(GTK_BOX(stage), card);
net_note_kicker(card, slides[s].kicker);
net_note_title(card, slides[s].title);
for (int i = 0; i < 8 && slides[s].line[i]; i++)
net_note_line(card, slides[s].line[i], FALSE);
if (slides[s].tip)
net_note_line(card, slides[s].tip, TRUE);
}
gtk_widget_add_tick_callback(L->stack, math_note_tick, T, NULL);
T->note_last_w = -1;
math_rescale(T, 0);
net_notes_target = NULL;
nav = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
gtk_box_set_homogeneous(GTK_BOX(nav), TRUE);
gtk_widget_set_margin_top(nav, 10);
gtk_box_append(GTK_BOX(page), nav);
L->prev_btn = gtk_button_new_with_label(tr("net_slide_prev"));
gtk_widget_add_css_class(L->prev_btn, "btn-primary");
g_signal_connect(L->prev_btn, "clicked", G_CALLBACK(math_slide_prev), L);
gtk_box_append(GTK_BOX(nav), L->prev_btn);
L->next_btn = gtk_button_new_with_label(tr("net_slide_next"));
gtk_widget_add_css_class(L->next_btn, "btn-primary");
g_signal_connect(L->next_btn, "clicked", G_CALLBACK(math_slide_next), L);
gtk_box_append(GTK_BOX(nav), L->next_btn);
L->idx = 0;
math_slide_apply(L);
return page;
}
void math_lessons_apply_lang(void) {
math_ensure();
for (int y = 0; y < 5; y++) {
for (int i = 0; i < MATH_N; i++)
math_slide_apply(&math_tracks[y].lessons[i]);
}
}
static void paint_hex(cairo_t *cr, unsigned hex) {
cairo_set_source_rgb(cr, ((hex >> 16) & 255) / 255.0,
((hex >> 8) & 255) / 255.0, (hex & 255) / 255.0);
}
static int board_px_w(void) {
return (int)(MATH_PAD * 2 + MATH_GX * MATH_CELL);
}
static int board_px_h(void) {
return (int)(MATH_PAD * 2 + MATH_GY * MATH_CELL);
}
static void board_to_screen(int gx, int gy, double *sx, double *sy) {
*sx = MATH_PAD + gx * MATH_CELL;
*sy = board_px_h() - MATH_PAD - gy * MATH_CELL;
}
static gboolean board_snap(double x, double y, int *gx, int *gy) {
double cx = (x - MATH_PAD) / MATH_CELL;
double cy = (board_px_h() - MATH_PAD - y) / MATH_CELL;
int ix = (int)lround(cx);
int iy = (int)lround(cy);
if (ix < 0 || ix > MATH_GX || iy < 0 || iy > MATH_GY)
return FALSE;
if (fabs(cx - ix) > 0.45 || fabs(cy - iy) > 0.45)
return FALSE;
*gx = ix;
*gy = iy;
return TRUE;
}
static void board_draw(GtkDrawingArea *area, cairo_t *cr, int width, int height,
gpointer data) {
MathBoard *b = data;
int i;
static const char *names[] = {"A", "B", "C", "D"};
(void)area;
(void)width;
(void)height;
paint_hex(cr, app_theme.surface1);
cairo_rectangle(cr, 0, 0, board_px_w(), board_px_h());
cairo_fill(cr);
cairo_set_line_width(cr, 1);
cairo_set_source_rgba(cr,
((app_theme.subtext >> 16) & 255) / 255.0,
((app_theme.subtext >> 8) & 255) / 255.0,
(app_theme.subtext & 255) / 255.0, 0.55);
for (i = 0; i <= MATH_GX; i++) {
double x = MATH_PAD + i * MATH_CELL;
cairo_move_to(cr, x, MATH_PAD);
cairo_line_to(cr, x, board_px_h() - MATH_PAD);
}
for (i = 0; i <= MATH_GY; i++) {
double y = MATH_PAD + i * MATH_CELL;
cairo_move_to(cr, MATH_PAD, y);
cairo_line_to(cr, board_px_w() - MATH_PAD, y);
}
cairo_stroke(cr);
paint_hex(cr, app_theme.text);
cairo_set_line_width(cr, 1.6);
cairo_move_to(cr, MATH_PAD, board_px_h() - MATH_PAD);
cairo_line_to(cr, board_px_w() - MATH_PAD, board_px_h() - MATH_PAD);
cairo_move_to(cr, MATH_PAD, board_px_h() - MATH_PAD);
cairo_line_to(cr, MATH_PAD, MATH_PAD);
cairo_stroke(cr);
if (b->nf >= 2) {
double x0, y0, x1, y1;
paint_hex(cr, app_theme.accent);
cairo_set_line_width(cr, 2);
board_to_screen(b->fx[0], b->fy[0], &x0, &y0);
board_to_screen(b->fx[1], b->fy[1], &x1, &y1);
cairo_move_to(cr, x0, y0);
cairo_line_to(cr, x1, y1);
if (b->nf >= 3) {
double x2, y2;
board_to_screen(b->fx[2], b->fy[2], &x2, &y2);
cairo_move_to(cr, x0, y0);
cairo_line_to(cr, x2, y2);
}
cairo_stroke(cr);
}
for (i = 0; i < b->nf && i < 4; i++) {
double x, y;
board_to_screen(b->fx[i], b->fy[i], &x, &y);
paint_hex(cr, app_theme.accent);
cairo_arc(cr, x, y, 5.5, 0, 2 * G_PI);
cairo_fill(cr);
paint_hex(cr, app_theme.text);
cairo_set_font_size(cr, 13);
cairo_move_to(cr, x + 7, y - 7);
cairo_show_text(cr, names[i]);
}
for (i = 0; i < b->n; i++) {
double x, y;
char label[8];
board_to_screen(b->ux[i], b->uy[i], &x, &y);
if (b->checked)
paint_hex(cr, b->correct ? app_theme.success : app_theme.error);
else
paint_hex(cr, app_theme.success);
cairo_arc(cr, x, y, 5.5, 0, 2 * G_PI);
cairo_fill(cr);
paint_hex(cr, app_theme.text);
g_snprintf(label, sizeof label, "%d", i + 1);
cairo_set_font_size(cr, 13);
cairo_move_to(cr, x + 7, y + 14);
cairo_show_text(cr, label);
}
}
static int board_has_fixed(MathBoard *b, int x, int y) {
for (int i = 0; i < b->nf; i++)
if (b->fx[i] == x && b->fy[i] == y)
return 1;
return 0;
}
static void board_pressed(GtkGestureClick *gesture, int n_press, double x, double y,
gpointer data) {
MathBoard *b = data;
int gx, gy, i;
(void)gesture;
(void)n_press;
if (!board_snap(x, y, &gx, &gy) || board_has_fixed(b, gx, gy))
return;
for (i = 0; i < b->n; i++) {
if (b->ux[i] == gx && b->uy[i] == gy) {
memmove(&b->ux[i], &b->ux[i + 1], (b->n - i - 1) * sizeof(int));
memmove(&b->uy[i], &b->uy[i + 1], (b->n - i - 1) * sizeof(int));
b->n--;
b->checked = 0;
gtk_widget_queue_draw(b->area);
return;
}
}
if (b->n >= b->need || b->n >= MATH_PTS)
return;
b->ux[b->n] = gx;
b->uy[b->n] = gy;
b->n++;
b->checked = 0;
gtk_widget_queue_draw(b->area);
}
static void board_undo(GtkButton *button, gpointer data) {
MathBoard *b = data;
(void)button;
if (b->n <= 0)
return;
b->n--;
b->checked = 0;
gtk_widget_queue_draw(b->area);
}
static void board_clear(GtkButton *button, gpointer data) {
MathBoard *b = data;
(void)button;
b->n = 0;
b->checked = 0;
gtk_widget_queue_draw(b->area);
}
static void math_ex_check(GtkButton *button, gpointer data) {
MathExCtx *ctx = data;
int ok = 0;
(void)button;
for (int i = 0; i < ctx->n; i++) {
int good = 0;
if (ctx->boards[i]) {
MathBoard *b = ctx->boards[i];
good = math_draw_ok(b->spec, b->ux, b->uy, b->n);
b->checked = 1;
b->correct = good;
gtk_widget_queue_draw(b->area);
} else if (ctx->entries[i]) {
const char *text = gtk_editable_get_text(GTK_EDITABLE(ctx->entries[i]));
gtk_widget_remove_css_class(ctx->entries[i], "ok");
gtk_widget_remove_css_class(ctx->entries[i], "wrong");
good = math_answer_ok(ctx->items[i].answer, text);
gtk_widget_add_css_class(ctx->entries[i], good ? "ok" : "wrong");
}
if (good)
ok++;
if (ctx->hints[i])
gtk_widget_set_visible(ctx->hints[i], TRUE);
}
if (ok == ctx->n) {
set_feedback(ctx->feedback, TRUE, tr("feedback_ok"));
math_mark_done(ctx->track, ctx->lesson_id);
} else {
set_feedback(ctx->feedback, FALSE, tr("feedback_retry_short"));
}
}
GtkWidget *math_practice_page(int year, int lesson_id, const char *back_page,
const char *title_key, const char *heading_key,
const MathItem *items, int n) {
GtkWidget *page;
GtkWidget *scroll;
GtkWidget *body;
GtkWidget *check;
GtkWidget *head;
GtkWidget *intro;
MathExCtx *ctx = g_new0(MathExCtx, 1);
math_ensure();
if (year < 0 || year > 4)
year = 0;
page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_margin_start(page, 32);
gtk_widget_set_margin_end(page, 32);
gtk_widget_set_margin_top(page, 24);
gtk_widget_set_margin_bottom(page, 24);
gtk_box_append(GTK_BOX(page), top_bar(back_page, title_key, NULL));
scroll = gtk_scrolled_window_new();
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_vexpand(scroll, TRUE);
gtk_widget_set_margin_top(scroll, 12);
gtk_box_append(GTK_BOX(page), scroll);
body = gtk_box_new(GTK_ORIENTATION_VERTICAL, 16);
gtk_widget_set_hexpand(body, TRUE);
gtk_widget_set_margin_start(body, 4);
gtk_widget_set_margin_end(body, 4);
gtk_widget_set_margin_bottom(body, 12);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), body);
head = gtk_label_new(NULL);
i18n_bind(head, heading_key, 0);
gtk_widget_set_halign(head, GTK_ALIGN_START);
gtk_label_set_wrap(GTK_LABEL(head), TRUE);
gtk_widget_add_css_class(head, "quiz-heading");
gtk_box_append(GTK_BOX(body), head);
intro = gtk_label_new(NULL);
i18n_bind(intro, "math_quiz_intro", 0);
gtk_widget_set_halign(intro, GTK_ALIGN_START);
gtk_label_set_wrap(GTK_LABEL(intro), TRUE);
gtk_label_set_xalign(GTK_LABEL(intro), 0);
gtk_widget_add_css_class(intro, "quiz-intro");
gtk_box_append(GTK_BOX(body), intro);
ctx->items = items;
ctx->n = n;
ctx->track = &math_tracks[year];
ctx->lesson_id = lesson_id;
ctx->entries = g_new0(GtkWidget *, n);
ctx->boards = g_new0(MathBoard *, n);
ctx->hints = g_new0(GtkWidget *, n);
for (int i = 0; i < n; i++) {
GtkWidget *card = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
GtkWidget *prompt = gtk_label_new(items[i].prompt);
GtkWidget *hint = gtk_label_new(items[i].hint);
gtk_widget_set_halign(card, GTK_ALIGN_START);
gtk_box_append(GTK_BOX(body), card);
gtk_label_set_wrap(GTK_LABEL(prompt), TRUE);
gtk_label_set_xalign(GTK_LABEL(prompt), 0);
gtk_widget_set_halign(prompt, GTK_ALIGN_START);
gtk_box_append(GTK_BOX(card), prompt);
if (math_is_draw(items[i].answer)) {
MathBoard *b = g_new0(MathBoard, 1);
GtkWidget *area = gtk_drawing_area_new();
GtkGesture *click = gtk_gesture_click_new();
GtkWidget *tools = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
GtkWidget *undo = gtk_button_new();
GtkWidget *clear = gtk_button_new();
GtkWidget *note = gtk_label_new(NULL);
b->spec = items[i].answer;
b->need = math_draw_need(items[i].answer);
if (b->need < 1)
b->need = 1;
b->nf = math_draw_fixed(items[i].answer, b->fx, b->fy, 8);
b->area = area;
ctx->boards[i] = b;
gtk_widget_set_size_request(area, board_px_w(), board_px_h());
gtk_widget_set_halign(area, GTK_ALIGN_START);
gtk_drawing_area_set_content_width(GTK_DRAWING_AREA(area), board_px_w());
gtk_drawing_area_set_content_height(GTK_DRAWING_AREA(area), board_px_h());
gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(area), board_draw, b, NULL);
gtk_widget_add_controller(area, GTK_EVENT_CONTROLLER(click));
g_signal_connect(click, "pressed", G_CALLBACK(board_pressed), b);
gtk_box_append(GTK_BOX(card), area);
i18n_bind(note, "math_draw_hint", 0);
gtk_label_set_wrap(GTK_LABEL(note), TRUE);
gtk_widget_add_css_class(note, "hint");
gtk_widget_set_halign(note, GTK_ALIGN_START);
gtk_box_append(GTK_BOX(card), note);
i18n_bind(undo, "math_undo", 1);
i18n_bind(clear, "math_clear", 1);
g_signal_connect(undo, "clicked", G_CALLBACK(board_undo), b);
g_signal_connect(clear, "clicked", G_CALLBACK(board_clear), b);
gtk_box_append(GTK_BOX(tools), undo);
gtk_box_append(GTK_BOX(tools), clear);
gtk_box_append(GTK_BOX(card), tools);
} else {
GtkWidget *entry = gtk_entry_new();
gtk_entry_set_placeholder_text(GTK_ENTRY(entry), tr("math_answer_ph"));
gtk_widget_set_hexpand(entry, TRUE);
gtk_widget_set_size_request(entry, 280, -1);
gtk_box_append(GTK_BOX(card), entry);
ctx->entries[i] = entry;
}
gtk_label_set_wrap(GTK_LABEL(hint), TRUE);
gtk_label_set_xalign(GTK_LABEL(hint), 0);
gtk_widget_set_halign(hint, GTK_ALIGN_START);
gtk_widget_add_css_class(hint, "hint");
gtk_widget_set_visible(hint, FALSE);
gtk_box_append(GTK_BOX(card), hint);
ctx->hints[i] = hint;
}
check = gtk_button_new();
i18n_bind(check, "check", 1);
gtk_widget_add_css_class(check, "btn-primary");
gtk_widget_set_halign(check, GTK_ALIGN_START);
gtk_box_append(GTK_BOX(body), check);
g_signal_connect(check, "clicked", G_CALLBACK(math_ex_check), ctx);
ctx->feedback = gtk_label_new("");
gtk_widget_set_halign(ctx->feedback, GTK_ALIGN_START);
gtk_box_append(GTK_BOX(body), ctx->feedback);
return page;
}

1362
src/math_check.c Normal file

File diff suppressed because it is too large Load diff

176
src/math_i18n.inc Normal file
View file

@ -0,0 +1,176 @@
{"mat_years_sub", "Opakování základní školy a čtyři ročníky gymnázia.", "Elementary review and four years of gymnasium mathematics."},
{"math_quiz_intro", "Napište výsledek. Zlomky jako 3/4, desetinnou čárku i tečku, π jako pi, odmocninu jako sqrt(2) nebo √2. Více čísel oddělte středníkem.", "Write the result. Fractions as 3/4, a decimal comma or point, π as pi, a root as sqrt(2) or √2. Separate several numbers with a semicolon."},
{"math_answer_ph", "výsledek", "result"},
{"math_undo", "Zpět", "Undo"},
{"math_clear", "Smazat body", "Clear points"},
{"math_draw_hint", "Klepněte na mřížku. Osa y směřuje nahoru.", "Tap the grid. The y-axis points up."},
{"mat0_year", "Opakování základní školy", "Elementary-school review"},
{"mat0_sub", "Pořadí operací, zlomky, procenta, rovnice a rýsování.", "Order of operations, fractions, percentages, equations and a construction."},
{"mat0_unit1", "Pořadí operací", "Order of operations"},
{"mat0_unit1_sub", "Závorky, násobení a dělení, sčítání a odčítání.", "Brackets, then multiplication and division, then addition and subtraction."},
{"mat0_ex1_title", "Příklady", "Practice"},
{"mat0_quiz1_head", "Pořadí operací", "Order of operations"},
{"mat0_unit2", "Zlomky", "Fractions"},
{"mat0_unit2_sub", "Sčítání, odčítání, násobení a dělení zlomků.", "Adding, subtracting, multiplying and dividing fractions."},
{"mat0_ex2_title", "Příklady", "Practice"},
{"mat0_quiz2_head", "Zlomky", "Fractions"},
{"mat0_unit3", "Desetinná čísla", "Decimals"},
{"mat0_unit3_sub", "Sčítání, násobení a dělení desetinných čísel.", "Adding, multiplying and dividing decimals."},
{"mat0_ex3_title", "Příklady", "Practice"},
{"mat0_quiz3_head", "Desetinná čísla", "Decimals"},
{"mat0_unit4", "Procenta", "Percentages"},
{"mat0_unit4_sub", "Procento ze základu, počet procent a cena po slevě.", "A percentage of a base, the rate, and a price after a discount."},
{"mat0_ex4_title", "Příklady", "Practice"},
{"mat0_quiz4_head", "Procenta", "Percentages"},
{"mat0_unit5", "Poměr a měřítko", "Ratio and scale"},
{"mat0_unit5_sub", "Dělení v poměru, úměra a měřítko mapy.", "Sharing in a ratio, proportion and the scale of a map."},
{"mat0_ex5_title", "Příklady", "Practice"},
{"mat0_quiz5_head", "Poměr a měřítko", "Ratio and scale"},
{"mat0_unit6", "Záporná čísla", "Negative numbers"},
{"mat0_unit6_sub", "Sčítání, násobení a absolutní hodnota.", "Addition, multiplication and absolute value."},
{"mat0_ex6_title", "Příklady", "Practice"},
{"mat0_quiz6_head", "Záporná čísla", "Negative numbers"},
{"mat0_unit7", "Lineární rovnice", "Linear equations"},
{"mat0_unit7_sub", "Ekvivalentní úpravy a jednoduché rovnice.", "Equivalent steps and simple equations."},
{"mat0_ex7_title", "Příklady", "Practice"},
{"mat0_quiz7_head", "Lineární rovnice", "Linear equations"},
{"mat0_unit8", "Obvod, obsah a čtverec", "Perimeter, area and a square"},
{"mat0_unit8_sub", "Obdélník, trojúhelník a doplnění čtverce na mřížce.", "A rectangle, a triangle, and completing a square on the grid."},
{"mat0_ex8_title", "Příklady", "Practice"},
{"mat0_quiz8_head", "Obvod, obsah a čtverec", "Perimeter, area and a square"},
{"mat_year1", "1. ročník", "Year 1"},
{"mat_sub", "Dělitelnost, výrazy, rovnice, planimetrie a konstrukce.", "Divisibility, expressions, equations, plane figures and a construction."},
{"mat_unit1", "Dělitelnost a prvočísla", "Divisibility and primes"},
{"mat_unit1_sub", "Největší společný dělitel, násobek a rozklad.", "Highest common factor, lowest common multiple and factorisation."},
{"mat_ex1_title", "Příklady", "Practice"},
{"mat_quiz1_head", "Dělitelnost a prvočísla", "Divisibility and primes"},
{"mat_unit2", "Mocniny a odmocniny", "Powers and roots"},
{"mat_unit2_sub", "Mocniny se stejným základem a druhá odmocnina.", "Powers with the same base and the square root."},
{"mat_ex2_title", "Příklady", "Practice"},
{"mat_quiz2_head", "Mocniny a odmocniny", "Powers and roots"},
{"mat_unit3", "Algebraické výrazy", "Algebraic expressions"},
{"mat_unit3_sub", "Dosazení, druhá mocnina součtu a rozklad rozdílu čtverců.", "Substitution, the square of a sum and a difference of squares."},
{"mat_ex3_title", "Příklady", "Practice"},
{"mat_quiz3_head", "Algebraické výrazy", "Algebraic expressions"},
{"mat_unit4", "Lineární rovnice s úpravami", "Linear equations"},
{"mat_unit4_sub", "Rovnice s koeficientem, závorkou a zlomkem.", "Equations with a coefficient, a bracket and a fraction."},
{"mat_ex4_title", "Příklady", "Practice"},
{"mat_quiz4_head", "Lineární rovnice s úpravami", "Linear equations"},
{"mat_unit5", "Slovní úlohy", "Word problems"},
{"mat_unit5_sub", "Dráha, společná práce, směs a sleva.", "Distance, joint work, a mixture and a discount."},
{"mat_ex5_title", "Příklady", "Practice"},
{"mat_quiz5_head", "Slovní úlohy", "Word problems"},
{"mat_unit6", "Planimetrie", "Plane figures"},
{"mat_unit6_sub", "Úhly trojúhelníku, vnější úhel a pravidelný mnohoúhelník.", "Angles of a triangle, the exterior angle and a regular polygon."},
{"mat_ex6_title", "Příklady", "Practice"},
{"mat_quiz6_head", "Planimetrie", "Plane figures"},
{"mat_unit7", "Obvody a obsahy", "Perimeter and area"},
{"mat_unit7_sub", "Kružnice, lichoběžník a Pythagorova věta.", "The circle, the trapezium and Pythagoras."},
{"mat_ex7_title", "Příklady", "Practice"},
{"mat_quiz7_head", "Obvody a obsahy", "Perimeter and area"},
{"mat_unit8", "Konstrukce trojúhelníku", "Constructing a triangle"},
{"mat_unit8_sub", "Vzdálenost bodů a trojúhelník daný třemi stranami.", "The distance between points and a triangle given by three sides."},
{"mat_ex8_title", "Příklady", "Practice"},
{"mat_quiz8_head", "Konstrukce trojúhelníku", "Constructing a triangle"},
{"mat_year2", "2. ročník", "Year 2"},
{"mat2_sub", "Funkce, soustavy, kvadratické rovnice, goniometrie a kružnice.", "Functions, systems, quadratic equations, trigonometry and circles."},
{"mat2_unit1", "Lineární funkce", "Linear functions"},
{"mat2_unit1_sub", "Předpis, průsečík s osou y, směrnice a nulový bod.", "The formula, the y-intercept, the slope and a root."},
{"mat2_ex1_title", "Příklady", "Practice"},
{"mat2_quiz1_head", "Lineární funkce", "Linear functions"},
{"mat2_unit2", "Soustavy rovnic", "Systems of equations"},
{"mat2_unit2_sub", "Dvě lineární rovnice o dvou neznámých.", "Two linear equations in two unknowns."},
{"mat2_ex2_title", "Příklady", "Practice"},
{"mat2_quiz2_head", "Soustavy rovnic", "Systems of equations"},
{"mat2_unit3", "Kvadratické rovnice", "Quadratic equations"},
{"mat2_unit3_sub", "Rozklad, diskriminant a dvojnásobný kořen.", "Factoring, the discriminant and a double root."},
{"mat2_ex3_title", "Příklady", "Practice"},
{"mat2_quiz3_head", "Kvadratické rovnice", "Quadratic equations"},
{"mat2_unit4", "Lomené výrazy", "Rational expressions"},
{"mat2_unit4_sub", "Krácení, definiční obor a násobení zlomků s proměnnou.", "Cancelling, the domain and multiplying algebraic fractions."},
{"mat2_ex4_title", "Příklady", "Practice"},
{"mat2_quiz4_head", "Lomené výrazy", "Rational expressions"},
{"mat2_unit5", "Mocniny s racionálním exponentem", "Rational exponents"},
{"mat2_unit5_sub", "Odmocniny, součin mocnin a záporný exponent.", "Roots, a product of powers and a negative exponent."},
{"mat2_ex5_title", "Příklady", "Practice"},
{"mat2_quiz5_head", "Mocniny s racionálním exponentem", "Rational exponents"},
{"mat2_unit6", "Goniometrie pravoúhlého trojúhelníku", "Right-triangle trigonometry"},
{"mat2_unit6_sub", "Sinus, kosinus a tangens ostrého úhlu.", "Sine, cosine and tangent of an acute angle."},
{"mat2_ex6_title", "Příklady", "Practice"},
{"mat2_quiz6_head", "Goniometrie pravoúhlého trojúhelníku", "Right-triangle trigonometry"},
{"mat2_unit7", "Pythagorova a Eukleidovy věty", "Pythagoras and Euclid"},
{"mat2_unit7_sub", "Přepona, obsah čtverce nad ní a úseky přepony.", "The hypotenuse, the square on it, and the projections on it."},
{"mat2_ex7_title", "Příklady", "Practice"},
{"mat2_quiz7_head", "Pythagorova a Eukleidovy věty", "Pythagoras and Euclid"},
{"mat2_unit8", "Kružnice a Thaletova věta", "Circles and Thales"},
{"mat2_unit8_sub", "Obvodový a středový úhel, délka oblouku a pravý úhel nad průměrem.", "An inscribed and a central angle, arc length, and the right angle in a semicircle."},
{"mat2_ex8_title", "Příklady", "Practice"},
{"mat2_quiz8_head", "Kružnice a Thaletova věta", "Circles and Thales"},
{"mat_year3", "3. ročník", "Year 3"},
{"mat3_sub", "Logaritmus, posloupnosti, pravděpodobnost a stereometrie.", "Logarithms, sequences, probability and solid geometry."},
{"mat3_unit1", "Kvadratická funkce", "The quadratic function"},
{"mat3_unit1_sub", "Vrchol, kořeny a hodnota paraboly.", "The vertex, the roots and a value of a parabola."},
{"mat3_ex1_title", "Příklady", "Practice"},
{"mat3_quiz1_head", "Kvadratická funkce", "The quadratic function"},
{"mat3_unit2", "Exponenciální funkce a logaritmus", "Exponentials and logarithms"},
{"mat3_unit2_sub", "Mocnina se základem a logaritmus jako její opak.", "A power and the logarithm as its inverse."},
{"mat3_ex2_title", "Příklady", "Practice"},
{"mat3_quiz2_head", "Exponenciální funkce a logaritmus", "Exponentials and logarithms"},
{"mat3_unit3", "Goniometrické funkce", "Trigonometric functions"},
{"mat3_unit3_sub", "Sinus a kosinus na jednotkové kružnici.", "Sine and cosine on the unit circle."},
{"mat3_ex3_title", "Příklady", "Practice"},
{"mat3_quiz3_head", "Goniometrické funkce", "Trigonometric functions"},
{"mat3_unit4", "Sinová a kosinová věta", "The sine and cosine rules"},
{"mat3_unit4_sub", "Obecný trojúhelník, poloměr kružnice a obsah.", "A general triangle, the circumradius and the area."},
{"mat3_ex4_title", "Příklady", "Practice"},
{"mat3_quiz4_head", "Sinová a kosinová věta", "The sine and cosine rules"},
{"mat3_unit5", "Posloupnosti a řady", "Sequences and series"},
{"mat3_unit5_sub", "Aritmetická a geometrická posloupnost a nekonečná řada.", "An arithmetic and a geometric sequence, and an infinite series."},
{"mat3_ex5_title", "Příklady", "Practice"},
{"mat3_quiz5_head", "Posloupnosti a řady", "Sequences and series"},
{"mat3_unit6", "Kombinatorika", "Combinatorics"},
{"mat3_unit6_sub", "Permutace, variace a kombinace.", "Permutations, variations and combinations."},
{"mat3_ex6_title", "Příklady", "Practice"},
{"mat3_quiz6_head", "Kombinatorika", "Combinatorics"},
{"mat3_unit7", "Pravděpodobnost", "Probability"},
{"mat3_unit7_sub", "Klasická pravděpodobnost, kostka, mince a karty.", "Classical probability with a die, a coin and cards."},
{"mat3_ex7_title", "Příklady", "Practice"},
{"mat3_quiz7_head", "Pravděpodobnost", "Probability"},
{"mat3_unit8", "Stereometrie", "Solid geometry"},
{"mat3_unit8_sub", "Krychle, válec, koule a jehlan.", "The cube, the cylinder, the sphere and a pyramid."},
{"mat3_ex8_title", "Příklady", "Practice"},
{"mat3_quiz8_head", "Stereometrie", "Solid geometry"},
{"mat_year4", "4. ročník", "Year 4"},
{"mat4_sub", "Analytická geometrie, derivace, integrál a maturitní opakování.", "Coordinate geometry, derivatives, integrals and a maturita review."},
{"mat4_unit1", "Vektory", "Vectors"},
{"mat4_unit1_sub", "Velikost, součet, skalární součin a koncový bod.", "Magnitude, a sum, the dot product and an endpoint."},
{"mat4_ex1_title", "Příklady", "Practice"},
{"mat4_quiz1_head", "Vektory", "Vectors"},
{"mat4_unit2", "Analytická přímka", "The line in the plane"},
{"mat4_unit2_sub", "Směrnice, úsek, parametrické vyjádření a vzdálenost od přímky.", "Slope, intercept, a parametric equation and the distance from a line."},
{"mat4_ex2_title", "Příklady", "Practice"},
{"mat4_quiz2_head", "Analytická přímka", "The line in the plane"},
{"mat4_unit3", "Kuželosečky", "Conic sections"},
{"mat4_unit3_sub", "Kružnice a elipsa v osové poloze.", "A circle and an ellipse aligned with the axes."},
{"mat4_ex3_title", "Příklady", "Practice"},
{"mat4_quiz3_head", "Kuželosečky", "Conic sections"},
{"mat4_unit4", "Komplexní čísla", "Complex numbers"},
{"mat4_unit4_sub", "Součet, součin, absolutní hodnota a mocnina i.", "A sum, a product, the modulus and a power of i."},
{"mat4_ex4_title", "Příklady", "Practice"},
{"mat4_quiz4_head", "Komplexní čísla", "Complex numbers"},
{"mat4_unit5", "Derivace", "The derivative"},
{"mat4_unit5_sub", "Derivace mocniny, tečna a derivace sinu.", "The derivative of a power, a tangent and the derivative of sine."},
{"mat4_ex5_title", "Příklady", "Practice"},
{"mat4_quiz5_head", "Derivace", "The derivative"},
{"mat4_unit6", "Průběh funkce", "The shape of a function"},
{"mat4_unit6_sub", "Stacionární body, lokální extrémy a nulová derivace.", "Stationary points, local extrema and a zero derivative."},
{"mat4_ex6_title", "Příklady", "Practice"},
{"mat4_quiz6_head", "Průběh funkce", "The shape of a function"},
{"mat4_unit7", "Integrál", "The integral"},
{"mat4_unit7_sub", "Určitý integrál mocniny jako obsah pod grafem.", "The definite integral of a power as the area under the graph."},
{"mat4_ex7_title", "Příklady", "Practice"},
{"mat4_quiz7_head", "Integrál", "The integral"},
{"mat4_unit8", "Maturitní mix", "Maturita mix"},
{"mat4_unit8_sub", "Logaritmus, kombinace, vektor, zlomek a kvadratická rovnice.", "A logarithm, a combination, a vector, a fraction and a quadratic equation."},
{"mat4_ex8_title", "Příklady", "Practice"},
{"mat4_quiz8_head", "Maturitní mix", "Maturita mix"},

View file

@ -61,6 +61,7 @@ void load_progress(void) {
lit_load_progress();
sci_load_progress();
fyz_load_progress();
math_load_progress();
}
/* Redraw the exercise rails of every unit that already has a page. */

View file

@ -23,7 +23,7 @@ static GPtrArray *search_items;
static gboolean subject_open(int i) {
return i == 0 || i == NET_SUBJ || i == HW_SUBJ || i == CZ_SUBJ
|| i == ON_SUBJ || i == PHY_SUBJ || i == SCI_SUBJ;
|| i == ON_SUBJ || i == MATH_SUBJ || i == PHY_SUBJ || i == SCI_SUBJ;
}
static const char *subject_page(int i) {
@ -37,6 +37,8 @@ static const char *subject_page(int i) {
return "czechmap";
if (i == ON_SUBJ)
return "onyears";
if (i == MATH_SUBJ)
return "matyears";
if (i == PHY_SUBJ)
return "fyzyears";
if (i == SCI_SUBJ)
@ -275,6 +277,47 @@ static void catalog_rebuild(void) {
g_snprintf(page, sizeof(page), "lit4ex%d", i);
catalog_add(tr(key), tr("search_exercise"), page, FALSE, 3, "kviz quiz");
}
catalog_add_key("Matematika", "search_lesson", "matyears", FALSE, 1, "matematika");
catalog_add_key("mat0_year", "search_lesson", "mat0map", FALSE, 1, "matematika zakladka");
catalog_add_key("mat_year1", "search_lesson", "matmap", FALSE, 1, "matematika rocnik");
catalog_add_key("mat_year2", "search_lesson", "mat2map", FALSE, 1, "matematika rocnik");
catalog_add_key("mat_year3", "search_lesson", "mat3map", FALSE, 1, "matematika rocnik");
catalog_add_key("mat_year4", "search_lesson", "mat4map", FALSE, 1, "matematika rocnik");
for (i = 1; i <= MATH_N; i++) {
g_snprintf(key, sizeof(key), "mat0_unit%d", i);
g_snprintf(page, sizeof(page), "mat0unit%d", i);
catalog_add_key(key, "search_lesson", page, FALSE, 2, "matematika");
g_snprintf(page, sizeof(page), "mat0ex%d", i);
catalog_add(tr(key), tr("search_exercise"), page, FALSE, 3, "priklady");
}
for (i = 1; i <= MATH_N; i++) {
g_snprintf(key, sizeof(key), "mat_unit%d", i);
g_snprintf(page, sizeof(page), "matunit%d", i);
catalog_add_key(key, "search_lesson", page, FALSE, 2, "matematika");
g_snprintf(page, sizeof(page), "matex%d", i);
catalog_add(tr(key), tr("search_exercise"), page, FALSE, 3, "priklady");
}
for (i = 1; i <= MATH_N; i++) {
g_snprintf(key, sizeof(key), "mat2_unit%d", i);
g_snprintf(page, sizeof(page), "mat2unit%d", i);
catalog_add_key(key, "search_lesson", page, FALSE, 2, "matematika");
g_snprintf(page, sizeof(page), "mat2ex%d", i);
catalog_add(tr(key), tr("search_exercise"), page, FALSE, 3, "priklady");
}
for (i = 1; i <= MATH_N; i++) {
g_snprintf(key, sizeof(key), "mat3_unit%d", i);
g_snprintf(page, sizeof(page), "mat3unit%d", i);
catalog_add_key(key, "search_lesson", page, FALSE, 2, "matematika");
g_snprintf(page, sizeof(page), "mat3ex%d", i);
catalog_add(tr(key), tr("search_exercise"), page, FALSE, 3, "priklady");
}
for (i = 1; i <= MATH_N; i++) {
g_snprintf(key, sizeof(key), "mat4_unit%d", i);
g_snprintf(page, sizeof(page), "mat4unit%d", i);
catalog_add_key(key, "search_lesson", page, FALSE, 2, "matematika");
g_snprintf(page, sizeof(page), "mat4ex%d", i);
catalog_add(tr(key), tr("search_exercise"), page, FALSE, 3, "priklady");
}
catalog_add_key("fyz_year1", "search_lesson", "fyzmap", FALSE, 1, "fyzika rocnik");
catalog_add_key("fyz_year2", "search_lesson", "fyz2map", FALSE, 1, "fyzika rocnik");
catalog_add_key("fyz_year3", "search_lesson", "fyz3map", FALSE, 1, "fyzika rocnik");

View file

@ -79,6 +79,8 @@ void refresh_stats_ui(void) {
progress_for_sci(&all);
else if (s == PHY_SUBJ)
progress_for_fyz(&all);
else if (s == MATH_SUBJ)
progress_for_math(&all);
else
progress_for_units(sub_unit_start[s], sub_unit_count[s], &all);
}
@ -110,6 +112,7 @@ void refresh_stats_ui(void) {
gboolean has_content = (s == NET_SUBJ) || (s == HW_SUBJ)
|| (s == ON_SUBJ) || (s == CZ_SUBJ)
|| (s == SCI_SUBJ) || (s == PHY_SUBJ)
|| (s == MATH_SUBJ)
|| sub_unit_count[s] > 0;
if (!count || !bar)
@ -128,6 +131,8 @@ void refresh_stats_ui(void) {
progress_for_sci(&sp);
else if (s == PHY_SUBJ)
progress_for_fyz(&sp);
else if (s == MATH_SUBJ)
progress_for_math(&sp);
else
progress_for_units(sub_unit_start[s], sub_unit_count[s], &sp);
if (has_content) {
@ -399,6 +404,7 @@ GtkWidget *build_stats_page(void) {
gboolean has_units = (s == NET_SUBJ) || (s == HW_SUBJ)
|| (s == CZ_SUBJ) || (s == ON_SUBJ)
|| (s == SCI_SUBJ) || (s == PHY_SUBJ)
|| (s == MATH_SUBJ)
|| sub_unit_count[s] > 0;
gboolean has_deutsch_units = sub_unit_count[s] > 0;

View file

@ -5,7 +5,8 @@
/* ------------------------------------------------------------------ */
/* Index 0 (Deutsch), NET_SUBJ (networks), HW_SUBJ (hardware), CZ_SUBJ
* (Czech), ON_SUBJ (civics), PHY_SUBJ (physics) and SCI_SUBJ
* (Czech), ON_SUBJ (civics), MATH_SUBJ (mathematics), PHY_SUBJ
* (physics) and SCI_SUBJ
* (natural sciences) are unlocked. */
const char *sub_keys[NUM_SUBJECTS] = {
"Deutsch",
@ -155,7 +156,7 @@ void draw_czech_flag(GtkDrawingArea *area, cairo_t *cr,
static gboolean subject_is_open(int i) {
return i == 0 || i == NET_SUBJ || i == HW_SUBJ || i == CZ_SUBJ
|| i == ON_SUBJ || i == PHY_SUBJ || i == SCI_SUBJ;
|| i == ON_SUBJ || i == MATH_SUBJ || i == PHY_SUBJ || i == SCI_SUBJ;
}
static GtkWidget *subject_icon(int i) {
@ -178,6 +179,9 @@ static GtkWidget *subject_icon(int i) {
else if (i == ON_SUBJ)
gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(area),
draw_civics_icon, NULL, NULL);
else if (i == MATH_SUBJ)
gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(area),
draw_math_icon, NULL, NULL);
else if (i == PHY_SUBJ)
gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(area),
draw_physics_icon, NULL, NULL);
@ -200,6 +204,8 @@ static const char *subject_target(int i) {
return "czechmap";
if (i == ON_SUBJ)
return "onyears";
if (i == MATH_SUBJ)
return "matyears";
if (i == PHY_SUBJ)
return "fyzyears";
if (i == SCI_SUBJ)

View file

@ -434,5 +434,6 @@ void apply_theme(void) {
lit_rail_theme_reset();
sci_rail_theme_reset();
fyz_rail_theme_reset();
math_rail_theme_reset();
book_rail_theme_reset();
}

View file

@ -36,6 +36,11 @@ char *app_progress_fyz;
char *app_progress_fyz2;
char *app_progress_fyz3;
char *app_progress_fyz4;
char *app_progress_mat0;
char *app_progress_mat;
char *app_progress_mat2;
char *app_progress_mat3;
char *app_progress_mat4;
static char *dup_dirname(const char *path) {
char *tmp = g_strdup(path);
@ -188,6 +193,11 @@ static void set_progress_paths(const char *write_root) {
g_free(app_progress_fyz2);
g_free(app_progress_fyz3);
g_free(app_progress_fyz4);
g_free(app_progress_mat0);
g_free(app_progress_mat);
g_free(app_progress_mat2);
g_free(app_progress_mat3);
g_free(app_progress_mat4);
app_progress_dir = g_build_filename(write_root, PROGRESS_DIR_NAME, NULL);
app_progress_u1 = g_build_filename(app_progress_dir, "unit1.conf", NULL);
@ -212,6 +222,11 @@ static void set_progress_paths(const char *write_root) {
app_progress_fyz2 = g_build_filename(app_progress_dir, "fyz2.conf", NULL);
app_progress_fyz3 = g_build_filename(app_progress_dir, "fyz3.conf", NULL);
app_progress_fyz4 = g_build_filename(app_progress_dir, "fyz4.conf", NULL);
app_progress_mat0 = g_build_filename(app_progress_dir, "mat0.conf", NULL);
app_progress_mat = g_build_filename(app_progress_dir, "mat.conf", NULL);
app_progress_mat2 = g_build_filename(app_progress_dir, "mat2.conf", NULL);
app_progress_mat3 = g_build_filename(app_progress_dir, "mat3.conf", NULL);
app_progress_mat4 = g_build_filename(app_progress_dir, "mat4.conf", NULL);
}
/* Resolve read-only asset root, chdir there, and point progress at a writable