from fontTools.misc import transform
from fontParts.base.base import (
BaseObject,
TransformationMixin,
PointPositionMixin,
SelectionMixin,
IdentifierMixin,
dynamicProperty,
reference
)
from fontParts.base import normalizers
from fontParts.base.deprecated import DeprecatedPoint, RemovedPoint
class BasePoint(
BaseObject,
TransformationMixin,
PointPositionMixin,
SelectionMixin,
IdentifierMixin,
DeprecatedPoint,
RemovedPoint
):
"""
A point object. This object is almost always
created with :meth:`BaseContour.appendPoint`,
the pen returned by :meth:`BaseGlyph.getPen`
or the point pen returned by :meth:`BaseGLyph.getPointPen`.
An orphan point can be created like this::
>>> point = RPoint()
"""
copyAttributes = (
"type",
"smooth",
"x",
"y",
"name"
)
def _reprContents(self):
contents = [
"%s" % self.type,
("({x}, {y})".format(x=self.x, y=self.y)),
]
if self.name is not None:
contents.append("name='%s'" % self.name)
if self.smooth:
contents.append("smooth=%r" % self.smooth)
return contents
# -------
# Parents
# -------
# Contour
_contour = None
contour = dynamicProperty("contour",
"The point's parent :class:`BaseContour`.")
def _get_contour(self):
if self._contour is None:
return None
return self._contour()
def _set_contour(self, contour):
if self._contour is not None:
raise AssertionError("contour for point already set")
if contour is not None:
contour = reference(contour)
self._contour = contour
# Glyph
glyph = dynamicProperty("glyph", "The point's parent :class:`BaseGlyph`.")
def _get_glyph(self):
if self._contour is None:
return None
return self.contour.glyph
# Layer
layer = dynamicProperty("layer", "The point's parent :class:`BaseLayer`.")
def _get_layer(self):
if self._contour is None:
return None
return self.glyph.layer
# Font
font = dynamicProperty("font", "The point's parent :class:`BaseFont`.")
def _get_font(self):
if self._contour is None:
return None
return self.glyph.font
# ----------
# Attributes
# ----------
# type
type = dynamicProperty(
"base_type",
"""
The point type defined with a :ref:`type-string`.
The possible types are:
+----------+---------------------------------+
| move | An on-curve move to. |
+----------+---------------------------------+
| line | An on-curve line to. |
+----------+---------------------------------+
| curve | An on-curve cubic curve to. |
+----------+---------------------------------+
| qcurve | An on-curve quadratic curve to. |
+----------+---------------------------------+
| offcurve | An off-curve. |
+----------+---------------------------------+
""")
def _get_base_type(self):
value = self._get_type()
value = normalizers.normalizePointType(value)
return value
def _set_base_type(self, value):
value = normalizers.normalizePointType(value)
self._set_type(value)
[docs]
def _get_type(self):
"""
This is the environment implementation
of :attr:`BasePoint.type`. This must
return a :ref:`type-string` defining
the point type.
Subclasses must override this method.
"""
self.raiseNotImplementedError()
[docs]
def _set_type(self, value):
"""
This is the environment implementation
of :attr:`BasePoint.type`. **value**
will be a :ref:`type-string` defining
the point type. It will have been normalized
with :func:`normalizers.normalizePointType`.
Subclasses must override this method.
"""
self.raiseNotImplementedError()
# smooth
smooth = dynamicProperty(
"base_smooth",
"""
A ``bool`` indicating if the point is smooth or not. ::
>>> point.smooth
False
>>> point.smooth = True
"""
)
def _get_base_smooth(self):
value = self._get_smooth()
value = normalizers.normalizeBoolean(value)
return value
def _set_base_smooth(self, value):
value = normalizers.normalizeBoolean(value)
self._set_smooth(value)
[docs]
def _get_smooth(self):
"""
This is the environment implementation of
:attr:`BasePoint.smooth`. This must return
a ``bool`` indicating the smooth state.
Subclasses must override this method.
"""
self.raiseNotImplementedError()
[docs]
def _set_smooth(self, value):
"""
This is the environment implementation of
:attr:`BasePoint.smooth`. **value** will
be a ``bool`` indicating the smooth state.
It will have been normalized with
:func:`normalizers.normalizeBoolean`.
Subclasses must override this method.
"""
self.raiseNotImplementedError()
# x
x = dynamicProperty(
"base_x",
"""
The x coordinate of the point.
It must be an :ref:`type-int-float`. ::
>>> point.x
100
>>> point.x = 101
"""
)
def _get_base_x(self):
value = self._get_x()
value = normalizers.normalizeX(value)
return value
def _set_base_x(self, value):
value = normalizers.normalizeX(value)
self._set_x(value)
[docs]
def _get_x(self):
"""
This is the environment implementation of
:attr:`BasePoint.x`. This must return an
:ref:`type-int-float`.
Subclasses must override this method.
"""
self.raiseNotImplementedError()
[docs]
def _set_x(self, value):
"""
This is the environment implementation of
:attr:`BasePoint.x`. **value** will be
an :ref:`type-int-float`.
Subclasses must override this method.
"""
self.raiseNotImplementedError()
# y
y = dynamicProperty(
"base_y",
"""
The y coordinate of the point.
It must be an :ref:`type-int-float`. ::
>>> point.y
100
>>> point.y = 101
"""
)
def _get_base_y(self):
value = self._get_y()
value = normalizers.normalizeY(value)
return value
def _set_base_y(self, value):
value = normalizers.normalizeY(value)
self._set_y(value)
[docs]
def _get_y(self):
"""
This is the environment implementation of
:attr:`BasePoint.y`. This must return an
:ref:`type-int-float`.
Subclasses must override this method.
"""
self.raiseNotImplementedError()
[docs]
def _set_y(self, value):
"""
This is the environment implementation of
:attr:`BasePoint.y`. **value** will be
an :ref:`type-int-float`.
Subclasses must override this method.
"""
self.raiseNotImplementedError()
# --------------
# Identification
# --------------
# index
index = dynamicProperty(
"base_index",
"""
The index of the point within the ordered
list of the parent glyph's point. This
attribute is read only. ::
>>> point.index
0
"""
)
def _get_base_index(self):
value = self._get_index()
value = normalizers.normalizeIndex(value)
return value
[docs]
def _get_index(self):
"""
Get the point's index.
This must return an ``int``.
Subclasses may override this method.
"""
contour = self.contour
if contour is None:
return None
return contour.points.index(self)
# name
name = dynamicProperty(
"base_name",
"""
The name of the point. This will be a
:ref:`type-string` or ``None``.
>>> point.name
'my point'
>>> point.name = None
"""
)
def _get_base_name(self):
value = self._get_name()
if value is not None:
value = normalizers.normalizePointName(value)
return value
def _set_base_name(self, value):
if value is not None:
value = normalizers.normalizePointName(value)
self._set_name(value)
[docs]
def _get_name(self):
"""
This is the environment implementation of
:attr:`BasePoint.name`. This must return a
:ref:`type-string` or ``None``. The returned
value will be normalized with
:func:`normalizers.normalizePointName`.
Subclasses must override this method.
"""
self.raiseNotImplementedError()
[docs]
def _set_name(self, value):
"""
This is the environment implementation of
:attr:`BasePoint.name`. **value** will be
a :ref:`type-string` or ``None``. It will
have been normalized with
:func:`normalizers.normalizePointName`.
Subclasses must override this method.
"""
self.raiseNotImplementedError()
# --------------
# Transformation
# --------------
# -------------
# Normalization
# -------------
[docs]
def round(self):
"""
Round the point's coordinate.
>>> point.round()
This applies to the following:
* x
* y
"""
self._round()
[docs]
def _round(self, **kwargs):
"""
This is the environment implementation of
:meth:`BasePoint.round`.
Subclasses may override this method.
"""
self.x = normalizers.normalizeVisualRounding(self.x)
self.y = normalizers.normalizeVisualRounding(self.y)