Skip to content

Commit 436b7da

Browse files
Added main README and method-rs/README
1 parent b2ecd3d commit 436b7da

File tree

5 files changed

+292
-86
lines changed

5 files changed

+292
-86
lines changed

README.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# Blocks in 3D
2+
3+
În acest proiect am implementat o bibliotecă numită `libchunk`, care permite
4+
modelarea lumilor tri-dimensionale folosind structuri cubice
5+
(idee inspirată din Minecraft).
6+
7+
Proiectul reprezintă un bun exercițiu practic pentru a înțelege:
8+
- matricile și operațiile pe pointeri în C
9+
- structurile vectoriale și referințele mutabile în Rust
10+
11+
12+
În implementarea în Rust, mi-am aprofundat abilitățile de a scrie **teste unitare parametrizate**
13+
cu [`rstest`](https://crates.io/crates/rstest) și am creat de la zero un script
14+
personalizat de `checker` pentru validarea rezultatelor.
15+
16+
## Structura proiectului
17+
18+
- `chunk_gen`
19+
- Validare punct în spațiu
20+
- Amplasare bloc
21+
- Generare cuboid/sferă
22+
- `chunk_process`:
23+
- Algoritmi de umplere
24+
- Creare înveliș
25+
- `chunk_transform`:
26+
- Rotație 90° pe axa Oy (plan xOz)
27+
- TODO: gravitație
28+
- `chunk_compress`: compresia/decompresia matricii 3D într-un șir de octeți
29+
30+
31+
32+
## 📌 Amplasare bloc
33+
34+
Funcția modifică tipul blocului la coordonatele **(x, y, z)** primite.
35+
36+
Pentru modularitate, am creat o funcție auxiliară care returnează **true** dacă
37+
coordonatele sunt în interiorul chunk-ului și **false** în caz contrar.
38+
39+
40+
`chunk_place_block` si `is_inside` sunt apelate pe tot parcursul proiectului
41+
pentru modificarea blocurilor, respectiv verificarea coordonatelor.
42+
43+
44+
## 📦 Creare cuboid
45+
46+
Funcția primește 2 colțuri opuse ale unui paralelipiped dreptunghic.
47+
Coordonatele de iterație pornesc de la minimul fiecărei axe și merg până la maxim.
48+
Astfel, chiar dacă nu conteaza ordinea colțurilor, chunk-ul este parcurs corect.
49+
50+
## 🪩 Creare sferă
51+
52+
Notez cu `r` = întregul cel mai mare (`ceil`) la care se rotunjeste raza.
53+
o raza de **-1.2** se va rotunji la **-2**, iar pentru **1.2** `r` va fi egal cu **2**.
54+
55+
Pentru fiecare offset din intervalul `[-r, r]` pe cele 3 axe, se calculează distanța euclidiană
56+
față de centru. Dacă distanța ≤ raza reală, plasez blocul; altfel, îl ignor.
57+
58+
> **Distanta euclidiana** se calculeaza (folosind functiile `pow` si `sqrt` din biblioteca `math.h`)
59+
> ca suma patratelor diferentelor coordontalor pe cele 3 axe.
60+
>
61+
> `dist(P1, P2) = sqrt((P1.x - P2.x)^ 2 + (P1.y - P2.y)^ 2 + (P1.z - P2.z)^2 )`
62+
63+
## 📦 Înveliș
64+
65+
Dacă `target_block` = `shell_block`, algoritmul clasic ar umple matricea complet.
66+
Pentru a evita această problemă,
67+
folosesc o structura pentru a retine coordonatele **(x, y, z)** de interes,
68+
descoperite in timpul iterarii matricii 3D.
69+
70+
Mai apoi, pentru fiecare astfel de punct,
71+
functia `wrapper` plasează `shell_block` în locul vecinilor diferiți de `target_block`.
72+
73+
Pentru a înveli un bloc, este nevoie de a verifica 8 puncte alăturate:
74+
**(x±1, y±1, z±1)**.
75+
76+
## Fill
77+
78+
Algoritm recursiv de umplere: pornește dintr-un punct și vizitează vecinii de același tip,
79+
fără să mai fie nevoie de memorie suplimentară pentru marcarea blocurilor vizitate.
80+
81+
Cazul în care `target_block` este egal cu `new_block` este tratat separat
82+
înaintea rulării algoritmului de umplere: se va returna matricea ințială.
83+
84+
85+
86+
## ⟳ Rotirea în jurul axei Oy
87+
88+
Se alocă o nouă matrice:
89+
- `new_width = old_depth`
90+
- `new_depth = old_width`
91+
92+
> Practic interschimbă dimensiunile pentru **lățime** și **adâncime**.
93+
94+
95+
Valorile sunt copiate conform regulii:
96+
```c
97+
new_mat[x][y][z] = chunk[z][y][depth - 1 - x]
98+
```
99+
100+
## 📥 Compresie
101+
102+
1. **Serializare**: matricea 3D este aplatizată într-un vector liniar (ordinea **y->z->x**).
103+
2. Vectorul este parcurs pentru a determina secvențe de blocuri identice consecutive:
104+
- Se generează un vector de perechi `(num_occurrences, tip_block)`
105+
- Dacă numărul depășește 4095, se începe un nou **run**
106+
3. Transformarea **run**-urilor în octeți:
107+
- <32 apariții => codificare pe 1 octet
108+
- ≥32 apariții => codificare pe 2 octeți
109+
110+
111+
112+
## 📤 Decompresie
113+
114+
1. Se alocă matricea 3D pe baza dimensiunilor cunoscute.
115+
2. Se itereaza vectorul codificarii (byte cu byte), reconstruind blocurile.
116+
3. Decodificarea se bazează pe marcatorii din biții 5 și 6:
117+
- `0` => **run** pe 1 octet (5 biți pentru numărul de aparitii)
118+
- `10` => **run** pe 2 octeți (11 biți pentru număr de aparitii)
119+
120+
Coordonatele **(x, y, z)** se actualizează în timpul reconstrucției vectorului astfel:
121+
1. Incrementează `x`
122+
2. Dacă `x` depășește limita, resetează `x = 0` și incrementează `z`
123+
3. Dacă `z` depășește limita, resetează `z = 0` și incrementează `y`
124+
4. Dacă `y` depășește limita (caz anormal), funcția se oprește forțat
125+
126+
127+
128+
## Semnificația biților
129+
130+
| Interval n | Primul octet (MSB -> LSB)| Al doilea octet (MSB -> LSB) |
131+
|:--------------|:-------------------------|:-----------------------------|
132+
| 1 ≤ n < 32 | b1 b0 0 n4 n3 n2 n1 n0 | - |
133+
| 32 ≤ n < 4096 | b1 b0 1 0 n11 n10 n9 n8 | n7 n6 n5 n4 n3 n2 n1 n0 |
134+
135+
- `b1 b0` = tipul blocului (cei mai semnificativi 2 biți)
136+
- `0` / `10` = markeri pentru lungimea run-ului (1 sau 2 octeți)
137+
- `nX` = biții care codifică numărul de apariții
138+
- **MSB** = primul bit din stânga al unui octet
139+
- **LSB** = ultimul bit din dreapta al unui octet
140+
141+
Un octet codifică atât tipul blocului, cât și numărul de apariții.
142+
- 1 ≤ n < 32 => codificare pe 1 octet
143+
- 32 ≤ n < 4096 => codificare pe 2 octeți
144+
145+
Bitul 6 este markerul:
146+
- `0` => 1 octet
147+
- `10` => 2 octeți
148+
Tipul blocului ocupă cei mai semnificativi 2 biți.
149+
150+
151+
## Operații pe biți
152+
153+
- Putere a lui 2: `1 << n`
154+
- Setare bit: `byte |= (1 << i)`
155+
- Verificare valoare (0/1) bit:
156+
- In C: `if (byte & (1 << i))`
157+
- In Rust: `if byte & (1 << i) > 0`

method-C/README.md

Lines changed: 2 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
Structura proiectului:
44

55
- `chunk_gen.c`:
6+
- Validare punct în spațiu
67
- Amplasare bloc
78
- Generare cuboid/sferă
89
- `chunk_process.c`:
@@ -15,9 +16,7 @@ Structura proiectului:
1516

1617
## Creare cuboid
1718

18-
Funcția primește 2 colțuri opuse ale unui paralelipiped dreptunghic.
19-
Coordonatele de iterație pornesc de la minimul fiecărei axe și merg până la maxim.
20-
Astfel, chiar dacă ordinea colțurilor este arbitrară, volumul este parcurs corect.
19+
2120

2221
```c
2322
for (int x = MIN(x0, x1); x <= MAX(x0, x1); x++)
@@ -27,16 +26,7 @@ for (int x = MIN(x0, x1); x <= MAX(x0, x1); x++)
2726

2827
## Creare sferă
2928

30-
Notez cu `r` = întregul cel mai mare (`ceil`) la care se rotunjeste raza.
31-
o raza de **-1.2** se va rotunji la **-2**, iar pentru **1.2** `r` va fi egal cu **2**.
32-
33-
Pentru fiecare offset din intervalul `[-r, r]` pe cele 3 axe, se calculează distanța euclidiană
34-
față de centru. Dacă distanța ≤ raza reală, plasez blocul; altfel, îl ignor.
3529

36-
> **Distanta euclidiana** se calculeaza (folosind functiile `pow` si `sqrt` din biblioteca `math.h`)
37-
> ca suma patratelor diferentelor coordontalor pe cele 3 axe.
38-
>
39-
> `dist(P1, P2) = sqrt((P1.x - P2.x)^ 2 + (P1.y - P2.y)^ 2 + (P1.z - P2.z)^2 )`
4030

4131
```c
4232
int r = (int) ceil(radius);
@@ -67,27 +57,6 @@ Pentru a înveli un bloc, este nevoie de a verifica 8 puncte alăturate:
6757
**(x±1, y±1, z±1)**.
6858
6959
70-
## Fill
71-
72-
Algoritm recursiv de umplere: pornește dintr-un punct și vizitează vecinii de același tip,
73-
fără să mai fie nevoie de memorie suplimentară pentru marcarea blocurilor vizitate.
74-
75-
Cazul în care `target_block` este egal cu `new_block` este tratat separat
76-
inaintea rularii algoritmului de umplere: se va returna matricea intiala.
77-
78-
## Rotirea în jurul axei Oy
79-
80-
Se alocă o nouă matrice:
81-
- `new_width = old_depth`
82-
- `new_depth = old_width`
83-
84-
> Practic interschimbă dimensiunile pentru **lățime** și **adâncime**.
85-
86-
87-
Valorile sunt copiate conform regulii:
88-
```c
89-
new_mat[x][y][z] = chunk[z][y][depth - 1 - x]
90-
```
9160
9261
## Gravitație (TODO)
9362
@@ -99,54 +68,7 @@ Chiar dacă algoritmul nu este corect, iată care sunt pașii:
9968
- La final, corpurile sunt repoziționate pe axa **Oy**
10069
- Âtâta timp cât planul superior (paralel cu xOz) este gol: micșorez înălțimea
10170
102-
## Compresie
103-
104-
1. Serializare: matricea 3D este aplatizată într-un vector liniar (ordinea **y->z->x**).
105-
2. Vectorul e parcurs pentru a determina secvențe de blocuri identice consecutive:
106-
- Se generează un vector de perechi `(num_occurrences, tip_block)`
107-
- Dacă numărul depășește 4095, se începe un nou **run**
108-
3. Transformarea în octeți:
109-
- <32 apariții => codificare pe 1 octet
110-
- ≥32 apariții => codificare pe 2 octeți
111-
112-
Pentru că dimensiunea finală este necunoscută,
113-
buffer-ul se redimensionează dinamic.
114-
115-
## Decompresie
116-
117-
1. Se alocă matricea 3D pe baza dimensiunilor cunoscute.
118-
2. Se parcurg octeții cu un pointer, reconstruind blocurile.
119-
3. Decodificarea se bazează pe markerii din biții 5 și 6:
120-
- `0` => **run** pe 1 octet (5 biți pentru numărul de aparitii)
121-
- `10` => **run** pe 2 octeți (11 biți pentru număr de aparitii)
122-
123-
Coordonatele **(x,y,z)** sunt actualizate pe măsură ce vectorul este reconstruit.
124-
125-
## Operații pe biți
126-
127-
- Putere a lui 2: `1 << n`
128-
- Setare bit: `byte |= (1 << i)`
129-
- Verificare valoare (0/1) bit: `if (byte & (1 << i))`
130-
131-
## Semnificația biților
132-
133-
| Interval n | Primul octet (MSB -> LSB)| Al doilea octet (MSB -> LSB) |
134-
|:--------------|:-------------------------|:-----------------------------|
135-
| 1 ≤ n < 32 | b1 b0 0 n4 n3 n2 n1 n0 | - |
136-
| 32 ≤ n < 4096 | b1 b0 1 0 n11 n10 n9 n8 | n7 n6 n5 n4 n3 n2 n1 n0 |
13771
138-
- `b1 b0` = tipul blocului (cei mai semnificativi 2 biți)
139-
- `0` / `10` = markeri pentru lungimea run-ului (1 sau 2 octeți)
140-
- `nX` = biții care codifică numărul de apariții
141-
- **MSB** = primul bit din stânga al unui octet
142-
- **LSB** = ultimul bit din dreapta al unui octet
14372
144-
Un octet codifică atât tipul blocului, cât și numărul de apariții.
145-
- 1 ≤ n < 32 => codificare pe 1 octet
146-
- 32 ≤ n < 4096 => codificare pe 2 octeți
14773
148-
Bitul 6 este markerul:
149-
- `0` => 1 octet
150-
- `10` => 2 octeți
151-
Tipul blocului ocupă cei mai semnificativi 2 biți.
15274

0 commit comments

Comments
 (0)