voxcity 0.5.14__py3-none-any.whl → 0.5.16__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of voxcity might be problematic. Click here for more details.

@@ -1,3 +1,28 @@
1
+ """ENVI-met model file exporter module.
2
+
3
+ This module provides functionality to export voxel city data to ENVI-met INX format.
4
+ ENVI-met is a three-dimensional microclimate model designed to simulate surface-plant-air
5
+ interactions in urban environments.
6
+
7
+ Key Features:
8
+ - Converts voxel grids to ENVI-met compatible format
9
+ - Handles building heights, vegetation, materials, and terrain
10
+ - Supports telescoping grid for vertical mesh refinement
11
+ - Generates complete INX files with all required parameters
12
+ - Creates plant database (EDB) files for 3D vegetation
13
+
14
+ Main Functions:
15
+ - prepare_grids: Processes input grids for ENVI-met format
16
+ - create_xml_content: Generates INX file XML content
17
+ - export_inx: Main function to export model to INX format
18
+ - generate_edb_file: Creates plant database file
19
+ - array_to_string: Helper functions for grid formatting
20
+
21
+ Dependencies:
22
+ - numpy: For array operations
23
+ - datetime: For timestamp generation
24
+ """
25
+
1
26
  import os
2
27
  import numpy as np
3
28
  import datetime
@@ -9,57 +34,98 @@ from ..utils.lc import convert_land_cover
9
34
  def array_to_string(arr):
10
35
  """Convert a 2D numpy array to a string representation with comma-separated values.
11
36
 
37
+ This function formats array values for ENVI-met INX files, where each row must be:
38
+ 1. Indented by 5 spaces
39
+ 2. Values separated by commas
40
+ 3. No trailing comma
41
+
12
42
  Args:
13
- arr: 2D numpy array to convert
43
+ arr (numpy.ndarray): 2D numpy array to convert
14
44
 
15
45
  Returns:
16
- String representation with each row indented by 5 spaces and values comma-separated
46
+ str: String representation with each row indented by 5 spaces and values comma-separated
47
+
48
+ Example:
49
+ >>> arr = np.array([[1, 2], [3, 4]])
50
+ >>> print(array_to_string(arr))
51
+ 1,2
52
+ 3,4
17
53
  """
18
54
  return '\n'.join(' ' + ','.join(str(cell) for cell in row) for row in arr)
19
55
 
20
56
  def array_to_string_with_value(arr, value):
21
57
  """Convert a 2D numpy array to a string representation, replacing all values with a constant.
22
58
 
59
+ This function is useful for creating uniform value grids in ENVI-met INX files,
60
+ such as for soil profiles or fixed height indicators.
61
+
23
62
  Args:
24
- arr: 2D numpy array to convert
25
- value: Value to use for all cells
63
+ arr (numpy.ndarray): 2D numpy array to convert (only shape is used)
64
+ value (str or numeric): Value to use for all cells
26
65
 
27
66
  Returns:
28
- String representation with each row indented by 5 spaces and constant value repeated
67
+ str: String representation with each row indented by 5 spaces and constant value repeated
68
+
69
+ Example:
70
+ >>> arr = np.zeros((2, 2))
71
+ >>> print(array_to_string_with_value(arr, '0'))
72
+ 0,0
73
+ 0,0
29
74
  """
30
75
  return '\n'.join(' ' + ','.join(str(value) for cell in row) for row in arr)
31
76
 
32
77
  def array_to_string_int(arr):
33
78
  """Convert a 2D numpy array to a string representation of rounded integers.
34
79
 
80
+ This function is used for grids that must be represented as integers in ENVI-met,
81
+ such as building numbers or terrain heights. Values are rounded to nearest integer.
82
+
35
83
  Args:
36
- arr: 2D numpy array to convert
84
+ arr (numpy.ndarray): 2D numpy array to convert
37
85
 
38
86
  Returns:
39
- String representation with each row indented by 5 spaces and values rounded to integers
87
+ str: String representation with each row indented by 5 spaces and values rounded to integers
88
+
89
+ Example:
90
+ >>> arr = np.array([[1.6, 2.3], [3.7, 4.1]])
91
+ >>> print(array_to_string_int(arr))
92
+ 2,2
93
+ 4,4
40
94
  """
41
95
  return '\n'.join(' ' + ','.join(str(int(cell+0.5)) for cell in row) for row in arr)
42
96
 
43
97
  def prepare_grids(building_height_grid_ori, building_id_grid_ori, canopy_height_grid_ori, land_cover_grid_ori, dem_grid_ori, meshsize, land_cover_source):
44
98
  """Prepare and process input grids for ENVI-met model.
45
99
 
100
+ This function performs several key transformations on input grids:
101
+ 1. Flips grids vertically to match ENVI-met coordinate system
102
+ 2. Handles missing values and border conditions
103
+ 3. Converts land cover classes to ENVI-met vegetation and material codes
104
+ 4. Processes building IDs and heights
105
+ 5. Adjusts DEM relative to minimum elevation
106
+
46
107
  Args:
47
- building_height_grid_ori: Original building height grid
48
- building_id_grid_ori: Original building ID grid
49
- canopy_height_grid_ori: Original canopy height grid
50
- land_cover_grid_ori: Original land cover grid
51
- dem_grid_ori: Original DEM grid
52
- meshsize: Size of mesh cells
53
- land_cover_source: Source of land cover data
108
+ building_height_grid_ori (numpy.ndarray): Original building height grid (meters)
109
+ building_id_grid_ori (numpy.ndarray): Original building ID grid
110
+ canopy_height_grid_ori (numpy.ndarray): Original canopy height grid (meters)
111
+ land_cover_grid_ori (numpy.ndarray): Original land cover grid (class codes)
112
+ dem_grid_ori (numpy.ndarray): Original DEM grid (meters)
113
+ meshsize (float): Size of mesh cells in meters
114
+ land_cover_source (str): Source of land cover data for class conversion
54
115
 
55
116
  Returns:
56
- Tuple of processed grids:
57
- - building_height_grid: Processed building heights
58
- - building_id_grid: Processed building IDs
59
- - land_cover_veg_grid: Vegetation codes grid
60
- - land_cover_mat_grid: Material codes grid
61
- - canopy_height_grid: Processed canopy heights
62
- - dem_grid: Processed DEM
117
+ tuple: Processed grids:
118
+ - building_height_grid (numpy.ndarray): Building heights
119
+ - building_id_grid (numpy.ndarray): Building IDs
120
+ - land_cover_veg_grid (numpy.ndarray): Vegetation codes
121
+ - land_cover_mat_grid (numpy.ndarray): Material codes
122
+ - canopy_height_grid (numpy.ndarray): Canopy heights
123
+ - dem_grid (numpy.ndarray): Processed DEM
124
+
125
+ Notes:
126
+ - Building heights at grid borders are set to 0
127
+ - DEM is normalized to minimum elevation
128
+ - Land cover is converted based on source-specific mapping
63
129
  """
64
130
  # Flip building height grid vertically and replace NaN with 10m height
65
131
  building_height_grid = np.flipud(np.nan_to_num(building_height_grid_ori, nan=10.0)).copy()
@@ -118,26 +184,35 @@ def prepare_grids(building_height_grid_ori, building_id_grid_ori, canopy_height_
118
184
  def create_xml_content(building_height_grid, building_id_grid, land_cover_veg_grid, land_cover_mat_grid, canopy_height_grid, dem_grid, meshsize, rectangle_vertices, **kwargs):
119
185
  """Create XML content for ENVI-met INX file.
120
186
 
187
+ This function generates the complete XML structure for an ENVI-met INX file,
188
+ including model metadata, geometry settings, and all required grid data.
189
+
121
190
  Args:
122
- building_height_grid: Processed building heights
123
- building_id_grid: Processed building IDs
124
- land_cover_veg_grid: Vegetation codes grid
125
- land_cover_mat_grid: Material codes grid
126
- canopy_height_grid: Processed canopy heights
127
- dem_grid: Processed DEM
128
- meshsize: Size of mesh cells
129
- rectangle_vertices: Vertices defining model area
191
+ building_height_grid (numpy.ndarray): Processed building heights
192
+ building_id_grid (numpy.ndarray): Processed building IDs
193
+ land_cover_veg_grid (numpy.ndarray): Vegetation codes grid
194
+ land_cover_mat_grid (numpy.ndarray): Material codes grid
195
+ canopy_height_grid (numpy.ndarray): Processed canopy heights
196
+ dem_grid (numpy.ndarray): Processed DEM
197
+ meshsize (float): Size of mesh cells in meters
198
+ rectangle_vertices (list): Vertices defining model area as [(lon, lat), ...]
130
199
  **kwargs: Additional keyword arguments:
131
- - author_name: Name of model author
132
- - model_description: Description of model
133
- - domain_building_max_height_ratio: Ratio of domain height to max building height
134
- - useTelescoping_grid: Whether to use telescoping grid
135
- - verticalStretch: Vertical stretch factor
136
- - startStretch: Height to start stretching
137
- - min_grids_Z: Minimum vertical grid cells
200
+ - author_name (str): Name of model author
201
+ - model_description (str): Description of model
202
+ - domain_building_max_height_ratio (float): Ratio of domain height to max building height
203
+ - useTelescoping_grid (bool): Whether to use telescoping grid
204
+ - verticalStretch (float): Vertical stretch factor
205
+ - startStretch (float): Height to start stretching
206
+ - min_grids_Z (int): Minimum vertical grid cells
138
207
 
139
208
  Returns:
140
- String containing complete XML content for INX file
209
+ str: Complete XML content for INX file
210
+
211
+ Notes:
212
+ - Automatically determines location information from coordinates
213
+ - Handles both telescoping and uniform vertical grids
214
+ - Sets appropriate defaults for optional parameters
215
+ - Includes all required ENVI-met model settings
141
216
  """
142
217
  # XML template defining the structure of an ENVI-met INX file
143
218
  xml_template = """<ENVI-MET_Datafile>
@@ -366,11 +441,19 @@ def create_xml_content(building_height_grid, building_id_grid, land_cover_veg_gr
366
441
  return xml_template
367
442
 
368
443
  def save_file(content, output_file_path):
369
- """Save content to a file.
444
+ """Save content to a file with UTF-8 encoding.
445
+
446
+ This function ensures consistent file encoding and error handling when
447
+ saving ENVI-met files.
370
448
 
371
449
  Args:
372
- content: String content to save
373
- output_file_path: Path to save file to
450
+ content (str): String content to save
451
+ output_file_path (str): Path to save file to
452
+
453
+ Notes:
454
+ - Creates parent directories if they don't exist
455
+ - Uses UTF-8 encoding for compatibility
456
+ - Overwrites existing file if present
374
457
  """
375
458
  with open(output_file_path, 'w', encoding='utf-8') as file:
376
459
  file.write(content)
@@ -378,19 +461,28 @@ def save_file(content, output_file_path):
378
461
  def export_inx(building_height_grid_ori, building_id_grid_ori, canopy_height_grid_ori, land_cover_grid_ori, dem_grid_ori, meshsize, land_cover_source, rectangle_vertices, **kwargs):
379
462
  """Export model data to ENVI-met INX file format.
380
463
 
464
+ This is the main function for exporting voxel city data to ENVI-met format.
465
+ It coordinates the entire export process from grid preparation to file saving.
466
+
381
467
  Args:
382
- building_height_grid_ori: Original building height grid
383
- building_id_grid_ori: Original building ID grid
384
- canopy_height_grid_ori: Original canopy height grid
385
- land_cover_grid_ori: Original land cover grid
386
- dem_grid_ori: Original DEM grid
387
- meshsize: Size of mesh cells
388
- land_cover_source: Source of land cover data
389
- rectangle_vertices: Vertices defining model area
468
+ building_height_grid_ori (numpy.ndarray): Original building height grid
469
+ building_id_grid_ori (numpy.ndarray): Original building ID grid
470
+ canopy_height_grid_ori (numpy.ndarray): Original canopy height grid
471
+ land_cover_grid_ori (numpy.ndarray): Original land cover grid
472
+ dem_grid_ori (numpy.ndarray): Original DEM grid
473
+ meshsize (float): Size of mesh cells in meters
474
+ land_cover_source (str): Source of land cover data
475
+ rectangle_vertices (list): Vertices defining model area
390
476
  **kwargs: Additional keyword arguments:
391
- - output_directory: Directory to save output
392
- - file_basename: Base filename for output
477
+ - output_directory (str): Directory to save output
478
+ - file_basename (str): Base filename for output
393
479
  - Other args passed to create_xml_content()
480
+
481
+ Notes:
482
+ - Creates output directory if it doesn't exist
483
+ - Handles grid preparation and transformation
484
+ - Generates complete INX file with all required data
485
+ - Uses standardized file naming convention
394
486
  """
395
487
  # Prepare grids
396
488
  building_height_grid_inx, building_id_grid, land_cover_veg_grid_inx, land_cover_mat_grid_inx, canopy_height_grid_inx, dem_grid_inx = prepare_grids(
@@ -409,10 +501,21 @@ def export_inx(building_height_grid_ori, building_id_grid_ori, canopy_height_gri
409
501
  def generate_edb_file(**kwargs):
410
502
  """Generate ENVI-met database file for 3D plants.
411
503
 
504
+ Creates a plant database file (EDB) containing definitions for trees of
505
+ different heights with customizable leaf area density profiles.
506
+
412
507
  Args:
413
508
  **kwargs: Keyword arguments:
414
- - lad: Leaf area density (default 1.0)
415
- - trunk_height_ratio: Ratio of trunk height to total height (default 11.76/19.98)
509
+ - lad (float): Leaf area density in m²/m³ (default 1.0)
510
+ - trunk_height_ratio (float): Ratio of trunk height to total height
511
+ (default 11.76/19.98)
512
+
513
+ Notes:
514
+ - Generates plants for heights from 1-50m
515
+ - Uses standardized plant IDs in format 'HxxW01'
516
+ - Includes physical properties like wood density
517
+ - Sets seasonal variation profiles
518
+ - Creates complete ENVI-met plant database format
416
519
  """
417
520
 
418
521
  lad = kwargs.get('lad')
@@ -527,13 +630,21 @@ def generate_edb_file(**kwargs):
527
630
  def generate_lad_profile(height, trunk_height_ratio, lad = '1.00000'):
528
631
  """Generate leaf area density profile for a plant.
529
632
 
633
+ Creates a vertical profile of leaf area density (LAD) values for ENVI-met
634
+ plant definitions, accounting for trunk space and crown distribution.
635
+
530
636
  Args:
531
- height: Total height of plant
532
- trunk_height_ratio: Ratio of trunk height to total height
533
- lad: Leaf area density value as string (default '1.00000')
637
+ height (int): Total height of plant in meters
638
+ trunk_height_ratio (float): Ratio of trunk height to total height
639
+ lad (str): Leaf area density value as string (default '1.00000')
534
640
 
535
641
  Returns:
536
- String containing LAD profile data
642
+ str: LAD profile data formatted for ENVI-met EDB file
643
+
644
+ Notes:
645
+ - LAD values start above trunk height
646
+ - Uses 5-space indentation for ENVI-met format
647
+ - Profile follows format: "z-level,x,y,LAD"
537
648
  """
538
649
  lad_profile = []
539
650
  # Only add LAD values above trunk height
@@ -545,14 +656,22 @@ def generate_lad_profile(height, trunk_height_ratio, lad = '1.00000'):
545
656
  def find_min_n(a, r, S_target, max_n=1000000):
546
657
  """Find minimum number of terms needed in geometric series to exceed target sum.
547
658
 
659
+ Used for calculating telescoping grid parameters to achieve desired domain height.
660
+ Solves for n in the equation: a(1-r^n)/(1-r) > S_target
661
+
548
662
  Args:
549
- a: First term of series
550
- r: Common ratio
551
- S_target: Target sum to exceed
552
- max_n: Maximum number of terms to try (default 1000000)
663
+ a (float): First term of series (base cell size)
664
+ r (float): Common ratio (stretch factor)
665
+ S_target (float): Target sum to exceed (desired height)
666
+ max_n (int): Maximum number of terms to try (default 1000000)
553
667
 
554
668
  Returns:
555
- Minimum number of terms needed, or None if not possible within max_n
669
+ int or None: Minimum number of terms needed, or None if not possible within max_n
670
+
671
+ Notes:
672
+ - Handles special case of r=1 (arithmetic series)
673
+ - Protects against overflow with large exponents
674
+ - Returns None if solution not found within max_n terms
556
675
  """
557
676
  n = 1
558
677
  while n <= max_n:
@@ -3,6 +3,19 @@ Module for handling MagicaVoxel .vox files.
3
3
 
4
4
  This module provides functionality for converting 3D numpy arrays to MagicaVoxel .vox files,
5
5
  including color mapping and splitting large models into smaller chunks.
6
+
7
+ The module handles:
8
+ - Color map conversion and optimization
9
+ - Large model splitting into MagicaVoxel-compatible chunks
10
+ - Custom palette creation
11
+ - Coordinate system transformation
12
+ - Batch export of multiple .vox files
13
+
14
+ Key Features:
15
+ - Supports models larger than MagicaVoxel's 256³ size limit
16
+ - Automatic color palette optimization
17
+ - Preserves color mapping across chunks
18
+ - Handles coordinate system differences between numpy and MagicaVoxel
6
19
  """
7
20
 
8
21
  # Required imports for voxel file handling and array manipulation
@@ -10,21 +23,35 @@ import numpy as np
10
23
  from pyvox.models import Vox
11
24
  from pyvox.writer import VoxWriter
12
25
  import os
13
- from ..utils.visualization import get_default_voxel_color_map
26
+ from ..utils.visualization import get_voxel_color_map
14
27
 
15
28
  def convert_colormap_and_array(original_map, original_array):
16
29
  """
17
30
  Convert a color map with arbitrary indices to sequential indices starting from 0
18
31
  and update the corresponding 3D numpy array.
19
32
 
33
+ This function optimizes the color mapping by:
34
+ 1. Converting arbitrary color indices to sequential ones
35
+ 2. Creating a new mapping that preserves color relationships
36
+ 3. Updating the voxel array to use the new sequential indices
37
+
20
38
  Args:
21
- original_map (dict): Dictionary with integer keys and RGB color value lists
22
- original_array (numpy.ndarray): 3D array with integer values corresponding to color map keys
39
+ original_map (dict): Dictionary with integer keys and RGB color value lists.
40
+ Each key is a color index, and each value is a list of [R,G,B] values.
41
+ original_array (numpy.ndarray): 3D array with integer values corresponding to color map keys.
42
+ The array contains indices that match the keys in original_map.
23
43
 
24
44
  Returns:
25
45
  tuple: (new_color_map, new_array)
26
46
  - new_color_map (dict): Color map with sequential indices starting from 0
27
- - new_array (numpy.ndarray): Updated array with new indices
47
+ - new_array (numpy.ndarray): Updated array with new sequential indices
48
+
49
+ Example:
50
+ >>> color_map = {5: [255,0,0], 10: [0,255,0]}
51
+ >>> array = np.array([[[5,10],[10,5]]])
52
+ >>> new_map, new_array = convert_colormap_and_array(color_map, array)
53
+ >>> print(new_map)
54
+ {0: [255,0,0], 1: [0,255,0]}
28
55
  """
29
56
  # Get all the keys and sort them
30
57
  keys = sorted(original_map.keys())
@@ -48,13 +75,25 @@ def convert_colormap_and_array(original_map, original_array):
48
75
 
49
76
  def create_custom_palette(color_map):
50
77
  """
51
- Create a palette array from a color map dictionary.
78
+ Create a palette array from a color map dictionary suitable for MagicaVoxel format.
79
+
80
+ This function:
81
+ 1. Creates a 256x4 RGBA palette array
82
+ 2. Sets full opacity (alpha=255) for all colors by default
83
+ 3. Reserves index 0 for transparent black (void)
84
+ 4. Maps colors sequentially starting from index 1
52
85
 
53
86
  Args:
54
- color_map (dict): Dictionary mapping indices to RGB color values
87
+ color_map (dict): Dictionary mapping indices to RGB color values.
88
+ Each value should be a list of 3 integers [R,G,B] in range 0-255.
55
89
 
56
90
  Returns:
57
- numpy.ndarray: 256x4 array containing RGBA color values
91
+ numpy.ndarray: 256x4 array containing RGBA color values.
92
+ - Shape: (256, 4)
93
+ - Type: uint8
94
+ - Format: [R,G,B,A] for each color
95
+ - Index 0: [0,0,0,0] (transparent)
96
+ - Indices 1-255: Colors from color_map with alpha=255
58
97
  """
59
98
  # Initialize empty palette with alpha channel
60
99
  palette = np.zeros((256, 4), dtype=np.uint8)
@@ -68,13 +107,20 @@ def create_custom_palette(color_map):
68
107
 
69
108
  def create_mapping(color_map):
70
109
  """
71
- Create a mapping from color map keys to sequential indices.
110
+ Create a mapping from color map keys to sequential indices for MagicaVoxel compatibility.
111
+
112
+ Creates a mapping that:
113
+ - Reserves index 0 for void space
114
+ - Reserves index 1 (typically for special use)
115
+ - Maps colors sequentially starting from index 2
72
116
 
73
117
  Args:
74
- color_map (dict): Dictionary mapping indices to RGB color values
118
+ color_map (dict): Dictionary mapping indices to RGB color values.
119
+ The keys can be any integers, they will be remapped sequentially.
75
120
 
76
121
  Returns:
77
- dict: Mapping from original indices to sequential indices starting at 2
122
+ dict: Mapping from original indices to sequential indices starting at 2.
123
+ Example: {original_index1: 2, original_index2: 3, ...}
78
124
  """
79
125
  # Create mapping starting at index 2 (0 is void, 1 is reserved)
80
126
  return {value: i+2 for i, value in enumerate(color_map.keys())}
@@ -83,12 +129,26 @@ def split_array(array, max_size=255):
83
129
  """
84
130
  Split a 3D array into smaller chunks that fit within MagicaVoxel size limits.
85
131
 
132
+ This function handles large voxel models by:
133
+ 1. Calculating required splits in each dimension
134
+ 2. Dividing the model into chunks of max_size or smaller
135
+ 3. Yielding each chunk with its position information
136
+
86
137
  Args:
87
- array (numpy.ndarray): 3D array to split
88
- max_size (int): Maximum size allowed for each dimension
138
+ array (numpy.ndarray): 3D array to split.
139
+ Can be any size, will be split into chunks of max_size or smaller.
140
+ max_size (int, optional): Maximum size allowed for each dimension.
141
+ Defaults to 255 (MagicaVoxel's limit is 256).
89
142
 
90
143
  Yields:
91
- tuple: (sub_array, (i,j,k)) where sub_array is the chunk and (i,j,k) are the chunk indices
144
+ tuple: (sub_array, (i,j,k))
145
+ - sub_array: numpy.ndarray of size <= max_size in each dimension
146
+ - (i,j,k): tuple of indices indicating chunk position in the original model
147
+
148
+ Example:
149
+ >>> array = np.ones((300, 300, 300))
150
+ >>> for chunk, (i,j,k) in split_array(array):
151
+ ... print(f"Chunk at position {i},{j},{k} has shape {chunk.shape}")
92
152
  """
93
153
  # Calculate number of splits needed in each dimension
94
154
  x, y, z = array.shape
@@ -113,13 +173,29 @@ def numpy_to_vox(array, color_map, output_file):
113
173
  """
114
174
  Convert a numpy array to a MagicaVoxel .vox file.
115
175
 
176
+ This function handles the complete conversion process:
177
+ 1. Creates a custom color palette from the color map
178
+ 2. Generates value mapping for voxel indices
179
+ 3. Transforms coordinates to match MagicaVoxel's system
180
+ 4. Saves the model in .vox format
181
+
116
182
  Args:
117
- array (numpy.ndarray): 3D array containing voxel data
118
- color_map (dict): Dictionary mapping indices to RGB color values
119
- output_file (str): Path to save the .vox file
183
+ array (numpy.ndarray): 3D array containing voxel data.
184
+ Values should correspond to keys in color_map.
185
+ color_map (dict): Dictionary mapping indices to RGB color values.
186
+ Each value should be a list of [R,G,B] values (0-255).
187
+ output_file (str): Path to save the .vox file.
188
+ Will overwrite if file exists.
120
189
 
121
190
  Returns:
122
- tuple: (value_mapping, palette, shape) containing the index mapping, color palette and output shape
191
+ tuple: (value_mapping, palette, shape)
192
+ - value_mapping: dict mapping original indices to MagicaVoxel indices
193
+ - palette: numpy.ndarray of shape (256,4) containing RGBA values
194
+ - shape: tuple of (width, height, depth) of the output model
195
+
196
+ Note:
197
+ - Coordinates are transformed to match MagicaVoxel's coordinate system
198
+ - Z-axis is flipped and axes are reordered in the process
123
199
  """
124
200
  # Create color palette and value mapping
125
201
  palette = create_custom_palette(color_map)
@@ -142,15 +218,34 @@ def export_large_voxel_model(array, color_map, output_prefix, max_size=255, base
142
218
  """
143
219
  Export a large voxel model by splitting it into multiple .vox files.
144
220
 
221
+ This function handles models of any size by:
222
+ 1. Creating the output directory if needed
223
+ 2. Splitting the model into manageable chunks
224
+ 3. Saving each chunk as a separate .vox file
225
+ 4. Maintaining consistent color mapping across all chunks
226
+
145
227
  Args:
146
- array (numpy.ndarray): 3D array containing voxel data
147
- color_map (dict): Dictionary mapping indices to RGB color values
148
- output_prefix (str): Directory to save the .vox files
149
- max_size (int): Maximum size allowed for each dimension
150
- base_filename (str): Base name for the output files
228
+ array (numpy.ndarray): 3D array containing voxel data.
229
+ Can be any size, will be split into chunks if needed.
230
+ color_map (dict): Dictionary mapping indices to RGB color values.
231
+ Each value should be a list of [R,G,B] values (0-255).
232
+ output_prefix (str): Directory to save the .vox files.
233
+ Will be created if it doesn't exist.
234
+ max_size (int, optional): Maximum size allowed for each dimension.
235
+ Defaults to 255 (MagicaVoxel's limit is 256).
236
+ base_filename (str, optional): Base name for the output files.
237
+ Defaults to 'chunk'. Final filenames will be {base_filename}_{i}_{j}_{k}.vox
151
238
 
152
239
  Returns:
153
- tuple: (value_mapping, palette) containing the index mapping and color palette
240
+ tuple: (value_mapping, palette)
241
+ - value_mapping: dict mapping original indices to MagicaVoxel indices
242
+ - palette: numpy.ndarray of shape (256,4) containing RGBA values
243
+
244
+ Example:
245
+ >>> array = np.ones((500,500,500))
246
+ >>> color_map = {1: [255,0,0]}
247
+ >>> export_large_voxel_model(array, color_map, "output/model")
248
+ # Creates files like: output/model/chunk_0_0_0.vox, chunk_0_0_1.vox, etc.
154
249
  """
155
250
  # Create output directory if it doesn't exist
156
251
  os.makedirs(output_prefix, exist_ok=True)
@@ -168,16 +263,31 @@ def export_magicavoxel_vox(array, output_dir, base_filename='chunk', voxel_color
168
263
  """
169
264
  Export a voxel model to MagicaVoxel .vox format.
170
265
 
266
+ This is the main entry point for voxel model export. It handles:
267
+ 1. Color map management (using default if none provided)
268
+ 2. Color index optimization
269
+ 3. Large model splitting and export
270
+ 4. Progress reporting
271
+
171
272
  Args:
172
- array (numpy.ndarray): 3D array containing voxel data
173
- output_dir (str): Directory to save the .vox files
174
- base_filename (str): Base name for the output files
273
+ array (numpy.ndarray): 3D array containing voxel data.
274
+ Values should correspond to keys in voxel_color_map.
275
+ output_dir (str): Directory to save the .vox files.
276
+ Will be created if it doesn't exist.
277
+ base_filename (str, optional): Base name for the output files.
278
+ Defaults to 'chunk'. Used when model is split into multiple files.
175
279
  voxel_color_map (dict, optional): Dictionary mapping indices to RGB color values.
176
- If None, uses default color map.
280
+ If None, uses default color map from utils.visualization.
281
+ Each value should be a list of [R,G,B] values (0-255).
282
+
283
+ Note:
284
+ - Large models are automatically split into multiple files
285
+ - Color mapping is optimized and made sequential
286
+ - Progress information is printed to stdout
177
287
  """
178
288
  # Use default color map if none provided
179
289
  if voxel_color_map is None:
180
- voxel_color_map = get_default_voxel_color_map()
290
+ voxel_color_map = get_voxel_color_map()
181
291
 
182
292
  # Convert color map and array to sequential indices
183
293
  converted_voxel_color_map, converted_array = convert_colormap_and_array(voxel_color_map, array)