"""Club shape."""
from numbers import Number
import numpy as np
from ...data.dataset import Dataset
from ..bases.point_collection import PointCollection
[docs]
class Club(PointCollection):
"""
Class for the club shape.
.. plot::
:scale: 75
:caption:
This shape is generated using the panda dataset.
from data_morph.data.loader import DataLoader
from data_morph.shapes.points import Club
_ = Club(DataLoader.load_dataset('panda')).plot()
Parameters
----------
dataset : Dataset
The starting dataset to morph into other shapes.
"""
def __init__(self, dataset: Dataset) -> None:
x_bounds = dataset.data_bounds.x_bounds
y_bounds = dataset.data_bounds.y_bounds
x_shift = sum(x_bounds) / 2
y_shift = sum(y_bounds) / 2
scale_factor = min(x_bounds.range, y_bounds.range) / 75
x_lobes, y_lobes = self._get_lobes(scale_factor)
x_stem, y_stem = self._get_stem(scale_factor)
xs = x_shift + np.concatenate(x_lobes + x_stem)
ys = y_shift + np.concatenate(y_lobes + y_stem)
super().__init__(*np.stack([xs, ys], axis=1))
@staticmethod
def _get_arc(
r: Number,
t: np.ndarray,
angle_offset: np.float64,
x_offset: Number,
y_offset: Number,
) -> tuple[np.ndarray, np.ndarray]:
"""
Get arc of a circle.
Parameters
----------
r : Number
The radius of the circle.
t : numpy.ndarray
The values to sample at in radians.
angle_offset : numpy.float64
Angle at which to start the arc in radians.
x_offset : Number
A constant value to shift the *x* coordinates by.
y_offset : Number
A constant value to shift the *y* coordinates by.
Returns
-------
tuple[numpy.ndarray, numpy.ndarray]
The *x* and *y* coordinates for the arc.
"""
x = r * np.cos(t + angle_offset) + x_offset
y = r * np.sin(t + angle_offset) + y_offset
return x, y
@classmethod
def _get_lobes(
cls, scale_factor: Number
) -> tuple[list[np.ndarray], list[np.ndarray]]:
"""
Get the lobes of the club.
Parameters
----------
scale_factor : Number
The factor to scale up/down the radius of the arcs used to calculate the lobes.
Returns
-------
tuple[list[numpy.ndarray], list[numpy.ndarray]]
The *x* and *y* coordinates for the lobes.
"""
radius = 15 * scale_factor
top_lobe_y_offset = 18 * scale_factor
bottom_lobes_x_offset = 15 * scale_factor
bottom_lobes_y_offset = 9 * scale_factor
t = np.linspace(0, (2 - 1 / 3) * np.pi, num=30)
x_top, y_top = cls._get_arc(radius, t, -np.pi / 3, 0, top_lobe_y_offset)
x_bottom_left, y_bottom_left = cls._get_arc(
radius, t, np.pi / 3, -bottom_lobes_x_offset, -bottom_lobes_y_offset
)
x_bottom_right, y_bottom_right = cls._get_arc(
radius, t, np.pi, bottom_lobes_x_offset, -bottom_lobes_y_offset
)
x_lobes = [x_top, x_bottom_left, x_bottom_right]
y_lobes = [y_top, y_bottom_left, y_bottom_right]
return x_lobes, y_lobes
@classmethod
def _get_stem(
cls, scale_factor: Number
) -> tuple[list[np.ndarray], list[np.ndarray]]:
"""
Get the stem of the club.
Parameters
----------
scale_factor : Number
The factor to scale up/down the stem.
Returns
-------
tuple[list[numpy.ndarray], list[numpy.ndarray]]
The *x* and *y* coordinates for the stem.
"""
stem_x_offset = 8 * scale_factor
stem_y_offset = 34 * scale_factor
stem_scaler = 0.35 / scale_factor
stem_x_pad = 1.5 * scale_factor
# stem bottom
x_line = np.linspace(-stem_x_offset, stem_x_offset, num=8)
y_line = np.repeat(-stem_y_offset, 8)
# left part of the stem
x_left = np.linspace(-(stem_x_offset - stem_x_pad), -stem_x_pad, num=6)
y_left = stem_scaler * np.power(x_left + stem_x_offset, 2) - stem_y_offset
# right part of the stem
x_right = np.linspace(stem_x_pad, stem_x_offset - stem_x_pad, num=6)
y_right = stem_scaler * np.power(x_right - stem_x_offset, 2) - stem_y_offset
x_stem = [x_line, x_left, x_right]
y_stem = [y_line, y_left, y_right]
return x_stem, y_stem