math-tompy 2024.8.13.13.26__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.
- math_tompy-2024.8.13.13.26/.gitignore +4 -0
- math_tompy-2024.8.13.13.26/PKG-INFO +14 -0
- math_tompy-2024.8.13.13.26/README.md +1 -0
- math_tompy-2024.8.13.13.26/pyproject.toml +24 -0
- math_tompy-2024.8.13.13.26/src/math_tompy/__init__.py +0 -0
- math_tompy-2024.8.13.13.26/src/math_tompy/decimal.py +27 -0
- math_tompy-2024.8.13.13.26/src/math_tompy/float.py +52 -0
- math_tompy-2024.8.13.13.26/src/math_tompy/special.py +84 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: math-tompy
|
|
3
|
+
Version: 2024.8.13.13.26
|
|
4
|
+
Summary: Extra math for tompy projects
|
|
5
|
+
Author: tompy
|
|
6
|
+
Classifier: Development Status :: 3 - Alpha
|
|
7
|
+
Classifier: Intended Audience :: Developers
|
|
8
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
Extra math for tompy projects
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Extra math for tompy projects
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "math-tompy"
|
|
7
|
+
version = "2024.08.13.13.26"
|
|
8
|
+
dependencies = []
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "tompy" },
|
|
11
|
+
]
|
|
12
|
+
description = "Extra math for tompy projects"
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
requires-python = ">=3.9"
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Programming Language :: Python :: 3.9",
|
|
19
|
+
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[tool.hatch.build.targets.wheel]
|
|
24
|
+
packages = ["src/math_tompy"]
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def normalize_value(value: Decimal, minimum: Decimal, maximum: Decimal) -> Decimal:
|
|
5
|
+
"""Scale value position between minimum and maximum to [0:1] bound"""
|
|
6
|
+
|
|
7
|
+
# Scale value and maximum relative to zero to enable finding fractional value by straightforward division
|
|
8
|
+
zero_scaled_value: Decimal = value - minimum
|
|
9
|
+
zero_scaled_maximum: Decimal = maximum - minimum
|
|
10
|
+
|
|
11
|
+
# Divide scaled values
|
|
12
|
+
normalized_value: Decimal = zero_scaled_value / zero_scaled_maximum
|
|
13
|
+
|
|
14
|
+
return normalized_value
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def clamp_or_normalize_value(value: Decimal, minimum: Decimal, maximum: Decimal) -> Decimal:
|
|
18
|
+
new_value: Decimal
|
|
19
|
+
|
|
20
|
+
if value < minimum:
|
|
21
|
+
new_value = Decimal("0.0")
|
|
22
|
+
elif value > maximum:
|
|
23
|
+
new_value = Decimal("1.0")
|
|
24
|
+
else:
|
|
25
|
+
new_value = normalize_value(value=value, minimum=minimum, maximum=maximum)
|
|
26
|
+
|
|
27
|
+
return new_value
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from enum import Enum, auto
|
|
2
|
+
from math import inf
|
|
3
|
+
from random import normalvariate
|
|
4
|
+
|
|
5
|
+
Variability = float
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Rounding(Enum):
|
|
9
|
+
ROUND = auto()
|
|
10
|
+
CEILING = auto()
|
|
11
|
+
FLOOR = auto()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def round_to_nearest_multiple(number, round_to, rounding: Rounding = Rounding.ROUND):
|
|
15
|
+
if round_to <= 0:
|
|
16
|
+
raise ValueError(f"Rounding factor can not be 0 or negative.")
|
|
17
|
+
|
|
18
|
+
remainder = number % round_to
|
|
19
|
+
|
|
20
|
+
if rounding == Rounding.ROUND:
|
|
21
|
+
nearest = number + round_to - remainder if 2 * remainder >= round_to else number - remainder
|
|
22
|
+
elif rounding == Rounding.CEILING:
|
|
23
|
+
nearest = number + round_to - remainder
|
|
24
|
+
elif rounding == Rounding.FLOOR:
|
|
25
|
+
nearest = number - remainder
|
|
26
|
+
else:
|
|
27
|
+
raise ValueError(f"rounding is not a valid Rounding value: '{rounding}")
|
|
28
|
+
|
|
29
|
+
return nearest
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_variability_amount(variability: Variability,
|
|
33
|
+
coloring_image_axis_size: int) -> float:
|
|
34
|
+
if variability == 0.0:
|
|
35
|
+
variability_amount: float = variability
|
|
36
|
+
else:
|
|
37
|
+
# TODO: get scaled or truncated value rather than retrying until value generated that fits in desired range
|
|
38
|
+
# search term: Truncated normal distribution
|
|
39
|
+
# https://scipy.github.io/devdocs/reference/generated/scipy.stats.truncnorm.html
|
|
40
|
+
# https://github.com/jessemzhang/tn_test/blob/master/truncated_normal/truncated_normal.py
|
|
41
|
+
# package name: truncnorm
|
|
42
|
+
# package name: pydistributions
|
|
43
|
+
variation_value: float = inf
|
|
44
|
+
while variation_value < -1.0 or variation_value > 1.0:
|
|
45
|
+
variation_value: float = normalvariate(mu=0.0, sigma=0.25)
|
|
46
|
+
|
|
47
|
+
# Maximum extent of axis considered around point
|
|
48
|
+
variability_radius: float = variability * coloring_image_axis_size
|
|
49
|
+
# Specific extent selected with normal distribution value
|
|
50
|
+
variability_amount: float = variation_value * variability_radius
|
|
51
|
+
|
|
52
|
+
return variability_amount
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
from fractions import Fraction
|
|
3
|
+
from typing import NamedTuple
|
|
4
|
+
|
|
5
|
+
# TODO: use Numeric to make parts of this more generic
|
|
6
|
+
Numeric = int | float | Decimal | Fraction
|
|
7
|
+
|
|
8
|
+
# TODO: Point1Df as Vector1 in Decimal
|
|
9
|
+
Point1Df = float
|
|
10
|
+
Point2Df = tuple[float, float]
|
|
11
|
+
Point1Di = int
|
|
12
|
+
Point2Di = tuple[int, int]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Range1Df(NamedTuple):
|
|
16
|
+
minimum: float
|
|
17
|
+
maximum: float
|
|
18
|
+
|
|
19
|
+
# TODO: implement as __len__
|
|
20
|
+
def size(self) -> float:
|
|
21
|
+
# Measuring stretch between fence posts
|
|
22
|
+
# TODO: confirm that '1.0' should not be added here, because it is measuring between, not at
|
|
23
|
+
return self.maximum - self.minimum + 1.0
|
|
24
|
+
# return self.maximum - self.minimum
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Range1Di(NamedTuple):
|
|
28
|
+
minimum: int
|
|
29
|
+
maximum: int
|
|
30
|
+
|
|
31
|
+
# TODO: implement as __len__
|
|
32
|
+
def size(self) -> int:
|
|
33
|
+
# Measuring amount of fence posts
|
|
34
|
+
return self.maximum - self.minimum + 1
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Range2Df(NamedTuple):
|
|
38
|
+
x_axis: Range1Df
|
|
39
|
+
y_axis: Range1Df
|
|
40
|
+
|
|
41
|
+
def area(self) -> float:
|
|
42
|
+
return self.x_axis.size() * self.y_axis.size()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Range2Di(NamedTuple):
|
|
46
|
+
x_axis: Range1Di
|
|
47
|
+
y_axis: Range1Di
|
|
48
|
+
|
|
49
|
+
def area(self) -> float:
|
|
50
|
+
return self.x_axis.size() * self.y_axis.size()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# TODO: improve naming with words specific to bucketed int values and tiny-mini float values
|
|
54
|
+
def clamp_svg_value_to_range(svg_image_point: Point1Df, svg_image_axis: Range1Df) -> Point1Df:
|
|
55
|
+
value: Point1Df
|
|
56
|
+
if svg_image_point < svg_image_axis.minimum:
|
|
57
|
+
value = svg_image_axis.minimum
|
|
58
|
+
elif svg_image_point > svg_image_axis.maximum:
|
|
59
|
+
value = svg_image_axis.maximum
|
|
60
|
+
else:
|
|
61
|
+
value = svg_image_point
|
|
62
|
+
return value
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# TODO: improve naming with words specific to bucketed int values and tiny-mini float values
|
|
66
|
+
def clamp_image_value_to_range(coloring_image_point: Point1Di, coloring_image_axis: Range1Di) -> Point1Di:
|
|
67
|
+
value: Point1Di
|
|
68
|
+
if coloring_image_point < coloring_image_axis.minimum:
|
|
69
|
+
value = coloring_image_axis.minimum
|
|
70
|
+
elif coloring_image_point > coloring_image_axis.maximum:
|
|
71
|
+
value = coloring_image_axis.maximum
|
|
72
|
+
else:
|
|
73
|
+
value = coloring_image_point
|
|
74
|
+
return value
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# TODO: make naming generic to not focus on
|
|
78
|
+
def get_coloring_image_fraction(svg_image_point: Point1Df,
|
|
79
|
+
svg_axis_range: Range1Df) -> float:
|
|
80
|
+
clamped_svg_image_y_axis_point: Point1Df = clamp_svg_value_to_range(svg_image_point=svg_image_point,
|
|
81
|
+
svg_image_axis=svg_axis_range)
|
|
82
|
+
axis_scaler: float = 1.0 / svg_axis_range.size()
|
|
83
|
+
coloring_image_scaled_y_axis_point: float = clamped_svg_image_y_axis_point * axis_scaler
|
|
84
|
+
return coloring_image_scaled_y_axis_point
|