-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathsvg_handler.py
More file actions
144 lines (90 loc) · 5.07 KB
/
Copy pathsvg_handler.py
File metadata and controls
144 lines (90 loc) · 5.07 KB
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
'''
Copyright (C) 2022 https://github.com/aliemen/
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 3 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, see <https://www.gnu.org/licenses/>.
'''
import numpy as np
from svgpathtools import svg2paths
class SVG_Handler():
def __init__(self, svg_path : str):
self.path = svg_path
# load svg file to process as parametrized function
self._load_svg()
def _load_svg(self):
tmp_paths, tmp_attributes = svg2paths(self.path)
self.all_functions = [] # here we will safe all callable saved curves
self.all_paths = []
for path in tmp_paths: # iterate through all paths
tmp_paths = []
for path_obj in path:
#print(path_obj)
numpy_poly = path_obj.poly() # every curve ranges from 0 to 1
self.all_functions += [numpy_poly] # append curve to internal array of curves
tmp_paths += [numpy_poly]
self.all_paths += [tmp_paths]
self.line_count = len(self.all_functions) # total number of lines
#self._sort_functions() # kind of sorts the distinct paths --> does not really work if you used a "free hand drawing" in the svg
def _get_next_func(self, prev_func, left_funcs): # returns index of next function
end_point = prev_func(1)
left_beginnings = np.array([func_t(0) for func_t in left_funcs], dtype=complex)
nearest_index = np.argmin(np.abs(np.conjugate(left_beginnings - end_point)))
return nearest_index
def _sort_functions(self): # should sort functions such that the curve is mostly "steady"
#print(self.all_functions)
tmp_funcs = self.all_functions
new_functions = [tmp_funcs[0]]
tmp_funcs.pop(0)
for i in range(1, self.line_count): # here add next function and remove if from list
next_ind = self._get_next_func(new_functions[-1], tmp_funcs)
new_functions += [tmp_funcs[next_ind]]
tmp_funcs.pop(next_ind)
self.all_functions = new_functions
def get_whole_image(self, N_per_curve=50): # returns all data points for the whole image (takes into account that discontinuities should not be connected)
t_param = np.linspace(0, 1, N_per_curve)
ret_path = []
#print(points_to_plot)
for i, path in enumerate(self.all_paths):
points_to_plot = [] # self.get_point(t_param)
for func in path:
points_to_plot += [func(t_t) for t_t in t_param]
ret_path += [points_to_plot]
#print(ret_path)
#ret_path = np.array(ret_path, dtype=complex)
real_part = [[points_t.real for points_t in path] for path in ret_path]
imag_part = [[-points_t.imag for points_t in path] for path in ret_path]
#print(ret_path)
#print(real_part)
#print(imag_part)
#print(real_part)
return real_part, imag_part # minus because of some weired normalization?!
def _get_parameter_func(self, t):
func_index = int(t * self.line_count)
if func_index == self.line_count: # means we are at the end
return 1, self.all_functions[func_index-1]
lower_bound = func_index/self.line_count
upper_bount = lower_bound + 1/self.line_count
new_ret_t = t/(upper_bount-lower_bound) - lower_bound/(upper_bount-lower_bound)
return new_ret_t, self.all_functions[func_index]
def _single_points(self, t, reverse): # t is between zero and one
if reverse:
t = -t + 1 # reverse for T \in [0, 1]
use_t, use_func = self._get_parameter_func(t)
return use_func(use_t)
def get_point(self, t, reverse=False):
if isinstance(t, float):
t = [t]
t = np.array(t)
assert np.all(0 <= t) and np.all(t <= 1), "t has to be between 0 and 1"
return np.array([self._single_points(t_t, reverse) for t_t in t])
#if isinstance(t, float):
# return self._single_points(t)
#elif isinstance(t, list):
#assert False, "Parameter t has to be an instance of list (containing floats) or float"