Upload 7 files
Browse files- Besiege_blocks_markov.json +1301 -0
- README.md +136 -13
- app.py +306 -0
- get_machine_from_json.py +50 -0
- requirements.txt +5 -0
- user_system_prompt.txt +1 -0
- utils.py +783 -0
Besiege_blocks_markov.json
ADDED
|
@@ -0,0 +1,1301 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"0": {
|
| 3 |
+
"name": "起始方块",
|
| 4 |
+
"block_type": "基础",
|
| 5 |
+
"bbox_size": [
|
| 6 |
+
1.0,
|
| 7 |
+
1.0,
|
| 8 |
+
1.0
|
| 9 |
+
],
|
| 10 |
+
"bc_gc": [
|
| 11 |
+
0.0,
|
| 12 |
+
0.0,
|
| 13 |
+
0.0
|
| 14 |
+
],
|
| 15 |
+
"bc_bp": [
|
| 16 |
+
[
|
| 17 |
+
0.0,
|
| 18 |
+
0.0,
|
| 19 |
+
0.5
|
| 20 |
+
],
|
| 21 |
+
[
|
| 22 |
+
0.0,
|
| 23 |
+
0.0,
|
| 24 |
+
-0.5
|
| 25 |
+
],
|
| 26 |
+
[
|
| 27 |
+
-0.5,
|
| 28 |
+
0.0,
|
| 29 |
+
0.0
|
| 30 |
+
],
|
| 31 |
+
[
|
| 32 |
+
0.5,
|
| 33 |
+
0.0,
|
| 34 |
+
0.0
|
| 35 |
+
],
|
| 36 |
+
[
|
| 37 |
+
0.0,
|
| 38 |
+
0.5,
|
| 39 |
+
0.0
|
| 40 |
+
],
|
| 41 |
+
[
|
| 42 |
+
0.0,
|
| 43 |
+
-0.5,
|
| 44 |
+
0.0
|
| 45 |
+
]
|
| 46 |
+
]
|
| 47 |
+
},
|
| 48 |
+
"15": {
|
| 49 |
+
"name": "小型木块",
|
| 50 |
+
"block_type": "基础",
|
| 51 |
+
"bbox_size": [
|
| 52 |
+
1.0,
|
| 53 |
+
1.0,
|
| 54 |
+
1.0
|
| 55 |
+
],
|
| 56 |
+
"bc_gc": [
|
| 57 |
+
0.0,
|
| 58 |
+
0.0,
|
| 59 |
+
0.5
|
| 60 |
+
],
|
| 61 |
+
"bc_bp": [
|
| 62 |
+
[
|
| 63 |
+
0.0,
|
| 64 |
+
0.0,
|
| 65 |
+
1.0
|
| 66 |
+
],
|
| 67 |
+
[
|
| 68 |
+
-0.5,
|
| 69 |
+
0.0,
|
| 70 |
+
0.5
|
| 71 |
+
],
|
| 72 |
+
[
|
| 73 |
+
0.5,
|
| 74 |
+
0.0,
|
| 75 |
+
0.5
|
| 76 |
+
],
|
| 77 |
+
[
|
| 78 |
+
0.0,
|
| 79 |
+
0.5,
|
| 80 |
+
0.5
|
| 81 |
+
],
|
| 82 |
+
[
|
| 83 |
+
0.0,
|
| 84 |
+
-0.5,
|
| 85 |
+
0.5
|
| 86 |
+
]
|
| 87 |
+
]
|
| 88 |
+
},
|
| 89 |
+
"1": {
|
| 90 |
+
"name": "木块",
|
| 91 |
+
"block_type": "基础",
|
| 92 |
+
"bbox_size": [
|
| 93 |
+
1.0,
|
| 94 |
+
1.0,
|
| 95 |
+
2.0
|
| 96 |
+
],
|
| 97 |
+
"bc_gc": [
|
| 98 |
+
0.0,
|
| 99 |
+
0.0,
|
| 100 |
+
1.0
|
| 101 |
+
],
|
| 102 |
+
"bc_bp": [
|
| 103 |
+
[
|
| 104 |
+
0.0,
|
| 105 |
+
0.0,
|
| 106 |
+
2.0
|
| 107 |
+
],
|
| 108 |
+
[
|
| 109 |
+
-0.5,
|
| 110 |
+
0.0,
|
| 111 |
+
0.5
|
| 112 |
+
],
|
| 113 |
+
[
|
| 114 |
+
-0.5,
|
| 115 |
+
0.0,
|
| 116 |
+
1.5
|
| 117 |
+
],
|
| 118 |
+
[
|
| 119 |
+
0.5,
|
| 120 |
+
0.0,
|
| 121 |
+
0.5
|
| 122 |
+
],
|
| 123 |
+
[
|
| 124 |
+
0.5,
|
| 125 |
+
0.0,
|
| 126 |
+
1.5
|
| 127 |
+
],
|
| 128 |
+
[
|
| 129 |
+
0.0,
|
| 130 |
+
0.5,
|
| 131 |
+
0.5
|
| 132 |
+
],
|
| 133 |
+
[
|
| 134 |
+
0.0,
|
| 135 |
+
0.5,
|
| 136 |
+
1.5
|
| 137 |
+
],
|
| 138 |
+
[
|
| 139 |
+
0.0,
|
| 140 |
+
-0.5,
|
| 141 |
+
0.5
|
| 142 |
+
],
|
| 143 |
+
[
|
| 144 |
+
0.0,
|
| 145 |
+
-0.5,
|
| 146 |
+
1.5
|
| 147 |
+
]
|
| 148 |
+
]
|
| 149 |
+
},
|
| 150 |
+
"41": {
|
| 151 |
+
"name": "木杆",
|
| 152 |
+
"block_type": "基础",
|
| 153 |
+
"bbox_size": [
|
| 154 |
+
1.0,
|
| 155 |
+
1.0,
|
| 156 |
+
2.0
|
| 157 |
+
],
|
| 158 |
+
"bc_gc": [
|
| 159 |
+
0.0,
|
| 160 |
+
0.0,
|
| 161 |
+
1.0
|
| 162 |
+
],
|
| 163 |
+
"bc_bp": [
|
| 164 |
+
[
|
| 165 |
+
0.0,
|
| 166 |
+
0.0,
|
| 167 |
+
2.0
|
| 168 |
+
],
|
| 169 |
+
[
|
| 170 |
+
-0.5,
|
| 171 |
+
0.0,
|
| 172 |
+
0.5
|
| 173 |
+
],
|
| 174 |
+
[
|
| 175 |
+
-0.5,
|
| 176 |
+
0.0,
|
| 177 |
+
1.5
|
| 178 |
+
],
|
| 179 |
+
[
|
| 180 |
+
0.5,
|
| 181 |
+
0.0,
|
| 182 |
+
0.5
|
| 183 |
+
],
|
| 184 |
+
[
|
| 185 |
+
0.5,
|
| 186 |
+
0.0,
|
| 187 |
+
1.5
|
| 188 |
+
],
|
| 189 |
+
[
|
| 190 |
+
0.0,
|
| 191 |
+
0.5,
|
| 192 |
+
0.5
|
| 193 |
+
],
|
| 194 |
+
[
|
| 195 |
+
0.0,
|
| 196 |
+
0.5,
|
| 197 |
+
1.5
|
| 198 |
+
],
|
| 199 |
+
[
|
| 200 |
+
0.0,
|
| 201 |
+
-0.5,
|
| 202 |
+
0.5
|
| 203 |
+
],
|
| 204 |
+
[
|
| 205 |
+
0.0,
|
| 206 |
+
-0.5,
|
| 207 |
+
1.5
|
| 208 |
+
]
|
| 209 |
+
]
|
| 210 |
+
},
|
| 211 |
+
"63": {
|
| 212 |
+
"name": "原木",
|
| 213 |
+
"block_type": "基础",
|
| 214 |
+
"bbox_size": [
|
| 215 |
+
1.0,
|
| 216 |
+
1.0,
|
| 217 |
+
3.0
|
| 218 |
+
],
|
| 219 |
+
"bc_gc": [
|
| 220 |
+
0.0,
|
| 221 |
+
0.0,
|
| 222 |
+
1.5
|
| 223 |
+
],
|
| 224 |
+
"bc_bp": [
|
| 225 |
+
[
|
| 226 |
+
0.0,
|
| 227 |
+
0.0,
|
| 228 |
+
3.0
|
| 229 |
+
],
|
| 230 |
+
[
|
| 231 |
+
-0.5,
|
| 232 |
+
0.0,
|
| 233 |
+
0.5
|
| 234 |
+
],
|
| 235 |
+
[
|
| 236 |
+
-0.5,
|
| 237 |
+
0.0,
|
| 238 |
+
1.5
|
| 239 |
+
],
|
| 240 |
+
[
|
| 241 |
+
-0.5,
|
| 242 |
+
0.0,
|
| 243 |
+
2.5
|
| 244 |
+
],
|
| 245 |
+
[
|
| 246 |
+
0.5,
|
| 247 |
+
0.0,
|
| 248 |
+
0.5
|
| 249 |
+
],
|
| 250 |
+
[
|
| 251 |
+
0.5,
|
| 252 |
+
0.0,
|
| 253 |
+
1.5
|
| 254 |
+
],
|
| 255 |
+
[
|
| 256 |
+
0.5,
|
| 257 |
+
0.0,
|
| 258 |
+
2.5
|
| 259 |
+
],
|
| 260 |
+
[
|
| 261 |
+
0.0,
|
| 262 |
+
0.5,
|
| 263 |
+
0.5
|
| 264 |
+
],
|
| 265 |
+
[
|
| 266 |
+
0.0,
|
| 267 |
+
0.5,
|
| 268 |
+
1.5
|
| 269 |
+
],
|
| 270 |
+
[
|
| 271 |
+
0.0,
|
| 272 |
+
0.5,
|
| 273 |
+
2.5
|
| 274 |
+
],
|
| 275 |
+
[
|
| 276 |
+
0.0,
|
| 277 |
+
-0.5,
|
| 278 |
+
0.5
|
| 279 |
+
],
|
| 280 |
+
[
|
| 281 |
+
0.0,
|
| 282 |
+
-0.5,
|
| 283 |
+
1.5
|
| 284 |
+
],
|
| 285 |
+
[
|
| 286 |
+
0.0,
|
| 287 |
+
-0.5,
|
| 288 |
+
2.5
|
| 289 |
+
]
|
| 290 |
+
]
|
| 291 |
+
},
|
| 292 |
+
"28": {
|
| 293 |
+
"name": "转向铰链",
|
| 294 |
+
"block_type": "行走",
|
| 295 |
+
"bbox_size": [
|
| 296 |
+
1.0,
|
| 297 |
+
1.0,
|
| 298 |
+
1.0
|
| 299 |
+
],
|
| 300 |
+
"bc_gc": [
|
| 301 |
+
0.0,
|
| 302 |
+
0.0,
|
| 303 |
+
0.5
|
| 304 |
+
],
|
| 305 |
+
"bc_bp": [
|
| 306 |
+
[
|
| 307 |
+
0.0,
|
| 308 |
+
0.0,
|
| 309 |
+
1.0
|
| 310 |
+
]
|
| 311 |
+
]
|
| 312 |
+
},
|
| 313 |
+
"13": {
|
| 314 |
+
"name": "转向块",
|
| 315 |
+
"block_type": "行走",
|
| 316 |
+
"bbox_size": [
|
| 317 |
+
1.0,
|
| 318 |
+
1.0,
|
| 319 |
+
1.0
|
| 320 |
+
],
|
| 321 |
+
"bc_gc": [
|
| 322 |
+
0.0,
|
| 323 |
+
0.0,
|
| 324 |
+
0.5
|
| 325 |
+
],
|
| 326 |
+
"bc_bp": [
|
| 327 |
+
[
|
| 328 |
+
0.0,
|
| 329 |
+
0.0,
|
| 330 |
+
1.0
|
| 331 |
+
],
|
| 332 |
+
[
|
| 333 |
+
-0.5,
|
| 334 |
+
0.0,
|
| 335 |
+
0.5
|
| 336 |
+
],
|
| 337 |
+
[
|
| 338 |
+
0.5,
|
| 339 |
+
0.0,
|
| 340 |
+
0.5
|
| 341 |
+
],
|
| 342 |
+
[
|
| 343 |
+
0.0,
|
| 344 |
+
0.5,
|
| 345 |
+
0.5
|
| 346 |
+
],
|
| 347 |
+
[
|
| 348 |
+
0.0,
|
| 349 |
+
-0.5,
|
| 350 |
+
0.5
|
| 351 |
+
]
|
| 352 |
+
]
|
| 353 |
+
},
|
| 354 |
+
"2": {
|
| 355 |
+
"name": "动力轮",
|
| 356 |
+
"block_type": "行走",
|
| 357 |
+
"bbox_size": [
|
| 358 |
+
2.0,
|
| 359 |
+
2.0,
|
| 360 |
+
0.5
|
| 361 |
+
],
|
| 362 |
+
"bc_gc": [
|
| 363 |
+
0.0,
|
| 364 |
+
0.0,
|
| 365 |
+
0.25
|
| 366 |
+
],
|
| 367 |
+
"bc_bp": [
|
| 368 |
+
[
|
| 369 |
+
0.0,
|
| 370 |
+
0.0,
|
| 371 |
+
0.5
|
| 372 |
+
]
|
| 373 |
+
]
|
| 374 |
+
},
|
| 375 |
+
"40": {
|
| 376 |
+
"name": "无动力轮",
|
| 377 |
+
"block_type": "行走",
|
| 378 |
+
"bbox_size": [
|
| 379 |
+
2.0,
|
| 380 |
+
2.0,
|
| 381 |
+
0.5
|
| 382 |
+
],
|
| 383 |
+
"bc_gc": [
|
| 384 |
+
0.0,
|
| 385 |
+
0.0,
|
| 386 |
+
0.25
|
| 387 |
+
],
|
| 388 |
+
"bc_bp": [
|
| 389 |
+
[
|
| 390 |
+
0.0,
|
| 391 |
+
0.0,
|
| 392 |
+
0.5
|
| 393 |
+
]
|
| 394 |
+
]
|
| 395 |
+
},
|
| 396 |
+
"46": {
|
| 397 |
+
"name": "动力大轮",
|
| 398 |
+
"block_type": "行走",
|
| 399 |
+
"bbox_size": [
|
| 400 |
+
3.0,
|
| 401 |
+
3.0,
|
| 402 |
+
1.0
|
| 403 |
+
],
|
| 404 |
+
"bc_gc": [
|
| 405 |
+
0.0,
|
| 406 |
+
0.0,
|
| 407 |
+
0.5
|
| 408 |
+
],
|
| 409 |
+
"bc_bp": [
|
| 410 |
+
[
|
| 411 |
+
0.0,
|
| 412 |
+
0.0,
|
| 413 |
+
1
|
| 414 |
+
],
|
| 415 |
+
[
|
| 416 |
+
-1.5,
|
| 417 |
+
0.0,
|
| 418 |
+
1
|
| 419 |
+
],
|
| 420 |
+
[
|
| 421 |
+
1.5,
|
| 422 |
+
0.0,
|
| 423 |
+
1
|
| 424 |
+
],
|
| 425 |
+
[
|
| 426 |
+
0.0,
|
| 427 |
+
1.5,
|
| 428 |
+
1
|
| 429 |
+
],
|
| 430 |
+
[
|
| 431 |
+
0.0,
|
| 432 |
+
-1.5,
|
| 433 |
+
1
|
| 434 |
+
],
|
| 435 |
+
[
|
| 436 |
+
-1.5,
|
| 437 |
+
0.0,
|
| 438 |
+
0.5
|
| 439 |
+
],
|
| 440 |
+
[
|
| 441 |
+
1.5,
|
| 442 |
+
0.0,
|
| 443 |
+
0.5
|
| 444 |
+
],
|
| 445 |
+
[
|
| 446 |
+
0.0,
|
| 447 |
+
1.5,
|
| 448 |
+
0.5
|
| 449 |
+
],
|
| 450 |
+
[
|
| 451 |
+
0.0,
|
| 452 |
+
-1.5,
|
| 453 |
+
0.5
|
| 454 |
+
]
|
| 455 |
+
]
|
| 456 |
+
},
|
| 457 |
+
"60": {
|
| 458 |
+
"name": "无动力大轮",
|
| 459 |
+
"block_type": "行走",
|
| 460 |
+
"bbox_size": [
|
| 461 |
+
3.0,
|
| 462 |
+
3.0,
|
| 463 |
+
1.0
|
| 464 |
+
],
|
| 465 |
+
"bc_gc": [
|
| 466 |
+
0.0,
|
| 467 |
+
0.0,
|
| 468 |
+
0.5
|
| 469 |
+
],
|
| 470 |
+
"bc_bp": [
|
| 471 |
+
[
|
| 472 |
+
0.0,
|
| 473 |
+
0.0,
|
| 474 |
+
1
|
| 475 |
+
],
|
| 476 |
+
[
|
| 477 |
+
-1.5,
|
| 478 |
+
0.0,
|
| 479 |
+
1
|
| 480 |
+
],
|
| 481 |
+
[
|
| 482 |
+
1.5,
|
| 483 |
+
0.0,
|
| 484 |
+
1
|
| 485 |
+
],
|
| 486 |
+
[
|
| 487 |
+
0.0,
|
| 488 |
+
1.5,
|
| 489 |
+
1
|
| 490 |
+
],
|
| 491 |
+
[
|
| 492 |
+
0.0,
|
| 493 |
+
-1.5,
|
| 494 |
+
1
|
| 495 |
+
],
|
| 496 |
+
[
|
| 497 |
+
-1.5,
|
| 498 |
+
0.0,
|
| 499 |
+
0.5
|
| 500 |
+
],
|
| 501 |
+
[
|
| 502 |
+
1.5,
|
| 503 |
+
0.0,
|
| 504 |
+
0.5
|
| 505 |
+
],
|
| 506 |
+
[
|
| 507 |
+
0.0,
|
| 508 |
+
1.5,
|
| 509 |
+
0.5
|
| 510 |
+
],
|
| 511 |
+
[
|
| 512 |
+
0.0,
|
| 513 |
+
-1.5,
|
| 514 |
+
0.5
|
| 515 |
+
]
|
| 516 |
+
]
|
| 517 |
+
},
|
| 518 |
+
"50": {
|
| 519 |
+
"name": "小轮",
|
| 520 |
+
"block_type": "行走",
|
| 521 |
+
"bbox_size": [
|
| 522 |
+
0.5,
|
| 523 |
+
1.0,
|
| 524 |
+
1.5
|
| 525 |
+
],
|
| 526 |
+
"bc_gc": [
|
| 527 |
+
0.0,
|
| 528 |
+
0.0,
|
| 529 |
+
0.75
|
| 530 |
+
],
|
| 531 |
+
"bc_bp": []
|
| 532 |
+
},
|
| 533 |
+
"86": {
|
| 534 |
+
"name": "滑板轮",
|
| 535 |
+
"block_type": "行走",
|
| 536 |
+
"bbox_size": [
|
| 537 |
+
1.0,
|
| 538 |
+
1.0,
|
| 539 |
+
1.0
|
| 540 |
+
],
|
| 541 |
+
"bc_gc": [
|
| 542 |
+
0.0,
|
| 543 |
+
0.0,
|
| 544 |
+
0.5
|
| 545 |
+
],
|
| 546 |
+
"bc_bp": []
|
| 547 |
+
},
|
| 548 |
+
"19": {
|
| 549 |
+
"name": "万向节",
|
| 550 |
+
"block_type": "机械",
|
| 551 |
+
"bbox_size": [
|
| 552 |
+
1.0,
|
| 553 |
+
1.0,
|
| 554 |
+
1.0
|
| 555 |
+
],
|
| 556 |
+
"bc_gc": [
|
| 557 |
+
0.0,
|
| 558 |
+
0.0,
|
| 559 |
+
0.5
|
| 560 |
+
],
|
| 561 |
+
"bc_bp": [
|
| 562 |
+
[
|
| 563 |
+
0.0,
|
| 564 |
+
0.0,
|
| 565 |
+
1.0
|
| 566 |
+
],
|
| 567 |
+
[
|
| 568 |
+
-0.5,
|
| 569 |
+
0.0,
|
| 570 |
+
0.5
|
| 571 |
+
],
|
| 572 |
+
[
|
| 573 |
+
0.5,
|
| 574 |
+
0.0,
|
| 575 |
+
0.5
|
| 576 |
+
],
|
| 577 |
+
[
|
| 578 |
+
0.0,
|
| 579 |
+
0.5,
|
| 580 |
+
0.5
|
| 581 |
+
],
|
| 582 |
+
[
|
| 583 |
+
0.0,
|
| 584 |
+
-0.5,
|
| 585 |
+
0.5
|
| 586 |
+
]
|
| 587 |
+
]
|
| 588 |
+
},
|
| 589 |
+
"5": {
|
| 590 |
+
"name": "铰链",
|
| 591 |
+
"block_type": "机械",
|
| 592 |
+
"bbox_size": [
|
| 593 |
+
1.0,
|
| 594 |
+
1.0,
|
| 595 |
+
1.0
|
| 596 |
+
],
|
| 597 |
+
"bc_gc": [
|
| 598 |
+
0.0,
|
| 599 |
+
0.0,
|
| 600 |
+
0.5
|
| 601 |
+
],
|
| 602 |
+
"bc_bp": [
|
| 603 |
+
[
|
| 604 |
+
0.0,
|
| 605 |
+
0.0,
|
| 606 |
+
1.0
|
| 607 |
+
],
|
| 608 |
+
[
|
| 609 |
+
-0.5,
|
| 610 |
+
0.0,
|
| 611 |
+
0.5
|
| 612 |
+
],
|
| 613 |
+
[
|
| 614 |
+
0.5,
|
| 615 |
+
0.0,
|
| 616 |
+
0.5
|
| 617 |
+
],
|
| 618 |
+
[
|
| 619 |
+
0.0,
|
| 620 |
+
0.5,
|
| 621 |
+
0.5
|
| 622 |
+
],
|
| 623 |
+
[
|
| 624 |
+
0.0,
|
| 625 |
+
-0.5,
|
| 626 |
+
0.5
|
| 627 |
+
]
|
| 628 |
+
]
|
| 629 |
+
},
|
| 630 |
+
"44": {
|
| 631 |
+
"name": "球形关节",
|
| 632 |
+
"block_type": "机械",
|
| 633 |
+
"bbox_size": [
|
| 634 |
+
1.0,
|
| 635 |
+
1.0,
|
| 636 |
+
1.0
|
| 637 |
+
],
|
| 638 |
+
"bc_gc": [
|
| 639 |
+
0.0,
|
| 640 |
+
0.0,
|
| 641 |
+
0.5
|
| 642 |
+
],
|
| 643 |
+
"bc_bp": [
|
| 644 |
+
[
|
| 645 |
+
0.0,
|
| 646 |
+
0.0,
|
| 647 |
+
1.0
|
| 648 |
+
],
|
| 649 |
+
[
|
| 650 |
+
-0.5,
|
| 651 |
+
0.0,
|
| 652 |
+
0.5
|
| 653 |
+
],
|
| 654 |
+
[
|
| 655 |
+
0.5,
|
| 656 |
+
0.0,
|
| 657 |
+
0.5
|
| 658 |
+
],
|
| 659 |
+
[
|
| 660 |
+
0.0,
|
| 661 |
+
0.5,
|
| 662 |
+
0.5
|
| 663 |
+
],
|
| 664 |
+
[
|
| 665 |
+
0.0,
|
| 666 |
+
-0.5,
|
| 667 |
+
0.5
|
| 668 |
+
]
|
| 669 |
+
]
|
| 670 |
+
},
|
| 671 |
+
"76": {
|
| 672 |
+
"name": "轴连接件",
|
| 673 |
+
"block_type": "机械",
|
| 674 |
+
"bbox_size": [
|
| 675 |
+
1.0,
|
| 676 |
+
1.0,
|
| 677 |
+
1.0
|
| 678 |
+
],
|
| 679 |
+
"bc_gc": [
|
| 680 |
+
0.0,
|
| 681 |
+
0.0,
|
| 682 |
+
0.5
|
| 683 |
+
],
|
| 684 |
+
"bc_bp": [
|
| 685 |
+
[
|
| 686 |
+
0.0,
|
| 687 |
+
0.0,
|
| 688 |
+
1.0
|
| 689 |
+
]
|
| 690 |
+
]
|
| 691 |
+
},
|
| 692 |
+
"22": {
|
| 693 |
+
"name": "旋转块",
|
| 694 |
+
"block_type": "机械",
|
| 695 |
+
"bbox_size": [
|
| 696 |
+
1.0,
|
| 697 |
+
1.0,
|
| 698 |
+
1.0
|
| 699 |
+
],
|
| 700 |
+
"bc_gc": [
|
| 701 |
+
0.0,
|
| 702 |
+
0.0,
|
| 703 |
+
0.5
|
| 704 |
+
],
|
| 705 |
+
"bc_bp": [
|
| 706 |
+
[
|
| 707 |
+
0.0,
|
| 708 |
+
0.0,
|
| 709 |
+
1.0
|
| 710 |
+
],
|
| 711 |
+
[
|
| 712 |
+
-0.5,
|
| 713 |
+
0.0,
|
| 714 |
+
0.5
|
| 715 |
+
],
|
| 716 |
+
[
|
| 717 |
+
0.5,
|
| 718 |
+
0.0,
|
| 719 |
+
0.5
|
| 720 |
+
],
|
| 721 |
+
[
|
| 722 |
+
0.0,
|
| 723 |
+
0.5,
|
| 724 |
+
0.5
|
| 725 |
+
],
|
| 726 |
+
[
|
| 727 |
+
0.0,
|
| 728 |
+
-0.5,
|
| 729 |
+
0.5
|
| 730 |
+
]
|
| 731 |
+
]
|
| 732 |
+
},
|
| 733 |
+
"27": {
|
| 734 |
+
"name": "抓取器",
|
| 735 |
+
"block_type": "机械",
|
| 736 |
+
"bbox_size": [
|
| 737 |
+
1.0,
|
| 738 |
+
1.0,
|
| 739 |
+
1.0
|
| 740 |
+
],
|
| 741 |
+
"bc_gc": [
|
| 742 |
+
0.0,
|
| 743 |
+
0.0,
|
| 744 |
+
0.5
|
| 745 |
+
],
|
| 746 |
+
"bc_bp": [
|
| 747 |
+
[
|
| 748 |
+
0.0,
|
| 749 |
+
0.0,
|
| 750 |
+
1.0
|
| 751 |
+
]
|
| 752 |
+
]
|
| 753 |
+
},
|
| 754 |
+
"20": {
|
| 755 |
+
"name": "金属尖刺",
|
| 756 |
+
"block_type": "武器",
|
| 757 |
+
"bbox_size": [
|
| 758 |
+
0.2,
|
| 759 |
+
0.2,
|
| 760 |
+
2.4
|
| 761 |
+
],
|
| 762 |
+
"bc_gc": [
|
| 763 |
+
0.0,
|
| 764 |
+
0.0,
|
| 765 |
+
1.25
|
| 766 |
+
],
|
| 767 |
+
"bc_bp": []
|
| 768 |
+
},
|
| 769 |
+
"3": {
|
| 770 |
+
"name": "金属刀片",
|
| 771 |
+
"block_type": "武器",
|
| 772 |
+
"bbox_size": [
|
| 773 |
+
1.0,
|
| 774 |
+
0.1,
|
| 775 |
+
3.8
|
| 776 |
+
],
|
| 777 |
+
"bc_gc": [
|
| 778 |
+
0.0,
|
| 779 |
+
0.0,
|
| 780 |
+
2.0
|
| 781 |
+
],
|
| 782 |
+
"bc_bp": []
|
| 783 |
+
},
|
| 784 |
+
"11": {
|
| 785 |
+
"name": "火炮",
|
| 786 |
+
"block_type": "武器",
|
| 787 |
+
"bbox_size": [
|
| 788 |
+
1.0,
|
| 789 |
+
2.5,
|
| 790 |
+
1.0
|
| 791 |
+
],
|
| 792 |
+
"bc_gc": [
|
| 793 |
+
0.0,
|
| 794 |
+
-0.5,
|
| 795 |
+
0.5
|
| 796 |
+
],
|
| 797 |
+
"bc_bp": []
|
| 798 |
+
},
|
| 799 |
+
"23": {
|
| 800 |
+
"name": "炸弹",
|
| 801 |
+
"block_type": "武器",
|
| 802 |
+
"bbox_size": [
|
| 803 |
+
1.9,
|
| 804 |
+
1.9,
|
| 805 |
+
1.9
|
| 806 |
+
],
|
| 807 |
+
"bc_gc": [
|
| 808 |
+
0.0,
|
| 809 |
+
0.0,
|
| 810 |
+
0.95
|
| 811 |
+
],
|
| 812 |
+
"bc_bp": []
|
| 813 |
+
},
|
| 814 |
+
"36": {
|
| 815 |
+
"name": "巨石",
|
| 816 |
+
"block_type": "武器",
|
| 817 |
+
"bbox_size": [
|
| 818 |
+
1.9,
|
| 819 |
+
1.9,
|
| 820 |
+
1.9
|
| 821 |
+
],
|
| 822 |
+
"bc_gc": [
|
| 823 |
+
0.0,
|
| 824 |
+
0.0,
|
| 825 |
+
0.95
|
| 826 |
+
],
|
| 827 |
+
"bc_bp": []
|
| 828 |
+
},
|
| 829 |
+
"49": {
|
| 830 |
+
"name": "防滑垫",
|
| 831 |
+
"block_type": "护甲",
|
| 832 |
+
"bbox_size": [
|
| 833 |
+
0.8,
|
| 834 |
+
0.8,
|
| 835 |
+
0.2
|
| 836 |
+
],
|
| 837 |
+
"bc_gc": [
|
| 838 |
+
0.0,
|
| 839 |
+
0.0,
|
| 840 |
+
0.1
|
| 841 |
+
],
|
| 842 |
+
"bc_bp": []
|
| 843 |
+
},
|
| 844 |
+
"87": {
|
| 845 |
+
"name": "弹力垫",
|
| 846 |
+
"block_type": "护甲",
|
| 847 |
+
"bbox_size": [
|
| 848 |
+
0.8,
|
| 849 |
+
0.8,
|
| 850 |
+
0.2
|
| 851 |
+
],
|
| 852 |
+
"bc_gc": [
|
| 853 |
+
0.0,
|
| 854 |
+
0.0,
|
| 855 |
+
0.1
|
| 856 |
+
],
|
| 857 |
+
"bc_bp": []
|
| 858 |
+
},
|
| 859 |
+
"30": {
|
| 860 |
+
"name": "容器",
|
| 861 |
+
"block_type": "护甲",
|
| 862 |
+
"bbox_size": [
|
| 863 |
+
2.4,
|
| 864 |
+
3.0,
|
| 865 |
+
2.8
|
| 866 |
+
],
|
| 867 |
+
"bc_gc": [
|
| 868 |
+
0.0,
|
| 869 |
+
0.0,
|
| 870 |
+
1.4
|
| 871 |
+
],
|
| 872 |
+
"bc_bp": [[
|
| 873 |
+
0.0,
|
| 874 |
+
0.0,
|
| 875 |
+
1.0
|
| 876 |
+
]]
|
| 877 |
+
},
|
| 878 |
+
"6": {
|
| 879 |
+
"name": "刺球",
|
| 880 |
+
"block_type": "护甲",
|
| 881 |
+
"bbox_size": [
|
| 882 |
+
3.0,
|
| 883 |
+
3.0,
|
| 884 |
+
2.5
|
| 885 |
+
],
|
| 886 |
+
"bc_gc": [
|
| 887 |
+
0.0,
|
| 888 |
+
0.0,
|
| 889 |
+
1.25
|
| 890 |
+
],
|
| 891 |
+
"bc_bp": []
|
| 892 |
+
},
|
| 893 |
+
"16": {
|
| 894 |
+
"name": "弹簧",
|
| 895 |
+
"block_type": "行走",
|
| 896 |
+
"bbox_size": [
|
| 897 |
+
1.0,
|
| 898 |
+
1.0,
|
| 899 |
+
2.0
|
| 900 |
+
],
|
| 901 |
+
"bc_gc": [
|
| 902 |
+
0.0,
|
| 903 |
+
0.0,
|
| 904 |
+
1.0
|
| 905 |
+
],
|
| 906 |
+
"bc_bp": [
|
| 907 |
+
[
|
| 908 |
+
0.0,
|
| 909 |
+
0.0,
|
| 910 |
+
2.0
|
| 911 |
+
],
|
| 912 |
+
[
|
| 913 |
+
-0.5,
|
| 914 |
+
0.0,
|
| 915 |
+
1.5
|
| 916 |
+
],
|
| 917 |
+
|
| 918 |
+
[
|
| 919 |
+
0.5,
|
| 920 |
+
0.0,
|
| 921 |
+
1.5
|
| 922 |
+
],
|
| 923 |
+
|
| 924 |
+
[
|
| 925 |
+
0.0,
|
| 926 |
+
0.5,
|
| 927 |
+
1.5
|
| 928 |
+
],
|
| 929 |
+
[
|
| 930 |
+
0.0,
|
| 931 |
+
-0.5,
|
| 932 |
+
1.5
|
| 933 |
+
]
|
| 934 |
+
]
|
| 935 |
+
},
|
| 936 |
+
"7":{
|
| 937 |
+
"name": "钢筋",
|
| 938 |
+
"block_type": "线性",
|
| 939 |
+
"bbox_size": [],
|
| 940 |
+
"bc_gc": [],
|
| 941 |
+
"bc_bp": []
|
| 942 |
+
},
|
| 943 |
+
"9":{
|
| 944 |
+
"name": "皮筋",
|
| 945 |
+
"block_type": "线性",
|
| 946 |
+
"bbox_size": [],
|
| 947 |
+
"bc_gc": [],
|
| 948 |
+
"bc_bp": []
|
| 949 |
+
},
|
| 950 |
+
"35": {
|
| 951 |
+
"name": "配重物",
|
| 952 |
+
"block_type": "基础",
|
| 953 |
+
"bbox_size": [
|
| 954 |
+
1.0,
|
| 955 |
+
1.0,
|
| 956 |
+
1.0
|
| 957 |
+
],
|
| 958 |
+
"bc_gc": [
|
| 959 |
+
0.0,
|
| 960 |
+
0.0,
|
| 961 |
+
0.5
|
| 962 |
+
],
|
| 963 |
+
"bc_bp": [
|
| 964 |
+
[
|
| 965 |
+
0.0,
|
| 966 |
+
0.0,
|
| 967 |
+
1.0
|
| 968 |
+
],
|
| 969 |
+
[
|
| 970 |
+
-0.5,
|
| 971 |
+
0.0,
|
| 972 |
+
0.5
|
| 973 |
+
],
|
| 974 |
+
[
|
| 975 |
+
0.5,
|
| 976 |
+
0.0,
|
| 977 |
+
0.5
|
| 978 |
+
],
|
| 979 |
+
[
|
| 980 |
+
0.0,
|
| 981 |
+
0.5,
|
| 982 |
+
0.5
|
| 983 |
+
],
|
| 984 |
+
[
|
| 985 |
+
0.0,
|
| 986 |
+
-0.5,
|
| 987 |
+
0.5
|
| 988 |
+
]
|
| 989 |
+
]
|
| 990 |
+
},
|
| 991 |
+
"32": {
|
| 992 |
+
"name": "大型护甲板",
|
| 993 |
+
"block_type": "基础",
|
| 994 |
+
"bbox_size": [
|
| 995 |
+
1.8,
|
| 996 |
+
0.9,
|
| 997 |
+
0.3
|
| 998 |
+
],
|
| 999 |
+
"bc_gc": [
|
| 1000 |
+
0.0,
|
| 1001 |
+
0.0,
|
| 1002 |
+
0.15
|
| 1003 |
+
],
|
| 1004 |
+
"bc_bp": [
|
| 1005 |
+
]
|
| 1006 |
+
},
|
| 1007 |
+
"24": {
|
| 1008 |
+
"name": "小型护甲板",
|
| 1009 |
+
"block_type": "基础",
|
| 1010 |
+
"bbox_size": [
|
| 1011 |
+
0.9,
|
| 1012 |
+
0.9,
|
| 1013 |
+
0.3
|
| 1014 |
+
],
|
| 1015 |
+
"bc_gc": [
|
| 1016 |
+
0.0,
|
| 1017 |
+
0.0,
|
| 1018 |
+
0.15
|
| 1019 |
+
],
|
| 1020 |
+
"bc_bp": [
|
| 1021 |
+
]
|
| 1022 |
+
},
|
| 1023 |
+
"18": {
|
| 1024 |
+
"name": "活塞",
|
| 1025 |
+
"block_type": "机械",
|
| 1026 |
+
"bbox_size": [
|
| 1027 |
+
1.0,
|
| 1028 |
+
1.0,
|
| 1029 |
+
2.0
|
| 1030 |
+
],
|
| 1031 |
+
"bc_gc": [
|
| 1032 |
+
0.0,
|
| 1033 |
+
0.0,
|
| 1034 |
+
1.0
|
| 1035 |
+
],
|
| 1036 |
+
"bc_bp": [
|
| 1037 |
+
[
|
| 1038 |
+
0.0,
|
| 1039 |
+
0.0,
|
| 1040 |
+
2.0
|
| 1041 |
+
],
|
| 1042 |
+
[
|
| 1043 |
+
-0.5,
|
| 1044 |
+
0.0,
|
| 1045 |
+
1.5
|
| 1046 |
+
],
|
| 1047 |
+
|
| 1048 |
+
[
|
| 1049 |
+
0.5,
|
| 1050 |
+
0.0,
|
| 1051 |
+
1.5
|
| 1052 |
+
],
|
| 1053 |
+
|
| 1054 |
+
[
|
| 1055 |
+
0.0,
|
| 1056 |
+
0.5,
|
| 1057 |
+
1.5
|
| 1058 |
+
],
|
| 1059 |
+
[
|
| 1060 |
+
0.0,
|
| 1061 |
+
-0.5,
|
| 1062 |
+
1.5
|
| 1063 |
+
]
|
| 1064 |
+
]
|
| 1065 |
+
},
|
| 1066 |
+
"18_1": {
|
| 1067 |
+
"name": "活塞(收缩)",
|
| 1068 |
+
"block_type": "机械",
|
| 1069 |
+
"bbox_size": [
|
| 1070 |
+
1.0,
|
| 1071 |
+
1.0,
|
| 1072 |
+
1.0
|
| 1073 |
+
],
|
| 1074 |
+
"bc_gc": [
|
| 1075 |
+
0.0,
|
| 1076 |
+
0.0,
|
| 1077 |
+
0.5
|
| 1078 |
+
],
|
| 1079 |
+
"bc_bp": [
|
| 1080 |
+
[
|
| 1081 |
+
0.0,
|
| 1082 |
+
0.0,
|
| 1083 |
+
1.0
|
| 1084 |
+
],
|
| 1085 |
+
[
|
| 1086 |
+
-0.5,
|
| 1087 |
+
0.0,
|
| 1088 |
+
0.5
|
| 1089 |
+
],
|
| 1090 |
+
[
|
| 1091 |
+
0.5,
|
| 1092 |
+
0.0,
|
| 1093 |
+
0.5
|
| 1094 |
+
],
|
| 1095 |
+
[
|
| 1096 |
+
0.0,
|
| 1097 |
+
0.5,
|
| 1098 |
+
0.5
|
| 1099 |
+
],
|
| 1100 |
+
[
|
| 1101 |
+
0.0,
|
| 1102 |
+
-0.5,
|
| 1103 |
+
0.5
|
| 1104 |
+
]
|
| 1105 |
+
]
|
| 1106 |
+
},
|
| 1107 |
+
"51": {
|
| 1108 |
+
"name": "无动力大齿轮",
|
| 1109 |
+
"block_type": "机械",
|
| 1110 |
+
"bbox_size": [
|
| 1111 |
+
5.0,
|
| 1112 |
+
5.0,
|
| 1113 |
+
1.0
|
| 1114 |
+
],
|
| 1115 |
+
"bc_gc": [
|
| 1116 |
+
0.0,
|
| 1117 |
+
0.0,
|
| 1118 |
+
0.5
|
| 1119 |
+
],
|
| 1120 |
+
"bc_bp": [
|
| 1121 |
+
[
|
| 1122 |
+
0.0,
|
| 1123 |
+
0.0,
|
| 1124 |
+
1.0
|
| 1125 |
+
],
|
| 1126 |
+
[
|
| 1127 |
+
-1.0,
|
| 1128 |
+
0.0,
|
| 1129 |
+
1.0
|
| 1130 |
+
],
|
| 1131 |
+
|
| 1132 |
+
[
|
| 1133 |
+
1.0,
|
| 1134 |
+
0.0,
|
| 1135 |
+
1.0
|
| 1136 |
+
],
|
| 1137 |
+
|
| 1138 |
+
[
|
| 1139 |
+
0.0,
|
| 1140 |
+
1.0,
|
| 1141 |
+
1.0
|
| 1142 |
+
],
|
| 1143 |
+
[
|
| 1144 |
+
0.0,
|
| 1145 |
+
-1.0,
|
| 1146 |
+
1.0
|
| 1147 |
+
]
|
| 1148 |
+
]
|
| 1149 |
+
},
|
| 1150 |
+
"17": {
|
| 1151 |
+
"name": "圆盘锯",
|
| 1152 |
+
"block_type": "武器",
|
| 1153 |
+
"bbox_size": [
|
| 1154 |
+
2,
|
| 1155 |
+
2,
|
| 1156 |
+
0.5
|
| 1157 |
+
],
|
| 1158 |
+
"bc_gc": [
|
| 1159 |
+
0.0,
|
| 1160 |
+
0.0,
|
| 1161 |
+
0.25
|
| 1162 |
+
],
|
| 1163 |
+
"bc_bp": [
|
| 1164 |
+
]
|
| 1165 |
+
},
|
| 1166 |
+
"14": {
|
| 1167 |
+
"name": "飞行块",
|
| 1168 |
+
"block_type": "行走",
|
| 1169 |
+
"bbox_size": [
|
| 1170 |
+
1,
|
| 1171 |
+
1,
|
| 1172 |
+
1
|
| 1173 |
+
],
|
| 1174 |
+
"bc_gc": [
|
| 1175 |
+
0.0,
|
| 1176 |
+
0.0,
|
| 1177 |
+
0.5
|
| 1178 |
+
],
|
| 1179 |
+
"bc_bp": [
|
| 1180 |
+
]
|
| 1181 |
+
},
|
| 1182 |
+
"39": {
|
| 1183 |
+
"name": "中型动力齿轮",
|
| 1184 |
+
"block_type": "行走",
|
| 1185 |
+
"bbox_size": [
|
| 1186 |
+
2,
|
| 1187 |
+
2,
|
| 1188 |
+
1
|
| 1189 |
+
],
|
| 1190 |
+
"bc_gc": [
|
| 1191 |
+
0.0,
|
| 1192 |
+
0.0,
|
| 1193 |
+
0.5
|
| 1194 |
+
],
|
| 1195 |
+
"bc_bp": [
|
| 1196 |
+
[
|
| 1197 |
+
0.0,
|
| 1198 |
+
0.0,
|
| 1199 |
+
1.0
|
| 1200 |
+
]
|
| 1201 |
+
]
|
| 1202 |
+
},
|
| 1203 |
+
"4": {
|
| 1204 |
+
"name": "解耦器",
|
| 1205 |
+
"block_type": "机械",
|
| 1206 |
+
"bbox_size": [
|
| 1207 |
+
1,
|
| 1208 |
+
1,
|
| 1209 |
+
1
|
| 1210 |
+
],
|
| 1211 |
+
"bc_gc": [
|
| 1212 |
+
0.0,
|
| 1213 |
+
0.0,
|
| 1214 |
+
0.5
|
| 1215 |
+
],
|
| 1216 |
+
"bc_bp": [
|
| 1217 |
+
[
|
| 1218 |
+
0.0,
|
| 1219 |
+
0.0,
|
| 1220 |
+
1.0
|
| 1221 |
+
],
|
| 1222 |
+
[
|
| 1223 |
+
-0.5,
|
| 1224 |
+
0.0,
|
| 1225 |
+
0.5
|
| 1226 |
+
],
|
| 1227 |
+
[
|
| 1228 |
+
0.5,
|
| 1229 |
+
0.0,
|
| 1230 |
+
0.5
|
| 1231 |
+
],
|
| 1232 |
+
[
|
| 1233 |
+
0.0,
|
| 1234 |
+
0.5,
|
| 1235 |
+
0.5
|
| 1236 |
+
],
|
| 1237 |
+
[
|
| 1238 |
+
0.0,
|
| 1239 |
+
-0.5,
|
| 1240 |
+
0.5
|
| 1241 |
+
]
|
| 1242 |
+
]
|
| 1243 |
+
},
|
| 1244 |
+
"74": {
|
| 1245 |
+
"name": "热气球",
|
| 1246 |
+
"block_type": "行走",
|
| 1247 |
+
"bbox_size": [
|
| 1248 |
+
3,
|
| 1249 |
+
3,
|
| 1250 |
+
3
|
| 1251 |
+
],
|
| 1252 |
+
"bc_gc": [
|
| 1253 |
+
0.0,
|
| 1254 |
+
0.0,
|
| 1255 |
+
1.5
|
| 1256 |
+
],
|
| 1257 |
+
"bc_bp": [[
|
| 1258 |
+
0.0,
|
| 1259 |
+
0.0,
|
| 1260 |
+
3.0
|
| 1261 |
+
],
|
| 1262 |
+
[
|
| 1263 |
+
-1.5,
|
| 1264 |
+
0.0,
|
| 1265 |
+
1.5
|
| 1266 |
+
],
|
| 1267 |
+
[
|
| 1268 |
+
1.5,
|
| 1269 |
+
0.0,
|
| 1270 |
+
1.5
|
| 1271 |
+
],
|
| 1272 |
+
[
|
| 1273 |
+
0.0,
|
| 1274 |
+
1.5,
|
| 1275 |
+
1.5
|
| 1276 |
+
],
|
| 1277 |
+
[
|
| 1278 |
+
0.0,
|
| 1279 |
+
-1.5,
|
| 1280 |
+
1.5
|
| 1281 |
+
]
|
| 1282 |
+
]
|
| 1283 |
+
},
|
| 1284 |
+
"29": {
|
| 1285 |
+
"name": "装甲盘",
|
| 1286 |
+
"block_type": "护甲",
|
| 1287 |
+
"bbox_size": [
|
| 1288 |
+
2,
|
| 1289 |
+
2,
|
| 1290 |
+
0.3
|
| 1291 |
+
],
|
| 1292 |
+
"bc_gc": [
|
| 1293 |
+
0.0,
|
| 1294 |
+
0.0,
|
| 1295 |
+
0.15
|
| 1296 |
+
],
|
| 1297 |
+
"bc_bp": [
|
| 1298 |
+
]
|
| 1299 |
+
}
|
| 1300 |
+
|
| 1301 |
+
}
|
README.md
CHANGED
|
@@ -1,13 +1,136 @@
|
|
| 1 |
-
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
-
sdk: gradio
|
| 7 |
-
sdk_version:
|
| 8 |
-
app_file: app.py
|
| 9 |
-
pinned: false
|
| 10 |
-
license: mit
|
| 11 |
-
---
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Besiege Machine Generator
|
| 3 |
+
emoji: 🎮
|
| 4 |
+
colorFrom: purple
|
| 5 |
+
colorTo: blue
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: 4.44.1
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
license: mit
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
# 🎮 Besiege Machine Generator
|
| 14 |
+
|
| 15 |
+
使用 AI 生成你的 Besiege 机器设计!这个应用使用 DeepSeek AI 模型来理解你的描述,并生成可以直接在 Besiege 游戏中使用的 .bsg 文件。
|
| 16 |
+
|
| 17 |
+
## ✨ 特性
|
| 18 |
+
|
| 19 |
+
- 🤖 **AI 生成**:使用自然语言描述你想要的机器,AI 会自动生成
|
| 20 |
+
- 🔄 **手动转换**:如果你已经有 JSON 数据,可以直接转换为 .bsg 文件
|
| 21 |
+
- 💾 **一键下载**:生成后直接下载 .bsg 文件
|
| 22 |
+
- 🎨 **现代界面**:美观易用的用户界面
|
| 23 |
+
|
| 24 |
+
## 🚀 使用方法
|
| 25 |
+
|
| 26 |
+
### AI 生成模式
|
| 27 |
+
|
| 28 |
+
1. 在文本框中描述你想要创建的机器
|
| 29 |
+
- 例如:"创建一个四轮车,带有旋转块驱动"
|
| 30 |
+
2. (可选)调整高级设置
|
| 31 |
+
- Temperature:控制创意程度
|
| 32 |
+
- Max Tokens:控制生成长度
|
| 33 |
+
3. 点击"生成机器"按钮
|
| 34 |
+
4. 等待 AI 生成完成
|
| 35 |
+
5. 下载生成的 .bsg 文件
|
| 36 |
+
|
| 37 |
+
### 手动转换模式
|
| 38 |
+
|
| 39 |
+
1. 粘贴你的 JSON 机器数据
|
| 40 |
+
2. 点击"转换为 XML"按钮
|
| 41 |
+
3. 下载生成的 .bsg 文件
|
| 42 |
+
|
| 43 |
+
## 📋 JSON 格式说明
|
| 44 |
+
|
| 45 |
+
JSON 格式应该包含机器的方块信息,例如:
|
| 46 |
+
|
| 47 |
+
```json
|
| 48 |
+
[
|
| 49 |
+
{"id": "0", "order_id": 0, "parent": -1, "bp_id": -1},
|
| 50 |
+
{"id": "1", "order_id": 1, "parent": 0, "bp_id": 0},
|
| 51 |
+
...
|
| 52 |
+
]
|
| 53 |
+
```
|
| 54 |
+
|
| 55 |
+
### 字段说明:
|
| 56 |
+
|
| 57 |
+
- `id`: 方块类型 ID
|
| 58 |
+
- `order_id`: 方块的顺序编号
|
| 59 |
+
- `parent`: 父方块的 order_id(-1 表示根方块)
|
| 60 |
+
- `bp_id`: 连接到父方块的建造点 ID
|
| 61 |
+
|
| 62 |
+
对于线性方块(如橡皮筋,id=9):
|
| 63 |
+
|
| 64 |
+
```json
|
| 65 |
+
{"id": "9", "order_id": 2, "parent_a": 0, "bp_id_a": 0, "parent_b": 1, "bp_id_b": 2}
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
## 🎮 可用方块类型
|
| 69 |
+
|
| 70 |
+
- `0`: Starting Block(起始方块)
|
| 71 |
+
- `1`: Wooden Block(木块)
|
| 72 |
+
- `2`: Powered Wheel(动力轮)
|
| 73 |
+
- `15`: Small Wooden Block(小木块)
|
| 74 |
+
- `16`: Spring(弹簧)
|
| 75 |
+
- `22`: Rotating Block(旋转块)
|
| 76 |
+
- `30`: Container(容器)
|
| 77 |
+
- `35`: Ballast(配重)
|
| 78 |
+
- `36`: Boulder(巨石)
|
| 79 |
+
- `41`: Wooden Rod(木棒)
|
| 80 |
+
- `63`: Log(原木)
|
| 81 |
+
- `9`: Rubber Band(橡皮筋,线性方块)
|
| 82 |
+
|
| 83 |
+
## 🔧 技术栈
|
| 84 |
+
|
| 85 |
+
- **Frontend**: Gradio
|
| 86 |
+
- **AI Model**: DeepSeek-R1-Distill-Llama-8B
|
| 87 |
+
- **Backend**: Python
|
| 88 |
+
- **Libraries**: NumPy, SciPy, Hugging Face Hub
|
| 89 |
+
|
| 90 |
+
## ⚙️ 本地运行
|
| 91 |
+
|
| 92 |
+
1. 克隆仓库
|
| 93 |
+
```bash
|
| 94 |
+
git clone <your-repo-url>
|
| 95 |
+
cd BesiegeField-MachineGenerator
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
2. 安装依赖
|
| 99 |
+
```bash
|
| 100 |
+
pip install -r requirements.txt
|
| 101 |
+
```
|
| 102 |
+
|
| 103 |
+
3. 设置环境变量(可选,用于访问 HF 模型)
|
| 104 |
+
```bash
|
| 105 |
+
export HF_TOKEN=your_huggingface_token
|
| 106 |
+
```
|
| 107 |
+
|
| 108 |
+
4. 运行应用
|
| 109 |
+
```bash
|
| 110 |
+
python app.py
|
| 111 |
+
```
|
| 112 |
+
|
| 113 |
+
## 📝 注意事项
|
| 114 |
+
|
| 115 |
+
1. **生成质量**:AI 生成的机器可能需要在游戏中进行调整
|
| 116 |
+
2. **尺寸限制**:
|
| 117 |
+
- 长度(Z): ≤ 17
|
| 118 |
+
- 宽度(X): ≤ 17
|
| 119 |
+
- 高度(Y): ≤ 9.5
|
| 120 |
+
3. **方块连接**:新方块必须连接到现有方块的可建造点
|
| 121 |
+
4. **坐标系统**:使用左手坐标系,Y+ 向上,Z+ 向前,X+ 向右
|
| 122 |
+
|
| 123 |
+
## 🤝 贡献
|
| 124 |
+
|
| 125 |
+
欢迎提交 Issue 和 Pull Request!
|
| 126 |
+
|
| 127 |
+
## 📄 许可证
|
| 128 |
+
|
| 129 |
+
MIT License
|
| 130 |
+
|
| 131 |
+
## 🙏 致谢
|
| 132 |
+
|
| 133 |
+
- [Besiege](https://store.steampowered.com/app/346010/Besiege/) - 精彩的物理建造游戏
|
| 134 |
+
- [DeepSeek AI](https://huggingface.co/deepseek-ai) - 强大的 AI 模型
|
| 135 |
+
- [Gradio](https://gradio.app/) - 快速构建 ML 应用界面
|
| 136 |
+
|
app.py
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import os
|
| 3 |
+
from huggingface_hub import InferenceClient
|
| 4 |
+
from get_machine_from_json import string_to_bsg
|
| 5 |
+
import json
|
| 6 |
+
|
| 7 |
+
# 读取系统提示词
|
| 8 |
+
with open('user_system_prompt.txt', 'r', encoding='utf-8') as f:
|
| 9 |
+
SYSTEM_PROMPT = f.read()
|
| 10 |
+
|
| 11 |
+
# 读取示例(如果存在)
|
| 12 |
+
EXAMPLES = []
|
| 13 |
+
try:
|
| 14 |
+
with open('examples.json', 'r', encoding='utf-8') as f:
|
| 15 |
+
examples_data = json.load(f)
|
| 16 |
+
EXAMPLES = [[ex["description"]] for ex in examples_data.get("examples", [])]
|
| 17 |
+
except:
|
| 18 |
+
pass
|
| 19 |
+
|
| 20 |
+
# 初始化 HF 客户端 - 使用 DeepSeek 免费模型
|
| 21 |
+
def create_client():
|
| 22 |
+
"""创建 HF Inference 客户端"""
|
| 23 |
+
return InferenceClient(
|
| 24 |
+
model="deepseek-ai/DeepSeek-R1-Distill-Llama-8B",
|
| 25 |
+
token=os.environ.get("HF_TOKEN") # 从环境变量读取 token
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
def generate_machine(user_prompt, temperature=0.7, max_tokens=4096):
|
| 29 |
+
"""
|
| 30 |
+
使用 AI 生成机器设计的 JSON,然后转换为 XML
|
| 31 |
+
|
| 32 |
+
Args:
|
| 33 |
+
user_prompt: 用户输入的机器描述
|
| 34 |
+
temperature: 生成温度
|
| 35 |
+
max_tokens: 最大 token 数
|
| 36 |
+
|
| 37 |
+
Returns:
|
| 38 |
+
tuple: (ai_response, xml_string, status_message)
|
| 39 |
+
"""
|
| 40 |
+
if not user_prompt.strip():
|
| 41 |
+
return "", "", "❌ 请输入机器描述!"
|
| 42 |
+
|
| 43 |
+
try:
|
| 44 |
+
client = create_client()
|
| 45 |
+
|
| 46 |
+
# 构建消息
|
| 47 |
+
messages = [
|
| 48 |
+
{"role": "system", "content": SYSTEM_PROMPT},
|
| 49 |
+
{"role": "user", "content": user_prompt}
|
| 50 |
+
]
|
| 51 |
+
|
| 52 |
+
# 调用 API
|
| 53 |
+
response = ""
|
| 54 |
+
for message in client.chat_completion(
|
| 55 |
+
messages=messages,
|
| 56 |
+
temperature=temperature,
|
| 57 |
+
max_tokens=max_tokens,
|
| 58 |
+
stream=True,
|
| 59 |
+
):
|
| 60 |
+
if message.choices[0].delta.content:
|
| 61 |
+
response += message.choices[0].delta.content
|
| 62 |
+
|
| 63 |
+
# 尝试转换为 XML
|
| 64 |
+
try:
|
| 65 |
+
xml_string = string_to_bsg(response)
|
| 66 |
+
status = "✅ 生成成功!可以下载 .bsg 文件了。"
|
| 67 |
+
return response, xml_string, status
|
| 68 |
+
except Exception as e:
|
| 69 |
+
return response, "", f"⚠️ AI 生成完成,但转换 XML 时出错:{str(e)}"
|
| 70 |
+
|
| 71 |
+
except Exception as e:
|
| 72 |
+
return "", "", f"❌ 生成失败:{str(e)}"
|
| 73 |
+
|
| 74 |
+
def convert_json_to_xml(json_input):
|
| 75 |
+
"""
|
| 76 |
+
手动转换 JSON 到 XML
|
| 77 |
+
|
| 78 |
+
Args:
|
| 79 |
+
json_input: JSON 字符串或 JSON 数据
|
| 80 |
+
|
| 81 |
+
Returns:
|
| 82 |
+
tuple: (xml_string, status_message)
|
| 83 |
+
"""
|
| 84 |
+
if not json_input.strip():
|
| 85 |
+
return "", "❌ 请输入 JSON 数据!"
|
| 86 |
+
|
| 87 |
+
try:
|
| 88 |
+
xml_string = string_to_bsg(json_input)
|
| 89 |
+
return xml_string, "✅ 转换成功!"
|
| 90 |
+
except Exception as e:
|
| 91 |
+
return "", f"❌ 转换失败:{str(e)}"
|
| 92 |
+
|
| 93 |
+
def save_xml_to_file(xml_content):
|
| 94 |
+
"""保存 XML 到 .bsg 文件"""
|
| 95 |
+
if not xml_content:
|
| 96 |
+
return None
|
| 97 |
+
|
| 98 |
+
output_path = "generated_machine.bsg"
|
| 99 |
+
with open(output_path, 'w', encoding='utf-8') as f:
|
| 100 |
+
f.write(xml_content)
|
| 101 |
+
return output_path
|
| 102 |
+
|
| 103 |
+
# 自定义 CSS 样式,参考 index.html
|
| 104 |
+
custom_css = """
|
| 105 |
+
.gradio-container {
|
| 106 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
.header {
|
| 110 |
+
text-align: center;
|
| 111 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 112 |
+
padding: 30px;
|
| 113 |
+
border-radius: 10px;
|
| 114 |
+
color: white;
|
| 115 |
+
margin-bottom: 20px;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
.header h1 {
|
| 119 |
+
font-size: 2.5em;
|
| 120 |
+
margin-bottom: 10px;
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
.info-box {
|
| 124 |
+
background: #e7f3ff;
|
| 125 |
+
border-left: 4px solid #2196F3;
|
| 126 |
+
padding: 15px;
|
| 127 |
+
margin-bottom: 20px;
|
| 128 |
+
border-radius: 4px;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
.success-box {
|
| 132 |
+
background: #d4edda;
|
| 133 |
+
color: #155724;
|
| 134 |
+
padding: 15px;
|
| 135 |
+
border-radius: 8px;
|
| 136 |
+
border: 1px solid #c3e6cb;
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
.error-box {
|
| 140 |
+
background: #f8d7da;
|
| 141 |
+
color: #721c24;
|
| 142 |
+
padding: 15px;
|
| 143 |
+
border-radius: 8px;
|
| 144 |
+
border: 1px solid #f5c6cb;
|
| 145 |
+
}
|
| 146 |
+
"""
|
| 147 |
+
|
| 148 |
+
# 创建 Gradio 界面
|
| 149 |
+
with gr.Blocks(css=custom_css, theme=gr.themes.Soft(), title="🎮 Besiege Machine Generator") as demo:
|
| 150 |
+
|
| 151 |
+
# 头部
|
| 152 |
+
gr.HTML("""
|
| 153 |
+
<div class="header">
|
| 154 |
+
<h1>🎮 Besiege Machine Generator</h1>
|
| 155 |
+
<p style="font-size: 1.1em; opacity: 0.9;">使用 AI 生成你的 Besiege 机器设计</p>
|
| 156 |
+
<span style="background: rgba(255, 255, 255, 0.2); padding: 5px 15px; border-radius: 20px; font-size: 0.9em;">
|
| 157 |
+
✨ Powered by DeepSeek AI
|
| 158 |
+
</span>
|
| 159 |
+
</div>
|
| 160 |
+
""")
|
| 161 |
+
|
| 162 |
+
with gr.Tabs():
|
| 163 |
+
# Tab 1: AI 生成
|
| 164 |
+
with gr.Tab("🤖 AI 生成"):
|
| 165 |
+
gr.HTML("""
|
| 166 |
+
<div class="info-box">
|
| 167 |
+
<h4>💡 使用说明</h4>
|
| 168 |
+
<ul>
|
| 169 |
+
<li>描述你想要创建的机器</li>
|
| 170 |
+
<li>点击"生成机器"按钮</li>
|
| 171 |
+
<li>等待 AI 生成完成</li>
|
| 172 |
+
<li>下载生成的 .bsg 文件</li>
|
| 173 |
+
</ul>
|
| 174 |
+
</div>
|
| 175 |
+
""")
|
| 176 |
+
|
| 177 |
+
with gr.Row():
|
| 178 |
+
with gr.Column():
|
| 179 |
+
user_input = gr.Textbox(
|
| 180 |
+
label="描述你的机器 *",
|
| 181 |
+
placeholder="例如:创建一个四轮车,能够向前移动...",
|
| 182 |
+
lines=5
|
| 183 |
+
)
|
| 184 |
+
|
| 185 |
+
# 添加示例
|
| 186 |
+
if EXAMPLES:
|
| 187 |
+
gr.Examples(
|
| 188 |
+
examples=EXAMPLES,
|
| 189 |
+
inputs=user_input,
|
| 190 |
+
label="💡 示例提示"
|
| 191 |
+
)
|
| 192 |
+
|
| 193 |
+
with gr.Accordion("⚙️ 高级设置", open=False):
|
| 194 |
+
temperature = gr.Slider(
|
| 195 |
+
minimum=0.1,
|
| 196 |
+
maximum=1.5,
|
| 197 |
+
value=0.7,
|
| 198 |
+
step=0.1,
|
| 199 |
+
label="Temperature(温度)",
|
| 200 |
+
info="较高的值会产生更有创意但可能不太稳定的结果"
|
| 201 |
+
)
|
| 202 |
+
max_tokens = gr.Slider(
|
| 203 |
+
minimum=1024,
|
| 204 |
+
maximum=8192,
|
| 205 |
+
value=4096,
|
| 206 |
+
step=512,
|
| 207 |
+
label="Max Tokens(最大令牌数)",
|
| 208 |
+
info="生成的最大长度"
|
| 209 |
+
)
|
| 210 |
+
|
| 211 |
+
generate_btn = gr.Button("🚀 生成机器", variant="primary", size="lg")
|
| 212 |
+
|
| 213 |
+
status_output = gr.Markdown(label="状态")
|
| 214 |
+
|
| 215 |
+
with gr.Row():
|
| 216 |
+
with gr.Column():
|
| 217 |
+
ai_response = gr.Textbox(
|
| 218 |
+
label="AI 响应(JSON)",
|
| 219 |
+
lines=10,
|
| 220 |
+
max_lines=20,
|
| 221 |
+
show_copy_button=True
|
| 222 |
+
)
|
| 223 |
+
|
| 224 |
+
with gr.Column():
|
| 225 |
+
xml_output = gr.Textbox(
|
| 226 |
+
label="XML 输出",
|
| 227 |
+
lines=10,
|
| 228 |
+
max_lines=20,
|
| 229 |
+
show_copy_button=True
|
| 230 |
+
)
|
| 231 |
+
|
| 232 |
+
download_btn = gr.File(label="📥 下载 .bsg 文件")
|
| 233 |
+
|
| 234 |
+
# 绑定生成按钮
|
| 235 |
+
def generate_and_save(user_prompt, temp, max_tok):
|
| 236 |
+
ai_resp, xml_str, status = generate_machine(user_prompt, temp, max_tok)
|
| 237 |
+
file_path = save_xml_to_file(xml_str) if xml_str else None
|
| 238 |
+
return ai_resp, xml_str, status, file_path
|
| 239 |
+
|
| 240 |
+
generate_btn.click(
|
| 241 |
+
fn=generate_and_save,
|
| 242 |
+
inputs=[user_input, temperature, max_tokens],
|
| 243 |
+
outputs=[ai_response, xml_output, status_output, download_btn]
|
| 244 |
+
)
|
| 245 |
+
|
| 246 |
+
# Tab 2: 手动转换
|
| 247 |
+
with gr.Tab("🔄 手动转换"):
|
| 248 |
+
gr.HTML("""
|
| 249 |
+
<div class="info-box">
|
| 250 |
+
<h4>💡 使用说明</h4>
|
| 251 |
+
<ul>
|
| 252 |
+
<li>粘贴你的 JSON 机器数据</li>
|
| 253 |
+
<li>点击"转换为 XML"按钮</li>
|
| 254 |
+
<li>下载生成的 .bsg 文件</li>
|
| 255 |
+
</ul>
|
| 256 |
+
</div>
|
| 257 |
+
""")
|
| 258 |
+
|
| 259 |
+
json_input = gr.Textbox(
|
| 260 |
+
label="JSON 输入",
|
| 261 |
+
placeholder='粘贴你的 JSON 数据...',
|
| 262 |
+
lines=10,
|
| 263 |
+
max_lines=20
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
convert_btn = gr.Button("🔄 转换为 XML", variant="primary", size="lg")
|
| 267 |
+
|
| 268 |
+
status_manual = gr.Markdown(label="状态")
|
| 269 |
+
|
| 270 |
+
xml_output_manual = gr.Textbox(
|
| 271 |
+
label="XML 输出",
|
| 272 |
+
lines=10,
|
| 273 |
+
max_lines=20,
|
| 274 |
+
show_copy_button=True
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
download_manual_btn = gr.File(label="📥 下载 .bsg 文件")
|
| 278 |
+
|
| 279 |
+
# 绑定转换按钮
|
| 280 |
+
def convert_and_save(json_str):
|
| 281 |
+
xml_str, status = convert_json_to_xml(json_str)
|
| 282 |
+
file_path = save_xml_to_file(xml_str) if xml_str else None
|
| 283 |
+
return xml_str, status, file_path
|
| 284 |
+
|
| 285 |
+
convert_btn.click(
|
| 286 |
+
fn=convert_and_save,
|
| 287 |
+
inputs=[json_input],
|
| 288 |
+
outputs=[xml_output_manual, status_manual, download_manual_btn]
|
| 289 |
+
)
|
| 290 |
+
|
| 291 |
+
# 底部信息
|
| 292 |
+
gr.HTML("""
|
| 293 |
+
<div style="text-align: center; margin-top: 30px; padding: 20px; background: #f8f9fa; border-radius: 10px;">
|
| 294 |
+
<p style="color: #666; margin: 5px 0;">
|
| 295 |
+
🤖 使用 <a href="https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Llama-8B" target="_blank">DeepSeek-R1-Distill-Llama-8B</a> 模型
|
| 296 |
+
</p>
|
| 297 |
+
<p style="color: #666; margin: 5px 0;">
|
| 298 |
+
⚠️ 注意:生成的机器可能需要在游戏中进行调整
|
| 299 |
+
</p>
|
| 300 |
+
</div>
|
| 301 |
+
""")
|
| 302 |
+
|
| 303 |
+
# 启动应用
|
| 304 |
+
if __name__ == "__main__":
|
| 305 |
+
demo.launch()
|
| 306 |
+
|
get_machine_from_json.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sys import exception
|
| 2 |
+
import utils as dp # 使用别名dp,方便调用
|
| 3 |
+
from utils import extract_json_from_string
|
| 4 |
+
import os
|
| 5 |
+
|
| 6 |
+
def format_json(input_json):
|
| 7 |
+
try:
|
| 8 |
+
new_clean_json=[]
|
| 9 |
+
for json_info in input_json:
|
| 10 |
+
new_clean_dict={}
|
| 11 |
+
if int(json_info["id"]) not in [7,9]:
|
| 12 |
+
new_clean_dict["id"]=str(json_info["id"])
|
| 13 |
+
new_clean_dict["order_id"]=int(json_info["order_id"])
|
| 14 |
+
new_clean_dict["parent"]=int(json_info["parent"])
|
| 15 |
+
new_clean_dict["bp_id"]=int(json_info["bp_id"])
|
| 16 |
+
else:
|
| 17 |
+
new_clean_dict["id"]=str(json_info["id"])
|
| 18 |
+
new_clean_dict["order_id"]=int(json_info["order_id"])
|
| 19 |
+
new_clean_dict["parent_a"]=int(json_info["parent_a"])
|
| 20 |
+
new_clean_dict["bp_id_a"]=int(json_info["bp_id_a"])
|
| 21 |
+
new_clean_dict["parent_b"]=int(json_info["parent_b"])
|
| 22 |
+
new_clean_dict["bp_id_b"]=int(json_info["bp_id_b"])
|
| 23 |
+
new_clean_json.append(new_clean_dict)
|
| 24 |
+
return new_clean_json
|
| 25 |
+
except:
|
| 26 |
+
return input_json
|
| 27 |
+
|
| 28 |
+
def get_machine_from_json(solution_str,step,score,save_root):
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
tmp_path = Rf"{save_root}/step{step}score{score}.bsg"
|
| 32 |
+
solution_str =solution_str.replace("", "").replace("\\", "")
|
| 33 |
+
machine_json = extract_json_from_string(solution_str)
|
| 34 |
+
|
| 35 |
+
machine_json = format_json(machine_json)
|
| 36 |
+
dp.json_to_xml(machine_json,tmp_path)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def string_to_bsg(solution_str):
|
| 40 |
+
solution_str =solution_str.replace("", "").replace("\\", "")
|
| 41 |
+
|
| 42 |
+
machine_json = extract_json_from_string(solution_str)
|
| 43 |
+
machine_json = format_json(machine_json)
|
| 44 |
+
xml_string = dp.json_to_xml(machine_json)
|
| 45 |
+
return xml_string
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio==4.44.1
|
| 2 |
+
huggingface_hub==0.25.2
|
| 3 |
+
numpy==1.24.3
|
| 4 |
+
scipy==1.11.4
|
| 5 |
+
|
user_system_prompt.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
You are a machine builder. Your task is to generate a complete machine as a JSON file based on the user's request. Add new blocks to the initial structure; do not modify or delete it.\n\n**Rules:**\n1. **Coordinate System:** Left-handed coordinate system, y+ upwards, z+ forward and x+ right.\n1. **Block Placement:** New blocks must attach to `constructible_points` of existing blocks. Blocks cannot overlap.\n2. **size Limit:** The final machine must not exceed dimensions of 17 (Length, Z), 17 (Width, X), 9.5 (Height, Y).\n3. **Functionality:** Ensure functional blocks are oriented correctly.\n4. **Efficiency:** Use as few blocks as possible without hurting performance.\n5. **CoT & Token saving** You MUST think step by step, and MUST return both chat response and chain of thoughts (reasoning_content). Chat response in no more than 1,024 tokens, chain of thoughts no more than 800 tokens.\n\n**Block Data:**\nYou can only use blocks from this list. A block's default orientation is Z+.\n* **constructible_points:**\n * `id`: the i-th constructible_point of this block.\n * `pos`: coordinates relative to the building center(which is the constructible_point of the parent block) of this block.\n * `orientation`: orientation relative to the building center of this block.\n* **Tags:**\n * `Non-static`: Block can generate force or movement.\n * `Non-stable`: Connection to parent is not rigid (e.g., hinges, boulders).\n * `Linear`: Do not collide with other blocks, but will occupy two constructible_points.\n* **Special Blocks:**\n * **Boulder (id 36):** Does not physically connect to other blocks.\n * **Rubber Band (id 9):** A linear block that pulls its two connection points together.\n\n[{'name': 'Starting Block', 'description': 'Machine root. Cannot be placed or deleted, and only one can exist at a time. Initial position fixed, initial orientation is z+.', 'type id': 0, 'size': [1, 1, 1], 'constructible_points': [{'id': 0, 'pos': [0, 0, 0.5], 'orientation': 'Front'}, {'id': 1, 'pos': [0, 0, -0.5], 'orientation': 'Back'}, {'id': 2, 'pos': [-0.5, 0, 0], 'orientation': 'Left'}, {'id': 3, 'pos': [0.5, 0, 0], 'orientation': 'Right'}, {'id': 4, 'pos': [0, 0.5, 0], 'orientation': 'Up'}, {'id': 5, 'pos': [0, -0.5, 0], 'orientation': 'Down'}], 'mass': 0.25}, {'name': 'Small Wooden Block', 'description': 'A basic construction block, cubic in shape.', 'type id': 15, 'size': [1, 1, 1], 'constructible_points': [{'id': 0, 'pos': [0, 0, 1], 'orientation': 'Front'}, {'id': 1, 'pos': [-0.5, 0, 0.5], 'orientation': 'Left'}, {'id': 2, 'pos': [0.5, 0, 0.5], 'orientation': 'Right'}, {'id': 3, 'pos': [0, 0.5, 0.5], 'orientation': 'Up'}, {'id': 4, 'pos': [0, -0.5, 0.5], 'orientation': 'Down'}], 'mass': 0.3}, {'name': 'Wooden Block', 'description': 'A basic construction block.', 'type id': 1, 'size': [1, 1, 2], 'constructible_points': [{'id': 0, 'pos': [0, 0, 2], 'orientation': 'Front'}, {'id': 1, 'pos': [-0.5, 0, 0.5], 'orientation': 'Left'}, {'id': 2, 'pos': [-0.5, 0, 1.5], 'orientation': 'Left'}, {'id': 3, 'pos': [0.5, 0, 0.5], 'orientation': 'Right'}, {'id': 4, 'pos': [0.5, 0, 1.5], 'orientation': 'Right'}, {'id': 5, 'pos': [0, 0.5, 0.5], 'orientation': 'Up'}, {'id': 6, 'pos': [0, 0.5, 1.5], 'orientation': 'Up'}, {'id': 7, 'pos': [0, -0.5, 0.5], 'orientation': 'Down'}, {'id': 8, 'pos': [0, -0.5, 1.5], 'orientation': 'Down'}], 'mass': 0.5}, {'name': 'Wooden Rod', 'description': 'A basic construction block, slender and fragile.', 'type id': 41, 'size': [1, 1, 2], 'constructible_points': [{'id': 0, 'pos': [0, 0, 2], 'orientation': 'Front'}, {'id': 1, 'pos': [-0.5, 0, 0.5], 'orientation': 'Left'}, {'id': 2, 'pos': [-0.5, 0, 1.5], 'orientation': 'Left'}, {'id': 3, 'pos': [0.5, 0, 0.5], 'orientation': 'Right'}, {'id': 4, 'pos': [0.5, 0, 1.5], 'orientation': 'Right'}, {'id': 5, 'pos': [0, 0.5, 0.5], 'orientation': 'Up'}, {'id': 6, 'pos': [0, 0.5, 1.5], 'orientation': 'Up'}, {'id': 7, 'pos': [0, -0.5, 0.5], 'orientation': 'Down'}, {'id': 8, 'pos': [0, -0.5, 1.5], 'orientation': 'Down'}], 'mass': 0.5}, {'name': 'Log', 'description': 'A basic construction block.', 'type id': 63, 'size': [1, 1, 3], 'constructible_points': [{'id': 0, 'pos': [0, 0, 3], 'orientation': 'Front'}, {'id': 1, 'pos': [-0.5, 0, 0.5], 'orientation': 'Left'}, {'id': 2, 'pos': [-0.5, 0, 1.5], 'orientation': 'Left'}, {'id': 3, 'pos': [-0.5, 0, 2.5], 'orientation': 'Left'}, {'id': 4, 'pos': [0.5, 0, 0.5], 'orientation': 'Right'}, {'id': 5, 'pos': [0.5, 0, 1.5], 'orientation': 'Right'}, {'id': 6, 'pos': [0.5, 0, 2.5], 'orientation': 'Right'}, {'id': 7, 'pos': [0, 0.5, 0.5], 'orientation': 'Up'}, {'id': 8, 'pos': [0, 0.5, 1.5], 'orientation': 'Up'}, {'id': 9, 'pos': [0, 0.5, 2.5], 'orientation': 'Up'}, {'id': 10, 'pos': [0, -0.5, 0.5], 'orientation': 'Down'}, {'id': 11, 'pos': [0, -0.5, 1.5], 'orientation': 'Down'}, {'id': 12, 'pos': [0, -0.5, 2.5], 'orientation': 'Down'}], 'mass': 1}, {'name': 'Rotating Block', 'description': 'Powered, spins about its placement-normal axis. Only sub-blocks on constructible_points 1 to 4 rotate with it. Rotation torque is passed to its parent, scaled by the total weight of itself and all descendant sub-blocks; seen from the normal that points into the block, it turns clockwise, reversing to counter-clockwise only when the block faces x-.', 'type id': 22, 'size': [1, 1, 1], 'constructible_points': [{'id': 0, 'pos': [0, 0, 1], 'orientation': 'Front'}, {'id': 1, 'pos': [-0.5, 0, 0.5], 'orientation': 'Left'}, {'id': 2, 'pos': [0.5, 0, 0.5], 'orientation': 'Right'}, {'id': 3, 'pos': [0, 0.5, 0.5], 'orientation': 'Up'}, {'id': 4, 'pos': [0, -0.5, 0.5], 'orientation': 'Down'}], 'Special Attributes': {'Rotation Axis': 'Front', 'NonStatic': 'True', 'NonStable': 'True'}, 'mass': 1}, {'name': 'Boulder', 'description': 'A rock that will not directly connect to other blocks even if built on them, high mass.', 'type id': 36, 'size': [1.9, 1.9, 1.9], 'Special Attributes': {'NonStable': 'True'}, 'mass': 5}, {'name': 'Container', 'description': 'Has railing around the build point. If towards y+, can hold sub-blocks like a bowl. Mainly used to hold loose block such as boulder. keep around clear to avoid overlap.', 'type id': 30, 'size': [2.4, 3, 2.8], 'constructible_points': [{'id': 0, 'pos': [0, 0, 1], 'orientation': 'Front'}], 'mass': 0.5}, {'name': 'Spring', 'description': 'It primarily serves as a buffer and shock absorber. It is similar in shape to a wooden block, with all constructible_points located at the far end of the block.', 'type id': 16, 'size': [1, 1, 2], 'constructible_points': [{'id': 0, 'pos': [0, 0, 2], 'orientation': 'Front'}, {'id': 1, 'pos': [-0.5, 0, 1.5], 'orientation': 'Left'}, {'id': 2, 'pos': [0.5, 0, 1.5], 'orientation': 'Right'}, {'id': 3, 'pos': [0, 0.5, 1.5], 'orientation': 'Up'}, {'id': 4, 'pos': [0, -0.5, 1.5], 'orientation': 'Down'}], 'mass': 0.5}, {'name': 'Rubber Band', 'description': 'A linear block that attaches to two other blocks and can quickly pull the two ends together. Its tension force is almost entirely dependent on its length.', 'type id': 9, 'Special Attributes': {'Linear': 'True', 'NonStatic': 'True', 'Tension Direction': 'Towards the center of the line segment between the two constructible_points'}, 'mass': 0.4}, {'name': 'Ballast', 'description': 'It serves as a counterweight, has a large mass, and is shaped like a cube.', 'type id': 35, 'size': [1, 1, 1], 'constructible_points': [{'id': 0, 'pos': [0, 0, 1], 'orientation': 'Front'}, {'id': 1, 'pos': [-0.5, 0, 0.5], 'orientation': 'Left'}, {'id': 2, 'pos': [0.5, 0, 0.5], 'orientation': 'Right'}, {'id': 3, 'pos': [0, 0.5, 0.5], 'orientation': 'Up'}, {'id': 4, 'pos': [0, -0.5, 0.5], 'orientation': 'Down'}], 'mass': 3},{\n \"name\": \"Powered Wheel\",\n \"description\": \"Powered, a mechanical device used to move objects on the ground, the wheel's build orientation is tangent to its intended rolling direction, which lies parallel to the XZ plane.\",\n \"type id\": 2,\n \"size\": [2, 2, 0.5],\n \"constructible_points\": [\n {\"ID\": 0, \"pos\": [0, 0, 0.5], \"orientation\": \"Front\"}\n ],\n \"Special Attributes\": {\n \"Rotation Axis\": \"Front\",\n NonStatic': 'True', 'NonStable': 'True',\n \"direction of power\": [\n {\"facing\": \"x+\", \"direction\": \"z+\"},\n {\"facing\": \"x-\", \"direction\": \"z+\"},\n {\"facing\": \"z+\", \"direction\": \"x-\"},\n {\"facing\": \"z-\", \"direction\": \"x+\"}\n ]\n },\n \"mass\": 1\n }]\n\n**JSON Output Format: Do not add items not mentioned below**\n* **id**: block type_id\n* **order_id**: this is i-th block\n* **parent**: parent block's order_id\n* **bp_id**: parent block's constructible_point id\n* **Standard Block:** `{\"id\": <int>, \"order_id\": <int>, \"parent\": <int>, \"bp_id\": <int>}`\n* **Linear Block (id: 9):** `{\"id\": 9, \"order_id\": <int>, \"parent_a\": <int>, \"bp_id_a\": <int>, \"parent_b\": <int>, \"bp_id_b\": <int>}`\n\n**Final Response Format:**\nYour response must contain **only** these two parts:\n1. `Construction Idea:` A brief explanation of your design,remember to consider necessary block types, note them in ```necessary_blocks [type_1,type_2 ...]```, no more than 300 words.\n2. `JSON:` The complete JSON code inside a ```json ... ``` block. here is an example: ```json\n [\n {\"id\":\"0\",\"order_id\":0,\"parent\":-1,\"bp_id\":-1},\n {\"id\": <int>, \"order_id\": <int>, \"parent\": <int>, \"bp_id\": <int>},\n ...\n ]\n```
|
utils.py
ADDED
|
@@ -0,0 +1,783 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import json
|
| 3 |
+
from copy import deepcopy
|
| 4 |
+
import re
|
| 5 |
+
import ast
|
| 6 |
+
import numpy as np
|
| 7 |
+
from scipy.spatial.transform import Rotation as R
|
| 8 |
+
import uuid
|
| 9 |
+
import xml.etree.ElementTree as ET
|
| 10 |
+
|
| 11 |
+
BLOCKPROPERTYPATH=R"Besiege_blocks_markov.json"
|
| 12 |
+
|
| 13 |
+
FLIP_SENSITIVE_BLOCKS = ["2","46"]
|
| 14 |
+
def are_quaternions_similar(q1, angle_threshold=1e-3):
|
| 15 |
+
|
| 16 |
+
q2 = np.array([0, -0.7071068, 0, 0.7071068])
|
| 17 |
+
# 将四元数转换为旋转对象
|
| 18 |
+
r1 = R.from_quat(q1)
|
| 19 |
+
r2 = R.from_quat(q2)
|
| 20 |
+
|
| 21 |
+
# 计算两个旋转之间的夹角差异
|
| 22 |
+
relative_rotation = r1.inv() * r2
|
| 23 |
+
angle = relative_rotation.magnitude()
|
| 24 |
+
|
| 25 |
+
# 如果角度差异小于阈值,则认为两个四元数大致相同
|
| 26 |
+
return angle < angle_threshold
|
| 27 |
+
def generate_guid():
|
| 28 |
+
"""生成一个唯一的GUID"""
|
| 29 |
+
return str(uuid.uuid4())
|
| 30 |
+
|
| 31 |
+
def add_rotations(q1, q2):
|
| 32 |
+
"""叠加两个旋转四元数"""
|
| 33 |
+
r1 = R.from_quat(q1) # 从四元数创建旋转对象
|
| 34 |
+
r2 = R.from_quat(q2) # 从四元数创建旋转对象
|
| 35 |
+
r_combined = r1 * r2 # 叠加旋转
|
| 36 |
+
return r_combined.as_quat() # 返回四元数 (xyzw 格式)
|
| 37 |
+
def read_txt(path):
|
| 38 |
+
"""读取文本文件内容"""
|
| 39 |
+
with open(path, "r", encoding="utf-8") as file:
|
| 40 |
+
return file.read()
|
| 41 |
+
|
| 42 |
+
def write_file(path, content):
|
| 43 |
+
"""将内容写入文本文件"""
|
| 44 |
+
with open(path, 'w', encoding='utf-8') as file:
|
| 45 |
+
file.write(content)
|
| 46 |
+
def extract_json_from_string(input_string: str,return_raw_str=False):
|
| 47 |
+
if isinstance(input_string,list):
|
| 48 |
+
return input_string
|
| 49 |
+
if os.path.exists(input_string):
|
| 50 |
+
input_content = read_txt(input_string)
|
| 51 |
+
else:
|
| 52 |
+
input_content = deepcopy(input_string)
|
| 53 |
+
match = re.search(r"```json(.*?)```", input_content, re.DOTALL)
|
| 54 |
+
if match:
|
| 55 |
+
json_content = match.group(1).strip()
|
| 56 |
+
try:
|
| 57 |
+
if return_raw_str:
|
| 58 |
+
return json.loads(json_content),json_content
|
| 59 |
+
return json.loads(json_content)
|
| 60 |
+
except json.JSONDecodeError:
|
| 61 |
+
pass
|
| 62 |
+
|
| 63 |
+
try:
|
| 64 |
+
if return_raw_str:
|
| 65 |
+
return json.loads(input_content),input_content
|
| 66 |
+
return json.loads(input_content)
|
| 67 |
+
except json.JSONDecodeError:
|
| 68 |
+
try:
|
| 69 |
+
if return_raw_str:
|
| 70 |
+
return json.loads(input_content),input_content
|
| 71 |
+
return ast.literal_eval(input_content)
|
| 72 |
+
except (ValueError, SyntaxError):
|
| 73 |
+
if return_raw_str:
|
| 74 |
+
return None,""
|
| 75 |
+
return None
|
| 76 |
+
|
| 77 |
+
def get_relative_pos_list(bp_oldpos,ref_p,ref_r,scale=1,decimals=None):
|
| 78 |
+
bp_newpos = []
|
| 79 |
+
|
| 80 |
+
if ref_r.shape[0] != 3 or ref_r.shape[1] != 3:
|
| 81 |
+
ref_r = R.from_quat(ref_r).as_matrix() # 如果是四元数,转换为旋转矩阵
|
| 82 |
+
|
| 83 |
+
for point in bp_oldpos:
|
| 84 |
+
point_lp = ref_p+np.dot(ref_r, point*scale)
|
| 85 |
+
bp_newpos.append(tuple(point_lp))
|
| 86 |
+
bp_newpos = np.array(bp_newpos)#可建造点局部位置
|
| 87 |
+
|
| 88 |
+
if decimals!=None:
|
| 89 |
+
bp_newpos = np.round(bp_newpos,decimals=decimals)
|
| 90 |
+
|
| 91 |
+
return bp_newpos
|
| 92 |
+
|
| 93 |
+
def get_bbox(manu_lp,manu_lr,scale,bc_gc,bbox_size,gp,gr):
|
| 94 |
+
|
| 95 |
+
if manu_lr.shape[0] != 3 or manu_lr.shape[1] != 3:
|
| 96 |
+
manu_lr = R.from_quat(manu_lr).as_matrix()
|
| 97 |
+
if gr.shape[0] != 3 or gr.shape[1] != 3:
|
| 98 |
+
gr = R.from_quat(gr).as_matrix()
|
| 99 |
+
|
| 100 |
+
half_bbox_size = np.array(bbox_size) / 2.0
|
| 101 |
+
bbox_lp = []
|
| 102 |
+
for z in [-1, 1]:
|
| 103 |
+
for x in [-1, 1]:
|
| 104 |
+
for y in [-1, 1]:
|
| 105 |
+
point = (manu_lp+bc_gc) + (x * half_bbox_size[0], y * half_bbox_size[1], z * half_bbox_size[2])
|
| 106 |
+
bc_point = point-manu_lp
|
| 107 |
+
point_lp = manu_lp+np.dot(manu_lr, bc_point*scale)
|
| 108 |
+
bbox_lp.append(tuple(point_lp))
|
| 109 |
+
bbox_lp = np.array(bbox_lp)#碰撞盒顶点相对位置
|
| 110 |
+
|
| 111 |
+
bbox_gp = get_relative_pos_list(bbox_lp,gp,gr,decimals=2)
|
| 112 |
+
return bbox_lp,bbox_gp
|
| 113 |
+
|
| 114 |
+
def compute_normal_vector(vertices, bp):
|
| 115 |
+
bp = np.round(bp, decimals=3)
|
| 116 |
+
|
| 117 |
+
# 计算每个维度的最小和最大坐标值
|
| 118 |
+
min_coords = np.min(vertices, axis=0)
|
| 119 |
+
max_coords = np.max(vertices, axis=0)
|
| 120 |
+
|
| 121 |
+
# 初始化法向量
|
| 122 |
+
normal = np.zeros(3)
|
| 123 |
+
# epsilon = 5e-4 # 容差范围,还算严格
|
| 124 |
+
epsilon = 0.005 # 容差范围
|
| 125 |
+
|
| 126 |
+
# 检查点是否在x方向的面上
|
| 127 |
+
if abs(bp[0] - min_coords[0]) < epsilon:
|
| 128 |
+
normal = np.array([-1, 0, 0]) # 左面法向量
|
| 129 |
+
elif abs(bp[0] - max_coords[0]) < epsilon:
|
| 130 |
+
normal = np.array([1, 0, 0]) # 右面法向量
|
| 131 |
+
# 检查y方向
|
| 132 |
+
elif abs(bp[1] - min_coords[1]) < epsilon:
|
| 133 |
+
normal = np.array([0, -1, 0]) # 下面法向量
|
| 134 |
+
elif abs(bp[1] - max_coords[1]) < epsilon:
|
| 135 |
+
normal = np.array([0, 1, 0]) # 上面法向量
|
| 136 |
+
# 检查z方向
|
| 137 |
+
elif abs(bp[2] - min_coords[2]) < epsilon:
|
| 138 |
+
normal = np.array([0, 0, -1]) # 后面法向量
|
| 139 |
+
elif abs(bp[2] - max_coords[2]) < epsilon:
|
| 140 |
+
normal = np.array([0, 0, 1]) # 前面法向量
|
| 141 |
+
else:
|
| 142 |
+
raise ValueError("点不在长方体的任何一个面上")
|
| 143 |
+
|
| 144 |
+
return normal
|
| 145 |
+
|
| 146 |
+
def get_mybuildingpoints(bc_bp,manu_lp,manu_lr,gp,gr,bc_gc,bbox_size,scale=1):
|
| 147 |
+
|
| 148 |
+
bp_ori = np.array(bc_bp)
|
| 149 |
+
bp_lp = get_relative_pos_list(bp_ori,manu_lp,manu_lr,scale=scale)
|
| 150 |
+
bp_gp = get_relative_pos_list(bp_lp,gp,gr,decimals=2)
|
| 151 |
+
bbox_lp,bbox_gp = get_bbox(manu_lp,manu_lr,scale,bc_gc,bbox_size,gp,gr)
|
| 152 |
+
|
| 153 |
+
my_building_points = bp_gp.copy()
|
| 154 |
+
my_building_points_buildrotation=[]
|
| 155 |
+
# print(f"bp_gp:{bp_gp}")
|
| 156 |
+
|
| 157 |
+
for i in range(len(my_building_points)):
|
| 158 |
+
# print(f"bp_lp:{bp_lp[i]}")
|
| 159 |
+
# print(f"bbox_lp:{bbox_lp}")
|
| 160 |
+
normal_vector_l = compute_normal_vector(bbox_lp,bp_lp[i])
|
| 161 |
+
rotated_initvec = np.array([0,0,1])
|
| 162 |
+
building_points_rot_quat = rotation_quaternion(rotated_initvec,normal_vector_l)
|
| 163 |
+
my_building_points_buildrotation.append(building_points_rot_quat) #这个是对的
|
| 164 |
+
my_building_points_buildrotation = np.array(my_building_points_buildrotation)
|
| 165 |
+
|
| 166 |
+
return my_building_points,my_building_points_buildrotation
|
| 167 |
+
|
| 168 |
+
def rotation_quaternion(v_from, v_to):
|
| 169 |
+
"""计算从 v_from 到 v_to 的旋转四元数 (xyzw 格式)"""
|
| 170 |
+
v_from = v_from / np.linalg.norm(v_from) # 单位化
|
| 171 |
+
v_to = v_to / np.linalg.norm(v_to) # 单位化
|
| 172 |
+
|
| 173 |
+
# 计算旋转轴和旋转角
|
| 174 |
+
cross = np.cross(v_from, v_to)
|
| 175 |
+
dot = np.dot(v_from, v_to)
|
| 176 |
+
|
| 177 |
+
if np.allclose(cross, 0) and np.allclose(dot, 1):
|
| 178 |
+
return np.array([0, 0, 0, 1]) # 无需旋转
|
| 179 |
+
elif np.allclose(cross, 0) and np.allclose(dot, -1):
|
| 180 |
+
# 特殊情况:v_from 和 v_to 反方向
|
| 181 |
+
# 选择一个垂直于 v_from 的轴作为旋转轴
|
| 182 |
+
if np.isclose(v_from[0], 0) and np.isclose(v_from[1], 0):
|
| 183 |
+
axis = np.array([0, 1, 0]) # 如果 v_from 在 z 轴上,选择 y 轴作为旋转轴
|
| 184 |
+
else:
|
| 185 |
+
axis = np.cross(v_from, np.array([0, 1, 0])) # 选择垂直于 v_from 和 y 轴的向量
|
| 186 |
+
axis = axis / np.linalg.norm(axis)
|
| 187 |
+
angle = np.pi
|
| 188 |
+
else:
|
| 189 |
+
angle = np.arccos(dot)
|
| 190 |
+
axis = cross / np.linalg.norm(cross)
|
| 191 |
+
|
| 192 |
+
# 生成四元数
|
| 193 |
+
q = R.from_rotvec(axis * angle).as_quat() # xyzw格式
|
| 194 |
+
return q
|
| 195 |
+
|
| 196 |
+
def format_json(input_json):
|
| 197 |
+
try:
|
| 198 |
+
new_clean_json=[]
|
| 199 |
+
for json_info in input_json:
|
| 200 |
+
new_clean_dict={}
|
| 201 |
+
if int(json_info["id"]) not in [7,9]:
|
| 202 |
+
new_clean_dict["id"]=str(json_info["id"])
|
| 203 |
+
new_clean_dict["order_id"]=int(json_info["order_id"])
|
| 204 |
+
new_clean_dict["parent"]=int(json_info["parent"])
|
| 205 |
+
new_clean_dict["bp_id"]=int(json_info["bp_id"])
|
| 206 |
+
else:
|
| 207 |
+
new_clean_dict["id"]=str(json_info["id"])
|
| 208 |
+
new_clean_dict["order_id"]=int(json_info["order_id"])
|
| 209 |
+
new_clean_dict["parent_a"]=int(json_info["parent_a"])
|
| 210 |
+
new_clean_dict["bp_id_a"]=int(json_info["bp_id_a"])
|
| 211 |
+
new_clean_dict["parent_b"]=int(json_info["parent_b"])
|
| 212 |
+
new_clean_dict["bp_id_b"]=int(json_info["bp_id_b"])
|
| 213 |
+
new_clean_json.append(new_clean_dict)
|
| 214 |
+
return new_clean_json
|
| 215 |
+
except:
|
| 216 |
+
return input_json
|
| 217 |
+
|
| 218 |
+
def convert_to_numpy(data):
|
| 219 |
+
no_globalrt = True
|
| 220 |
+
for info in data:
|
| 221 |
+
if "GlobalPosition" in info:
|
| 222 |
+
info["GlobalPosition"] = np.array(info["GlobalPosition"])
|
| 223 |
+
info["GlobalRotation"] = np.array(info["GlobalRotation"])
|
| 224 |
+
no_globalrt = False
|
| 225 |
+
else:
|
| 226 |
+
|
| 227 |
+
keys_to_convert = ["corners", "building_center", "scale","manu_lr","manu_lp","bp_lr"]
|
| 228 |
+
for key in keys_to_convert:
|
| 229 |
+
if key in info:
|
| 230 |
+
info[key] = np.array(info[key])
|
| 231 |
+
|
| 232 |
+
new_data = [{"GlobalPosition":np.array([0,5.05,0]),"GlobalRotation":np.array([0,0,0,1])}]
|
| 233 |
+
if no_globalrt:
|
| 234 |
+
new_data.extend(data)
|
| 235 |
+
return new_data
|
| 236 |
+
|
| 237 |
+
return data # 返回原始数据
|
| 238 |
+
|
| 239 |
+
def get_3d_from_llm(block_sizes, input_info, gp, gr, log=False):
|
| 240 |
+
info = deepcopy(input_info)
|
| 241 |
+
for block in info:
|
| 242 |
+
order_id = int(block["order_id"])
|
| 243 |
+
block_id = str(block["id"])
|
| 244 |
+
|
| 245 |
+
# Handle scale
|
| 246 |
+
if "scale" not in block:
|
| 247 |
+
block["scale"] = np.array([1,1,1])
|
| 248 |
+
else:
|
| 249 |
+
print(f"警告!{order_id}改变了scale!使用初始值")
|
| 250 |
+
block["scale"] = np.array([1,1,1])
|
| 251 |
+
|
| 252 |
+
# Handle rotations
|
| 253 |
+
if "bp_lr" not in block:
|
| 254 |
+
if "manu_lr" not in block:
|
| 255 |
+
block["bp_lr"] = np.array([0,0,0,1])
|
| 256 |
+
elif "manu_lr" in block and str(block.get("parent", "")) not in ("-1", ""):
|
| 257 |
+
print(f"警告!{order_id}有manu_lr但不是根节点!旋转使用初始值")
|
| 258 |
+
block["bp_lr"] = np.array([0,0,0,1])
|
| 259 |
+
block.pop("manu_lr", None)
|
| 260 |
+
|
| 261 |
+
block_info = block_sizes[block_id]
|
| 262 |
+
parent = int(block.get("parent", -1))
|
| 263 |
+
|
| 264 |
+
# Handle parent cases
|
| 265 |
+
if parent == -1:
|
| 266 |
+
if block_id not in ("0","7", "9"):
|
| 267 |
+
print("警告!发现了非起始方块的无父节点块")
|
| 268 |
+
|
| 269 |
+
if block_id in ("7", "9"):
|
| 270 |
+
parent_a, parent_b = int(block["parent_a"]), int(block["parent_b"])
|
| 271 |
+
bp_id_a, bp_id_b = int(block["bp_id_a"]), int(block["bp_id_b"])
|
| 272 |
+
block["bp_lr"] = np.array([0,0,0,1])
|
| 273 |
+
block["manu_lr"] = add_rotations(
|
| 274 |
+
info[parent_a]["my_building_points_buildrotation"][bp_id_a],
|
| 275 |
+
block["bp_lr"]
|
| 276 |
+
)
|
| 277 |
+
block["manu_lp_a"] = info[parent_a]["my_building_points"][bp_id_a] - gp
|
| 278 |
+
block["manu_lp_b"] = info[parent_b]["my_building_points"][bp_id_b] - gp
|
| 279 |
+
else:
|
| 280 |
+
if "manu_lr" not in block:
|
| 281 |
+
block["manu_lr"] = np.array([0,0,0,1])
|
| 282 |
+
block["manu_lp"] = np.array([0,0,0])
|
| 283 |
+
else:
|
| 284 |
+
print("警告!发现了某个方块的manu_lr和manu_lp")
|
| 285 |
+
if block["manu_lr"].shape != (3, 3):
|
| 286 |
+
block["manu_lr"] = R.from_matrix(block["manu_lr"]).as_quat()
|
| 287 |
+
else:
|
| 288 |
+
try:
|
| 289 |
+
bp_id = int(block["bp_id"])
|
| 290 |
+
parent_rot = info[parent]["my_building_points_buildrotation"][bp_id]
|
| 291 |
+
block["manu_lr"] = add_rotations(parent_rot, block["bp_lr"])
|
| 292 |
+
block["manu_lp"] = info[parent]["my_building_points"][bp_id] - gp
|
| 293 |
+
except Exception:
|
| 294 |
+
print(f"警告!parent:{parent},order_id{order_id}的my_building_points或my_building_points_buildrotation不存在")
|
| 295 |
+
# print(info[parent])
|
| 296 |
+
pass
|
| 297 |
+
|
| 298 |
+
if block_id not in ("7", "9"):
|
| 299 |
+
if block_id in FLIP_SENSITIVE_BLOCKS:
|
| 300 |
+
block["flip"] = are_quaternions_similar(block["manu_lr"])
|
| 301 |
+
|
| 302 |
+
bc_bp = block_info['bc_bp']
|
| 303 |
+
bc_gc = block_info['bc_gc']
|
| 304 |
+
bbox_size = block_info['bbox_size']
|
| 305 |
+
|
| 306 |
+
if block_id == "30":
|
| 307 |
+
bc_gc = [0,0,0.5]
|
| 308 |
+
bbox_size = [1,1,1]
|
| 309 |
+
|
| 310 |
+
building_points, build_rotation = get_mybuildingpoints(
|
| 311 |
+
bc_bp, block["manu_lp"], block["manu_lr"], gp, gr,
|
| 312 |
+
bc_gc, bbox_size, scale=block["scale"]
|
| 313 |
+
)
|
| 314 |
+
block["my_building_points"] = building_points
|
| 315 |
+
block["my_building_points_buildrotation"] = build_rotation
|
| 316 |
+
|
| 317 |
+
if log:
|
| 318 |
+
print(f"block_id:{block_id}\nscale:{block['scale']}\nbc_gc:{bc_gc}\n"
|
| 319 |
+
f"bbox_size:{bbox_size}\nmanu_lp:{block['manu_lp']}\n"
|
| 320 |
+
f"manu_lr:{block['manu_lr']}\nmy_building_points:{building_points}\n"
|
| 321 |
+
f"my_building_points_buildrotation:{build_rotation}")
|
| 322 |
+
|
| 323 |
+
return info
|
| 324 |
+
|
| 325 |
+
def llm2xml_filetree(block_details, block_sizes_path, selected_menu=None):
|
| 326 |
+
with open(block_sizes_path, 'r', encoding='utf-8') as file:
|
| 327 |
+
block_sizes = json.load(file)
|
| 328 |
+
|
| 329 |
+
global_rt = block_details.pop(0)
|
| 330 |
+
gp, gr_quat = global_rt["GlobalPosition"], global_rt["GlobalRotation"]
|
| 331 |
+
gr_matrix = R.from_quat(gr_quat).as_matrix()
|
| 332 |
+
|
| 333 |
+
blocks_to_delete = set() # 使用集合以避免重复添加和快速查找
|
| 334 |
+
blocks_to_delete_feedback = []
|
| 335 |
+
|
| 336 |
+
#先对block_details做一个整体格式检查
|
| 337 |
+
linear = {"id","order_id","parent_a", "bp_id_a", "parent_b", "bp_id_b"}
|
| 338 |
+
non_linear = {"id","order_id", "parent", "bp_id"}
|
| 339 |
+
for i, block in enumerate(block_details):
|
| 340 |
+
if not (set(block.keys()) == linear or set(block.keys()) == non_linear):
|
| 341 |
+
blocks_to_delete.add(i)
|
| 342 |
+
blocks_to_delete_feedback.append(
|
| 343 |
+
f"警告:块(orderID {i})结构非法"
|
| 344 |
+
)
|
| 345 |
+
|
| 346 |
+
order_id_map = {int(b["order_id"]): b for b in block_details} # 方便快速查找父块
|
| 347 |
+
|
| 348 |
+
for i,block in enumerate(block_details):
|
| 349 |
+
is_linear = False
|
| 350 |
+
parent_order_a=-1
|
| 351 |
+
parent_order_b=-1
|
| 352 |
+
|
| 353 |
+
#检查一下value的格式
|
| 354 |
+
format_error = False
|
| 355 |
+
for k,v in block.items():
|
| 356 |
+
if k =="id":
|
| 357 |
+
if not isinstance(v,str):
|
| 358 |
+
if isinstance(v,int):
|
| 359 |
+
v = str(v)
|
| 360 |
+
else:
|
| 361 |
+
format_error = True
|
| 362 |
+
|
| 363 |
+
if k in["order_id","parent_a", "bp_id_a", "parent_b", "bp_id_b", "parent", "bp_id"]:
|
| 364 |
+
if not isinstance(v,int):
|
| 365 |
+
if isinstance(v,str):
|
| 366 |
+
try:
|
| 367 |
+
v = int(v)
|
| 368 |
+
except:
|
| 369 |
+
format_error = True
|
| 370 |
+
|
| 371 |
+
if format_error:
|
| 372 |
+
|
| 373 |
+
blocks_to_delete.add(i)
|
| 374 |
+
blocks_to_delete_feedback.append(f"警告:order{i}json格式非法")
|
| 375 |
+
continue
|
| 376 |
+
|
| 377 |
+
|
| 378 |
+
#先检查起始方块:
|
| 379 |
+
if i==0:
|
| 380 |
+
block_type = str(block["id"])
|
| 381 |
+
order_id = int(block["order_id"])
|
| 382 |
+
parent_order = int(block.get("parent", -2))
|
| 383 |
+
bp_id = int(block.get("bp_id", -2))
|
| 384 |
+
if any([block_type!="0",order_id!=0]):
|
| 385 |
+
blocks_to_delete.add(i)
|
| 386 |
+
blocks_to_delete_feedback.append(f"警告:起始方块非法")
|
| 387 |
+
continue
|
| 388 |
+
if any([parent_order!=-1,bp_id!=-1]):
|
| 389 |
+
#起始方块不规范,parent bpid改成-1 -1
|
| 390 |
+
block["parent"]=-1
|
| 391 |
+
block["bp_id"]=-1
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
order_id = int(block["order_id"])
|
| 395 |
+
parent_order = int(block.get("parent", -1))
|
| 396 |
+
if parent_order==-1 and order_id!=0:
|
| 397 |
+
is_linear = True
|
| 398 |
+
parent_order_a = int(block.get("parent_a", -1))
|
| 399 |
+
parent_order_b = int(block.get("parent_b", -1))
|
| 400 |
+
parents = [parent_order_a,parent_order_b]
|
| 401 |
+
else:
|
| 402 |
+
parents = [parent_order]
|
| 403 |
+
# 检查1: 父块是否已被标记为非法
|
| 404 |
+
if any(order in blocks_to_delete for order in parents):
|
| 405 |
+
blocks_to_delete.add(order_id)
|
| 406 |
+
blocks_to_delete_feedback.append(f"警告:块(orderID {order_id})的父块(orderID {parent_order})非法,因此也被标记为非法。")
|
| 407 |
+
continue
|
| 408 |
+
# 检查2: 父块的连接点(bp_id)是否有效
|
| 409 |
+
for i_th_parent,parent_order in enumerate(parents):
|
| 410 |
+
parent_block = order_id_map.get(parent_order)
|
| 411 |
+
if parent_block:
|
| 412 |
+
parent_block_id = str(parent_block["id"])
|
| 413 |
+
if i_th_parent==0:
|
| 414 |
+
bp_id = int(block.get("bp_id",block.get("bp_id_a",-1)))
|
| 415 |
+
elif i_th_parent==1:
|
| 416 |
+
bp_id = int(block.get("bp_id_b",-1))
|
| 417 |
+
else:
|
| 418 |
+
bp_id=-1
|
| 419 |
+
if parent_block_id in block_sizes and bp_id >= len(block_sizes[parent_block_id]["bc_bp"]):
|
| 420 |
+
blocks_to_delete.add(order_id)
|
| 421 |
+
blocks_to_delete_feedback.append(f"警告:块(orderID {order_id})的父块(ID {parent_block_id})不存在可建造点{bp_id}。")
|
| 422 |
+
continue
|
| 423 |
+
|
| 424 |
+
# 检查3: 双父块(线性块)的特殊处理
|
| 425 |
+
if (not is_linear) and str(block.get("id")) in ["7", "9"]:
|
| 426 |
+
blocks_to_delete.add(order_id)
|
| 427 |
+
blocks_to_delete_feedback.append(f"警告:块(orderID {order_id})是线性块但不存在双parent属性。")
|
| 428 |
+
continue
|
| 429 |
+
elif is_linear and (str(block.get("id")) not in ["7", "9"]):
|
| 430 |
+
blocks_to_delete.add(order_id)
|
| 431 |
+
blocks_to_delete_feedback.append(f"警告:块(orderID {order_id})存在双parent属性但不是线性块。")
|
| 432 |
+
continue
|
| 433 |
+
|
| 434 |
+
# print(blocks_to_delete_feedback)
|
| 435 |
+
|
| 436 |
+
if blocks_to_delete:
|
| 437 |
+
# 从 block_details 中过滤掉要删除的块
|
| 438 |
+
block_details = [b for b in block_details if int(b["order_id"]) not in blocks_to_delete]
|
| 439 |
+
|
| 440 |
+
# --- 计算 3D 位置并构建 XML 风格列表 ---
|
| 441 |
+
processed_details = get_3d_from_llm(block_sizes, block_details, gp, gr_matrix, log=False)
|
| 442 |
+
# print(block_details)
|
| 443 |
+
xml_block_details = [{"GlobalPosition": gp, "GlobalRotation": gr_quat}]
|
| 444 |
+
for block in processed_details:
|
| 445 |
+
xml_info = {
|
| 446 |
+
"id": block["id"],
|
| 447 |
+
"order_id": block["order_id"],
|
| 448 |
+
"guid": generate_guid()
|
| 449 |
+
}
|
| 450 |
+
|
| 451 |
+
if str(block["id"]) in ["7", "9"]: # 线性块
|
| 452 |
+
xml_info["Transform"] = {"Position": block["manu_lp_a"], "Rotation": np.array([0,0,0,1]), "Scale": block["scale"]}
|
| 453 |
+
xml_info["end-position"] = block["manu_lp_b"] - block["manu_lp_a"]
|
| 454 |
+
else: # 普通块
|
| 455 |
+
manu_lr = R.from_matrix(block["manu_lr"]).as_quat() if block["manu_lr"].shape == (3, 3) else block["manu_lr"]
|
| 456 |
+
xml_info["Transform"] = {"Position": block["manu_lp"], "Rotation": manu_lr, "Scale": block["scale"]}
|
| 457 |
+
if "flip" in block: # 轮子属性
|
| 458 |
+
xml_info.update({"flip": block["flip"], "auto": True, "autobrake": False})
|
| 459 |
+
if selected_menu and "special_props" in selected_menu:
|
| 460 |
+
xml_info["WheelDoubleSpeed"] = "WheelDoubleSpeed" in selected_menu["special_props"]
|
| 461 |
+
|
| 462 |
+
xml_block_details.append(xml_info)
|
| 463 |
+
|
| 464 |
+
# print("\n".join(blocks_to_delete_feedback))
|
| 465 |
+
|
| 466 |
+
return xml_block_details, processed_details, "\n".join(blocks_to_delete_feedback)
|
| 467 |
+
|
| 468 |
+
def facing(q_in):
|
| 469 |
+
q_z_pos = np.array([0, 0, 0, 1])
|
| 470 |
+
q_z_neg = np.array([0, 1, 0, 0])
|
| 471 |
+
q_x_neg = np.array([0, -0.7071068, 0, 0.7071068])
|
| 472 |
+
q_x_pos = np.array([0, 0.7071068, 0, 0.7071068])
|
| 473 |
+
q_y_pos = np.array([-0.7071068,0, 0,0.7071068])
|
| 474 |
+
q_y_neg = np.array([0.7071068,0, 0,0.7071068])
|
| 475 |
+
|
| 476 |
+
angle_threshold = 1e-3
|
| 477 |
+
rots = [q_z_pos,q_z_neg,q_x_neg,q_x_pos,q_y_pos,q_y_neg]
|
| 478 |
+
facing = ["z+","z-","x-","x+","y+","y-"]
|
| 479 |
+
# 将四元数转换为旋转对象
|
| 480 |
+
r1 = R.from_quat(q_in)
|
| 481 |
+
for q2 in range(len(rots)):
|
| 482 |
+
r2 = R.from_quat(rots[q2])
|
| 483 |
+
|
| 484 |
+
# 计算两个旋转之间的夹角差异
|
| 485 |
+
relative_rotation = r1.inv() * r2
|
| 486 |
+
angle = relative_rotation.magnitude()
|
| 487 |
+
|
| 488 |
+
# 如果角度差异小于阈值,则认为两个四元数大致相同
|
| 489 |
+
if(angle < angle_threshold):
|
| 490 |
+
return facing[q2]
|
| 491 |
+
|
| 492 |
+
return "Error!未找到正确方向"
|
| 493 |
+
|
| 494 |
+
def check_overlap_or_connection(cube1, cube2):
|
| 495 |
+
def get_bounds(vertices):
|
| 496 |
+
x_min = min(v[0] for v in vertices)
|
| 497 |
+
x_max = max(v[0] for v in vertices)
|
| 498 |
+
y_min = min(v[1] for v in vertices)
|
| 499 |
+
y_max = max(v[1] for v in vertices)
|
| 500 |
+
z_min = min(v[2] for v in vertices)
|
| 501 |
+
z_max = max(v[2] for v in vertices)
|
| 502 |
+
return x_min, x_max, y_min, y_max, z_min, z_max
|
| 503 |
+
|
| 504 |
+
x1_min, x1_max, y1_min, y1_max, z1_min, z1_max = get_bounds(cube1)
|
| 505 |
+
x2_min, x2_max, y2_min, y2_max, z2_min, z2_max = get_bounds(cube2)
|
| 506 |
+
|
| 507 |
+
if x1_max <= x2_min or x2_max <= x1_min:
|
| 508 |
+
return False
|
| 509 |
+
if y1_max <= y2_min or y2_max <= y1_min:
|
| 510 |
+
return False
|
| 511 |
+
if z1_max <= z2_min or z2_max <= z1_min:
|
| 512 |
+
return False
|
| 513 |
+
|
| 514 |
+
x_overlap = x1_min < x2_max and x2_min < x1_max
|
| 515 |
+
y_overlap = y1_min < y2_max and y2_min < y1_max
|
| 516 |
+
z_overlap = z1_min < z2_max and z2_min < z1_max
|
| 517 |
+
|
| 518 |
+
return x_overlap and y_overlap and z_overlap
|
| 519 |
+
|
| 520 |
+
|
| 521 |
+
def check_overlap(block_details,vis=True,corners_parent_llm_parent=None,
|
| 522 |
+
language="zh"):
|
| 523 |
+
|
| 524 |
+
def overlap_log(id1,id2):
|
| 525 |
+
head1 = "方块order_id"
|
| 526 |
+
head2 = "和方块order_id"
|
| 527 |
+
overlap_head = "重叠"
|
| 528 |
+
return f"{head1} {id1} {head2} {id2} {overlap_head}\n"
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
overlaps = []
|
| 532 |
+
connections = []
|
| 533 |
+
# 检查每对方块是否重叠
|
| 534 |
+
overlaps_info=""
|
| 535 |
+
# print(len(block_details))
|
| 536 |
+
# print(len(corners_parent_llm_parent))
|
| 537 |
+
for i in range(len(block_details)):
|
| 538 |
+
# print(block_details[i])
|
| 539 |
+
for j in range(i + 1, len(block_details)):
|
| 540 |
+
if "GlobalPosition" in block_details[i] or "GlobalPosition" in block_details[j]:continue
|
| 541 |
+
# if np.all(block_details[i] == 0) or np.all(block_details[j] == 0): continue
|
| 542 |
+
if "corners" in block_details[i] and "corners" in block_details[j]:
|
| 543 |
+
corners1, id1 = (block_details[i]["corners"],i)
|
| 544 |
+
corners2, id2 = (block_details[j]["corners"],j)
|
| 545 |
+
else:
|
| 546 |
+
corners1 = block_details[i]
|
| 547 |
+
id1 = i
|
| 548 |
+
corners2 = block_details[j]
|
| 549 |
+
id2 = j
|
| 550 |
+
|
| 551 |
+
#print(f"方块order_id {id1} 和方块order_id {id2}")
|
| 552 |
+
results = check_overlap_or_connection(corners1, corners2)
|
| 553 |
+
if results=="connected":
|
| 554 |
+
#print(f"方块order_id {id1} 和方块order_id {id2} 相交")
|
| 555 |
+
connections.append((id1, id2, corners1, corners2))
|
| 556 |
+
elif results:
|
| 557 |
+
if corners_parent_llm_parent !=None:
|
| 558 |
+
id1_type = str(corners_parent_llm_parent[id1][0])
|
| 559 |
+
id1_order = str(corners_parent_llm_parent[id1][1])
|
| 560 |
+
id1_parent_order = str(corners_parent_llm_parent[id1][2])
|
| 561 |
+
id2_type = str(corners_parent_llm_parent[id2][0])
|
| 562 |
+
id2_order = str(corners_parent_llm_parent[id2][1])
|
| 563 |
+
id2_parent_order = str(corners_parent_llm_parent[id2][2])
|
| 564 |
+
if id1_order==id2_parent_order:
|
| 565 |
+
if str(id1_type)=="30":#如果1是2的父节点,并且1是容器
|
| 566 |
+
pass
|
| 567 |
+
else:
|
| 568 |
+
|
| 569 |
+
overlaps_info+=overlap_log(id1,id2)
|
| 570 |
+
overlaps.append((id1, id2, corners1, corners2))
|
| 571 |
+
elif id2_order==id1_parent_order:
|
| 572 |
+
if str(id2_type)=="30":#如果2是1的父节点,并且2是容器
|
| 573 |
+
pass
|
| 574 |
+
else:
|
| 575 |
+
overlaps_info+=overlap_log(id1,id2)
|
| 576 |
+
overlaps.append((id1, id2, corners1, corners2))
|
| 577 |
+
else:
|
| 578 |
+
overlaps_info+=overlap_log(id1,id2)
|
| 579 |
+
overlaps.append((id1, id2, corners1, corners2))
|
| 580 |
+
else:
|
| 581 |
+
overlaps_info+=overlap_log(id1,id2)
|
| 582 |
+
overlaps.append((id1, id2, corners1, corners2))
|
| 583 |
+
|
| 584 |
+
if overlaps:
|
| 585 |
+
# print(f"共发现 {len(overlaps)} 处重叠。")
|
| 586 |
+
found_head = "共发现"
|
| 587 |
+
overlaps_head="处重叠"
|
| 588 |
+
overlaps_info+=f"{found_head} {len(overlaps)} {overlaps_head}\n"
|
| 589 |
+
else:
|
| 590 |
+
# print("没有重叠的方块。")
|
| 591 |
+
overlaps_info+="没有错误"
|
| 592 |
+
|
| 593 |
+
if vis:
|
| 594 |
+
# 可视化结果
|
| 595 |
+
pass
|
| 596 |
+
|
| 597 |
+
#print(overlaps_info)
|
| 598 |
+
return overlaps_info
|
| 599 |
+
|
| 600 |
+
|
| 601 |
+
def llm_feedback_3d(block_sizes, xml_block_details, block_details, autofit_gt=True, overlap_feedback=True, language="zh"):
|
| 602 |
+
with open(block_sizes, 'r', encoding='utf-8') as file:
|
| 603 |
+
block_sizes_content = json.load(file)
|
| 604 |
+
|
| 605 |
+
gp, gr = xml_block_details[0]["GlobalPosition"], xml_block_details[0]["GlobalRotation"]
|
| 606 |
+
corners_feedback_forquizzer = "块的3D信息:\n"
|
| 607 |
+
corners_feedback_forbuilder = "块的朝向信息:\n"
|
| 608 |
+
corners_parent_llm, corners_parent_llm_parent = [], []
|
| 609 |
+
|
| 610 |
+
for i, xml_block in enumerate(xml_block_details):
|
| 611 |
+
if "GlobalPosition" in xml_block: continue
|
| 612 |
+
|
| 613 |
+
block_id, order_id = xml_block["id"], xml_block["order_id"]
|
| 614 |
+
if str(block_id) in ("7", "9"):
|
| 615 |
+
corners_parent_llm_parent.append([block_id, order_id, -1])
|
| 616 |
+
corners_parent_llm.append(np.zeros((8,3)))
|
| 617 |
+
continue
|
| 618 |
+
|
| 619 |
+
x_transform = xml_block["Transform"]
|
| 620 |
+
pos, rot, scale = x_transform["Position"], x_transform["Rotation"], x_transform["Scale"]
|
| 621 |
+
# print(pos,rot,scale)
|
| 622 |
+
block_info = block_sizes_content[str(block_id)]
|
| 623 |
+
bbox_lp, bbox_gp = get_bbox(pos, rot, scale, block_info['bc_gc'], block_info['bbox_size'], gp, gr)
|
| 624 |
+
|
| 625 |
+
corners_parent_llm.append(bbox_gp)
|
| 626 |
+
corners_parent_llm_parent.append([block_id, order_id, block_details[i-1]["parent"]])
|
| 627 |
+
|
| 628 |
+
facing_dir = facing(rot)
|
| 629 |
+
corners_feedback_forquizzer += f"order_id:{order_id}\n朝向:{facing_dir}\n块近似长方体顶点位置:{bbox_gp.tolist()}\n"
|
| 630 |
+
corners_feedback_forbuilder += f"order_id:{order_id}\n朝向:{facing_dir}"
|
| 631 |
+
|
| 632 |
+
# Calculate machine dimensions
|
| 633 |
+
corners_arr = np.vstack([c for c in corners_parent_llm if c.size > 0])
|
| 634 |
+
min_vals, max_vals = corners_arr.min(axis=0), corners_arr.max(axis=0)
|
| 635 |
+
lowest_y, highest_y = min_vals[1], max_vals[1]
|
| 636 |
+
left_x, right_x = min_vals[0], max_vals[0]
|
| 637 |
+
back_z, forward_z = min_vals[2], max_vals[2]
|
| 638 |
+
|
| 639 |
+
geo_center = np.array([(right_x + left_x)/2, (highest_y + lowest_y)/2, (forward_z + back_z)/2])
|
| 640 |
+
|
| 641 |
+
if autofit_gt:
|
| 642 |
+
xml_block_details[0]["GlobalPosition"][1] -= (lowest_y - 0.5)
|
| 643 |
+
xml_block_details[0]["GlobalPosition"][0] -= geo_center[0]
|
| 644 |
+
xml_block_details[0]["GlobalPosition"][2] -= geo_center[2]
|
| 645 |
+
|
| 646 |
+
env_fail = (highest_y - lowest_y > 9.5) or (right_x - left_x > 17) or (forward_z - back_z > 17)
|
| 647 |
+
height, wide, long = round(highest_y - lowest_y, 2), round(right_x - left_x, 2), round(forward_z - back_z, 2)
|
| 648 |
+
|
| 649 |
+
# Validate machine structure
|
| 650 |
+
machine_structure_error = ""
|
| 651 |
+
if "corners" in block_details[1]:
|
| 652 |
+
for i, block in enumerate(block_details):
|
| 653 |
+
if "GlobalPosition" in block or str(block.get("id")) in ("7", "9"): continue
|
| 654 |
+
if not np.allclose(block["corners"], corners_parent_llm[i], atol=1e-2):
|
| 655 |
+
machine_structure_error += (f"order_id为{i}的方块的顶点信息不一致!\n"
|
| 656 |
+
f"顶点信息:{block['corners']}\n"
|
| 657 |
+
f"建造点相对信息反推的顶点信息:{corners_parent_llm[i]}\n")
|
| 658 |
+
|
| 659 |
+
overlap_infos = check_overlap(corners_parent_llm, vis=False, corners_parent_llm_parent=corners_parent_llm_parent, language=language) if overlap_feedback else "重叠检查被屏蔽"
|
| 660 |
+
|
| 661 |
+
return (corners_feedback_forquizzer, corners_feedback_forbuilder, env_fail,
|
| 662 |
+
long, wide, height, machine_structure_error, overlap_infos)
|
| 663 |
+
|
| 664 |
+
def create_xml(data):
|
| 665 |
+
"""要加很多功能,因为加入了大量的新块"""
|
| 666 |
+
|
| 667 |
+
machine = ET.Element("Machine", version="1", bsgVersion="1.3", name="gpt")
|
| 668 |
+
|
| 669 |
+
# 创建 Global 元素
|
| 670 |
+
global_elem = ET.SubElement(machine, "Global")
|
| 671 |
+
global_infos = data.pop(0)
|
| 672 |
+
gp = global_infos["GlobalPosition"]
|
| 673 |
+
gr = global_infos["GlobalRotation"]
|
| 674 |
+
# if gp[1]<1.5:
|
| 675 |
+
# print("警告,全局高度过低,小于1.5,调整到1.5")
|
| 676 |
+
# gp[1]=1.5
|
| 677 |
+
position = ET.SubElement(global_elem, "Position", x=str(gp[0]), y=str(gp[1]), z=str(gp[2]))
|
| 678 |
+
rotation = ET.SubElement(global_elem, "Rotation", x=str(gr[0]), y=str(gr[1]), z=str(gr[2]), w=str(gr[3]))
|
| 679 |
+
|
| 680 |
+
# 创建 Data 元素
|
| 681 |
+
data_elem = ET.SubElement(machine, "Data")
|
| 682 |
+
string_array = ET.SubElement(data_elem, "StringArray", key="requiredMods")
|
| 683 |
+
|
| 684 |
+
# 创建 Blocks 元素
|
| 685 |
+
blocks_elem = ET.SubElement(machine, "Blocks")
|
| 686 |
+
|
| 687 |
+
# 遍历 corners 数据并创建 Block 元素
|
| 688 |
+
for info in data:
|
| 689 |
+
|
| 690 |
+
block_id = info['id']
|
| 691 |
+
|
| 692 |
+
if info['id']=='18_1':
|
| 693 |
+
block_id ='18'
|
| 694 |
+
|
| 695 |
+
block = ET.SubElement(blocks_elem, "Block", id=str(block_id), guid=info['guid'])
|
| 696 |
+
transform = ET.SubElement(block, "Transform")
|
| 697 |
+
info_p = info['Transform']['Position']
|
| 698 |
+
position = ET.SubElement(transform, "Position", x=str(info_p[0]), y=str(info_p[1]), z=str(info_p[2]))
|
| 699 |
+
info_r = info['Transform']['Rotation']
|
| 700 |
+
rotation = ET.SubElement(transform, "Rotation", x=str(info_r[0]), y=str(info_r[1]), z=str(info_r[2]), w=str(info_r[3]))
|
| 701 |
+
info_s = info['Transform']['Scale']
|
| 702 |
+
scale = ET.SubElement(transform, "Scale", x=str(info_s[0]), y=str(info_s[1]), z=str(info_s[2]))
|
| 703 |
+
block_data = ET.SubElement(block, "Data")
|
| 704 |
+
if str(info['id'])=="0":
|
| 705 |
+
bmt = ET.SubElement(block_data, "Integer", key="bmt-version")
|
| 706 |
+
bmt.text = "1"
|
| 707 |
+
|
| 708 |
+
#线性块设置坐标
|
| 709 |
+
if str(info['id'])=="9":
|
| 710 |
+
bmt = ET.SubElement(block_data,"Single",key = "bmt-slider")
|
| 711 |
+
bmt.text = "10"
|
| 712 |
+
bmt = ET.SubElement(block_data,"StringArray",key = "bmt-contract")
|
| 713 |
+
bmt.text = "L"
|
| 714 |
+
bmt = ET.SubElement(block_data,"Boolean",key = "bmt-toggle")
|
| 715 |
+
bmt.text = "False"
|
| 716 |
+
|
| 717 |
+
if str(info['id'])=="7" or str(info['id'])=="9":
|
| 718 |
+
start_position = ET.SubElement(block_data,"Vector3",key = "start-position")
|
| 719 |
+
ET.SubElement(start_position, "X").text = str(0)
|
| 720 |
+
ET.SubElement(start_position, "Y").text = str(0)
|
| 721 |
+
ET.SubElement(start_position, "Z").text = str(0)
|
| 722 |
+
end_position = ET.SubElement(block_data,"Vector3",key = "end-position")
|
| 723 |
+
ET.SubElement(end_position, "X").text = str(info['end-position'][0])
|
| 724 |
+
ET.SubElement(end_position, "Y").text = str(info['end-position'][1])
|
| 725 |
+
ET.SubElement(end_position, "Z").text = str(info['end-position'][2])
|
| 726 |
+
|
| 727 |
+
|
| 728 |
+
|
| 729 |
+
if str(info['id'])=="22":
|
| 730 |
+
bmt = ET.SubElement(block_data, "Integer", key="bmt-version")
|
| 731 |
+
bmt.text = "1"
|
| 732 |
+
bmt = ET.SubElement(block_data,"Single",key = "bmt-speed")
|
| 733 |
+
bmt.text = "1"
|
| 734 |
+
bmt = ET.SubElement(block_data,"Single",key = "bmt-acceleration")
|
| 735 |
+
bmt.text = "Infinity"
|
| 736 |
+
bmt = ET.SubElement(block_data, "Boolean", key="bmt-auto-brake")
|
| 737 |
+
bmt.text = "True"
|
| 738 |
+
bmt = ET.SubElement(block_data, "Boolean", key="flipped")
|
| 739 |
+
bmt.text = "False"
|
| 740 |
+
|
| 741 |
+
if str(info['id'])=="35":
|
| 742 |
+
bmt = ET.SubElement(block_data,"Single",key = "bmt-mass")
|
| 743 |
+
bmt.text = "3"
|
| 744 |
+
|
| 745 |
+
|
| 746 |
+
#轮子镜像处理
|
| 747 |
+
if "auto" in info:
|
| 748 |
+
bmt = ET.SubElement(block_data, "Boolean", key="bmt-automatic")
|
| 749 |
+
bmt.text = "True"
|
| 750 |
+
bmt = ET.SubElement(block_data, "Boolean", key="bmt-auto-brake")
|
| 751 |
+
bmt.text = "False"
|
| 752 |
+
if "flip" in info and info["flip"]:
|
| 753 |
+
bmt = ET.SubElement(block_data, "Boolean", key="flipped")
|
| 754 |
+
bmt.text = "True"
|
| 755 |
+
if "WheelDoubleSpeed" in info and info["WheelDoubleSpeed"]:
|
| 756 |
+
bmt = ET.SubElement(block_data, "Single", key="bmt-speed")
|
| 757 |
+
bmt.text = "2"
|
| 758 |
+
|
| 759 |
+
# 将 ElementTree 转换为字符串
|
| 760 |
+
tree = ET.ElementTree(machine)
|
| 761 |
+
ET.indent(tree, space="\t", level=0)
|
| 762 |
+
xml_str = ET.tostring(machine, encoding="utf-8", method="xml", xml_declaration=True).decode("utf-8")
|
| 763 |
+
|
| 764 |
+
return xml_str
|
| 765 |
+
|
| 766 |
+
def json_to_xml(input_obj):
|
| 767 |
+
if isinstance(input_obj,str):
|
| 768 |
+
content = extract_json_from_string(input_obj)
|
| 769 |
+
elif isinstance(input_obj,list):
|
| 770 |
+
content = input_obj
|
| 771 |
+
else:
|
| 772 |
+
raise TypeError('Please make sure input type')
|
| 773 |
+
block_details = content
|
| 774 |
+
block_details = convert_to_numpy(block_details)
|
| 775 |
+
|
| 776 |
+
xml_block_details,block_details,_ = llm2xml_filetree(block_details,
|
| 777 |
+
BLOCKPROPERTYPATH,
|
| 778 |
+
selected_menu=None)
|
| 779 |
+
_,_,_,_,_,_,_,_ = llm_feedback_3d(block_sizes=BLOCKPROPERTYPATH,
|
| 780 |
+
xml_block_details=xml_block_details,
|
| 781 |
+
block_details = block_details)
|
| 782 |
+
xml_string = create_xml(xml_block_details)
|
| 783 |
+
return xml_string
|