vectorizer-svg 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
vectorizer-svg/core.py
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
from math import sqrt
|
|
2
|
+
|
|
3
|
+
class Vector:
|
|
4
|
+
def __init__(self, *coords):
|
|
5
|
+
if len(coords) == 1 and hasattr(coords[0], "__iter__"):
|
|
6
|
+
coords = tuple(coords[0])
|
|
7
|
+
self.coords = tuple(float(c) for c in coords)
|
|
8
|
+
if len(self.coords) == 0:
|
|
9
|
+
raise ValueError("Vector must have at least one component.")
|
|
10
|
+
|
|
11
|
+
def __len__(self):
|
|
12
|
+
return len(self.coords)
|
|
13
|
+
|
|
14
|
+
def __iter__(self):
|
|
15
|
+
return iter(self.coords)
|
|
16
|
+
|
|
17
|
+
def __repr__(self):
|
|
18
|
+
return f"Vector{self.coords}"
|
|
19
|
+
|
|
20
|
+
# عمليات أساسية
|
|
21
|
+
def __add__(self, other):
|
|
22
|
+
other = self._coerce(other)
|
|
23
|
+
return Vector(a + b for a, b in zip(self.coords, other.coords))
|
|
24
|
+
|
|
25
|
+
def __sub__(self, other):
|
|
26
|
+
other = self._coerce(other)
|
|
27
|
+
return Vector(a - b for a, b in zip(self.coords, other.coords))
|
|
28
|
+
|
|
29
|
+
def __neg__(self):
|
|
30
|
+
return Vector(-c for c in self.coords)
|
|
31
|
+
|
|
32
|
+
def __mul__(self, other):
|
|
33
|
+
if isinstance(other, (int, float)):
|
|
34
|
+
return Vector(c * other for c in self.coords)
|
|
35
|
+
raise TypeError("Use v.dot(w) or dot(v,w) for dot product, not v*w.")
|
|
36
|
+
|
|
37
|
+
def __rmul__(self, other):
|
|
38
|
+
return self.__mul__(other)
|
|
39
|
+
|
|
40
|
+
def _coerce(self, other):
|
|
41
|
+
if not isinstance(other, Vector):
|
|
42
|
+
other = Vector(other)
|
|
43
|
+
if len(other) != len(self):
|
|
44
|
+
raise ValueError("Vectors must have same dimension.")
|
|
45
|
+
return other
|
|
46
|
+
|
|
47
|
+
# Dot Product
|
|
48
|
+
def dot(self, other):
|
|
49
|
+
other = self._coerce(other)
|
|
50
|
+
return sum(a * b for a, b in zip(self.coords, other.coords))
|
|
51
|
+
|
|
52
|
+
# Cross Product
|
|
53
|
+
def cross(self, other):
|
|
54
|
+
other = self._coerce(other)
|
|
55
|
+
if len(self) == 3 and len(other) == 3:
|
|
56
|
+
a1, a2, a3 = self.coords
|
|
57
|
+
b1, b2, b3 = other.coords
|
|
58
|
+
return Vector(a2*b3 - a3*b2,
|
|
59
|
+
a3*b1 - a1*b3,
|
|
60
|
+
a1*b2 - a2*b1)
|
|
61
|
+
elif len(self) == 2 and len(other) == 2:
|
|
62
|
+
a1, a2 = self.coords
|
|
63
|
+
b1, b2 = other.coords
|
|
64
|
+
return a1*b2 - a2*b1
|
|
65
|
+
else:
|
|
66
|
+
raise ValueError("Cross product defined for 2D (scalar) or 3D vectors.")
|
|
67
|
+
|
|
68
|
+
# Norm + Unit Vector
|
|
69
|
+
def norm(self):
|
|
70
|
+
return sqrt(sum(c*c for c in self.coords))
|
|
71
|
+
|
|
72
|
+
def unit(self):
|
|
73
|
+
n = self.norm()
|
|
74
|
+
if n == 0:
|
|
75
|
+
raise ValueError("Zero vector has no unit direction.")
|
|
76
|
+
return (1.0 / n) * self
|
|
77
|
+
|
|
78
|
+
def to2d(self):
|
|
79
|
+
if len(self) == 2:
|
|
80
|
+
return self
|
|
81
|
+
elif len(self) >= 2:
|
|
82
|
+
return Vector(self.coords[0], self.coords[1])
|
|
83
|
+
else:
|
|
84
|
+
raise ValueError("Cannot project 1D or empty vector to 2D")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def gradient(f, point, h=1e-5):
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
point = tuple(float(x) for x in point)
|
|
92
|
+
n = len(point)
|
|
93
|
+
partials = []
|
|
94
|
+
for i in range(n):
|
|
95
|
+
forward = list(point)
|
|
96
|
+
backward = list(point)
|
|
97
|
+
forward[i] += h
|
|
98
|
+
backward[i] -= h
|
|
99
|
+
df = f(forward) - f(backward)
|
|
100
|
+
partials.append(df / (2*h))
|
|
101
|
+
return Vector(partials)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def vector_derivative(F, t, h=1e-5):
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
ft_plus = F(t + h)
|
|
108
|
+
ft_minus = F(t - h)
|
|
109
|
+
if not isinstance(ft_plus, Vector):
|
|
110
|
+
ft_plus = Vector(ft_plus)
|
|
111
|
+
if not isinstance(ft_minus, Vector):
|
|
112
|
+
ft_minus = Vector(ft_minus)
|
|
113
|
+
return (1.0 / (2*h)) * (ft_plus - ft_minus)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def plot_vectors(vectors, width=600, height=600, padding=60,
|
|
120
|
+
axis_range=None, show_coords=True, show_grid=True,
|
|
121
|
+
tick_count=7):
|
|
122
|
+
|
|
123
|
+
vs = [v if isinstance(v, Vector) else Vector(v) for v in vectors]
|
|
124
|
+
vs2d = [v.to2d() for v in vs]
|
|
125
|
+
|
|
126
|
+
xs = [0.0]; ys = [0.0]
|
|
127
|
+
for v in vs2d:
|
|
128
|
+
xs.append(v.coords[0]); ys.append(v.coords[1])
|
|
129
|
+
|
|
130
|
+
if axis_range is None:
|
|
131
|
+
xmin, xmax = min(xs), max(xs)
|
|
132
|
+
ymin, ymax = min(ys), max(ys)
|
|
133
|
+
|
|
134
|
+
if xmin == xmax: xmin -= 1; xmax += 1
|
|
135
|
+
if ymin == ymax: ymin -= 1; ymax += 1
|
|
136
|
+
|
|
137
|
+
dx = xmax - xmin; dy = ymax - ymin
|
|
138
|
+
xmin -= 0.3 * dx; xmax += 0.3 * dx
|
|
139
|
+
ymin -= 0.3 * dy; ymax += 0.3 * dy
|
|
140
|
+
else:
|
|
141
|
+
xmin, xmax, ymin, ymax = axis_range
|
|
142
|
+
|
|
143
|
+
def map_point(x, y):
|
|
144
|
+
sx = padding + (x - xmin) / (xmax - xmin) * (width - 2*padding)
|
|
145
|
+
sy = height - (padding + (y - ymin) / (ymax - ymin) * (height - 2*padding))
|
|
146
|
+
return sx, sy
|
|
147
|
+
|
|
148
|
+
svg = []
|
|
149
|
+
svg.append(f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" '
|
|
150
|
+
f'style="font-family: sans-serif;">')
|
|
151
|
+
|
|
152
|
+
svg.append("""
|
|
153
|
+
<defs>
|
|
154
|
+
<marker id="arrow" markerWidth="10" markerHeight="10" refX="5" refY="5"
|
|
155
|
+
orient="auto" markerUnits="strokeWidth">
|
|
156
|
+
<path d="M0,0 L10,5 L0,10 z" fill="blue"/>
|
|
157
|
+
</marker>
|
|
158
|
+
</defs>""")
|
|
159
|
+
|
|
160
|
+
svg.append('<rect x="0" y="0" width="100%" height="100%" fill="white"/>')
|
|
161
|
+
|
|
162
|
+
ox, oy = map_point(0, 0)
|
|
163
|
+
|
|
164
|
+
xticks = [xmin + i*(xmax-xmin)/(tick_count-1) for i in range(tick_count)]
|
|
165
|
+
yticks = [ymin + i*(ymax-ymin)/(tick_count-1) for i in range(tick_count)]
|
|
166
|
+
|
|
167
|
+
if show_grid:
|
|
168
|
+
for x in xticks:
|
|
169
|
+
sx, _ = map_point(x, 0)
|
|
170
|
+
svg.append(f'<line x1="{sx}" y1="{padding}" x2="{sx}" y2="{height-padding}" stroke="#eeeeee"/>')
|
|
171
|
+
for y in yticks:
|
|
172
|
+
_, sy = map_point(0, y)
|
|
173
|
+
svg.append(f'<line x1="{padding}" y1="{sy}" x2="{width-padding}" y2="{sy}" stroke="#eeeeee"/>')
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
x0,_ = map_point(xmin, 0)
|
|
179
|
+
x1,_ = map_point(xmax, 0)
|
|
180
|
+
_,y0 = map_point(0, ymin)
|
|
181
|
+
_,y1 = map_point(0, ymax)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
svg.append(f'<line x1="{x0}" y1="{oy}" x2="{x1}" y2="{oy}" stroke="black" stroke-width="2"/>')
|
|
187
|
+
svg.append(f'<line x1="{ox}" y1="{y0}" x2="{ox}" y2="{y1}" stroke="black" stroke-width="2"/>')
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
for x in xticks:
|
|
191
|
+
sx, sy = map_point(x, 0)
|
|
192
|
+
svg.append(f'<line x1="{sx}" y1="{oy-5}" x2="{sx}" y2="{oy+5}" stroke="black" stroke-width="1.2"/>')
|
|
193
|
+
svg.append(f'<text x="{sx-10}" y="{oy+20}" font-size="14" fill="black" font-family="sans-serif">{x:.1f}</text>')
|
|
194
|
+
|
|
195
|
+
for y in yticks:
|
|
196
|
+
sx, sy = map_point(0, y)
|
|
197
|
+
svg.append(f'<line x1="{ox-5}" y1="{sy}" x2="{ox+5}" y2="{sy}" stroke="black" stroke-width="1.2"/>')
|
|
198
|
+
svg.append(f'<text x="{ox+10}" y="{sy+5}" font-size="14" fill="black" font-family="sans-serif">{y:.1f}</text>')
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
# Vectors
|
|
207
|
+
for v in vs2d:
|
|
208
|
+
x, y = v.coords
|
|
209
|
+
sx, sy = map_point(x, y)
|
|
210
|
+
svg.append(f'<line x1="{ox}" y1="{oy}" x2="{sx}" y2="{sy}" stroke="blue" stroke-width="2.5" marker-end="url(#arrow)"/>')
|
|
211
|
+
|
|
212
|
+
if show_coords:
|
|
213
|
+
svg.append(f'<text x="{sx+8}" y="{sy-8}" font-size="13" font-family="sans-serif">'
|
|
214
|
+
f'({x:.2f}, {y:.2f})</text>')
|
|
215
|
+
|
|
216
|
+
svg.append("</svg>")
|
|
217
|
+
return "\n".join(svg)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vectorizer-svg
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Simple linear algabra library
|
|
5
|
+
Author: 0xSaad
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
|
|
9
|
+
# Vectorizer-svg
|
|
10
|
+
|
|
11
|
+
Simple Linear Algebra Library with SVG Manipulation
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install vectorizer
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- Vectors graph
|
|
22
|
+
- Operations on vectors
|
|
23
|
+
- Gradient vector
|
|
24
|
+
|
|
25
|
+
### Credits :
|
|
26
|
+
|
|
27
|
+
[0xSaad](https://x.com/0xdonzdev)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
vectorizer-svg/__init__.py,sha256=KmsOz7JHACIbWkH6X5zaBB9PcJEAKvyrBQZvDJumVmc,163
|
|
2
|
+
vectorizer-svg/core.py,sha256=I7QfB4HIH3Obebtp3JoYm0ed9gP4h6irmvTg_ovlWn8,6800
|
|
3
|
+
vectorizer_svg-0.1.0.dist-info/METADATA,sha256=ISzueaw6J9Kjf73WKopteRww9uZ50oW-Eila4cC49lk,448
|
|
4
|
+
vectorizer_svg-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
5
|
+
vectorizer_svg-0.1.0.dist-info/top_level.txt,sha256=o-LZZ0WEqGooVZT3NYvik6Jc3Wz7rCXQCRZTcxK2QOA,15
|
|
6
|
+
vectorizer_svg-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
vectorizer-svg
|