from typing import Union from xml.dom import minidom import numpy as np import matplotlib.pyplot as plt MX = [696, 696, 697.2] MY = [1.2, 0, 0] def str_pair_to_float(s: str) -> np.ndarray: return np.array([float(el) for el in s.split(",")]) def parse_buffer(s: str) -> Union[float, complex]: x, *y = s.split(",") if y: return float(x) + 1j * float(y[0]) else: return float(x) def transform_matrix_and_offset( x: tuple[float, float, float], y: tuple[float, float, float], rx: tuple[float, float, float], ry: tuple[float, float, float], ) -> tuple[np.ndarray, np.ndarray]: a = -( rx[0] * y[1] - rx[0] * y[2] - rx[1] * y[0] + rx[1] * y[2] + rx[2] * y[0] - rx[2] * y[1] ) / (x[1] * y[0] - x[2] * y[0] - x[0] * y[1] + x[2] * y[1] + x[0] * y[2] - x[1] * y[2]) b = -( rx[0] * x[1] - rx[0] * x[2] - rx[1] * x[0] + rx[1] * x[2] + rx[2] * x[0] - rx[2] * x[1] ) / (-x[1] * y[0] + x[2] * y[0] + x[0] * y[1] - x[2] * y[1] - x[0] * y[2] + x[1] * y[2]) c = -( ry[0] * y[1] - ry[0] * y[2] - ry[1] * y[0] + ry[1] * y[2] + ry[2] * y[0] - ry[2] * y[1] ) / (x[1] * y[0] - x[2] * y[0] - x[0] * y[1] + x[2] * y[1] + x[0] * y[2] - x[1] * y[2]) d = -( -ry[0] * x[1] + ry[0] * x[2] + ry[1] * x[0] - ry[1] * x[2] - ry[2] * x[0] + ry[2] * x[1] ) / (x[1] * y[0] - x[2] * y[0] - x[0] * y[1] + x[2] * y[1] + x[0] * y[2] - x[1] * y[2]) e = -( rx[0] * x[2] * y[1] - rx[0] * x[1] * y[2] - rx[1] * x[2] * y[0] + rx[1] * x[0] * y[2] + rx[2] * x[1] * y[0] - rx[2] * x[0] * y[1] ) / (-x[1] * y[0] + x[2] * y[0] + x[0] * y[1] - x[2] * y[1] - x[0] * y[2] + x[1] * y[2]) f = -( -ry[0] * x[2] * y[1] + ry[0] * x[1] * y[2] + ry[1] * x[2] * y[0] - ry[1] * x[0] * y[2] - ry[2] * x[1] * y[0] + ry[2] * x[0] * y[1] ) / (x[1] * y[0] - x[2] * y[0] - x[0] * y[1] + x[2] * y[1] + x[0] * y[2] - x[1] * y[2]) return np.array([[a, b], [c, d]]), np.array([e, f]) def parse_path(s: str) -> list[tuple[np.ndarray, np.ndarray]]: INSTRUCTIONS = "mMlLhHvV" lines: list[list[complex]] = [] current_line: list[complex] = [] pos = 0 + 0j def add_line_point(new_pos): nonlocal pos if not current_line: current_line.append(pos) pos = new_pos current_line.append(pos) s = iter(s + "M") instr = next(s) buffer = "" for char in s: if char not in INSTRUCTIONS: buffer += char continue arg = parse_buffer(buffer) buffer = "" if instr == "m": if current_line: lines.append(current_line) current_line = [] pos += arg elif instr == "M": if current_line: lines.append(current_line) current_line = [] pos = arg elif instr == "l": add_line_point(pos + arg) elif instr == "L": add_line_point(arg) elif instr == "v": add_line_point(pos + 1j * arg) elif instr == "V": add_line_point(0 + 1j * arg) elif instr == "h": add_line_point(pos + arg) elif instr == "H": add_line_point(arg) instr = char if current_line: lines.append(current_line) lines = np.array(lines) return [(line.real, line.imag) for line in lines] def main(): with minidom.parse("./data/Dong2005.svg") as doc: circles = [ (float(circ.getAttribute("cx")), float(circ.getAttribute("cy"))) for circ in doc.getElementsByTagName("circle") ] path_strings = [path.getAttribute("d") for path in doc.getElementsByTagName("path")] px, py = parse_path(path_strings[0])[0] M, off = transform_matrix_and_offset(px, py, MX, MY) x, y = (np.array(circles).dot(M.T) + off).T s = x.argsort() x = x[s] y = y[s] np.savetxt("data/Dong2005.csv", np.c_[x, y], fmt="%.5f") plt.plot(x, y) plt.show() if __name__ == "__main__": main()