#!/usr/bin/env python
"""
K.NAKADA, kengo.nakada@gmail.com
フォント文字を中心線へ変換する処理で共有する幾何データ型を定義する。
このモジュールは、処理段階の入出力データだけを保持する。
フォントの読み込み、輪郭抽出、塗りつぶし、中心線抽出、表示、移動命令生成は
それぞれ別モジュールで行う。
"""
from dataclasses import dataclass
from typing import Iterator, List, Tuple
import numpy as np
[docs]
@dataclass
class FontPoint:
"""XY平面上の一点を表す。"""
x: float
y: float
[docs]
def as_tuple(self) -> Tuple[float, float]:
"""座標をタプルとして返す。"""
return self.x, self.y
[docs]
def scaled(self, scale: float) -> "FontPoint":
"""原点基準で拡大縮小した点を返す。"""
return FontPoint(
x=self.x * scale,
y=self.y * scale,
)
[docs]
def shifted(
self,
x_offset: float,
y_offset: float,
) -> "FontPoint":
"""指定量だけ平行移動した点を返す。"""
return FontPoint(
x=self.x + x_offset,
y=self.y + y_offset,
)
[docs]
@dataclass
class FontOutlinePath:
"""フォントから取り出した一本の輪郭線を表す。"""
points: List[FontPoint]
closed: bool
def __iter__(self) -> Iterator[FontPoint]:
"""輪郭上の点を順に返す。"""
return iter(self.points)
def __len__(self) -> int:
"""輪郭点数を返す。"""
return len(self.points)
[docs]
def scaled(self, scale: float) -> "FontOutlinePath":
"""輪郭全体を拡大縮小した輪郭を返す。"""
points: List[FontPoint] = []
for point in self.points:
points.append(point.scaled(scale=scale))
return FontOutlinePath(
points=points,
closed=self.closed,
)
[docs]
def shifted(
self,
x_offset: float,
y_offset: float,
) -> "FontOutlinePath":
"""輪郭全体を平行移動した輪郭を返す。"""
points: List[FontPoint] = []
for point in self.points:
points.append(
point.shifted(
x_offset=x_offset,
y_offset=y_offset,
)
)
return FontOutlinePath(
points=points,
closed=self.closed,
)
[docs]
@dataclass
class FontOutlineGlyph:
"""指定文字列から取り出した輪郭線群を表す。"""
text: str
paths: List[FontOutlinePath]
def __iter__(self) -> Iterator[FontOutlinePath]:
"""輪郭線を順に返す。"""
return iter(self.paths)
def __len__(self) -> int:
"""輪郭線数を返す。"""
return len(self.paths)
[docs]
def scaled(self, scale: float) -> "FontOutlineGlyph":
"""輪郭線群全体を拡大縮小した文字輪郭を返す。"""
paths: List[FontOutlinePath] = []
for path in self.paths:
paths.append(path.scaled(scale=scale))
return FontOutlineGlyph(
text=self.text,
paths=paths,
)
[docs]
def shifted(
self,
x_offset: float,
y_offset: float,
) -> "FontOutlineGlyph":
"""輪郭線群全体を平行移動した文字輪郭を返す。"""
paths: List[FontOutlinePath] = []
for path in self.paths:
paths.append(
path.shifted(
x_offset=x_offset,
y_offset=y_offset,
)
)
return FontOutlineGlyph(
text=self.text,
paths=paths,
)
[docs]
@dataclass
class FontFilledGlyph:
"""輪郭を格子上に塗りつぶした文字領域を表す。"""
text: str
mask: np.ndarray
x_min: float
y_min: float
pixel_size: float
[docs]
def index_to_point(
self,
row_index: int,
column_index: int,
) -> FontPoint:
"""配列インデックスをXY座標へ変換する。"""
x = self.x_min + float(column_index) * self.pixel_size
y = self.y_min + float(row_index) * self.pixel_size
return FontPoint(
x=x,
y=y,
)
[docs]
@dataclass
class FontCenterlinePath:
"""中心線一本を表す。"""
points: List[FontPoint]
def __iter__(self) -> Iterator[FontPoint]:
"""中心線上の点を順に返す。"""
return iter(self.points)
def __len__(self) -> int:
"""中心線上の点数を返す。"""
return len(self.points)
[docs]
@dataclass
class FontCenterlineGlyph:
"""指定文字列から得た中心線群を表す。"""
text: str
paths: List[FontCenterlinePath]
def __iter__(self) -> Iterator[FontCenterlinePath]:
"""中心線を順に返す。"""
return iter(self.paths)
def __len__(self) -> int:
"""中心線数を返す。"""
return len(self.paths)
[docs]
@dataclass
class FontStrokePath:
"""一回ペンを下ろして書く一本の筆画を表す。
FontCenterlinePath は、画像処理で抽出された中心線である。
FontStrokePath は、ロボットが実際に一筆でなぞる単位である。
paths の分割は、ペンアップ・ペンダウンの単位に対応する。
"""
points: List[FontPoint]
def __iter__(self) -> Iterator[FontPoint]:
"""筆画上の点を順に返す。"""
return iter(self.points)
def __len__(self) -> int:
"""筆画上の点数を返す。"""
return len(self.points)
[docs]
@dataclass
class FontStrokeGlyph:
"""筆順付き文字データを表す。
paths の順序が、そのままロボットに書かせる順序である。
各 FontStrokePath は、一回ペンを下ろして書く単位である。
"""
text: str
paths: List[FontStrokePath]
def __iter__(self) -> Iterator[FontStrokePath]:
"""筆画を筆順通りに返す。"""
return iter(self.paths)
def __len__(self) -> int:
"""筆画数を返す。"""
return len(self.paths)
[docs]
@dataclass
class FontMotionCommand:
"""中心線または筆画から作成したXY相対移動命令を表す。"""
pen: float
dx: float
dy: float
[docs]
def main() -> None:
"""共通データ型の生成を確認する。"""
stroke_glyph = FontStrokeGlyph(
text="A",
paths=[
FontStrokePath(
points=[
FontPoint(x=0.0, y=0.0),
FontPoint(x=10.0, y=20.0),
],
),
FontStrokePath(
points=[
FontPoint(x=10.0, y=20.0),
FontPoint(x=20.0, y=0.0),
],
),
FontStrokePath(
points=[
FontPoint(x=5.0, y=10.0),
FontPoint(x=15.0, y=10.0),
],
),
],
)
print(stroke_glyph)
print(len(stroke_glyph))
if __name__ == "__main__":
main()