FallingStars123 commited on
Commit
b0998d1
·
verified ·
1 Parent(s): da6757d

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. main.py +615 -0
main.py ADDED
@@ -0,0 +1,615 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pyvista as pv
3
+ from nbtlib import File, Compound, Int, ByteArray, load
4
+ import re
5
+ import os
6
+
7
+ # 定义方块类型和子类型映射
8
+ BLOCK_TYPE_MAP = {
9
+ "log": 0,
10
+ "planks": 1,
11
+ "stairs": 2,
12
+ "slab": 3,
13
+ "fence": 4,
14
+ "glass_pane": 5,
15
+ "door": 6,
16
+ "functional": 7, # 比如箱子、工作台这种
17
+ "grass_block": 8,
18
+ "air": 9
19
+ }
20
+
21
+ STAIR_SUBTYPE_MAP = {
22
+ "oak_stairs": 0,
23
+ "dark_oak_stairs": 1,
24
+ "birch_stairs": 2,
25
+ "spruce_stairs": 3
26
+ }
27
+
28
+ def encode_block_data(decoded_data):
29
+ """
30
+ 编码解码后的 block_data,处理负数和特殊编码的方块 ID,恢复为原始数据格式。
31
+ """
32
+ encoded_data = []
33
+ for value in decoded_data:
34
+ if value <= 127:
35
+ # 如果值小于等于 127,直接添加
36
+ encoded_data.append(value)
37
+ else:
38
+ # 需要分割为多个字节,处理超出 127 的数值
39
+ negative_value = (value % 128) - 128 # 计算负数部分
40
+ next_value = value // 128 # 计算增量部分
41
+
42
+ # 确保编码后的值在 -128 到 127 范围内
43
+ if negative_value < -128 or negative_value > 127:
44
+ raise ValueError(f"编码值 {negative_value} 超出了 ByteArray 可接受范围")
45
+ if next_value < -128 or next_value > 127:
46
+ raise ValueError(f"增量值 {next_value} 超出了 ByteArray 可接受范围")
47
+
48
+ encoded_data.append(negative_value)
49
+ encoded_data.append(next_value)
50
+
51
+ return encoded_data
52
+
53
+ def decode_block_data(block_data):
54
+ """
55
+ 解码 block_data,把负数和特殊编码的方块 ID 转换为正确的无符号数或计算值。
56
+ """
57
+ decoded_data = []
58
+ i = 0
59
+
60
+ while i < len(block_data):
61
+ value = block_data[i]
62
+
63
+ if value == 127:
64
+ decoded_data.append(value) # Start mark as Byte(127)
65
+ i += 1
66
+ elif value < 0:
67
+ negative_value = value
68
+ next_value = block_data[i + 1]
69
+
70
+ # 根据规则计算最终值
71
+ calculated_value = (next_value + 1) * 128 + negative_value
72
+ decoded_data.append(calculated_value)
73
+ i += 2 # 跳过下一个增量值
74
+ else:
75
+ decoded_data.append(value) # 正常的 Byte(x)
76
+ i += 1
77
+
78
+ return decoded_data
79
+
80
+ def build_output_data(block_data, palette, width, height, length):
81
+ """
82
+ 根据 block_data 和 palette 构建输出数据,包括方块名称和三维坐标。
83
+ """
84
+ # 解码 block_data
85
+ decoded_block_data = decode_block_data(block_data)
86
+
87
+ # 构建反向映射
88
+ id_to_block = {v: k for k, v in palette.items()}
89
+
90
+ # 构建输出数据
91
+ output_data = []
92
+ index = 0
93
+ for y in range(height):
94
+ for z in range(length):
95
+ for x in range(width):
96
+ if index >= len(decoded_block_data):
97
+ break
98
+ block_id = decoded_block_data[index]
99
+ block_name = id_to_block.get(block_id, 'unknown')
100
+ output_data.append({
101
+ 'block': block_name,
102
+ 'coordinates': (x, y, z)
103
+ })
104
+ index += 1
105
+
106
+ print(f"✅ 成功加载 {len(output_data)} 个方块数据!")
107
+ print(f"✅ 成功加载 {len(palette)} 个方块 ID!")
108
+ print(f"✅ 地图尺寸 (宽度x): {int(width)},(高度y): {int(height)},(长度z): {int(length)}")
109
+
110
+ return output_data
111
+
112
+ def generate_schem(block_array, palette, width, height, length, filename):
113
+ """
114
+ 将方块数组和 palette 转换为 .schem 文件。
115
+
116
+ 参数:
117
+ block_array: 3D numpy 数组,表示方块在空间中的排布。
118
+ palette: 字典,映射方块名称到方块 ID(如 {'minecraft:air': 0, 'minecraft:gold_block': 1})。
119
+ width: X 轴方向的长度。
120
+ height: Y 轴方向的长度。
121
+ length: Z 轴方向的长度。
122
+ filename: 输出的 .schem 文件名(如 'output.schem')。
123
+ """
124
+
125
+ # 构建 BlockData,确保 Y 轴倒序,这样在 Minecraft 中是“从下往上”展示
126
+ block_data = []
127
+ for y in range(height):
128
+ for z in range(length):
129
+ for x in range(width):
130
+ block_data.append(block_array[y, z, x])
131
+
132
+ # 编码 block_data
133
+ encoded_block_data = encode_block_data(block_data)
134
+
135
+ # 转换为 ByteArray 格式(编码后的数据不会超出 int8 范围)
136
+ block_data = ByteArray(encoded_block_data)
137
+
138
+ # 构建 NBT 数据
139
+ schem_data = File(Compound({
140
+ 'Palette': Compound({block: Int(id) for block, id in palette.items()}),
141
+ 'PaletteMax': Int(len(palette)),
142
+ 'BlockData': block_data,
143
+ 'Width': Int(width),
144
+ 'Height': Int(height),
145
+ 'Length': Int(length),
146
+ 'Version': Int(1)
147
+ }))
148
+
149
+ # 保存为 .schem 文件
150
+ with open(filename, 'wb') as f:
151
+ schem_data.write(f)
152
+
153
+ print(f"✅ 成功生成 '{filename}' 文件!")
154
+
155
+ def parse_short(value):
156
+ return int(value.split('(')[1].rstrip(')'))
157
+
158
+ def preview_point_cloud(output_data, air_block='minecraft:air', point_size=50):
159
+ """
160
+ 使用 PyVista 可视化 Minecraft 方块数据的点云。
161
+ """
162
+ points = []
163
+ colors = []
164
+
165
+ for data in output_data:
166
+ x, y, z = data['coordinates']
167
+ block_name = data['block']
168
+
169
+ if block_name == air_block:
170
+ continue # 跳过空气方块
171
+
172
+ # 把坐标加入点云,同时把 y 和 z 对调,让模型“站正”
173
+ points.append([x, z, y])
174
+
175
+ # 给不同的方块一个颜色(简单用哈希生成颜色)
176
+ color = hash(block_name) % 0xFFFFFF # 转成 24 位颜色
177
+ r = (color >> 16) & 0xFF
178
+ g = (color >> 8) & 0xFF
179
+ b = color & 0xFF
180
+ colors.append([r, g, b])
181
+
182
+ # 转换为 NumPy 数组
183
+ points = np.array(points)
184
+ colors = np.array(colors)
185
+
186
+ if len(points) == 0:
187
+ print("❌ 没有可视化的方块(可能都是空气方块)")
188
+ return
189
+
190
+ # 用 PyVista 创建点云
191
+ cloud = pv.PolyData(points)
192
+ cloud['colors'] = colors / 255.0 # PyVista 需要 0-1 范围的 RGB
193
+
194
+ # 绘图
195
+ plotter = pv.Plotter()
196
+ plotter.add_points(cloud, scalars='colors', rgb=True, point_size=point_size)
197
+ plotter.show()
198
+
199
+ def preview_cubes_with_colors(output_data, air_block='minecraft:air'):
200
+ """
201
+ 根据 Minecraft 方块数据生成立方体,并为每个方块设置不同的颜色。
202
+ """
203
+ plotter = pv.Plotter()
204
+
205
+ for data in output_data:
206
+ x, y, z = data['coordinates']
207
+ block_name = data['block']
208
+
209
+ if block_name == air_block:
210
+ continue # 跳过空气方块
211
+
212
+ # 生成立方体
213
+ cube = pv.Cube(center=(x, z, y), x_length=1, y_length=1, z_length=1)
214
+
215
+ # 生成颜色(哈希转颜色)
216
+ color = hash(block_name) % 0xFFFFFF
217
+ r = (color >> 16) & 0xFF
218
+ g = (color >> 8) & 0xFF
219
+ b = color & 0xFF
220
+ color = [r / 255.0, g / 255.0, b / 255.0]
221
+
222
+ # 直接在 add_mesh 里传颜色,避免 point_data 的问题
223
+ plotter.add_mesh(cube, color=color, show_edges=False)
224
+
225
+ plotter.show()
226
+
227
+ def preview_slices(output_data, slice_axis='z', air_block='minecraft:air'):
228
+ """
229
+ 使用 PyVista 可视化 Minecraft 方块数据的切片展示。
230
+ """
231
+ slices = []
232
+ blocks = []
233
+
234
+ for data in output_data:
235
+ x, y, z = data['coordinates']
236
+ block_name = data['block']
237
+
238
+ if block_name == air_block:
239
+ continue # 跳过空气方块
240
+
241
+ # 按切片轴对方块进行分组
242
+ if slice_axis == 'z':
243
+ slices.append(z)
244
+ elif slice_axis == 'y':
245
+ slices.append(y)
246
+ else:
247
+ slices.append(x)
248
+
249
+ blocks.append([x, y, z])
250
+
251
+ # 获取唯一的切片层(去重)
252
+ slice_layers = list(set(slices))
253
+ slice_layers.sort()
254
+
255
+ # 构建并展示每一层切片
256
+ plotter = pv.Plotter()
257
+ for slice_layer in slice_layers:
258
+ slice_data = [block for i, block in enumerate(blocks) if blocks[i][2] == slice_layer]
259
+ points = np.array(slice_data)
260
+
261
+ if len(points) > 0:
262
+ cloud = pv.PolyData(points)
263
+ plotter.add_points(cloud, color='blue', point_size=5)
264
+
265
+ plotter.show()
266
+
267
+ def rotate_block(x, y, z, rotation_angle, max_x, max_z):
268
+ if rotation_angle == 90:
269
+ return z, y, max_x - x
270
+ elif rotation_angle == 180:
271
+ return max_x - x, y, max_z - z
272
+ elif rotation_angle == 270:
273
+ return max_z - z, y, x
274
+ return x, y, z
275
+
276
+ def rotate_attr_vector(block_type, attr_vector, rotation_angle):
277
+ if block_type == BLOCK_TYPE_MAP["stairs"] or block_type == BLOCK_TYPE_MAP["door"]:
278
+ facing = attr_vector[0]
279
+ new_facing = (facing + rotation_angle // 90) % 4
280
+ attr_vector[0] = new_facing
281
+ elif block_type == BLOCK_TYPE_MAP["log"]:
282
+ axis = attr_vector[0]
283
+ axis_map = {0: 2, 2: 0} if rotation_angle in [90, 270] else {0: 0, 1: 1, 2: 2}
284
+ attr_vector[0] = axis_map.get(axis, axis)
285
+ elif block_type in [BLOCK_TYPE_MAP["fence"], BLOCK_TYPE_MAP["glass_pane"]]:
286
+ east, north, south, waterlogged, west = attr_vector
287
+ if rotation_angle == 90:
288
+ attr_vector = [north, west, east, waterlogged, south]
289
+ elif rotation_angle == 180:
290
+ attr_vector = [west, south, north, waterlogged, east]
291
+ elif rotation_angle == 270:
292
+ attr_vector = [south, east, west, waterlogged, north]
293
+ return attr_vector
294
+
295
+ def mirror_block(x, y, z, mirror_direction, max_x, max_z):
296
+ if mirror_direction == "north_south":
297
+ return max_x - x, y, z
298
+ elif mirror_direction == "east_west":
299
+ return x, y, max_z - z
300
+ return x, y, z
301
+
302
+ def mirror_attr_vector(block_type, attr_vector, mirror_direction):
303
+ if block_type == BLOCK_TYPE_MAP["stairs"] or block_type == BLOCK_TYPE_MAP["door"]:
304
+ facing = attr_vector[0]
305
+ if mirror_direction == "north_south":
306
+ attr_vector[0] = facing if facing in [0, 2] else 4 - facing
307
+ if block_type == BLOCK_TYPE_MAP["door"]:
308
+ attr_vector[2] = abs(attr_vector[2] - 1)
309
+ elif block_type == BLOCK_TYPE_MAP["stairs"]:
310
+ if attr_vector[2] in {1, 2, 3, 4}:
311
+ attr_vector[2] = {1: 2, 2: 1, 3: 4, 4: 3}.get(attr_vector[2], attr_vector[2])
312
+ elif mirror_direction == "east_west":
313
+ attr_vector[0] = facing if facing in [1, 3] else 2 - facing
314
+ if block_type == BLOCK_TYPE_MAP["door"]:
315
+ attr_vector[2] = abs(attr_vector[2] - 1)
316
+ elif block_type == BLOCK_TYPE_MAP["stairs"]:
317
+ if attr_vector[2] in {1, 2, 3, 4}:
318
+ attr_vector[2] = {1: 2, 2: 1, 3: 4, 4: 3}.get(attr_vector[2], attr_vector[2])
319
+ elif block_type in [BLOCK_TYPE_MAP["fence"], BLOCK_TYPE_MAP["glass_pane"]]:
320
+ east, north, south, waterlogged, west = attr_vector
321
+ if mirror_direction == "north_south":
322
+ attr_vector = [east, south, north, waterlogged, west]
323
+ elif mirror_direction == "east_west":
324
+ attr_vector = [west, north, south, waterlogged, east]
325
+ return attr_vector
326
+
327
+ def parse_block(block_str):
328
+ match = re.match(r"minecraft:(\w+)(?:\[(.*?)\])?", block_str)
329
+ if not match:
330
+ return None
331
+
332
+ block_name, properties = match.groups()
333
+ for key in BLOCK_TYPE_MAP:
334
+ if key in block_name:
335
+ block_type = BLOCK_TYPE_MAP[key]
336
+ break
337
+ else:
338
+ print(f"未知方块类型: {block_name}")
339
+ return None
340
+
341
+ attr_vector = []
342
+ subtype = -1
343
+ prop_dict = {}
344
+ if properties:
345
+ prop_dict = dict(prop.split('=') for prop in properties.split(','))
346
+
347
+ if block_type == BLOCK_TYPE_MAP["stairs"]:
348
+ subtype = STAIR_SUBTYPE_MAP.get(block_name, -1)
349
+ attr_vector = [
350
+ ["north", "east", "south", "west"].index(prop_dict.get("facing", "north")),
351
+ 0 if prop_dict.get("half", "bottom") == "bottom" else 1,
352
+ ["straight","inner_left", "inner_right", "outer_left", "outer_right"].index(prop_dict.get("shape", "straight")),
353
+ 0 if prop_dict.get("waterlogged", "false") == "false" else 1
354
+ ]
355
+ elif block_type == BLOCK_TYPE_MAP["log"]:
356
+ axis_map = {'x': 0, 'y': 1, 'z': 2}
357
+ attr_vector = [axis_map.get(prop_dict.get('axis', 'y'))]
358
+ elif block_type == BLOCK_TYPE_MAP["slab"]:
359
+ attr_vector = [
360
+ 0 if prop_dict.get("type", "bottom") == "bottom" else 1,
361
+ 0 if prop_dict.get("waterlogged", "false") == "false" else 1
362
+ ]
363
+ elif block_type in (BLOCK_TYPE_MAP["fence"], BLOCK_TYPE_MAP["glass_pane"]):
364
+ attr_vector = [
365
+ 0 if prop_dict.get("east", "false") == "false" else 1,
366
+ 0 if prop_dict.get("north", "false") == "false" else 1,
367
+ 0 if prop_dict.get("south", "false") == "false" else 1,
368
+ 0 if prop_dict.get("waterlogged", "false") == "false" else 1,
369
+ 0 if prop_dict.get("west", "false") == "false" else 1
370
+ ]
371
+ elif block_type == BLOCK_TYPE_MAP["door"]:
372
+ attr_vector = [
373
+ ["north", "east", "south", "west"].index(prop_dict.get("facing", "north")),
374
+ 0 if prop_dict.get("half", "lower") == "lower" else 1,
375
+ 0 if prop_dict.get("hinge", "left") == "left" else 1,
376
+ 0 if prop_dict.get("open", "false") == "false" else 1,
377
+ 0 if prop_dict.get("powered", "false") == "false" else 1
378
+ ]
379
+ elif block_type == BLOCK_TYPE_MAP["grass_block"]:
380
+ attr_vector = [0 if prop_dict.get("snowy", "false") == "false" else 1]
381
+ elif block_type == BLOCK_TYPE_MAP["air"]:
382
+ attr_vector = []
383
+ while len(attr_vector) < 5:
384
+ attr_vector.append(-1)
385
+ return (block_type, subtype, attr_vector)
386
+
387
+ def process_block_data(schem_file):
388
+ # 确保 schem 文件夹存在
389
+ if not os.path.exists("schem"):
390
+ os.makedirs("schem")
391
+
392
+ # 加载 .schem 文件
393
+ schem_path = os.path.join("schem", schem_file)
394
+ schem_data = load(schem_path)
395
+ palette = schem_data['Palette']
396
+ block_data = schem_data['BlockData']
397
+ width = schem_data['Width']
398
+ height = schem_data['Height']
399
+ length = schem_data['Length']
400
+
401
+ # 解码 block_data
402
+ decoded_block_data = decode_block_data(block_data)
403
+
404
+ # 构建输出数据
405
+ output_data = build_output_data(decoded_block_data, palette, width, height, length)
406
+
407
+ # 用户输入检测
408
+ while True:
409
+ user_input = input("请输入��可视化的选项(1: 点云, 2: 切片, 3: 彩色立方体, 13: 点云和彩色立方体, q/Q: 退出):")
410
+
411
+ if user_input.lower() in ['q', 'Q']:
412
+ print("退出程序。")
413
+ break
414
+ else:
415
+ # 检查用户输入中是否包含 1、2 或 3
416
+ if '1' in user_input:
417
+ preview_point_cloud(output_data)
418
+ if '2' in user_input:
419
+ preview_slices(output_data)
420
+ if '3' in user_input:
421
+ preview_cubes_with_colors(output_data)
422
+ if '1' not in user_input and '2' not in user_input and '3' not in user_input:
423
+ print("无效的输入,请重新输入。")
424
+
425
+ # 保存方块数据到文本文件
426
+ with open('block_data.txt', 'w', encoding='utf-8') as f:
427
+ for block in output_data:
428
+ x, y, z = block['coordinates']
429
+ block_name = block['block']
430
+ if block_name == 'minecraft:dirt':
431
+ block_name = 'minecraft:grass_block[snowy=false]'
432
+ if x == 9 and y == 9 and z == 9:
433
+ block_name = 'minecraft:air'
434
+ elif x == 0 and y == 9 and z == 9:
435
+ block_name = 'minecraft:air'
436
+ elif x == 0 and y == 9 and z == 0:
437
+ block_name = 'minecraft:air'
438
+ elif x == 9 and y == 9 and z == 0:
439
+ block_name = 'minecraft:air'
440
+ f.write(f"{x},{y},{z},{block_name}\n")
441
+
442
+ # 保存元数据
443
+ with open('metadata.txt', 'w', encoding='utf-8') as f:
444
+ f.write(f"{width},{height},{length}\n")
445
+ for block, block_id in palette.items():
446
+ f.write(f"{block},{block_id}\n")
447
+ print("✅ 方块数据已成功导出到 block_data.txt!")
448
+ print("✅ 元数据已成功导出到 metadata.txt!")
449
+
450
+ def parse_and_process_block_data():
451
+ input_file = "block_data.txt"
452
+ output_file = "parsed_block_data.txt"
453
+
454
+ with open(input_file, "r", encoding="utf-8") as f:
455
+ block_data = f.readlines()
456
+
457
+ with open(output_file, "w", encoding="utf-8") as f:
458
+ for line in block_data:
459
+ parts = line.strip().split(',', 3)
460
+ x, y, z, block_name = parts
461
+ result = int(x), int(y), int(z), parse_block(block_name)
462
+ if result:
463
+ f.write(f"{result}\n")
464
+ print(f"✅ 解析完成,结果已保存到 {output_file}")
465
+
466
+ def get_unique_arrays(arrays):
467
+ """返回独特数组的数组(使用哈希表优化)"""
468
+ seen = set()
469
+ unique_arrays = []
470
+ for arr in arrays:
471
+ arr_hashable = tuple(map(tuple, arr)) # 将数组转换为可哈希的元组
472
+ if arr_hashable not in seen:
473
+ unique_arrays.append(arr)
474
+ seen.add(arr_hashable)
475
+ return unique_arrays
476
+
477
+ def generate_rotated_and_mirrored_data():
478
+ input_file = "parsed_block_data.txt"
479
+ output_file = "block_data_"
480
+
481
+ blocks_original, blocks_90, blocks_180, blocks_270, blocks_mirror_north_south, blocks_mirror_east_west = [], [], [], [], [], []
482
+
483
+ with open('metadata.txt', 'r', encoding='utf-8') as f:
484
+ lines = f.readlines()
485
+ width, height, length = map(parse_short, lines[0].strip().split(','))
486
+
487
+ with open(input_file, "r", encoding="utf-8") as f:
488
+ for line in f:
489
+ line = line.strip()
490
+ if not line:
491
+ continue
492
+ parts = eval(line)
493
+ x, y, z, (block_type, subtype, attr_vector) = parts
494
+
495
+ blocks_original.append([x, y, z, block_type, subtype] + attr_vector)
496
+
497
+ for angle in [90, 180, 270]:
498
+ new_x, new_y, new_z = rotate_block(x, y, z, angle, width-1, length-1)
499
+ new_attr_vector = rotate_attr_vector(block_type, attr_vector.copy(), angle)
500
+ locals()[f"blocks_{angle}"].append([new_x, new_y, new_z, block_type, subtype] + new_attr_vector)
501
+
502
+ for direction in ["north_south", "east_west"]:
503
+ new_x, new_y, new_z = mirror_block(x, y, z, direction, width-1, length-1)
504
+ new_attr_vector = mirror_attr_vector(block_type, attr_vector.copy(), direction)
505
+ locals()[f"blocks_mirror_{direction}"].append([new_x, new_y, new_z, block_type, subtype] + new_attr_vector)
506
+
507
+ arrays = [blocks_original, blocks_90, blocks_180, blocks_270, blocks_mirror_north_south, blocks_mirror_east_west]
508
+ for i in range(len(arrays)):
509
+ arrays[i] = sorted(arrays[i], key=lambda block: (block[0], block[1], block[2]))
510
+
511
+ original_array = np.array(blocks_original, dtype=np.int32)
512
+ mirror_north_south_array = np.array(blocks_mirror_north_south, dtype=np.int32)
513
+ mirror_east_west_array = np.array(blocks_mirror_east_west, dtype=np.int32)
514
+
515
+ original_array = original_array[np.lexsort(original_array[:, :3].T)]
516
+ mirror_north_south_array = mirror_north_south_array[np.lexsort(mirror_north_south_array[:, :3].T)]
517
+ mirror_east_west_array = mirror_east_west_array[np.lexsort(mirror_east_west_array[:, :3].T)]
518
+
519
+ if not np.array_equal(original_array, mirror_north_south_array):
520
+ for i in range(len(original_array)):
521
+ if not np.array_equal(original_array[i], mirror_north_south_array[i]):
522
+ print(f"原数组: {original_array[i]}")
523
+ print(f"南北镜像后: {mirror_north_south_array[i]}")
524
+ else:
525
+ print("原数组与南北镜像后的数组完全一致!")
526
+
527
+ unique_arrays = get_unique_arrays(arrays)
528
+ for i, array in enumerate(unique_arrays):
529
+ array = np.array(array, dtype=np.int32)
530
+ max_x, max_y, max_z = array[:, 0].max(), array[:, 1].max(), array[:, 2].max()
531
+ structure = np.full((max_x + 1, max_y + 1, max_z + 1, 7), -1, dtype=np.int32)
532
+
533
+ for x, y, z, block_type, subtype, *attr_vector in array:
534
+ structure[x, y, z] = [block_type, subtype] + attr_vector
535
+
536
+ # 确保 npy 文件夹存在
537
+ if not os.path.exists("npy"):
538
+ os.makedirs("npy")
539
+
540
+ # 保存到 npy 文件夹
541
+ np.save(os.path.join("npy", output_file + str(i)), structure)
542
+ print(f"✅ 数据成功保存到 npy/{output_file + str(i)}.npy,形状为 {structure.shape}")
543
+
544
+ def compare_npy_and_txt(npy_file, input_txt, check_file):
545
+ """
546
+ 比较 .npy 文件和解析后的文本文件,检查两者是否一致。
547
+
548
+ 参数:
549
+ npy_file: .npy 文件路径。
550
+ input_txt: 解析后的文本文件路径。
551
+ check_file: 保存比对结果的文本文件路径。
552
+ """
553
+ # 加载 .npy 数据
554
+ structure = np.load(npy_file)
555
+
556
+ # 获取形状信息
557
+ W, H, D, _ = structure.shape
558
+
559
+ # 把 npy 文件还原为文本格式
560
+ reconstructed_lines = []
561
+
562
+ # 修正遍历顺序,确保是 (x, y, z)
563
+ for y in range(H):
564
+ for z in range(D):
565
+ for x in range(W):
566
+ block_data = structure[x, y, z]
567
+
568
+ # 不跳过 -1,保留检查完整性
569
+ block_type, subtype, *attr_vector = block_data
570
+ block_type = int(block_type)
571
+ subtype = int(subtype)
572
+ attr_vector = [int(v) for v in attr_vector]
573
+
574
+ # 还原成文本行格式
575
+ reconstructed_line = f"({x}, {y}, {z}, ({block_type}, {subtype}, {attr_vector}))"
576
+ reconstructed_lines.append(reconstructed_line)
577
+
578
+ # 读取原始文本数据
579
+ with open(input_txt, "r", encoding="utf-8") as f:
580
+ original_lines = [line.strip() for line in f.readlines()]
581
+
582
+ # 比对并写入检查文件
583
+ with open(check_file, "w", encoding="utf-8") as f:
584
+ max_len = max(len(original_lines), len(reconstructed_lines))
585
+
586
+ for i in range(max_len):
587
+ orig = original_lines[i] if i < len(original_lines) else "[原文件缺失行]"
588
+ recon = reconstructed_lines[i] if i < len(reconstructed_lines) else "[还原文件缺失行]"
589
+
590
+ if orig != recon:
591
+ f.write(f"❌ 差异行:\n原始: {orig}\n还原: {recon}\n\n")
592
+ else:
593
+ f.write(f"✅ 一致行:\n{orig}\n\n")
594
+
595
+ print(f"检查完成,结果保存到 {check_file}")
596
+
597
+ def check_accuracy_of_txt2npy():
598
+ # 比较生成的 .npy 文件和解析后的文本文件
599
+ npy_file = "npy/block_data_0.npy" # 替换为你的 .npy 文件路径
600
+ input_txt = "parsed_block_data.txt" # 替换为你的解析后的文本文件路径
601
+ check_file = "check_txt2npy.txt" # 替换为你的检查结果文件路径
602
+ compare_npy_and_txt(npy_file, input_txt, check_file)
603
+
604
+ def main():
605
+ schem_file = "WoodHouse_3.schem" # 替换为你的 .schem 文件路径
606
+ process_block_data(schem_file)
607
+ parse_and_process_block_data()
608
+ generate_rotated_and_mirrored_data()
609
+
610
+ # 是否需要检查生成的 .npy 与 .txt 文件的一致性
611
+ check_accuracy_of_txt2npy()
612
+
613
+
614
+ if __name__ == "__main__":
615
+ main()