diff --git a/tkintertools/three/constants.py b/tkintertools/three/constants.py new file mode 100644 index 0000000..09004df --- /dev/null +++ b/tkintertools/three/constants.py @@ -0,0 +1,8 @@ +"""All constants for 3D""" + +import platform + +FONT = "Microsoft YaHei" if platform.system( +) == "Windows" else "PingFang SC" if platform.system() == "Darwin" else "Noto Sans" + +SIZE = -20 diff --git a/tkintertools/three/engine.py b/tkintertools/three/engine.py index 0d52a84..21d35a6 100644 --- a/tkintertools/three/engine.py +++ b/tkintertools/three/engine.py @@ -8,7 +8,9 @@ import tkinter import typing -from ..core import constants, containers +import tkintertools.core.containers + +from . import constants __all__ = [ "Canvas", @@ -26,12 +28,12 @@ ] -class Canvas(containers.Canvas): +class Canvas(tkintertools.core.containers.Canvas): """Base class of 3D Canvas""" def __init__( self, - master: containers.Tk | containers.Canvas, + master: tkintertools.core.containers.Tk | tkintertools.core.containers.Canvas, *, expand: typing.Literal["", "x", "y", "xy"] = "xy", zoom_item: bool = False, @@ -39,7 +41,7 @@ def __init__( free_anchor: bool = False, **kwargs, ) -> None: - containers.Canvas.__init__( + tkintertools.core.containers.Canvas.__init__( self, master, expand=expand, zoom_item=zoom_item, keep_ratio=keep_ratio, free_anchor=free_anchor, **kwargs) self._components: list[Component] = [] @@ -68,7 +70,7 @@ class Space(Canvas): def __init__( self, - master: containers.Tk | containers.Canvas, + master: tkintertools.core.containers.Tk | tkintertools.core.containers.Canvas, *, expand: typing.Literal["", "x", "y", "xy"] = "xy", zoom_item: bool = False, diff --git a/tkintertools/three/engine_.py b/tkintertools/three/engine_.py deleted file mode 100644 index 684de06..0000000 --- a/tkintertools/three/engine_.py +++ /dev/null @@ -1,221 +0,0 @@ -"""Core codes of 3D""" - -import abc -import array -import dataclasses -import math -import platform -import statistics -import tkinter -import typing - -from ..core import constants, containers - - -class Scene: - """存储真实数据,静止""" - - def __init__(self) -> None: - """""" - self._lights: list[Light] = [] - self._components: list[Component] = [] - self._geometries: list[Geometry] = [] - self._cameras = list[Camera] = [] - - @property - def components(self) -> tuple["Component", ...]: - """Return all `Component` of this Scene""" - return tuple(self._components) - - @property - def geometries(self) -> tuple["Geometry", ...]: - """Return all `Geometry` of this Scene""" - return tuple(self._geometries) - - @property - def lights(self) -> tuple["Light", ...]: - """Return all `Light` of this Scene""" - return tuple(self._lights) - - @property - def cameras(self) -> tuple["Camera", ...]: - """""" - return tuple(self._cameras) - - -class Camera: - """数据转换规则集,被动调用""" - - def __init__( - self, - scene: Scene, - position: tuple[int, int, int] = (0, 0, 0), - towards: tuple[float, float, float] = (1, 0, 0), - *, - hFOV: float = math.pi/3 - ) -> None: - """""" - self.scene = scene - self.position = position - self.towards = towards - self.hFOV = hFOV - - self.canvas: Canvas | None = None - - scene._cameras.append(self) - - def project(self, coordinate: tuple[float, float, float]) -> tuple[float, float]: - """""" - distance = math.dist(self.position, coordinate) - relative_dis = distance - coordinate[0] - if relative_dis <= 1e-16: - return math.inf, math.inf - k = distance / relative_dis - return coordinate[1]*k, coordinate[2]*k - - -class Canvas(containers.Canvas): - """显示画布""" - - def __init__( - self, - master: containers.Tk | containers.Canvas, - *, - expand: typing.Literal["", "x", "y", "xy"] = "xy", - zoom_item: bool = False, - keep_ratio: typing.Literal["min", "max"] | None = None, - free_anchor: bool = False, - **kwargs, - ) -> None: - """""" - containers.Canvas.__init__(self, master, expand=expand, zoom_item=zoom_item, - keep_ratio=keep_ratio, free_anchor=free_anchor, **kwargs) - # self["bg"] = "black" - self["highlightthickness"] = 0 - - self.camera: Camera = None - - self._components: dict[int, Component] = { - component.draw(self): component for component in self.scene.components} - - self.update_draw() - - def update_draw(self) -> None: - """""" - for item, component in self._components.items(): - component.update(item, camera=self) - - for item in sorted(self._components, key=lambda x: self._components[x].distance(self.position)): - self.lift(item) - - -class Light: - """光线,作用于 Scene""" - - -class Shade: - """阴影,作用于 Scene""" - - -class Component(abc.ABC): - """""" - - def __init__( - self, - scene: Scene, - *coordinates: tuple[float, float, float], - ) -> None: - """""" - self.coordinates = [array.array("f", lst) for lst in coordinates] - scene._components.append(self) - - @abc.abstractmethod - def draw(self, camera: Canvas) -> int: - """""" - - @abc.abstractmethod - def distance(self, pivot: tuple[float, float, float]) -> float: - """""" - - def update(self, item: int, *, camera: Canvas) -> None: - """""" - x, y = list(map(camera.project, self.coordinates)) - camera.coords(item, x-3, y-3, x+3, y+3) - - -class Point(Component): - """""" - - def __init__( - self, - scene: Scene, - position: tuple[float, float, float], - *, - fill: str = "#000000", - ) -> None: - """""" - Component.__init__(self, scene, list(position)) - self.fill = fill - - def draw(self, canvas: Canvas) -> int: - return canvas.create_oval(0, 0, 0, 0, fill=self.fill) - - def distance(self, pivot: tuple[float, float, float]) -> float: - return math.dist(self.coordinates[0], pivot) - - -class Line(Component): - """""" - - def __init__( - self, - scene: Scene, - start: tuple[float, float, float], - end: tuple[float, float, float], - ) -> None: - """""" - Component.__init__(self, scene, list(start), list(end)) - - def draw(self, canvas: Canvas) -> int: - return canvas.create_line(0, 0, 0, 0) - - def distance(self, pivot: tuple[float, float, float]) -> float: - return math.dist(map(statistics.mean, zip(*self.coordinates)), pivot) - - -class Curve: - """""" - - -class Plane(Component): - """""" - - def __init__( - self, - scene: Scene, - *position: tuple[float, float, float], - ) -> None: - """""" - Component.__init__(self, scene, *map(list, position)) - - def draw(self, canvas: Canvas) -> int: - return canvas.create_polygon(0, 0, 0, 0) - - def distance(self, pivot: tuple[float, float, float]) -> float: - return math.dist(map(statistics.mean, zip(*self.coordinates)), pivot) - - -class Surface: - """""" - - -class Text: - """""" - - -class Image: - """""" - - -class Geometry: - """""" diff --git a/tkintertools/three/geometries.py b/tkintertools/three/geometries.py index 9fe782e..ee8c33c 100644 --- a/tkintertools/three/geometries.py +++ b/tkintertools/three/geometries.py @@ -1,6 +1,5 @@ """Standard Geometries""" - from . import engine __all__ = [ @@ -16,7 +15,7 @@ class Cuboid(engine.Geometry): - """""" + """Cuboid""" def __init__( self, @@ -64,7 +63,7 @@ def __init__( class Tetrahedron(engine.Geometry): - """""" + """Tetrahedron""" def __init__( self, diff --git a/tkintertools/three/mathmetica.py b/tkintertools/three/mathmetica.py deleted file mode 100644 index 2c08b30..0000000 --- a/tkintertools/three/mathmetica.py +++ /dev/null @@ -1,89 +0,0 @@ -"""All 3D operations""" - - -import array -import dataclasses -import functools -import math - -Vector: array.array = functools.partial(array.array, "f") -"""Efficient array""" - - -@dataclasses.dataclass -class Quaternion: - """Incomplete implementation of quaternion""" - - w: float = 0 - x: float = 0 - y: float = 0 - z: float = 0 - - def __mul__(self, other: "Quaternion") -> "Quaternion": - """quaternion multiplication""" - w = self.w*other.w - self.x*other.x - self.y*other.y - self.z*other.z - x = self.x*other.w + self.w*other.x - self.z*other.y + self.y*other.z - y = self.y*other.w + self.z*other.x + self.w*other.y - self.x*other.z - z = self.z*other.w - self.y*other.x + self.x*other.y + self.w*other.z - return Quaternion(w, x, y, z) - - @property - def conjugate(self) -> "Quaternion": - """conjugate quaternion""" - return Quaternion(self.w, -self.x, -self.y, -self.z) - - @property - def imaginary(self) -> tuple[float, float, float]: - """imaginary part""" - return self.x, self.y, self.z - - -def rotate(coordinate: array.array, radian: float, *, axis: tuple[array.array, array.array]) -> None: - """""" - for i, v in enumerate(axis[0]): - coordinate[i] -= v - axis[1][i] -= v - - radian /= 2 - norm = math.hypot(*axis[1]) - - q = Quaternion(math.cos(radian), * - (math.sin(radian)*i/norm for i in axis[1])) - p = Quaternion(0, *coordinate) - - coordinate[:] = (q*p*q.conjugate).imaginary - - for i, v in enumerate(axis[0]): - coordinate[i] += v - axis[1][i] += v # XXX Is it to be needed really? - - -def translate(coordinate: array.array, delta: array.array, *, reference: array.array | None = None) -> None: - """""" - if reference is None: - for i, v in enumerate(delta): - coordinate[i] += v - else: - pass - - -def scale(coordinate: array.array, delta: array.array, *, reference: array.array | None = None) -> None: - """""" - if reference is None: - for i, v in enumerate(delta): - coordinate[i] *= v - else: - for i, v in enumerate(delta): - coordinate[i] += (coordinate[i]-reference[i]) * (v-1) - - -def reflect() -> None: - """""" - - -def shear() -> None: - """""" - - -def project(coordinate: array.array) -> None: - """"""