Test Failed
Push — master ( e2adc5...74fa5e )
by Andrey
03:09
created

df3split()   F

Complexity

Conditions 9

Size

Total Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 9
c 3
b 0
f 0
dl 0
loc 43
rs 3
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 os
11
import argparse
12
import struct
13
14
from PIL import Image
15
16
from df3tools.exceptions import Df3Exception
17
18
19
def from_big_endian(bytestring):
20
    """ Convert big-endian bytestring to int """
21
    bytestring = bytestring.rjust(4, b'\x00')
22
    return struct.unpack(">L", bytestring)[0]
23
24
25
def split_by_n(seq, chunk_size):
26
    """ Generator splitting a sequence into chunks """
27
    while seq:
28
        yield seq[:chunk_size]
29
        seq = seq[chunk_size:]
30
31
32
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...
33
    """
34
    Split POV-Ray density file (DF3) to a series of separate images.
35
36
    :param filename: path to DF3 file to process
37
    :param prefix: output files prefix
38
    :param img_format: output files format (tga, png, etc.)
39
    :param silent: suppress output (info messages, progress etc.)
40
41
    """
42
    if os.isfile(filename):
0 ignored issues
show
Bug introduced by
The Module os does not seem to have a member named isfile.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
43
        raise Df3Exception("File not found: " + filename)
44
45
    with open(filename, "rb") as df3_file:
46
        # detect size
47
        header = df3_file.read(6)
48
        sizes = [from_big_endian(v) for v in split_by_n(header, 2)]
49
        width, height, num_layers = sizes
50
        if not silent:
51
            print("Size: %dx%d, %d layers" % (width, height, num_layers))
52
53
        # detect byte width
54
        data = df3_file.read()
55
        byte_width = int(float(len(data)) / (width * height * num_layers))
56
        if not silent:
57
            plural = ' s'[byte_width > 1]
58
            print("Voxel resolution: %d byte%s" % (byte_width, plural))
59
60
        # parse data and save images
61
        for img_num, img_data in enumerate(split_by_n(data, width * height)):
62
            # values = split_by_n(img_data, byte_width)
63
            # pixels = [from_big_endian(v) for v in values]
64
            layer_num = str(img_num).zfill(len(str(num_layers)))
65
            img = Image.new("L", (width, height))
66
            img.putdata(img_data)
67
            img.save("%s%s.%s" % (prefix, layer_num, img_format.lower()))
68
            percentage = float(img_num + 1) / num_layers * 100
69
            if not silent:
70
                sys.stdout.write("Processing data [%.2f%%]\r" % percentage)
71
                sys.stdout.flush()
72
73
        if not silent:
74
            print("\nDone.")
75
76
77
def main():
78
    """ Main script execution """
79
    parser = argparse.ArgumentParser(description="""
80
    Split POV-Ray density file (DF3) to a series of separate images
81
    """)
82
    parser.add_argument("df3file", help="DF3 filename, including path")
83
    parser.add_argument("-t", "--format", type=str,
84
                        choices=["tga", "png"],
85
                        default="tga",
86
                        help="Output files format")
87
    parser.add_argument("-p", "--prefix", type=str,
88
                        default="layer",
89
                        help="Output files prefix")
90
    parser.add_argument("-s", "--silent", help="Suppress output",
91
                        default=False, action="store_true")
92
93
    args = parser.parse_args()
94
95
    df3split(args.df3file, args.prefix, args.format, args.silent)
96
97
98
if __name__ == "__main__":
99
    main()
100