def __truediv__(self, scalar): '''Returns a new instance where all the moments are divided by a scalar.''' return type(self)(self.m0 / scalar, self.m1 / scalar, self.m2 / scalar) def moments_from_pointlist(pl): '''Constructs a Moments object from a list of points. Parameters ---------- pl : PointList list of points from which the moments are computed Returns ------- Moments, Moments2d or Moments3d raw moments of the point list, depending on the value of `ndim` provided ''' arr = pl.points m0 = len(arr) m1 = arr.sum(axis=0) m2 = np.dot(arr.T, arr) if arr.shape[1] == 2: return Moments2d(m0, m1, m2) if arr.shape[1] == 3: return Moments3d(m0, m1, m2) return Moments(m0, m1, m2) _bc.register_cast(PointList, Moments, moments_from_pointlist)
points : `numpy.ndarray(shape=(N,2))` The list of 2D vertices in numpy. ''' # ----- internal representations ----- @property def shapely(self): '''Shapely representation for fast intersection operations.''' if not hasattr(self, '_shapely'): import shapely.geometry as _sg self._shapely = _sg.Polygon(self.points) self._shapely = self._shapely.buffer( 0.0001 ) # to clean up any (multi and/or non-simple) polygon into a simple polygon return self._shapely _bc.register_castable(np.ndarray, Polygon, lambda x: castable_ndarray_PointList(x, 2)) _bc.register_cast(np.ndarray, Polygon, lambda x: Polygon(x, check=False)) _bc.register_cast(PointList2d, Polygon, lambda x: Polygon(x.points, check=False)) # ----- joining volumes ----- register_join_volume(Rect, Polygon, join_volume_shapely) register_join_volume(Polygon, Rect, join_volume_shapely) register_join_volume(Polygon, Polygon, join_volume_shapely)
def multiply(self, other): if not isinstance(other, Sim2d): return super(Sim2d, self).__mul__(other) return Sim2d( self << other.offset, self.scale * other.scale, self.angle - other.angle if self.on else self.angle + other.angle, not self.on != (not other.on) # fastest xor ) multiply.__doc__ = Dliso.multiply.__doc__ def invert(self): invScale = 1 / self.scale invAngle = self.angle if self.on else -self.angle mat = Sim2d.get_linear(invAngle, self.on, invScale) return Sim2d(np.dot(mat, -self.offset), invScale, invAngle, self.on) invert.__doc__ = Dliso.invert.__doc__ # ----- casting ----- _bc.register_cast( Sim2d, Dliso, lambda x: Dliso(offset=x.offset, scale=x.scale, unitary=x.unitary)) _bc.register_cast( Sim2d, Aff2d, lambda x: Aff2d(offset=x.offset, linear=Lin2d.from_matrix(x.linear)))
], removed_version="0.6.0", docstring_prefix=" ") def from_bounding_rect(x): '''Returns an axis-aligned ellipse bounded by the given axis-aligned rectangle x.''' if not isinstance(x, Rect): raise ValueError("Input type must be a `Rect`, '{}' given.".format( x.__class__)) return Ellipse( Aff2d(linear=Lin2d(scale=[x.w / 2, x.h / 2]), offset=x.center_pt)) # ----- casting ----- register_cast( Ellipse, Hyperellipsoid, lambda x: Hyperellipsoid(cast(x.aff_tfm, Aff), make_normalised=False)) register_cast(Hyperellipsoid, Ellipse, lambda x: Ellipse(cast(x.aff_tfm, Aff2d), make_normalised=False)) register_castable(Hyperellipsoid, Ellipse, lambda x: x.ndim == 2) def cast_Ellipse_to_Moments2d(obj): '''Extracts Moments2d from an Ellipse instance.''' a = np.pi / 4 moments = Moments2d(np.pi, [0, 0], [[a, 0], [0, a]]) # unit circle's moments return transform(obj.aff_tfm, moments) # transform register_cast(Ellipse, Moments2d, cast_Ellipse_to_Moments2d)
det.__doc__ = Aff.det.__doc__ # ----- methods ----- def __init__(self, offset=np.zeros(2), linear=Lin2d()): self.offset = offset self.linear = linear def __repr__(self): return "Aff2d(offset={}, linear={})".format(self.offset, self.linear) # ----- casting ----- _bc.register_cast( Aff2d, Aff, lambda x: Aff(weights=x.weight, bias=x.offset, check_shapes=False)) _bc.register_cast( Aff, Aff2d, lambda x: Aff2d(offset=x.bias, linear=Lin2d.from_matrix(x.weight))) _bc.register_castable(Aff, Aff2d, lambda x: x.ndim == 2) # ----- transform functions ----- def transform_Aff2d_on_Moments2d(aff_tfm, moments): '''Transform a Moments2d using a 2D affine transformation. Parameters ---------- aff_tfm : Aff2d
'''Raw moments up to 2nd order of 3D points.''' from mt import np import sys as _sys import mt.base.casting as _bc from ..geo import ThreeD from ..geond import Moments, moments_from_pointlist from .point_list import PointList3d __all__ = ['Moments3d'] class Moments3d(ThreeD, Moments): '''Raw moments up to 2nd order of points living in 3D. See Moments for more details.''' def __repr__(self): return "Moments3d(m0={}, mean={}, cov={})".format(self.m0, self.mean, self.cov.tolist()) _bc.register_cast(Moments3d, Moments, lambda x: Moments(x.m0, x.m1, x.m2)) _bc.register_cast(Moments, Moments3d, lambda x: Moments3d(x.m0, x.m1, x.m2)) _bc.register_castable(Moments, Moments3d, lambda x: x.ndim==3) _bc.register_cast(PointList3d, Moments3d, moments_from_pointlist)
'''The base class to represent a point. For efficiency reasons, please try to bunch points into arrays or lists and use appropriate representations instead of using single points implemented here. ''' import numpy as np import mt.base.casting as _bc from ..geo import TwoD from ..geond import Point, castable_ndarray_Point __all__ = ['Point2d'] class Point2d(TwoD, Point): '''A 2D point. See Point for more details.''' pass _bc.register_castable(np.ndarray, Point2d, lambda x: castable_ndarray_Point(x, 2)) _bc.register_cast(np.ndarray, Point2d, lambda x: Point2d(x, check=False)) _bc.register_cast(Point2d, Point, lambda x: Point(x.point, check=False)) _bc.register_cast(Point, Point2d, lambda x: Point2d(x.point, check=False)) _bc.register_castable(Point, Point2d, lambda x: x.ndim == 2)
def multiply(self, other): if not isinstance(other, Dlt): return super(Dlt, self).multiply(other) return Dlt(self << other.offset, self.scale*other.scale) multiply.__doc__ = Aff.multiply.__doc__ def invert(self): return Dlt(-self.offset/self.scale, 1/self.scale) invert.__doc__ = Aff.invert.__doc__ # ----- casting ----- _bc.register_cast(Dlt, Aff, lambda x: Aff(weight=x.weight, bias=x.bias, check_shapes=False)) _bc.register_cast(Aff, Dlt, lambda x: Dlt(offset=x.bias, scale=np.diagonal(x.weight))) _bc.register_castable(Aff, Dlt, lambda x: np.count_nonzero(x.weight - np.diag(np._diagonal(x.weight))) > 0) # ----- transform functions ----- def transform_Dlt_on_Moments(dlt_tfm, moments): '''Transform the Moments using an affine transformation. Parameters ---------- dlt_tfm : Dlt general dilatation moments : Moments
""" if u == 0: # special case, deform to translation only return Dltra(offset=v) # scale == 1 s = math.exp(u) t = ((s - 1) / u) * v return Dltra(offset=t, scale=s) def pow(self, k: float): """Raises the Dltra to a scalar power.""" u, v = self.logm() return Dltra.expm(u * k, v * k) # ----- casting ----- _bc.register_cast(Dltra, Dliso, lambda x: Dliso(offset=x.offset, scale=x.scale)) _bc.register_cast( Dltra, Dlt, lambda x: Dlt(offset=x.offset, scale=np.diag(x.scale), check_shapes=False), ) # ----- approximation ------ def approx_Dliso_to_Dltra(obj): """Approximates an Dliso instance with a Dltra by ignoring the unitary part.""" return Dltra(offset=obj.offset, scale=obj.scale) register_approx(Dliso, Dltra, approx_Dliso_to_Dltra)
Returns ------- Momens2d th ecollection of moments up to 2nd order Examples -------- >>> from mt.geo2d.polygon_integral import to_moments2d >>> import mt.geo2d.polygon as mp >>> poly = mp.Polygon([[3,3],[2,2],[3,1]]) >>> m = to_moments2d(poly) >>> round(m.m0, 3) -1.0 >>> round(m.m1.sum(), 3) -4.667 >>> round(m.m2.sum(), 3) -22.0 >>> round(m.mean.sum(), 3) 4.667 >>> round(m.cov.sum(), 3) -22.444 ''' m0 = signed_area(poly) m1 = [moment_x(poly), moment_y(poly)] mxy = moment_xy(poly) m2 = [[moment_xx(poly), mxy], [mxy, moment_yy(poly)]] return Moments2d(m0, m1, m2) _bc.register_cast(Polygon, Moments2d, to_moments2d)
@property def center_pt(self): '''center point''' return self.dlt_tfm.offset @property def size(self): '''box size''' return np.abs(self.dlt_tfm.scale * 2) # ----- methods ----- def __init__(self, offset_or_dltra, scale=1): if isinstance(min_coords, Dltra): self.dltra_tfm = offset_or_dltra else: self.dltra_tfm = Dltra(offset=offset_or_dltra, scale=scale) def __repr__(self): return "Hypercube({})".format(self.dltra_tfm) # ----- casting ----- register_cast(Hypercube, Hyperbox, lambda x: Hyperbox(cast(x.dltra_tfm, Dlt))) # ----- approximation ------ register_approx(Hyperbox, Hypercube, lambda x: Hypercube(approx(x.dlt_tfm, Dltra)))
@property def surface_area(self): '''The surface area of the ellipsoid.''' raise NotImplementedError( "It's quite complicated to compute the surface area of an ellipsoid. I will only be implemented when there's a real demand. MT has to check with the Wiki page first." ) def normalised(self): '''Returns an equivalent ellipsoid where f1, f2 and f3 are perpendicular (linearly independent).''' return Ellipsoid(self.aff_tfm, make_normalised=True) # ----- casting ----- register_cast(Ellipsoid, Hyperellipsoid, lambda x: Hyperellipsoid(x.aff_tfm, make_normalised=False)) register_cast(Hyperellipsoid, Ellipsoid, lambda x: Ellipsoid(x.aff_tfm, make_normalised=False)) register_castable(Hyperellipsoid, Ellipsoid, lambda x: x.ndim == 3) # ----- bounding ----- def upper_bound_Ellipsoid_to_Box(obj): '''Returns a bounding axis-aligned box of the ellipsoid. Parameters ---------- obj : Ellipsoid the ellipsoid to be upper-bounded
@property def bias_dim(self): '''Returns the dimension of the bias vector. Raises ValueError if it is not 3.''' if self.bias.shape[0] != 3: raise ValueError("Expected bias dim to be 3, but seeing {}.".format(self.bias.shape[0])) # ----- methods ----- def __repr__(self): return "Aff3d(weight_diagonal={}, bias={})".format(self.weight.diagonal(), self.bias) # ----- casting ----- _bc.register_cast(Aff3d, Aff, lambda x: Aff(weights=x.weight, bias=x.offset, check_shapes=False)) _bc.register_cast(Aff, Aff3d, lambda x: Aff3d(weight=x.weight, bias=x.bias, check_shape=False)) _bc.register_castable(Aff, Aff3d, lambda x: x.ndim==3) # ----- transform functions ----- def transform_Aff3d_on_Moments3d(aff_tfm, moments): '''Transform a Moments3d using a 3D affine transformation. Parameters ---------- aff_tfm : Aff3d 3D affine transformation moments : Moments3d
'''The base class to represent a list of points.''' import numpy as np import mt.base.casting as _bc from ..geo import ThreeD from ..geond import PointList, castable_ndarray_PointList __all__ = ['PointList3d'] class PointList3d(ThreeD, PointList): '''A list of 3D points. See PointList for more details.''' pass _bc.register_castable(np.ndarray, PointList3d, lambda x: castable_ndarray_PointList(x, 3)) _bc.register_cast(np.ndarray, PointList3d, lambda x: PointList3d(x, check=False)) _bc.register_cast(PointList3d, PointList, lambda x: PointList(x.points, check=False)) _bc.register_cast(PointList, PointList3d, lambda x: PointList3d(x.points, check=False)) _bc.register_castable(PointList, PointList3d, lambda x: x.ndim == 3)
self.unitary = unitary def __repr__(self): return "Iso(offset={}, unitary_diagonal={})".format( self.offset, self.unitary.diagonal()) # ----- base adaptation ----- def multiply(self, other): if not isinstance(other, Iso): return super(Iso, self).__mul__(other) return Iso(offset=self << other.offset, unitary=np.dot(self.unitary, other.unitary)) multiply.__doc__ = Dliso.multiply.__doc__ def invert(self): invUnitary = np.linalg.inv( self.unitary ) # slow, and assuming the unitary matrix is invertible return Iso(offset=np.dot(invUnitary, -self.offset), unitary=invUnitary) invert.__doc__ = Dliso.invert.__doc__ # ----- casting ----- _bc.register_cast(Iso, Dliso, lambda x: Dliso(offset=x.offset, unitary=x.unitary)) _bc.register_cast(Iso, Aff, lambda x: Aff(bias=x.offset, weight=x.weight))