Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore EPSILON, EPSILON2, set_epsilon() from the original Planar package #114

Merged
merged 1 commit into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 40 additions & 18 deletions src/affine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
__version__ = "3.0dev"

EPSILON: float = 1e-5
EPSILON2: float = 1e-10


class AffineError(Exception):
Expand Down Expand Up @@ -134,11 +135,6 @@ class Affine:
h: float = field(default=0.0, converter=float)
i: float = field(default=1.0, converter=float)

def __attrs_post_init__(self):
# Prevent property from changing between initialization and
# computation. precision is a cached property.
_ = self.precision

@classmethod
def from_gdal(cls, c: float, a: float, b: float, f: float, d: float, e: float):
"""Use same coefficient order as GDAL's GetGeoTransform().
Expand Down Expand Up @@ -315,11 +311,6 @@ def __repr__(self) -> str:
f" {self.d!r}, {self.e!r}, {self.f!r})"
)

@cached_property
def precision(self):
"""Numerical precision of comparison methods."""
return EPSILON

def to_gdal(self):
"""Return same coefficient order expected by GDAL's SetGeoTransform().

Expand Down Expand Up @@ -379,7 +370,7 @@ def _scaling(self):
det2 = (a * e - b * d) ** 2

delta = trace**2 / 4.0 - det2
if delta < self.precision:
if delta < EPSILON2:
delta = 0.0

sqrt_delta = math.sqrt(delta)
Expand Down Expand Up @@ -425,7 +416,7 @@ def rotation_angle(self) -> float:
@property
def is_identity(self) -> bool:
"""True if this transform equals the identity matrix, within rounding limits."""
return self is identity or self.almost_equals(identity, self.precision)
return self is identity or self.almost_equals(identity, EPSILON)

@property
def is_rectilinear(self) -> bool:
Expand All @@ -434,8 +425,8 @@ def is_rectilinear(self) -> bool:
i.e., whether a shape would remain axis-aligned, within rounding
limits, after applying the transform.
"""
return (abs(self.a) < self.precision and abs(self.e) < self.precision) or (
abs(self.d) < self.precision and abs(self.b) < self.precision
return (abs(self.a) < EPSILON and abs(self.e) < EPSILON) or (
abs(self.d) < EPSILON and abs(self.b) < EPSILON
)

@property
Expand All @@ -446,7 +437,7 @@ def is_conformal(self) -> bool:
transform, within rounding limits. This implies that the
transform has no effective shear.
"""
return abs(self.a * self.b + self.d * self.e) < self.precision
return abs(self.a * self.b + self.d * self.e) < EPSILON

@property
def is_orthonormal(self) -> bool:
Expand All @@ -461,8 +452,8 @@ def is_orthonormal(self) -> bool:
a, b, d, e = self.a, self.b, self.d, self.e
return (
self.is_conformal
and abs(1.0 - (a * a + d * d)) < self.precision
and abs(1.0 - (b * b + e * e)) < self.precision
and abs(1.0 - (a * a + d * d)) < EPSILON
and abs(1.0 - (b * b + e * e)) < EPSILON
)

@cached_property
Expand Down Expand Up @@ -518,7 +509,7 @@ def almost_equals(self, other, precision: Optional[float] = None) -> bool:
True if absolute difference between each element
of each respective transform matrix < ``precision``.
"""
precision = precision or self.precision
precision = precision or EPSILON
return all(abs(sv - ov) < precision for sv, ov in zip(self, other))

@cached_property
Expand Down Expand Up @@ -721,3 +712,34 @@ def dumpsw(obj) -> str:
"""
center = obj * Affine.translation(0.5, 0.5)
return "\n".join(repr(getattr(center, x)) for x in list("adbecf")) + "\n"


def set_epsilon(epsilon: float) -> None:
"""Set the global absolute error value and rounding limit.

This value is accessible via the affine.EPSILON global variable.

Parameters
----------
epsilon : float
The global absolute error value and rounding limit for
approximate floating point comparison operations.

Returns
-------
None

Notes
-----
The default value of ``0.00001`` is suitable for values that are in
the "countable range". You may need a larger epsilon when using
large absolute values, and a smaller value for very small values
close to zero. Otherwise approximate comparison operations will not
behave as expected.
"""
global EPSILON, EPSILON2
EPSILON = float(epsilon)
EPSILON2 = EPSILON**2


set_epsilon(1e-5)
7 changes: 1 addition & 6 deletions src/affine/tests/test_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import pytest

import affine
from affine import EPSILON, Affine
from affine import Affine


def seq_almost_equal(t1, t2, error=0.00001):
Expand Down Expand Up @@ -445,11 +445,6 @@ def test_rmul_tuple():
(2.0, 2.0) * t


def test_transform_precision():
t = Affine.rotation(45.0)
assert t.precision == EPSILON


def test_associative():
point = (12, 5)
trans = Affine.translation(-10.0, -5.0)
Expand Down
Loading