AxiomX 0.1.2__tar.gz → 0.1.4__tar.gz
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.
- axiomx-0.1.4/AxiomX/__init__.py +20 -0
- {axiomx-0.1.2 → axiomx-0.1.4}/AxiomX/calculus.py +11 -36
- axiomx-0.1.4/AxiomX/constants.py +69 -0
- {axiomx-0.1.2 → axiomx-0.1.4}/AxiomX/exp.py +15 -2
- {axiomx-0.1.2 → axiomx-0.1.4}/AxiomX/functions.py +21 -7
- axiomx-0.1.4/AxiomX/graph.py +332 -0
- axiomx-0.1.4/AxiomX/hyperbolic.py +56 -0
- {axiomx-0.1.2 → axiomx-0.1.4}/AxiomX/matrix.py +148 -2
- axiomx-0.1.4/AxiomX/trig.py +143 -0
- axiomx-0.1.4/AxiomX.egg-info/PKG-INFO +218 -0
- {axiomx-0.1.2 → axiomx-0.1.4}/AxiomX.egg-info/SOURCES.txt +1 -0
- axiomx-0.1.4/PKG-INFO +218 -0
- axiomx-0.1.4/README.md +206 -0
- {axiomx-0.1.2 → axiomx-0.1.4}/pyproject.toml +1 -1
- {axiomx-0.1.2 → axiomx-0.1.4}/setup.py +1 -1
- axiomx-0.1.2/AxiomX/__init__.py +0 -14
- axiomx-0.1.2/AxiomX/constants.py +0 -17
- axiomx-0.1.2/AxiomX/hyperbolic.py +0 -35
- axiomx-0.1.2/AxiomX/trig.py +0 -83
- axiomx-0.1.2/AxiomX.egg-info/PKG-INFO +0 -133
- axiomx-0.1.2/PKG-INFO +0 -133
- axiomx-0.1.2/README.md +0 -121
- {axiomx-0.1.2 → axiomx-0.1.4}/AxiomX.egg-info/dependency_links.txt +0 -0
- {axiomx-0.1.2 → axiomx-0.1.4}/AxiomX.egg-info/top_level.txt +0 -0
- {axiomx-0.1.2 → axiomx-0.1.4}/LICENSE +0 -0
- {axiomx-0.1.2 → axiomx-0.1.4}/setup.cfg +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
AxiomX 0.1.4, the pure Python Scientific Math Library.
|
|
5
|
+
|
|
6
|
+
We have updated a lot and have improved accuracy. Trig Functions are calculated using CORDIC. Also we have updated our graphing system, with zooming, panning and gridlines. If you have any suggestions, email us at axiomx.python@gmail.com. Thank you all for acknowledging AxiomX.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
print("AxiomX loading...")
|
|
10
|
+
time.sleep(3)
|
|
11
|
+
print("AxiomX is ready to use!")
|
|
12
|
+
|
|
13
|
+
import AxiomX.constants
|
|
14
|
+
import AxiomX.calculus
|
|
15
|
+
import AxiomX.functions
|
|
16
|
+
import AxiomX.exp
|
|
17
|
+
import AxiomX.trig
|
|
18
|
+
import AxiomX.hyperbolic
|
|
19
|
+
|
|
20
|
+
version = '0.1.0'
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AxiomX.calculus - the calculus module of AxiomX.
|
|
3
|
+
|
|
4
|
+
AxiomX.calculus helps in doing advanced calculus like evaluating integrals, summations and also does convergence analysis.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
|
|
1
8
|
# integrating integrals
|
|
2
9
|
def integrate(function, lowlim, uplim, n=10000):
|
|
10
|
+
"""Integrates functions with the help of Simpson's Rule."""
|
|
3
11
|
h = (uplim - lowlim) / n
|
|
4
12
|
s = function(lowlim) + function(uplim)
|
|
5
13
|
|
|
@@ -13,6 +21,7 @@ def integrate(function, lowlim, uplim, n=10000):
|
|
|
13
21
|
return s * h / 3
|
|
14
22
|
|
|
15
23
|
def summation(lowlim, uplim, function, max_terms=10**6):
|
|
24
|
+
"""Sums up a function from a given lower limit to an upper limit (also supports infinity)."""
|
|
16
25
|
total = 0
|
|
17
26
|
count = 0
|
|
18
27
|
|
|
@@ -42,6 +51,7 @@ def summation(lowlim, uplim, function, max_terms=10**6):
|
|
|
42
51
|
return total
|
|
43
52
|
|
|
44
53
|
def converges(f, tolerance=1e-6, max_terms=10**6):
|
|
54
|
+
"""Check if a given function converges or diverges."""
|
|
45
55
|
total = 0
|
|
46
56
|
prev = 0
|
|
47
57
|
|
|
@@ -57,39 +67,4 @@ def converges(f, tolerance=1e-6, max_terms=10**6):
|
|
|
57
67
|
|
|
58
68
|
prev = total
|
|
59
69
|
|
|
60
|
-
return False
|
|
61
|
-
|
|
62
|
-
def lim(f, xlim, tol=1e-5):
|
|
63
|
-
# Try direct substitution
|
|
64
|
-
try:
|
|
65
|
-
return f(xlim)
|
|
66
|
-
except ZeroDivisionError:
|
|
67
|
-
pass # expected for limits
|
|
68
|
-
except Exception as e:
|
|
69
|
-
return None # or raise e if you want strict behavior
|
|
70
|
-
|
|
71
|
-
hs = [1e-1, 1e-2, 1e-3, 1e-4, 1e-5]
|
|
72
|
-
left_vals = []
|
|
73
|
-
right_vals = []
|
|
74
|
-
|
|
75
|
-
for h in hs:
|
|
76
|
-
try:
|
|
77
|
-
left_vals.append(f(xlim - h))
|
|
78
|
-
except ZeroDivisionError:
|
|
79
|
-
continue
|
|
80
|
-
|
|
81
|
-
try:
|
|
82
|
-
right_vals.append(f(xlim + h))
|
|
83
|
-
except ZeroDivisionError:
|
|
84
|
-
continue
|
|
85
|
-
|
|
86
|
-
if not left_vals or not right_vals:
|
|
87
|
-
return None
|
|
88
|
-
|
|
89
|
-
left = left_vals[-1]
|
|
90
|
-
right = right_vals[-1]
|
|
91
|
-
|
|
92
|
-
if abs(left - right) < tol:
|
|
93
|
-
return (left + right) / 2
|
|
94
|
-
|
|
95
|
-
return None
|
|
70
|
+
return False
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AxiomX.constants - the constants module of AxiomX.
|
|
3
|
+
|
|
4
|
+
AxiomX provides a large variety of constants, unlike the default math library which contains only pi, e and tau. Our library also contains series of constants like Bernoulli numbers, Stieltjes Numbers, Harmonic numbers, etc.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from AxiomX.calculus import *
|
|
8
|
+
from AxiomX.functions import *
|
|
9
|
+
from AxiomX.exp import *
|
|
10
|
+
|
|
11
|
+
pi = 3.141592653589793
|
|
12
|
+
e = 2.718281828459045
|
|
13
|
+
tau = 2*pi
|
|
14
|
+
lemniscate = 2.622057554292119
|
|
15
|
+
gauss = lemniscate / pi
|
|
16
|
+
euler_mascheroni = 0.577215664901533
|
|
17
|
+
sqrt_2 = 2**0.5
|
|
18
|
+
sqrt_3 = 3**0.5
|
|
19
|
+
sqrt_5 = 5**0.5
|
|
20
|
+
golden_ratio = (1 + sqrt_5) / 2
|
|
21
|
+
silver_ratio = 1 + sqrt_2
|
|
22
|
+
gelfond = e**pi
|
|
23
|
+
gelfond_schneider = 2**sqrt_2
|
|
24
|
+
infinity = float("inf")
|
|
25
|
+
|
|
26
|
+
def metallic_ratio(n):
|
|
27
|
+
"""Returns the metallic ratio of order n."""
|
|
28
|
+
return (n + (n**2 + 4)**0.5) / 2
|
|
29
|
+
|
|
30
|
+
def harmonic_number(n):
|
|
31
|
+
"""Returns the nth Harmonic number."""
|
|
32
|
+
return summation(1, n, lambda x:1/x)
|
|
33
|
+
|
|
34
|
+
def bernoulli(n):
|
|
35
|
+
"""Returns the nth Bernoulli number."""
|
|
36
|
+
B = [0] * (n + 1)
|
|
37
|
+
B[0] = 1
|
|
38
|
+
|
|
39
|
+
for m in range(1, n + 1):
|
|
40
|
+
B[m] = 0
|
|
41
|
+
for k in range(m):
|
|
42
|
+
B[m] -= (bc(m + 1, k) * B[k])
|
|
43
|
+
B[m] /= (m + 1)
|
|
44
|
+
|
|
45
|
+
if n % 2 == 1:
|
|
46
|
+
if n == 1:
|
|
47
|
+
return 0.5
|
|
48
|
+
return 0.0
|
|
49
|
+
return B[n]
|
|
50
|
+
|
|
51
|
+
from decimal import Decimal, getcontext
|
|
52
|
+
|
|
53
|
+
def stieltjes(m, N=200000):
|
|
54
|
+
"""Returns the mth Stieltjes number."""
|
|
55
|
+
if m==0:
|
|
56
|
+
return euler_mascheroni
|
|
57
|
+
getcontext().prec = 40 # extra precision buffer
|
|
58
|
+
|
|
59
|
+
s = Decimal(0)
|
|
60
|
+
|
|
61
|
+
for k in range(1, N+1):
|
|
62
|
+
kD = Decimal(k)
|
|
63
|
+
ln = kD.ln()
|
|
64
|
+
s += (ln**m) / kD
|
|
65
|
+
|
|
66
|
+
Nln = Decimal(N).ln()
|
|
67
|
+
correction = (Nln**(m+1)) / Decimal(m+1)
|
|
68
|
+
|
|
69
|
+
return float(+ (s - correction)) # unary + applies rounding
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AxiomX.exp - the Exponential and Logarithmic functions module of AxiomX.
|
|
3
|
+
|
|
4
|
+
The AxiomX.exp module contains exponential and logarithmic functions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from AxiomX.constants import *
|
|
8
|
+
|
|
1
9
|
def exp(n):
|
|
10
|
+
"""Returns e**n, where e = 2.718281828459045."""
|
|
2
11
|
return e**n
|
|
3
12
|
|
|
4
13
|
def ln(x):
|
|
14
|
+
"""Return the the natural logarithm of x."""
|
|
5
15
|
if x <= 0:
|
|
6
16
|
raise ValueError("ln(x) is undefined for x <= 0")
|
|
7
17
|
k = 0
|
|
@@ -23,10 +33,13 @@ def ln(x):
|
|
|
23
33
|
return 2*s + k * (0.693147180559945309417232121458176568) # ln 2
|
|
24
34
|
|
|
25
35
|
def log10(x):
|
|
36
|
+
"""Returns the common logarithm of x."""
|
|
26
37
|
return ln(x) / ln(10)
|
|
27
38
|
|
|
28
39
|
def log2(x):
|
|
40
|
+
"""Returns the binary logarithm of x."""
|
|
29
41
|
return log10(x) / log10(2)
|
|
30
42
|
|
|
31
|
-
def log(
|
|
32
|
-
|
|
43
|
+
def log(x, b):
|
|
44
|
+
"""Returns the logarithm of x to the base b."""
|
|
45
|
+
return log2(x) / log2(b)
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
from AxiomX.constants import *
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
AxiomX.functions - the Arithmetic functions module of AxiomX.
|
|
5
|
+
|
|
6
|
+
This module contains arithmetic functions like square root, absolute value, cube root, Gamma function, etc.
|
|
7
|
+
"""
|
|
8
|
+
|
|
1
9
|
def absolute(x):
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
else:
|
|
5
|
-
return -x
|
|
10
|
+
"""Returns the absolute value of x."""
|
|
11
|
+
return x if x >= 0 else -x
|
|
6
12
|
|
|
7
13
|
def sqrt(a, x0=None, tol=1e-30, max_iter=20):
|
|
14
|
+
"""Returns the square root of a by Halley's Method."""
|
|
8
15
|
if a < 0:
|
|
9
16
|
raise ValueError("a must be non-negative")
|
|
10
17
|
|
|
@@ -27,6 +34,7 @@ def sqrt(a, x0=None, tol=1e-30, max_iter=20):
|
|
|
27
34
|
|
|
28
35
|
|
|
29
36
|
def cbrt(N, tolerance=1e-10, max_iterations=1000):
|
|
37
|
+
"""Returns the cube root of N by Newton-Raphson method."""
|
|
30
38
|
x = N / 3.0 if N != 0 else 0.0
|
|
31
39
|
for i in range(max_iterations):
|
|
32
40
|
x_next = x - (x**3 - N) / (3 * x**2)
|
|
@@ -36,6 +44,7 @@ def cbrt(N, tolerance=1e-10, max_iterations=1000):
|
|
|
36
44
|
return x
|
|
37
45
|
|
|
38
46
|
def gamma(x):
|
|
47
|
+
"""Returns the Gamma function evaluated at x."""
|
|
39
48
|
# Lanczos approximation constants
|
|
40
49
|
p = [
|
|
41
50
|
0.99999999999980993,
|
|
@@ -62,8 +71,9 @@ def gamma(x):
|
|
|
62
71
|
return sqrt(2 * pi) * (x + g + 0.5)**(x + 0.5) * (e**(-(x + g + 0.5))) * t
|
|
63
72
|
|
|
64
73
|
def factorial(x):
|
|
74
|
+
"""Calculates the factorial of x with the help of Gamma function."""
|
|
65
75
|
if (x // 1 == x):
|
|
66
|
-
f = 1
|
|
76
|
+
f = 1.0
|
|
67
77
|
while x > 0:
|
|
68
78
|
f *= x
|
|
69
79
|
x -= 1
|
|
@@ -72,22 +82,25 @@ def factorial(x):
|
|
|
72
82
|
return gamma(x+1)
|
|
73
83
|
|
|
74
84
|
def zeta(n):
|
|
85
|
+
"""Returns the value of the Riemann Zeta Function evaluated at n."""
|
|
75
86
|
zetval = 0
|
|
76
87
|
if n <= 1:
|
|
77
88
|
raise ValueError("zeta(n) diverges for n <= 1")
|
|
78
|
-
for _ in range(1,
|
|
89
|
+
for _ in range(1, 1000001):
|
|
79
90
|
zetval += (1 / _**n)
|
|
80
91
|
return zetval
|
|
81
92
|
|
|
82
93
|
def beta(n):
|
|
94
|
+
"""Returns the value of the Dirichlet Beta function evaluated at n."""
|
|
83
95
|
if n == 0:
|
|
84
96
|
return 0.5
|
|
85
97
|
total = 0.0
|
|
86
|
-
for i in range(
|
|
98
|
+
for i in range(1000000):
|
|
87
99
|
total += ((-1)**i) / ((2*i + 1)**n)
|
|
88
100
|
return total
|
|
89
101
|
|
|
90
102
|
def bc(n, k):
|
|
103
|
+
"""Returns the number of n things taken r at a time."""
|
|
91
104
|
if k < 0 or k > n:
|
|
92
105
|
raise ValueError("Binomial coefficient (n k) doesn't work for negative n or k, or if n < k")
|
|
93
106
|
|
|
@@ -100,6 +113,7 @@ def bc(n, k):
|
|
|
100
113
|
return result
|
|
101
114
|
|
|
102
115
|
def pascal(row):
|
|
116
|
+
"""Returns a list containing all numbers on that row of the Pascal's Triangle."""
|
|
103
117
|
pr = []
|
|
104
118
|
for i in range(0, row+1):
|
|
105
119
|
pr.append(int(bc(row, i)))
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AxiomX.graph - the graphing module of AxiomX.
|
|
3
|
+
|
|
4
|
+
This module provides interactive 2D plotting using turtle graphics.
|
|
5
|
+
|
|
6
|
+
Features
|
|
7
|
+
--------
|
|
8
|
+
- Multiple function plotting
|
|
9
|
+
- Zoom and pan controls
|
|
10
|
+
- Dynamic grid and axis rendering
|
|
11
|
+
- Real-time mouse coordinate tracking
|
|
12
|
+
|
|
13
|
+
Controls
|
|
14
|
+
--------
|
|
15
|
+
Keyboard:
|
|
16
|
+
PageUp → Zoom in
|
|
17
|
+
PageDown → Zoom out
|
|
18
|
+
Arrow keys → Pan (← ↑ → ↓)
|
|
19
|
+
|
|
20
|
+
Mouse:
|
|
21
|
+
Move cursor to see real-time coordinates
|
|
22
|
+
|
|
23
|
+
Buttons:
|
|
24
|
+
Zoom In / Zoom Out / Arrow buttons available at bottom
|
|
25
|
+
|
|
26
|
+
Example
|
|
27
|
+
-------
|
|
28
|
+
>>> import AxiomX.graph import plot
|
|
29
|
+
>>> plot(lambda x: x**2, -10, 10)
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
import tkinter as tk
|
|
33
|
+
import turtle
|
|
34
|
+
|
|
35
|
+
def plot(funcs, start, end, step=0.1, scale=20, limit=1000):
|
|
36
|
+
"""
|
|
37
|
+
Plot one or more mathematical functions on an interactive graph.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
funcs : callable or list of callables
|
|
42
|
+
A function f(x) or list of functions to plot.
|
|
43
|
+
Example: lambda x: x**2
|
|
44
|
+
start : float
|
|
45
|
+
Starting value of x-axis.
|
|
46
|
+
end : float
|
|
47
|
+
Ending value of x-axis.
|
|
48
|
+
step : float, optional
|
|
49
|
+
Step size for plotting points (default is 0.1).
|
|
50
|
+
scale : float, optional
|
|
51
|
+
Zoom level of the graph (default is 20).
|
|
52
|
+
limit : float, optional
|
|
53
|
+
Maximum absolute y-value before skipping (prevents overflow).
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
None
|
|
58
|
+
|
|
59
|
+
Features
|
|
60
|
+
--------
|
|
61
|
+
- Supports multiple functions with different colors
|
|
62
|
+
- Infinite-style grid with dynamic tick spacing
|
|
63
|
+
- Zoom and pan using keyboard or buttons
|
|
64
|
+
- Displays real-time mouse coordinates
|
|
65
|
+
|
|
66
|
+
Controls
|
|
67
|
+
--------
|
|
68
|
+
PageUp / PageDown : Zoom in / out
|
|
69
|
+
Arrow Keys : Pan graph
|
|
70
|
+
Mouse Move : Show coordinates
|
|
71
|
+
|
|
72
|
+
Examples
|
|
73
|
+
--------
|
|
74
|
+
>>> plot(lambda x: x**2, -10, 10)
|
|
75
|
+
|
|
76
|
+
>>> plot([lambda x: x, lambda x: x**2], -5, 5)
|
|
77
|
+
|
|
78
|
+
Notes
|
|
79
|
+
-----
|
|
80
|
+
- Discontinuous functions (like tan(x)) are handled by skipping large jumps.
|
|
81
|
+
- Functions returning None or extremely large values are ignored.
|
|
82
|
+
- Uses turtle graphics internally, so performance may vary for small step sizes.
|
|
83
|
+
"""
|
|
84
|
+
if not isinstance(funcs, list):
|
|
85
|
+
funcs = [funcs]
|
|
86
|
+
|
|
87
|
+
turtle.hideturtle()
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
turtle.reset()
|
|
91
|
+
except turtle.Terminator:
|
|
92
|
+
turtle.Screen()
|
|
93
|
+
|
|
94
|
+
screen = turtle.Screen()
|
|
95
|
+
root = screen._root # access tk window
|
|
96
|
+
root.geometry("800x600")
|
|
97
|
+
root.resizable(False, False)
|
|
98
|
+
turtle.hideturtle()
|
|
99
|
+
screen.title("AxiomX Graph")
|
|
100
|
+
screen.listen()
|
|
101
|
+
|
|
102
|
+
t = turtle.Turtle(visible=False)
|
|
103
|
+
t.speed(0)
|
|
104
|
+
t.hideturtle()
|
|
105
|
+
|
|
106
|
+
turtle.tracer(0, 0)
|
|
107
|
+
|
|
108
|
+
state = {"scale": scale,"offset_x": 0,"offset_y": 0}
|
|
109
|
+
|
|
110
|
+
def draw():
|
|
111
|
+
t.clear()
|
|
112
|
+
scale = state["scale"]
|
|
113
|
+
t.color("lightgray")
|
|
114
|
+
xrange = int(400 / state['scale'])
|
|
115
|
+
|
|
116
|
+
x_start = int(-xrange - state["offset_x"])
|
|
117
|
+
x_end = int(xrange - state["offset_x"])
|
|
118
|
+
|
|
119
|
+
for x in range(x_start, x_end):
|
|
120
|
+
sx = (x + state["offset_x"]) * state["scale"]
|
|
121
|
+
t.penup()
|
|
122
|
+
t.goto(sx, -300)
|
|
123
|
+
t.pendown()
|
|
124
|
+
t.goto(sx, 300)
|
|
125
|
+
|
|
126
|
+
y_range = int(300 / state["scale"])
|
|
127
|
+
for y in range(-y_range, y_range+1):
|
|
128
|
+
sy = (y + state["offset_y"]) * state["scale"]
|
|
129
|
+
t.penup()
|
|
130
|
+
t.goto(-400, sy)
|
|
131
|
+
t.pendown()
|
|
132
|
+
t.goto(400, sy)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
t.color("black")
|
|
136
|
+
|
|
137
|
+
# ================= AXES =================
|
|
138
|
+
# X-axis (y = 0)
|
|
139
|
+
t.penup()
|
|
140
|
+
t.goto(-400, (0 + state["offset_y"]) * state["scale"])
|
|
141
|
+
t.pendown()
|
|
142
|
+
t.goto(400, (0 + state["offset_y"]) * state["scale"])
|
|
143
|
+
|
|
144
|
+
# Y-axis (x = 0)
|
|
145
|
+
t.penup()
|
|
146
|
+
t.goto((0 + state["offset_x"]) * state["scale"], -300)
|
|
147
|
+
t.pendown()
|
|
148
|
+
t.goto((0 + state["offset_x"]) * state["scale"], 300)
|
|
149
|
+
|
|
150
|
+
# ================= X TICKS =================
|
|
151
|
+
# ================= X-AXIS TICKS (INFINITE STYLE) =================
|
|
152
|
+
|
|
153
|
+
t.color("black")
|
|
154
|
+
|
|
155
|
+
x_range = 400 / state["scale"]
|
|
156
|
+
|
|
157
|
+
# dynamic tick spacing (prevents clutter)
|
|
158
|
+
tick_step = max(1, int((2 * x_range) / 10))
|
|
159
|
+
|
|
160
|
+
# start from left visible edge
|
|
161
|
+
x_tick = int((-x_range - state["offset_x"]) // tick_step) * tick_step
|
|
162
|
+
|
|
163
|
+
while x_tick <= (x_range - state["offset_x"]):
|
|
164
|
+
sx = (x_tick + state["offset_x"]) * state["scale"]
|
|
165
|
+
|
|
166
|
+
# Tick mark
|
|
167
|
+
t.penup()
|
|
168
|
+
t.goto(sx, state["offset_y"] * state["scale"] - 5)
|
|
169
|
+
t.pendown()
|
|
170
|
+
t.goto(sx, state["offset_y"] * state["scale"] + 5)
|
|
171
|
+
|
|
172
|
+
# Label
|
|
173
|
+
t.penup()
|
|
174
|
+
t.goto(sx, state["offset_y"] * state["scale"] - 20)
|
|
175
|
+
|
|
176
|
+
label = "0" if abs(x_tick) < 1e-9 else str(x_tick)
|
|
177
|
+
t.write(label, align="center", font=("Arial", 8, "normal"))
|
|
178
|
+
|
|
179
|
+
x_tick += tick_step
|
|
180
|
+
|
|
181
|
+
# ================= Y TICKS =================
|
|
182
|
+
|
|
183
|
+
t.color("black")
|
|
184
|
+
|
|
185
|
+
y_range = 300 / state["scale"]
|
|
186
|
+
|
|
187
|
+
# dynamic spacing
|
|
188
|
+
y_tick_step = max(1, int((2 * y_range) / 10))
|
|
189
|
+
|
|
190
|
+
# start from bottom visible region (WITH offset)
|
|
191
|
+
y_tick = int((-y_range - state["offset_y"]) // y_tick_step) * y_tick_step
|
|
192
|
+
|
|
193
|
+
y_end = (y_range - state["offset_y"])
|
|
194
|
+
|
|
195
|
+
while y_tick <= y_end:
|
|
196
|
+
sy = (y_tick + state["offset_y"]) * state["scale"]
|
|
197
|
+
|
|
198
|
+
if abs(y_tick) < 1e-9:
|
|
199
|
+
y_tick += y_tick_step
|
|
200
|
+
continue
|
|
201
|
+
|
|
202
|
+
# Tick mark
|
|
203
|
+
t.penup()
|
|
204
|
+
t.goto((state["offset_x"]) * state["scale"] - 5, sy)
|
|
205
|
+
t.pendown()
|
|
206
|
+
t.goto((state["offset_x"]) * state["scale"] + 5, sy)
|
|
207
|
+
|
|
208
|
+
# Label
|
|
209
|
+
t.penup()
|
|
210
|
+
t.goto((state["offset_x"]) * state["scale"] - 10, sy - 5)
|
|
211
|
+
t.write(f"{y_tick}", align="right", font=("Arial", 8, "normal"))
|
|
212
|
+
|
|
213
|
+
y_tick += y_tick_step
|
|
214
|
+
# ================= FUNCTIONS =================
|
|
215
|
+
colors = ["red", "blue", "green", "purple"]
|
|
216
|
+
|
|
217
|
+
for i, f in enumerate(funcs):
|
|
218
|
+
t.color(colors[i % len(colors)])
|
|
219
|
+
t.penup()
|
|
220
|
+
|
|
221
|
+
x = start
|
|
222
|
+
drawing = False
|
|
223
|
+
|
|
224
|
+
while x <= end:
|
|
225
|
+
try:
|
|
226
|
+
y = f(x)
|
|
227
|
+
|
|
228
|
+
if y is None or abs(y) > limit:
|
|
229
|
+
raise ValueError
|
|
230
|
+
|
|
231
|
+
sx = (x + state["offset_x"]) * state["scale"]
|
|
232
|
+
sy = (y + state["offset_y"]) * state["scale"]
|
|
233
|
+
|
|
234
|
+
if not drawing:
|
|
235
|
+
t.goto(sx, sy)
|
|
236
|
+
t.pendown()
|
|
237
|
+
drawing = True
|
|
238
|
+
else:
|
|
239
|
+
t.goto(sx, sy)
|
|
240
|
+
|
|
241
|
+
except:
|
|
242
|
+
t.penup()
|
|
243
|
+
drawing = False
|
|
244
|
+
|
|
245
|
+
x += step
|
|
246
|
+
|
|
247
|
+
turtle.update()
|
|
248
|
+
|
|
249
|
+
# ================= ZOOM =================
|
|
250
|
+
def zoom_in():
|
|
251
|
+
state["scale"] *= 1.2
|
|
252
|
+
draw()
|
|
253
|
+
|
|
254
|
+
def zoom_out():
|
|
255
|
+
state["scale"] /= 1.2
|
|
256
|
+
draw()
|
|
257
|
+
|
|
258
|
+
def pan_left():
|
|
259
|
+
state["offset_x"] += 1
|
|
260
|
+
draw()
|
|
261
|
+
|
|
262
|
+
def pan_right():
|
|
263
|
+
state["offset_x"] -= 1
|
|
264
|
+
draw()
|
|
265
|
+
|
|
266
|
+
def pan_up():
|
|
267
|
+
state["offset_y"] -= 1
|
|
268
|
+
draw()
|
|
269
|
+
|
|
270
|
+
def pan_down():
|
|
271
|
+
state["offset_y"] += 1
|
|
272
|
+
draw()
|
|
273
|
+
|
|
274
|
+
# ================= MOUSE COORDINATES =================
|
|
275
|
+
|
|
276
|
+
canvas = screen.getcanvas()
|
|
277
|
+
|
|
278
|
+
coord_t = turtle.Turtle(visible=False)
|
|
279
|
+
coord_t.penup()
|
|
280
|
+
coord_t.color("black")
|
|
281
|
+
|
|
282
|
+
def on_mouse_move(event):
|
|
283
|
+
# Get canvas size
|
|
284
|
+
width = canvas.winfo_width()
|
|
285
|
+
height = canvas.winfo_height()
|
|
286
|
+
|
|
287
|
+
# Convert screen pixels → turtle coordinates
|
|
288
|
+
x_screen = event.x - width / 2
|
|
289
|
+
y_screen = height / 2 - event.y
|
|
290
|
+
|
|
291
|
+
# Convert → graph coordinates (VERY IMPORTANT)
|
|
292
|
+
x_graph = x_screen / state["scale"] - state["offset_x"]
|
|
293
|
+
y_graph = y_screen / state["scale"] - state["offset_y"]
|
|
294
|
+
|
|
295
|
+
show_coords(x_graph, y_graph)
|
|
296
|
+
|
|
297
|
+
def show_coords(x, y):
|
|
298
|
+
coord_t.clear()
|
|
299
|
+
|
|
300
|
+
# Display position (top-left corner)
|
|
301
|
+
coord_t.goto(-380, 260)
|
|
302
|
+
|
|
303
|
+
# Round for clean display
|
|
304
|
+
text = f"x = {x:.3f}, y = {y:.3f}"
|
|
305
|
+
|
|
306
|
+
coord_t.write(text, font=("Arial", 10, "normal"))
|
|
307
|
+
|
|
308
|
+
# Bind mouse movement
|
|
309
|
+
canvas.bind("<Motion>", on_mouse_move)
|
|
310
|
+
|
|
311
|
+
# Bind keys
|
|
312
|
+
screen.listen()
|
|
313
|
+
screen.onkey(zoom_in, "Next")
|
|
314
|
+
screen.onkey(zoom_out, "Prior")
|
|
315
|
+
screen.onkey(pan_right, "Right")
|
|
316
|
+
screen.onkey(pan_left, "Left")
|
|
317
|
+
screen.onkey(pan_up, "Up")
|
|
318
|
+
screen.onkey(pan_down, "Down")
|
|
319
|
+
|
|
320
|
+
root = screen._root
|
|
321
|
+
|
|
322
|
+
draw()
|
|
323
|
+
frame = tk.Frame(root)
|
|
324
|
+
frame.pack(side="bottom")
|
|
325
|
+
|
|
326
|
+
tk.Button(frame, text="Zoom In", command=zoom_in).pack(side="left",anchor="ne")
|
|
327
|
+
tk.Button(frame, text="Zoom Out", command=zoom_out).pack(side="left",anchor="ne")
|
|
328
|
+
tk.Button(frame, text="←", command=pan_left).pack(side="left",anchor="ne")
|
|
329
|
+
tk.Button(frame, text="→", command=pan_right).pack(side="left",anchor="ne")
|
|
330
|
+
tk.Button(frame, text="↑", command=pan_up).pack(side="left",anchor="ne")
|
|
331
|
+
tk.Button(frame, text="↓", command=pan_down).pack(side="left",anchor="ne")
|
|
332
|
+
turtle.mainloop()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AxiomX.hyperbolic - the hyperbolic function module.
|
|
3
|
+
|
|
4
|
+
This module contains all hyperbolic functions and their inverses.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from AxiomX.exp import ln
|
|
8
|
+
from AxiomX.constants import e
|
|
9
|
+
|
|
10
|
+
def sinh(x):
|
|
11
|
+
"""Returns the hyperbolic sine of x."""
|
|
12
|
+
return (e**x - e**(-x))/2
|
|
13
|
+
|
|
14
|
+
def cosh(x):
|
|
15
|
+
"""Returns the hyperbolic cosine of x."""
|
|
16
|
+
return (e**x + e**(-x))/2
|
|
17
|
+
|
|
18
|
+
def tanh(x):
|
|
19
|
+
"""Returns the hyperbolic tangent of x."""
|
|
20
|
+
return sinh(x) / cosh(x)
|
|
21
|
+
|
|
22
|
+
def coth(x):
|
|
23
|
+
"""Returns the hyperbolic cotangent of x."""
|
|
24
|
+
return cosh(x) / sinh(x)
|
|
25
|
+
|
|
26
|
+
def sech(x):
|
|
27
|
+
"""Returns the hyperbolic secant of x."""
|
|
28
|
+
return 1 / cosh(x)
|
|
29
|
+
|
|
30
|
+
def cosech(x):
|
|
31
|
+
"""Returns the hyperbolic cosecant of x."""
|
|
32
|
+
return 1 / sinh(x)
|
|
33
|
+
|
|
34
|
+
def arcsinh(x):
|
|
35
|
+
"""Returns the inverse hyperbolic sine of x."""
|
|
36
|
+
return ln(x + (x**2 + 1)**0.5)
|
|
37
|
+
|
|
38
|
+
def arccosh(x):
|
|
39
|
+
"""Returns the inverse hyperbolic cosine of x."""
|
|
40
|
+
return abs(arcsinh((x**2 - 1)**0.5))
|
|
41
|
+
|
|
42
|
+
def arccoth(x):
|
|
43
|
+
"""Returns the inverse hyperbolic tangent of x."""
|
|
44
|
+
return 0.5 * ln((x+1)/(x-1))
|
|
45
|
+
|
|
46
|
+
def arctanh(x):
|
|
47
|
+
"""Returns the inverse hyperbolic cotangent of x."""
|
|
48
|
+
return arccoth(1/x)
|
|
49
|
+
|
|
50
|
+
def arcsech(x):
|
|
51
|
+
"""Returns the inverse hyperbolic secant of x."""
|
|
52
|
+
return arccosh(1/x)
|
|
53
|
+
|
|
54
|
+
def arccosech(x):
|
|
55
|
+
"""Returns the inverse hyperbolic cosecant of x."""
|
|
56
|
+
return arcsinh(1/x)
|