diff --git a/CHANGELOG.md b/CHANGELOG.md index db6e82bfd7f412c4188d241edb12d929e31261f9..650493b606b4eee31424a302ef0ce077d7e309a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ ChangeLog/更新日志 ================= +[2.6.2] - 2023-05-26 +-------------------- + +### Features/新增 + +- [X] Added `tkintertools` sub-module `tools_3d` to support drawing 3D graphics +新增`tkintertools`子模块`tools_3d`以支持绘制3d图形 + ### Optimized/优化 [2.6.1] - 2023-05-21 diff --git a/README.md b/README.md index 024c8e21be6103037f23bd5f5a0b3b45e1ab4cf2..003c9ce9a90ec853372efa6b17588cbc4c0061c6 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ License - ChangeLog + ChangeLog ToDos @@ -56,11 +56,11 @@ pip install tkintertools ### Development version/开发版本 -* Version/版本 : 2.6.1 -* Release Date/发布日期 : 2023/05/21 +* Version/版本 : 2.6.2 +* Release Date/发布日期 : 2023/05/26 ``` -pip install tkintertools-dev==2.6.1 +pip install tkintertools-dev==2.6.2 ``` 这个是作者正在开发的版本,有新功能,但不能保证稳定,bug 可能会比较多。 diff --git a/test.py b/test.py index a1ab420f45749fdadc2cad6fab5dbd3a61e1020d..7b62b0af3284f20775b4979ff9f98101f61bf999 100644 --- a/test.py +++ b/test.py @@ -1,118 +1,169 @@ """ Test program """ from math import cos, pi -from random import randint -from tkinter import TclError, messagebox +from random import randint, sample +from tkinter import Event, TclError, messagebox import tkintertools as tkt - - -def colorful( - ind=0, # type: int - color=[None, '#F1F1F1'] # type: list[str | None] -): # type: (...) -> None - """ Change color randomly and Gradiently """ - if not ind: - color[0], color[1] = color[1], '#%06X' % randint(0, 1 << 24) - color = tkt.color(color, ind) - color_ = tkt.color(color) - canvas_doc.configure(bg=color) - for widget in canvas_main._widget: - widget.color_fill[0], widget.color_text[0] = color, color_ - widget.state() - root.after(20, colorful, 0 if ind >= 1 else ind+0.01) - - -def draw(ind=0, n=200): # type: (int, int) -> None - """ Draw a sphere """ - canvas_graph.create_oval( - canvas_graph.rx*(500-ind/3), - canvas_graph.ry*(300-ind/3), - canvas_graph.rx*(600+ind), - canvas_graph.ry*(400+ind), - fill='' if ind else 'white', - outline=tkt.color(('#000000', '#FFFFFF'), cos(ind*pi/2/n)), width=3) - if ind < n: - root.after(10, draw, ind+1) - - -def update(ind=0): # type: (int) -> None - """ Load the progress """ - bar.load(ind) - if ind < 1: - root.after(2, update, ind+0.0002) - - -def shutdown(): # type: () -> None - """ Ask before quit """ - if messagebox.askyesno('Test program', 'Do you want to exit the test program?'): - root.quit() - - -root = tkt.Tk('tkintertools', 1280, 720, shutdown=shutdown) -root.minsize(640, 360) -canvas_main = tkt.Canvas(root, 1280, 720, 0, 0) -canvas_doc = tkt.Canvas(root, 1280, 720, -1280, 0) -canvas_graph = tkt.Canvas(root, 1280, 720, 1280, 0) - - -tkt.Button( - canvas_main, 10, 660, 200, 50, text='Doc', - command=lambda: (tkt.move(root, canvas_main, 1280*canvas_main.rx, 0, 500, mode='rebound'), - tkt.move(root, canvas_doc, 1280*canvas_doc.rx, 0, 500, mode='rebound'))) -tkt.Button( - canvas_main, 1070, 660, 200, 50, text='Image', - command=lambda: (tkt.move(root, canvas_main, -1280*canvas_main.rx, 0, 500, mode='rebound'), - tkt.move(root, canvas_graph, -1280*canvas_graph.rx, 0, 500, mode='rebound'))) -tkt.Button( - canvas_doc, 1070, 660, 200, 50, text='Back', - command=lambda: (tkt.move(root, canvas_main, -1280*canvas_main.rx, 0, 500, mode='rebound'), - tkt.move(root, canvas_doc, -1280*canvas_doc.rx, 0, 500, mode='rebound'))) -tkt.Button( - canvas_graph, 10, 660, 200, 50, text='Back', - command=lambda: (tkt.move(root, canvas_main, 1280*canvas_main.rx, 0, 500, mode='rebound'), - tkt.move(root, canvas_graph, 1280*canvas_graph.rx, 0, 500, mode='rebound'))) - -tkt.Text(canvas_main, 10, 10, 625, 300, radius=20, - text=('Centered and Rounded TextBox', 'Click to Input'), justify='center') -tkt.Text(canvas_main, 645, 10, 625, 300, - text=('Right-leaning TextBox', 'Click to Input'), cursor=' _') -tkt.Entry(canvas_main, 10, 320, 300, 35, radius=10, - text=('Rounded InputBox', 'Click to Input'), justify='center') -tkt.Entry(canvas_main, 970, 320, 300, 35, text=( - 'InputBox', 'Click to Input'), show='•') -tkt.Button(canvas_main, 10, 365, 300, 40, radius=10, text='Rounded Button', command=lambda: tkt.move( - canvas_main, label_1, 0, -170 * canvas_main.ry, 500, mode='flat')) -tkt.Button(canvas_main, 1070, 365, 200, 40, text='Button', command=lambda: tkt.move( - canvas_main, label_2, 0, -170 * canvas_main.ry, 500, mode='smooth')) -tkt.CheckButton(canvas_main, 10, 415, 35, radius=10, - text='Rounded CheckButton') -tkt.CheckButton(canvas_main, 1235, 415, 35, value=True, - text='CheckButton', justify='left') - -label_1 = tkt.Label(canvas_main, 235, 730, 400, 150, - radius=20, text='Rounded Label\nmove mode: flat') -label_2 = tkt.Label(canvas_main, 645, 730, 400, 150, - text='Label\nmove mode: smooth') -button_1 = tkt.Button(canvas_doc, 1070, 10, 200, 50, text='Colorful', - command=lambda: (button_1.set_live(False), colorful())) -button_2 = tkt.Button(canvas_graph, 10, 10, 200, 50, text='Draw', - command=lambda: (button_2.set_live(False), draw())) - -load = tkt.Button(canvas_main, 540, 365, 200, 40, text='Load', - command=lambda: (update(), load.set_live(False))) -bar = tkt.Progressbar(canvas_main, 320, 320, 640, 35) - -font_chooseer = tkt.Button(canvas_main, 500, 465, 280, 40, text='Select a Font', command=lambda: tkt.askfont( - root, lambda font: canvas_main.itemconfigure(font_chooseer.text, font=font))) - -canvas_doc.create_text( - 15, 360, text=tkt.__doc__, font=(tkt.FONT, 14), anchor='w') - -try: - canvas_graph.create_image( - 1150, 130, image=tkt.PhotoImage('tkintertools.png')) -except TclError: - print('\033[31mLoad tkintertools.png Error\033[0m') - -root.mainloop() +from tkintertools import constants as cnt +from tkintertools import tools_3d as t3d + + +class Application: + + def __init__(self): + self.root = tkt.Tk('tkintertools', 1280, 720, shutdown=self.shutdown) + self.root.minsize(640, 360) + self.canvas_main = tkt.Canvas(self.root, 1280, 720, 0, 0) + self.canvas_doc = tkt.Canvas(self.root, 1280, 720, -1280, 0) + self.canvas_graph = tkt.Canvas(self.root, 1280, 720, 1280, 0) + self.canvas_3d = tkt.Canvas(self.root, 1280, 720, 1280, 0) + + self.canvas_main_init() + self.canvas_doc_init() + self.canvas_graph_init() + self.canvas_3d_init() + + self.root.mainloop() + + def shutdown(self): # type: () -> None + """ Ask before quit """ + if messagebox.askyesno('Test program', 'Do you want to exit the test program?'): + self.root.quit() + + def colorful( + self, + ind=0, # type: int + color=[None, '#F1F1F1'] # type: list[str | None] + ): # type: (...) -> None + """ Change color randomly and Gradiently """ + if not ind: + color[0], color[1] = color[1], '#%06X' % randint(0, 1 << 24) + color = tkt.color(color, ind) + color_ = tkt.color(color) + self.canvas_doc.configure(bg=color) + for widget in self.canvas_main._widget: + widget.color_fill[0], widget.color_text[0] = color, color_ + widget.state() + self.root.after(20, self.colorful, 0 if ind >= 1 else ind+0.01) + + def draw(self, ind=0, n=200): # type: (int, int) -> None + """ Draw a sphere """ + self.canvas_graph.create_oval( + self.canvas_graph.rx*(500-ind/3), + self.canvas_graph.ry*(300-ind/3), + self.canvas_graph.rx*(600+ind), + self.canvas_graph.ry*(400+ind), + fill='' if ind else 'white', + outline=tkt.color(('#000000', '#FFFFFF'), cos(ind*pi/2/n)), width=3) + if ind < n: + self.root.after(10, self.draw, ind+1) + + def update(self, ind=0): # type: (int) -> None + """ Load the progress """ + self.bar.load(ind) + if ind < 1: + self.root.after(2, self.update, ind+0.0002) + + def canvas_main_init(self): + tkt.Button( + self.canvas_main, 10, 660, 200, 50, text='Doc', + command=lambda: (tkt.move(self.root, self.canvas_main, 1280*self.canvas_main.rx, 0, 500, mode='rebound'), + tkt.move(self.root, self.canvas_doc, 1280*self.canvas_doc.rx, 0, 500, mode='rebound'))) + tkt.Button( + self.canvas_main, 1070, 660, 200, 50, text='Image', + command=lambda: (tkt.move(self.root, self.canvas_main, -1280*self.canvas_main.rx, 0, 500, mode='rebound'), + tkt.move(self.root, self.canvas_graph, -1280*self.canvas_graph.rx, 0, 500, mode='rebound'))) + tkt.Text(self.canvas_main, 10, 10, 625, 300, radius=20, + text=('Centered and Rounded TextBox', 'Click to Input'), justify='center') + tkt.Text(self.canvas_main, 645, 10, 625, 300, + text=('Right-leaning TextBox', 'Click to Input'), cursor=' _') + tkt.Entry(self.canvas_main, 10, 320, 300, 35, radius=10, + text=('Rounded InputBox', 'Click to Input'), justify='center') + tkt.Entry(self.canvas_main, 970, 320, 300, 35, text=( + 'InputBox', 'Click to Input'), show='•') + tkt.Button(self.canvas_main, 10, 365, 300, 40, radius=10, text='Rounded Button', command=lambda: tkt.move( + self.canvas_main, label_1, 0, -170 * self.canvas_main.ry, 500, mode='flat')) + tkt.Button(self.canvas_main, 1070, 365, 200, 40, text='Button', command=lambda: tkt.move( + self.canvas_main, label_2, 0, -170 * self.canvas_main.ry, 500, mode='smooth')) + tkt.CheckButton(self.canvas_main, 10, 415, 35, radius=10, + text='Rounded CheckButton') + tkt.CheckButton(self.canvas_main, 1235, 415, 35, value=True, + text='CheckButton', justify='left') + label_1 = tkt.Label(self.canvas_main, 235, 730, 400, 150, + radius=20, text='Rounded Label\nmove mode: flat') + label_2 = tkt.Label(self.canvas_main, 645, 730, 400, 150, + text='Label\nmove mode: smooth') + load = tkt.Button(self.canvas_main, 540, 365, 200, 40, text='Load', + command=lambda: (self.update(), load.set_live(False))) + self.bar = tkt.Progressbar(self.canvas_main, 320, 320, 640, 35) + + font_chooseer = tkt.Button(self.canvas_main, 500, 465, 280, 40, text='Select a Font', command=lambda: tkt.askfont( + self.root, lambda font: self.canvas_main.itemconfigure(font_chooseer.text, font=font))) + + def canvas_doc_init(self): + tkt.Button( + self.canvas_doc, 1070, 660, 200, 50, text='Back', + command=lambda: (tkt.move(self.root, self.canvas_main, -1280*self.canvas_main.rx, 0, 500, mode='rebound'), + tkt.move(self.root, self.canvas_doc, -1280*self.canvas_doc.rx, 0, 500, mode='rebound'))) + button_1 = tkt.Button(self.canvas_doc, 1070, 10, 200, 50, text='Colorful', + command=lambda: (button_1.set_live(False), self.colorful())) + self.canvas_doc.create_text( + 15, 360, text=tkt.__doc__, font=(cnt.FONT, 14), anchor='w') + + def canvas_graph_init(self): + tkt.Button( + self.canvas_graph, 1070, 660, 200, 50, text='3D', + command=lambda: (tkt.move(self.root, self.canvas_graph, -1280*self.canvas_main.rx, 0, 500, mode='rebound'), + tkt.move(self.root, self.canvas_3d, -1280*self.canvas_graph.rx, 0, 500, mode='rebound'))) + tkt.Button( + self.canvas_graph, 10, 660, 200, 50, text='Back', + command=lambda: (tkt.move(self.root, self.canvas_main, 1280*self.canvas_main.rx, 0, 500, mode='rebound'), + tkt.move(self.root, self.canvas_graph, 1280*self.canvas_graph.rx, 0, 500, mode='rebound'))) + button_2 = tkt.Button(self.canvas_graph, 10, 10, 200, 50, text='Draw', + command=lambda: (button_2.set_live(False), self.draw())) + try: + self.canvas_graph.create_image( + 1150, 130, image=tkt.PhotoImage('tkintertools.png')) + except TclError: + print('\033[31mLoad tkintertools.png Error\033[0m') + + def canvas_3d_init(self): + self.create_3d() + tkt.Button( + self.canvas_3d, 10, 660, 200, 50, text='Back', + command=lambda: (tkt.move(self.root, self.canvas_graph, 1280*self.canvas_main.rx, 0, 500, mode='rebound'), + tkt.move(self.root, self.canvas_3d, 1280*self.canvas_graph.rx, 0, 500, mode='rebound'))) + + def spin(self, event): # type: (Event, list[float]) -> None + dx, dy = event.x - self.pos[0], event.y - self.pos[1] + self.pos = [event.x, event.y] + for item in self.lst_3ditems: + item.rotate(0, -dy/100, dx/100) + for item in self.lst_3ditems: + item.update(500, 640, 360) + + def create_3d(self): + self.lst_3ditems = [] # type: list[t3d.Cuboid] + self.pos = [0, 0] # type: list[float] + + def modify(event): + self.pos = [event.x, event.y] + + for _ in range(10): + cube = t3d.Cuboid( + self.canvas_3d, *sample(range(-200, 200), 3), *sample(range(50, 100), 3)) + cube.draw(500, 640, 360) + self.lst_3ditems.append(cube) + x, y, z = sample(range(-200, 200), 3) + tetr = t3d.Tetrahedron( + self.canvas_3d, *[(x+randint(-100, 100), y+randint(-100, 100), z+randint(-100, 100)) for _ in range(4)]) + tetr.draw(500, 640, 360) + self.lst_3ditems.append(tetr) + self.canvas_3d.focus_set() + self.canvas_3d.bind('', lambda event: modify(event)) + self.canvas_3d.bind('', self.spin) + + +if __name__ == '__main__': + Application() diff --git a/tkintertools/__init__.py b/tkintertools/__init__.py index 78a59b668c0485ae4aa68f89dcd11fb4eef393e9..7e1f680328ea719fe49f756cdd43de5749a59af2 100644 --- a/tkintertools/__init__.py +++ b/tkintertools/__init__.py @@ -37,7 +37,6 @@ if sys.version_info < (3, 7): # Version Check from .__main__ import (Button, Canvas, CheckButton, Entry, Label, PhotoImage, Progressbar, SetProcessDpiAwareness, Singleton, Text, Tk, Toplevel, askfont, color, move, text) -from .constants import * __author__ = 'Xiaokang2022<2951256653@qq.com>' __version__ = '2.6.1' @@ -49,5 +48,5 @@ __all__ = [ # Tool Classes 'PhotoImage', 'Singleton', # Tool Functions - 'move', 'text', 'color', 'askfont', 'SetProcessDpiAwareness' + 'move', 'text', 'color', 'askfont', 'SetProcessDpiAwareness', ] diff --git a/tkintertools/__main__.py b/tkintertools/__main__.py index 61e053ce69131ce0439da8a473a991662d4c362b..a875808c6f3127eae7dfb6a9f376c58de345d4c9 100644 --- a/tkintertools/__main__.py +++ b/tkintertools/__main__.py @@ -1,6 +1,6 @@ """ Main File """ -import math # 数学函数 +import math # 数学支持 import sys # DPI 兼容 import tkinter # 基础模块 from fractions import Fraction # 图片缩放 diff --git a/tkintertools/tools_3d.py b/tkintertools/tools_3d.py new file mode 100644 index 0000000000000000000000000000000000000000..817c5a3330a0e7fccfecfc0dae936cb4a1010cff --- /dev/null +++ b/tkintertools/tools_3d.py @@ -0,0 +1,309 @@ +""" 3D support """ + +import math # 数学支持 +import statistics # 数据统计 +import tkinter # 基础模块 +from typing import Iterable # 类型提示 + +import tkintertools # 类型提示 + + +def _cross( + matrix, # type: list[list[float]] + vector, # type: list[float] +): # type: (...) -> list[float] + """ 转换矩阵 """ + for i in range(3): + matrix[0][i] = sum(matrix[i][j]*vector[j] for j in range(3)) + return matrix[0] + + +class Point: + """ 点 """ + + def __init__(self, coords): # type: (list[float]) -> None + self.coords = list(coords) # 利用列表引用 + + def translate(self, dx=0, dy=0, dz=0): # type: (float, float, float) -> Point + """ 平移 """ + self.coords[0] += dx + self.coords[1] += dy + self.coords[2] += dz + return self + + def rotate(self, dx=0, dy=0, dz=0): # type: (float, float, float) -> Point + """ 旋转 """ + sa, sb, sc = math.sin(dx), math.sin(dy), math.sin(dz) + ca, cb, cc = math.cos(dx), math.cos(dy), math.cos(dz) + M = [[cc*cb, cc*sb*sa-sc*ca, cc*sb*ca+sc*sa], + [sc*cb, sc*sb*sa+cc*ca, sc*sb*ca-cc*sa], + [-sb, cb*sa, cb*ca]] + self.coords[0], self.coords[1], self.coords[2] = _cross(M, self.coords) + return self + + def project(self, distance): # type: (float) -> list[float] + """ 投影 """ + try: + coefficient = distance/(distance - self.coords[0]) + except: + return [distance, distance] + return [self.coords[1]*coefficient, self.coords[2]*coefficient] + + +class Line: + """ 线 """ + + def __init__( + self, + x1, # type: float + y1, # type: float + z1, # type: float + x2, # type: float + y2, # type: float + z2, # type: float + ): # type: (...) -> None + self.coords = [[x1, y1, z1], [x2, y2, z2]] + self.points = [Point(coord) for coord in self.coords] + + def translate(self, dx=0, dy=0, dz=0): # type: (float, float, float) -> Line + """ 平移 """ + for point in self.points: + point.translate(dx, dy, dz) + return self + + def rotate(self, dx=0, dy=0, dz=0): # type: (float, float, float) -> Line + """ 旋转 """ + for point in self.points: + point.rotate(dx, dy, dz) + return self + + def project(self, distance): # type: (float) -> list[list[float]] + """ 投影 """ + return [point.project(distance) for point in self.points] + + +class Side: + """ 面 """ + + def __init__(self, *coords): # type: (list[float]) -> None + self.coords = list(coords) + self.lines = [Line(*coords[ind-1], *coords[ind]) + for ind in range(len(coords))] + + def translate(self, dx=0, dy=0, dz=0): # type: (float, float, float) -> Side + """ 平移 """ + for line in self.lines: + line.translate(dx, dy, dz) + return self + + def rotate(self, dx=0, dy=0, dz=0): # type: (float, float, float) -> Side + """ 旋转 """ + for line in self.lines: + line.rotate(dx, dy, dz) + return self + + def project(self, distance): # type: (float) -> list[list[list[float]]] + """ 投影 """ + return [line.project(distance) for line in self.lines] + + +class Geometry: + """ 几何体 """ + + def __init__(self, canvas, *sides): # type: (tkintertools.Canvas, Side) -> None + """ + `canvas`: 显示的画布\n + `size`: 平面类`Side`\n + """ + self.canvas = canvas + self.coords = [] # type: list[list[float]] + self.sides = [] # type: list[Side] + self.items = [] # type: list[tkinter._CanvasItemId] + if sides: + self.append(*sides) + + def translate(self, dx=0, dy=0, dz=0): # type: (float, float, float) -> None + """ 平移 """ + for side in self.sides: + side.translate(dx, dy, dz) + + def rotate(self, dx=0, dy=0, dz=0): # type: (float, float, float) -> None + """ 旋转 """ + for side in self.sides: + side.rotate(dx, dy, dz) + + def center(self): # type: () -> tuple[float, float, float] + """ 几何中心 """ # NOTE: 对凹面几何体无效 + data = list(zip(*self.coords)) # 转置 + x_c = statistics.mean(data[0]) + y_c = statistics.mean(data[1]) + z_c = statistics.mean(data[2]) + return x_c, y_c, z_c + + def append(self, *sides): # type: (Side) -> None + """ 添加面 """ + for side in sides: + for line in side.lines: + for point in line.points: + if point not in self.coords: + self.coords.append(point) + self.sides.append(side) + + def update(self, distance, dx=0, dy=0): # type: (float, float, float) -> None + """ 更新几何体 """ + c = 0 + for side in self.sides: + coords = side.project(distance) + for coord in coords: + + k = [] + + for lst in coord: + if dx or dy: + lst[0] += 640 + lst[1] += 360 + k.append(lst[0]) + k.append(lst[1]) + + self.canvas.coords(self.items[c], k) + c += 1 + + def draw(self, distance, dx=0, dy=0): # type: (float, float, float) -> None + """ 绘制 """ + for side in self.sides: + coords = side.project(distance) + for coord in coords: + + if dx or dy: + for lst in coord: + lst[0] += dx + lst[1] += dy + + self.items.append(self.canvas.create_polygon( + *coord, outline='black')) + + +class Cuboid(Geometry): + """ 长方体 """ + + def __init__( + self, + canvas, # type: tkintertools.Canvas + x, # type: float + y, # type: float + z, # type: float + length, # type: float + width, # type: float + height, # type: float + ): # type: (...) -> None + """ + `canvas`: 父画布\n + `x`: 左上角x坐标\n + `y`: 左上角y坐标\n + `z`: 左上角z坐标\n + `length`: 长度\n + `width`: 宽度\n + `height`: 高度\n + """ + self.canvas = canvas + self.coords = [[x+l, y+w, z+h] + for l in (0, length) + for w in (0, width) + for h in (0, height)] + self.sides = [ + Side(self.coords[0], self.coords[1], + self.coords[3], self.coords[2]), + Side(self.coords[0], self.coords[1], + self.coords[5], self.coords[4]), + Side(self.coords[0], self.coords[2], + self.coords[6], self.coords[4]), + Side(self.coords[1], self.coords[3], + self.coords[7], self.coords[5]), + Side(self.coords[2], self.coords[3], + self.coords[7], self.coords[6]), + Side(self.coords[4], self.coords[5], + self.coords[7], self.coords[6]), + ] + self.items = [] # type: list[tkinter._CanvasItemId] + + +class Tetrahedron(Geometry): + """ 四面体 """ + + def __init__( + self, + canvas, # type: tkintertools.Canvas + p1, # type: Iterable[float] + p2, # type: Iterable[float] + p3, # type: Iterable[float] + p4, # type: Iterable[float] + ): # type: (...) -> None + """ + `canvas`: 父画布\n + `p1`: 第一个顶点\n + `p2`: 第二个顶点\n + `p3`: 第三个顶点\n + `p4`: 第四个顶点\n + """ + self.canvas = canvas + self.coords = [list(p1), list(p2), list(p3), list(p4)] + self.sides = [ + Side(p1, p2, p3), + Side(p1, p2, p4), + Side(p1, p3, p4), + Side(p2, p3, p4), + ] + self.items = [] # type: list[tkinter._CanvasItemId] + + +ORIGIN = Point((0,)*3) +""" 原点 """ + +LINE_X = Line(0, 0, 0, 1, 0, 0) +""" X 轴单位直线 """ +LINE_Y = Line(0, 0, 0, 0, 1, 0) +""" Y 轴单位直线 """ +LINE_Z = Line(0, 0, 0, 0, 0, 1) +""" Z 轴单位直线 """ + +SIDE_YZ = Side((0, 1, 1), (0, 1, -1), (0, -1, -1), (0, -1, 1)) +""" 垂直 X 轴单位平面 """ +SIDE_ZX = Side((1, 0, 1), (1, 0, -1), (-1, 0, -1), (-1, 0, 1)) +""" 垂直 Y 轴单位平面 """ +SIDE_XY = Side((1, 1, 0), (1, -1, 0), (-1, -1, 0), (-1, 1, 0)) +""" 垂直 Z 轴单位平面 """ + + +# class Ellipsoid: +# """ 椭球体 """ + +# def __init__( +# self, +# x, # type: float +# y, # type: float +# z, # type: float +# length, # type: float +# width, # type: float +# height # type: float +# ): # type: (...) -> None +# self.coords = [[x+l, y+w, z+h] +# for l in (0, length) +# for w in (0, width) +# for h in (0, height)] +# self.points = [Point(coord) for coord in self.coords] + +# def translate(self, dx=0, dy=0, dz=0): # type: (float, float, float) -> None +# """ 平移 """ +# for point in self.points: +# point.translate(dx, dy, dz) + +# def rotate(self, dx=0, dy=0, dz=0): # type: (float, float, float) -> None +# """ 旋转 """ +# for point in self.points: +# point.rotate(dx, dy, dz) + +# # type: (float) -> tuple[list[float], list[float]] +# def project(self, distance): +# """ 投影 """ +# coefficient = distance/(distance - self.coords[0]) +# return [self.coords[1]*coefficient, self.coords[2]*coefficient]