Welcome to mirror list, hosted at ThFree Co, Russian Federation.

svg_util.py « io_curve_svg - git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 42e900b4322ff02620a03e27ab494a9f885b47f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# <pep8 compliant>

import re


units = {"": 1.0,
         "px": 1.0,
         "in": 90.0,
         "mm": 90.0 / 25.4,
         "cm": 90.0 / 2.54,
         "pt": 1.25,
         "pc": 15.0,
         "em": 1.0,
         "ex": 1.0,
         "INVALID": 1.0,  # some DocBook files contain this
         }


def srgb_to_linearrgb(c):
    if c < 0.04045:
        return 0.0 if c < 0.0 else c * (1.0 / 12.92)
    else:
        return pow((c + 0.055) * (1.0 / 1.055), 2.4)


def check_points_equal(point_a, point_b):
    return (abs(point_a[0] - point_b[0]) < 1e-6 and
            abs(point_a[1] - point_b[1]) < 1e-6)

match_number = r"-?\d+(\.\d+)?([eE][-+]?\d+)?"
match_first_comma = r"^\s*(?=,)"
match_comma_pair = r",\s*(?=,)"
match_last_comma = r",\s*$"

array_of_floats_pattern = f"({match_number})|{match_first_comma}|{match_comma_pair}|{match_last_comma}"
re_array_of_floats_pattern = re.compile(array_of_floats_pattern)

def parse_array_of_floats(text):
    """
    Accepts comma or space separated list of floats (without units) and returns an array
    of floating point values.
    """
    elements = re_array_of_floats_pattern.findall(text)
    return [value_to_float(v[0]) for v in elements]


def read_float(s: str, i: int = 0):
    """
    Reads floating point value from a string. Parsing starts at the given index.

    Returns the value itself (as a string) and index of first character after the value.
    """
    start = i
    n = len(s)
    token = ''

    # Skip leading whitespace characters
    while i < n and (s[i].isspace() or s[i] == ','):
        i += 1

    if i == n:
        return "0", i

    # Read sign
    if s[i] == '-':
        token += '-'
        i += 1
    elif s[i] == '+':
        i += 1

    # Read integer part
    if s[i].isdigit():
        while i < n and s[i].isdigit():
            token += s[i]
            i += 1

    # Fractional part
    if i < n and s[i] == '.':
        token += '.'
        i += 1

        if i < n and s[i].isdigit():
            while i < n and s[i].isdigit():
                token += s[i]
                i += 1
        elif i == n or s[i].isspace() or s[i] == ',':
            # Inkscape sometimes uses weird float format with missed
            # fractional part after dot. Suppose zero fractional part
            # for this case
            pass
        else:
            raise Exception('Invalid float value near ' + s[start:start + 10])

    # Degree
    if i < n and (s[i] == 'e' or s[i] == 'E'):
        token += s[i]
        i += 1
        if s[i] == '+' or s[i] == '-':
            token += s[i]
            i += 1

        if s[i].isdigit():
            while i < n and s[i].isdigit():
                token += s[i]
                i += 1
        else:
            raise Exception('Invalid float value near ' + s[start:start + 10])

    return token, i


def parse_coord(coord, size):
    """
    Parse coordinate component to common basis

    Needed to handle coordinates set in cm, mm, inches.
    """

    token, last_char = read_float(coord)
    val = float(token)
    unit = coord[last_char:].strip()  # strip() in case there is a space

    if unit == '%':
        return float(size) / 100.0 * val
    else:
        return val * units[unit]

    return val


def value_to_float(value_encoded: str):
    """
    A simple wrapper around float() which supports empty strings (which are converted to 0).
    """
    if len(value_encoded) == 0:
        return 0
    return float(value_encoded)