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

feature/area fix #138

Merged
merged 37 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
beea5fd
length and precision
rhayes777 Oct 25, 2024
c0aea68
remove precision rounding
rhayes777 Oct 25, 2024
27177f5
trivial example of triangle from coordinates
rhayes777 Oct 25, 2024
8bfc837
flip mask;
rhayes777 Oct 25, 2024
37db5a1
upside down triangle
rhayes777 Oct 25, 2024
2375e78
testing and tweaking...
rhayes777 Oct 25, 2024
b35f3d4
fix flip mask broadcasting
rhayes777 Oct 25, 2024
67c1c8b
fudge to get first upsampled triangle into the right place
rhayes777 Oct 25, 2024
93223e9
one full triangle
rhayes777 Oct 25, 2024
a82b271
assertion;
rhayes777 Oct 25, 2024
8c4b9eb
got upside down working...
rhayes777 Oct 25, 2024
0ee8b24
fix
rhayes777 Oct 25, 2024
0e6d4b4
uh
rhayes777 Oct 25, 2024
c4db236
pretty plot
rhayes777 Oct 25, 2024
40b1b09
neighborhood
rhayes777 Oct 25, 2024
42c90db
test against upside down neighborhood
rhayes777 Oct 25, 2024
2a483a7
fix upside down neigborhood
rhayes777 Oct 25, 2024
582dc6f
update tests
rhayes777 Oct 25, 2024
9bba552
remove plotting test
rhayes777 Oct 25, 2024
3fcd83a
docs
rhayes777 Oct 25, 2024
1fa71a9
Merge branch 'main' into feature/area_fix
rhayes777 Nov 1, 2024
84ce1c1
replaced plot with assert
rhayes777 Nov 1, 2024
c2fd7be
simple vertices
rhayes777 Nov 1, 2024
2c0bcbb
unique vertices
rhayes777 Nov 1, 2024
957b313
small changes
rhayes777 Nov 1, 2024
ff7028b
with_vertices
rhayes777 Nov 1, 2024
a323a36
for indexes
rhayes777 Nov 1, 2024
f4d9d16
docs
rhayes777 Nov 1, 2024
9081464
for limits and scales -> 3 covering triangles
rhayes777 Nov 1, 2024
1693af7
fix
rhayes777 Nov 1, 2024
e5717a9
assertion
rhayes777 Nov 1, 2024
c2e1c5a
means
rhayes777 Nov 1, 2024
3d7c14b
plotting test for negative triangles
rhayes777 Nov 1, 2024
d4f5d3c
area method
rhayes777 Nov 1, 2024
2cb8eb1
len
rhayes777 Nov 1, 2024
fb6b27e
respect offset
rhayes777 Nov 1, 2024
58800da
compute indices and vertices together
rhayes777 Nov 1, 2024
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
3 changes: 3 additions & 0 deletions autoarray/structures/triangles/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ def __init__(
self.indices = indices
self.vertices = vertices

def __len__(self):
return len(self.triangles)

@property
def area(self) -> float:
"""
Expand Down
6 changes: 3 additions & 3 deletions autoarray/structures/triangles/array.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import Tuple

import numpy as np

from autoarray.structures.triangles.abstract import AbstractTriangles
Expand Down Expand Up @@ -84,7 +82,9 @@ def neighborhood(self) -> "ArrayTriangles":
Includes the current triangles and the triangles that share an edge with the current triangles.
"""
unique_vertices, inverse_indices = np.unique(
self._neighborhood_triangles().reshape(-1, 2), axis=0, return_inverse=True
self._neighborhood_triangles().reshape(-1, 2),
axis=0,
return_inverse=True,
)
new_indices = inverse_indices.reshape(-1, 3)

Expand Down
265 changes: 265 additions & 0 deletions autoarray/structures/triangles/coordinate_array.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
import numpy as np

from autoarray.structures.triangles.abstract import HEIGHT_FACTOR
from autoarray.structures.triangles.array import ArrayTriangles
from autoconf import cached_property


class CoordinateArrayTriangles:
def __init__(
self,
coordinates: np.ndarray,
side_length: float,
flipped: bool = False,
x_offset: float = 0.0,
y_offset: float = 0.0,
):
"""
Represents a set of triangles by integer coordinates.

Parameters
----------
coordinates
Integer x y coordinates for each triangle.
side_length
The side length of the triangles.
flipped
Whether the triangles are flipped upside down.
y_offset
An y_offset to apply to the y coordinates so that up-sampled triangles align.
"""
self.coordinates = coordinates
self.side_length = side_length
self.flipped = flipped

self.scaling_factors = np.array(
[0.5 * side_length, HEIGHT_FACTOR * side_length]
)
self.x_offset = x_offset
self.y_offset = y_offset

@cached_property
def triangles(self) -> np.ndarray:
"""
The vertices of the triangles as an Nx3x2 array.
"""
centres = self.centres
return np.stack(
(
centres
+ self.flip_array
* np.array(
[0.0, 0.5 * self.side_length * HEIGHT_FACTOR],
),
centres
+ self.flip_array
* np.array(
[0.5 * self.side_length, -0.5 * self.side_length * HEIGHT_FACTOR]
),
centres
+ self.flip_array
* np.array(
[-0.5 * self.side_length, -0.5 * self.side_length * HEIGHT_FACTOR]
),
),
axis=1,
)

@property
def centres(self) -> np.ndarray:
"""
The centres of the triangles.
"""
return self.scaling_factors * self.coordinates + np.array(
[self.x_offset, self.y_offset]
)

@cached_property
def flip_mask(self) -> np.ndarray:
"""
A mask for the triangles that are flipped.

Every other triangle is flipped so that they tessellate.
"""
mask = (self.coordinates[:, 0] + self.coordinates[:, 1]) % 2 != 0
if self.flipped:
mask = ~mask
return mask

@cached_property
def flip_array(self) -> np.ndarray:
"""
An array of 1s and -1s to flip the triangles.
"""
array = np.ones(self.coordinates.shape[0])
array[self.flip_mask] = -1

return array[:, np.newaxis]

def __iter__(self):
return iter(self.triangles)

def up_sample(self) -> "CoordinateArrayTriangles":
"""
Up-sample the triangles by adding a new vertex at the midpoint of each edge.
"""
new_coordinates = np.zeros((4 * self.coordinates.shape[0], 2))
n_normal = 4 * np.sum(~self.flip_mask)

new_coordinates[:n_normal] = np.vstack(
(
2 * self.coordinates[~self.flip_mask],
2 * self.coordinates[~self.flip_mask] + np.array([1, 0]),
2 * self.coordinates[~self.flip_mask] + np.array([-1, 0]),
2 * self.coordinates[~self.flip_mask] + np.array([0, 1]),
)
)
new_coordinates[n_normal:] = np.vstack(
(
2 * self.coordinates[self.flip_mask],
2 * self.coordinates[self.flip_mask] + np.array([1, 1]),
2 * self.coordinates[self.flip_mask] + np.array([-1, 1]),
2 * self.coordinates[self.flip_mask] + np.array([0, 1]),
)
)

return CoordinateArrayTriangles(
coordinates=new_coordinates,
side_length=self.side_length / 2,
flipped=True,
y_offset=self.y_offset + -0.25 * HEIGHT_FACTOR * self.side_length,
x_offset=self.x_offset,
)

def neighborhood(self) -> "CoordinateArrayTriangles":
"""
Create a new set of triangles that are the neighborhood of the current triangles.

Ensures that the new triangles are unique.
"""
new_coordinates = np.zeros((4 * self.coordinates.shape[0], 2))
n_normal = 4 * np.sum(~self.flip_mask)

new_coordinates[:n_normal] = np.vstack(
(
self.coordinates[~self.flip_mask],
self.coordinates[~self.flip_mask] + np.array([1, 0]),
self.coordinates[~self.flip_mask] + np.array([-1, 0]),
self.coordinates[~self.flip_mask] + np.array([0, -1]),
)
)
new_coordinates[n_normal:] = np.vstack(
(
self.coordinates[self.flip_mask],
self.coordinates[self.flip_mask] + np.array([1, 0]),
self.coordinates[self.flip_mask] + np.array([-1, 0]),
self.coordinates[self.flip_mask] + np.array([0, 1]),
)
)
return CoordinateArrayTriangles(
coordinates=np.unique(new_coordinates, axis=0),
side_length=self.side_length,
flipped=self.flipped,
y_offset=self.y_offset,
x_offset=self.x_offset,
)

@cached_property
def _vertices_and_indices(self):
flat_triangles = self.triangles.reshape(-1, 2)
vertices, inverse_indices = np.unique(
flat_triangles, axis=0, return_inverse=True
)
indices = inverse_indices.reshape(-1, 3)
return vertices, indices

@property
def vertices(self) -> np.ndarray:
"""
The unique vertices of the triangles.
"""
return self._vertices_and_indices[0]

@property
def indices(self) -> np.ndarray:
"""
The indices of the vertices of the triangles.
"""
return self._vertices_and_indices[1]

def with_vertices(self, vertices: np.ndarray) -> ArrayTriangles:
"""
Create a new set of triangles with the vertices replaced.

Parameters
----------
vertices
The new vertices to use.

Returns
-------
The new set of triangles with the new vertices.
"""
return ArrayTriangles(
indices=self.indices,
vertices=vertices,
)

def for_indexes(self, indexes: np.ndarray) -> "CoordinateArrayTriangles":
"""
Create a new CoordinateArrayTriangles containing triangles corresponding to the given indexes

Parameters
----------
indexes
The indexes of the triangles to include in the new CoordinateArrayTriangles.

Returns
-------
The new CoordinateArrayTriangles instance.
"""
return CoordinateArrayTriangles(
coordinates=self.coordinates[indexes],
side_length=self.side_length,
flipped=self.flipped,
y_offset=self.y_offset,
x_offset=self.x_offset,
)

@classmethod
def for_limits_and_scale(
cls,
x_min: float,
x_max: float,
y_min: float,
y_max: float,
**_,
):
x_mean = (x_min + x_max) / 2
y_mean = (y_min + y_max) / 2

max_side_length = max(x_max - x_min, y_max - y_min)

return cls(
coordinates=np.array(
[
[-1, 0],
[0, 0],
[1, 0],
]
),
side_length=max_side_length / HEIGHT_FACTOR,
x_offset=x_mean,
y_offset=y_mean,
)

@property
def means(self):
return np.mean(self.triangles, axis=1)

@property
def area(self):
return (3**0.5 / 4 * self.side_length**2) * len(self.coordinates)

def __len__(self):
return len(self.coordinates)
Loading
Loading