Test Failed
Push — master ( 907596...ba1e2a )
by Andrey
02:49
created

df3split()   D

Complexity

Conditions 8

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 8
c 3
b 0
f 0
dl 0
loc 40
rs 4
1
#!/usr/bin/env python
2
"""
3
A command-line tool for spliting POV-Ray density file (DF3)
4
to a series of separate images.
5
6
"""
7
8
from __future__ import print_function
9
import sys
10
import argparse
11
import struct
12
13
from PIL import Image
0 ignored issues
show
introduced by
Unable to import 'PIL'
Loading history...
14
15
16
def from_big_endian(bytestring):
17
    """ Convert big-endian bytestring to int """
18
    bytestring = bytestring.rjust(4, b'\x00')
19
    return struct.unpack(">L", bytestring)[0]
20
21
22
def split_by_n(seq, chunk_size):
23
    """ Generator splitting a sequence into chunks """
24
    while seq:
25
        yield seq[:chunk_size]
26
        seq = seq[chunk_size:]
27
28
29
def df3split(filename, prefix="layer", img_format='tga', silent=True):
0 ignored issues
show
Comprehensibility introduced by
This function exceeds the maximum number of variables (18/15).
Loading history...
30
    """
31
    Split POV-Ray density file (DF3) to a series of separate images.
32
33
    :param filename: path to DF3 file to process
34
    :param prefix: output files prefix
35
    :param img_format: output files format (tga, png, etc.)
36
    :param silent: suppress output (info messages, progress etc.)
37
38
    """
39
    with open(filename, "rb") as df3_file:
40
        # detect size
41
        header = df3_file.read(6)
42
        sizes = [from_big_endian(v) for v in split_by_n(header, 2)]
43
        width, height, num_layers = sizes
44
        if not silent:
45
            print("Size: %dx%d, %d layers" % (width, height, num_layers))
46
47
        # detect byte width
48
        data = df3_file.read()
49
        byte_width = int(float(len(data)) / (width * height * num_layers))
50
        if not silent:
51
            plural = ' s'[byte_width > 1]
52
            print("Voxel resolution: %d byte%s" % (byte_width, plural))
53
54
        # parse data and save images
55
        for img_num, img_data in enumerate(split_by_n(data, width * height)):
56
            # values = split_by_n(img_data, byte_width)
57
            # pixels = [from_big_endian(v) for v in values]
58
            layer_num = str(img_num).zfill(len(str(num_layers)))
59
            img = Image.new("L", (width, height))
60
            img.putdata(img_data)
61
            img.save("%s%s.%s" % (prefix, layer_num, img_format.lower()))
62
            percentage = float(img_num + 1) / num_layers * 100
63
            if not silent:
64
                sys.stdout.write("Processing data [%.2f%%]\r" % percentage)
65
                sys.stdout.flush()
66
67
        if not silent:
68
            print("\nDone.")
69
70
71
def main():
72
    """ Main script execution """
73
    parser = argparse.ArgumentParser(description="""
74
    Split POV-Ray density file (DF3) to a series of separate images
75
    """)
76
    parser.add_argument("df3file", help="DF3 filename, including path")
77
    parser.add_argument("-t", "--format", type=str,
78
                        choices=["tga", "png"],
79
                        default="tga",
80
                        help="Output files format")
81
    parser.add_argument("-p", "--prefix", type=str,
82
                        default="layer",
83
                        help="Output files prefix")
84
    parser.add_argument("-s", "--silent", help="Suppress output",
85
                        default=False, action="store_true")
86
87
    args = parser.parse_args()
88
89
    df3split(args.df3file, args.prefix, args.format, args.silent)
90
91
92
if __name__ == "__main__":
93
    main()
94