This repository has been archived by the owner on Aug 28, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
depix.py
executable file
·180 lines (153 loc) · 5.63 KB
/
depix.py
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/usr/bin/env python
# Depix is a program to convert plots back into datasets.
# Copyright (C) 2016 Toon Verstraelen <[email protected]>, Center
# for Molecular Modeling (CMM), Ghent University, Ghent, Belgium; all rights
# reserved unless otherwise stated.
#
# This file is part of Depix.
#
# Depix 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.
#
# Depix 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 <http://www.gnu.org/licenses/>
#
# --
import numpy as np
import sys
def load_pix_data_svg(fn):
x_axis = None
y_axis = None
px_data = None
def parse_svg_coordinates(name, s):
print(f"Parsing coordinates id='{name}' path='{s}'")
words = s.split()
result = []
if words[0] != "m" and words[0] != "M":
raise ValueError("Path data should either start with M or m.")
mode = "m"
for word in words[1:]:
if word == "V":
mode = "v"
continue
if word == "H":
mode = "h"
continue
values = word.split(",")
if mode == "m":
if len(values) != 2:
raise ValueError(f"Expecting two values after m, got {word}")
x = float(values[0])
y = float(values[1])
elif mode == "v":
if len(values) != 1:
raise ValueError(f"Expecting one value after v, got {word}")
y = float(values[0])
elif mode == "h":
if len(values) != 1:
raise ValueError(f"Expecting one value after h, got {word}")
x = float(values[0])
row = np.array([x, y])
if len(result) > 0 and words[0] == "m":
row += result[-1]
result.append(row)
return result
def parse_axis(name, path):
result = parse_svg_coordinates(name, path.getAttribute("d"))
if len(result) != 2:
raise ValueError(f"Expected two points, got {len(result)} ({name})")
words = name.split(":")
result.append(float(words[1]))
result.append(float(words[2]))
result.append(str(words[3]))
return result
def parse_data(path):
result = parse_svg_coordinates(name, path.getAttribute("d"))
return np.array(result)
from xml.dom.minidom import parse
dom = parse(fn)
paths = dom.getElementsByTagName("path")
for path in paths:
name = path.getAttribute("id")
if name.startswith("xaxis"):
if x_axis is not None:
raise ValueError("X-axis defined twice.")
x_axis = parse_axis(name, path)
elif name.startswith("yaxis"):
if y_axis is not None:
raise ValueError("X-axis defined twice.")
y_axis = parse_axis(name, path)
elif name.startswith("data"):
if px_data is not None:
raise ValueError("Data defined twice.")
px_data = parse_data(path)
print()
if x_axis is None:
raise ValueError("No x-axis found.")
if y_axis is None:
raise ValueError("No y-axis found.")
if px_data is None:
raise ValueError("No data points found.")
return x_axis, y_axis, px_data
def transform_px_data(x_axis, y_axis, px_data):
# Print the pixel data on screen, useful for debugging.
print("X axis specs")
print(x_axis)
print()
print("Y axis specs")
print(y_axis)
print()
print("Pixel data points")
print(px_data)
print()
# construct x- and y-unit vectors in pixel coordinates
px_xunit = x_axis[1] - x_axis[0]
px_yunit = y_axis[1] - y_axis[0]
# the affine transformation to pixel coordinates
mat_to_pix = np.array([px_xunit, px_yunit]).T
# the inverse affine transformation
mat_from_pix = np.linalg.inv(mat_to_pix)
# reference point for the x and y values
x_low = np.dot(mat_from_pix, x_axis[0])
y_low = np.dot(mat_from_pix, y_axis[0])
# transform the datapoints to data coordinates
data = np.dot(mat_from_pix, px_data.T).T
data[:, 0] -= x_low[0]
data[:, 1] -= y_low[1]
def convert_unit(values, low, high, kind):
if kind == "lin":
values[:] = values * (high - low) + low
elif kind == "log":
llow = np.log(low)
lhigh = np.log(high)
values[:] = np.exp(values * (lhigh - llow) + llow)
else:
raise NotImplementedError
# convert to plot units
convert_unit(data[:, 0], x_axis[2], x_axis[3], x_axis[4])
convert_unit(data[:, 1], y_axis[2], y_axis[3], y_axis[4])
return data
def process_file(fn):
if fn.endswith(".svg"):
x_axis, y_axis, px_data = load_pix_data_svg(fn)
else:
raise ValueError("Unsopported extension for file %s" % fn)
return transform_px_data(x_axis, y_axis, px_data)
def main():
args = sys.argv[1:]
if len(args) != 2:
raise ValueError("Expecting two arguments: input.svg output.dat")
print("Processing data from %s" % args[0])
print()
data = process_file(args[0])
np.savetxt(args[1], data)
print("Written data in plot units to %s" % args[1])
if __name__ == "__main__":
main()