forked from mttu-developers/konabot
Compare commits
33 Commits
raymarch_t
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| ec1f9627f3 | |||
| c0590dacbc | |||
| d748e242db | |||
| e2fd0809a5 | |||
|
2144b1e0eb
|
|||
|
7d0d53bead
|
|||
| 4bfcc9b41c | |||
|
2b1a1c19d7
|
|||
| cc486a6ac0 | |||
|
4d4bbc86dc
|
|||
| 8f1e0b11a0 | |||
| 7ba3035006 | |||
| 0afbbd2fdf | |||
| 3175817b63 | |||
| 129870709b | |||
| 8997c430c9 | |||
| 250eaaf59c | |||
| 733114b941 | |||
| cf52ea683b | |||
| d80d8d91c2 | |||
| 88f1f45b94 | |||
| b7f90b0c9e | |||
| 6b152235cf | |||
| b4f167e5f6 | |||
| b720504e48 | |||
| 5d93af0666 | |||
| 24e59a7f52 | |||
| 197535cd34 | |||
| c3c22e7145 | |||
| 6a68db70f5 | |||
| 3f3a375dd6 | |||
| facd2d0e84 | |||
| cc97ca5493 |
@ -5,6 +5,8 @@ ENV VIRTUAL_ENV=/app/.venv \
|
||||
PLAYWRIGHT_BROWSERS_PATH=/usr/lib/pw-browsers
|
||||
|
||||
# 安装所有都需要的底层依赖
|
||||
#
|
||||
# xz-utils: 解压需要它
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
libfontconfig1 libgl1 libegl1 libglvnd0 mesa-vulkan-drivers at-spi2-common fontconfig \
|
||||
@ -16,6 +18,7 @@ RUN apt-get update && \
|
||||
libatk-bridge2.0-0t64 libatspi2.0-0t64 libxcomposite1 libxdamage1 libxfixes3 \
|
||||
libxkbcommon0 libasound2t64 libnss3 fonts-noto-cjk fonts-noto-cjk-extra \
|
||||
fonts-noto-color-emoji \
|
||||
xz-utils \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
|
||||
@ -50,7 +50,14 @@ class ArtifactDepends:
|
||||
tasks = set()
|
||||
for f in self.callbacks:
|
||||
tasks.add(f(downloaded))
|
||||
return await asyncio.gather(*tasks, return_exceptions=True)
|
||||
result = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
|
||||
for r in result:
|
||||
if isinstance(r, BaseException):
|
||||
logger.warning("完成了二进制文件的下载,但是有未捕捉的错误")
|
||||
logger.exception(r)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class Config(BaseModel):
|
||||
@ -73,12 +80,7 @@ async def _():
|
||||
|
||||
async def _task(artifact: ArtifactDepends):
|
||||
async with semaphore:
|
||||
downloaded = await ensure_artifact(artifact)
|
||||
result = await artifact._finished(downloaded)
|
||||
for r in result:
|
||||
if isinstance(r, BaseException):
|
||||
logger.warning("完成了二进制文件的下载,但是有未捕捉的错误")
|
||||
logger.exception(r)
|
||||
await ensure_artifact(artifact)
|
||||
|
||||
tasks: set[asyncio.Task] = set()
|
||||
for a in artifact_list:
|
||||
@ -116,9 +118,16 @@ async def download_artifact(artifact: ArtifactDepends):
|
||||
f"下载到的二进制的 sha256 与需求不同 TARGET={artifact.target} REQUESTED={artifact.sha256} ACTUAL={m.hexdigest()}"
|
||||
)
|
||||
|
||||
await artifact._finished(True)
|
||||
|
||||
|
||||
async def ensure_artifact(artifact: ArtifactDepends) -> bool:
|
||||
"""
|
||||
确保所需的二进制存在。返回是否下载了这个二进制文件。
|
||||
"""
|
||||
|
||||
if not artifact.is_corresponding_platform():
|
||||
logger.debug(f"所需求的平台不是当前平台,跳过二进制下载 artifact={artifact}")
|
||||
return False
|
||||
|
||||
if not artifact.target.exists():
|
||||
@ -136,6 +145,7 @@ async def ensure_artifact(artifact: ArtifactDepends) -> bool:
|
||||
artifact.target.unlink()
|
||||
await download_artifact(artifact)
|
||||
return True
|
||||
await artifact._finished(False)
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
from contextlib import asynccontextmanager
|
||||
import os
|
||||
import asyncio
|
||||
from loguru import logger
|
||||
import sqlparse
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Any, Optional, Union, TYPE_CHECKING
|
||||
@ -10,10 +11,20 @@ import aiosqlite
|
||||
if TYPE_CHECKING:
|
||||
from . import DatabaseManager
|
||||
|
||||
# 全局数据库管理器实例
|
||||
_global_db_manager: Optional["DatabaseManager"] = None
|
||||
|
||||
|
||||
async def try_close_connection(conn: aiosqlite.Connection) -> bool:
|
||||
try:
|
||||
await conn.close()
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error("有的连接关闭失败了")
|
||||
logger.exception(e)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_global_db_manager() -> "DatabaseManager":
|
||||
"""获取全局数据库管理器实例"""
|
||||
global _global_db_manager
|
||||
@ -24,16 +35,10 @@ def get_global_db_manager() -> "DatabaseManager":
|
||||
return _global_db_manager
|
||||
|
||||
|
||||
def close_global_db_manager() -> None:
|
||||
"""关闭全局数据库管理器实例"""
|
||||
global _global_db_manager
|
||||
if _global_db_manager is not None:
|
||||
# 注意:这个函数应该在async环境中调用close_all_connections
|
||||
_global_db_manager = None
|
||||
|
||||
|
||||
class DatabaseManager:
|
||||
"""异步数据库管理器"""
|
||||
"""
|
||||
异步数据库管理器
|
||||
"""
|
||||
|
||||
def __init__(self, db_path: Optional[Union[str, Path]] = None, pool_size: int = 5):
|
||||
"""
|
||||
@ -56,6 +61,7 @@ class DatabaseManager:
|
||||
|
||||
async def _get_connection(self) -> aiosqlite.Connection:
|
||||
"""从连接池获取连接"""
|
||||
|
||||
async with self._lock:
|
||||
# 尝试从池中获取现有连接
|
||||
while self._connection_pool:
|
||||
@ -67,10 +73,7 @@ class DatabaseManager:
|
||||
return conn
|
||||
except:
|
||||
# 连接已失效,关闭它
|
||||
try:
|
||||
await conn.close()
|
||||
except:
|
||||
pass
|
||||
await try_close_connection(conn)
|
||||
|
||||
# 如果连接池为空,创建新连接
|
||||
conn = await aiosqlite.connect(self.db_path)
|
||||
@ -86,16 +89,31 @@ class DatabaseManager:
|
||||
self._connection_pool.append(conn)
|
||||
else:
|
||||
# 池已满,直接关闭连接
|
||||
try:
|
||||
await conn.close()
|
||||
except:
|
||||
pass
|
||||
await try_close_connection(conn)
|
||||
|
||||
@asynccontextmanager
|
||||
async def get_conn(self):
|
||||
"""
|
||||
从 db 中获取一个 Connection
|
||||
"""
|
||||
|
||||
conn = await self._get_connection()
|
||||
yield conn
|
||||
await self._return_connection(conn)
|
||||
|
||||
try:
|
||||
yield conn
|
||||
|
||||
# 只有当一切正常时才归还数据库连接
|
||||
await self._return_connection(conn)
|
||||
except Exception as e:
|
||||
logger.error("有模块使用一个连接时出现了错误")
|
||||
logger.exception(e)
|
||||
|
||||
try:
|
||||
await conn.rollback()
|
||||
await conn.close()
|
||||
except Exception as e:
|
||||
logger.error("在 Rollback 和关闭时也出现了问题")
|
||||
logger.exception(e)
|
||||
|
||||
async def query(
|
||||
self, query: str, params: Optional[tuple] = None
|
||||
@ -190,42 +208,14 @@ class DatabaseManager:
|
||||
else:
|
||||
await self.execute_script(script)
|
||||
|
||||
async def execute_many(self, command: str, seq_of_params: List[tuple]) -> None:
|
||||
"""执行多条非查询语句"""
|
||||
conn = await self._get_connection()
|
||||
try:
|
||||
await conn.executemany(command, seq_of_params)
|
||||
await conn.commit()
|
||||
except Exception as e:
|
||||
await conn.rollback()
|
||||
raise Exception(f"数据库批量执行失败: {str(e)}") from e
|
||||
finally:
|
||||
await self._return_connection(conn)
|
||||
|
||||
async def execute_many_values_by_sql_file(
|
||||
self, file_path: Union[str, Path], seq_of_params: List[tuple]
|
||||
) -> None:
|
||||
"""从 SQL 文件中读取一条语句,但是被不同值同时执行"""
|
||||
path = str(file_path) if isinstance(file_path, Path) else file_path
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
command = f.read()
|
||||
await self.execute_many(command, seq_of_params)
|
||||
|
||||
async def close_all_connections(self) -> None:
|
||||
"""关闭所有连接"""
|
||||
|
||||
async with self._lock:
|
||||
# 关闭池中的连接
|
||||
for conn in self._connection_pool:
|
||||
try:
|
||||
await conn.close()
|
||||
except:
|
||||
pass
|
||||
await try_close_connection(conn)
|
||||
self._connection_pool.clear()
|
||||
|
||||
# 关闭正在使用的连接
|
||||
for conn in self._in_use.copy():
|
||||
try:
|
||||
await conn.close()
|
||||
except:
|
||||
pass
|
||||
await try_close_connection(conn)
|
||||
self._in_use.clear()
|
||||
|
||||
@ -32,7 +32,7 @@ class PermManager:
|
||||
def __init__(self, db: DatabaseManager) -> None:
|
||||
self.db = db
|
||||
|
||||
async def check_has_permission_info(self, entities: _EntityLike, key: str):
|
||||
async def get_permission_info(self, entities: _EntityLike, key: str):
|
||||
entities = await _to_entity_chain(entities)
|
||||
key = key.removesuffix("*").removesuffix(".")
|
||||
key_split = key.split(".")
|
||||
@ -52,7 +52,7 @@ class PermManager:
|
||||
return None
|
||||
|
||||
async def check_has_permission(self, entities: _EntityLike, key: str) -> bool:
|
||||
res = await self.check_has_permission_info(entities, key)
|
||||
res = await self.get_permission_info(entities, key)
|
||||
if res is None:
|
||||
return False
|
||||
return res[2]
|
||||
|
||||
@ -43,15 +43,12 @@ class PermRepo:
|
||||
Raises:
|
||||
AssertionError: 如果创建后无法获取实体 ID。
|
||||
"""
|
||||
try:
|
||||
await self.conn.execute(
|
||||
s("create_entity.sql"),
|
||||
(entity.platform, entity.entity_type, entity.external_id),
|
||||
)
|
||||
await self.conn.commit()
|
||||
except Exception:
|
||||
await self.conn.rollback()
|
||||
raise
|
||||
await self.conn.execute(
|
||||
s("create_entity.sql"),
|
||||
(entity.platform, entity.entity_type, entity.external_id),
|
||||
)
|
||||
await self.conn.commit()
|
||||
|
||||
eid = await self._get_entity_id_or_none(entity)
|
||||
assert eid is not None
|
||||
return eid
|
||||
@ -119,12 +116,8 @@ class PermRepo:
|
||||
value: 要设置的配置值(True/False/None)。
|
||||
"""
|
||||
eid = await self.get_entity_id(entity)
|
||||
try:
|
||||
await self.conn.execute(s("update_perm_info.sql"), (eid, config_key, value))
|
||||
await self.conn.commit()
|
||||
except Exception:
|
||||
await self.conn.rollback()
|
||||
raise
|
||||
await self.conn.execute(s("update_perm_info.sql"), (eid, config_key, value))
|
||||
await self.conn.commit()
|
||||
|
||||
async def get_entity_id_batch(
|
||||
self, entities: list[PermEntity]
|
||||
@ -143,15 +136,11 @@ class PermRepo:
|
||||
# s("create_entity.sql"),
|
||||
# (entity.platform, entity.entity_type, entity.external_id),
|
||||
# )
|
||||
try:
|
||||
await self.conn.executemany(
|
||||
s("create_entity.sql"),
|
||||
[(e.platform, e.entity_type, e.external_id) for e in entities],
|
||||
)
|
||||
await self.conn.commit()
|
||||
except Exception:
|
||||
await self.conn.rollback()
|
||||
raise
|
||||
await self.conn.executemany(
|
||||
s("create_entity.sql"),
|
||||
[(e.platform, e.entity_type, e.external_id) for e in entities],
|
||||
)
|
||||
await self.conn.commit()
|
||||
val_placeholders = ", ".join(["(?, ?, ?)"] * len(entities))
|
||||
params = []
|
||||
for e in entities:
|
||||
|
||||
@ -1,4 +1,40 @@
|
||||
# 指令介绍
|
||||
简易 Raymarch 小玩具
|
||||
用法:march <scene>
|
||||
例:march sphere(1) cam(4).pos(-5).lookat(0)
|
||||
用法:march `[scene]`
|
||||
例:march torus(1.0, 0.2).color(1.0, 0.2, 0.2) torus(1.0, 0.2).rot(90, 0, 0).color(0.2, 0.2, 1.0) camera(4.0).pos(4, 0, 0).lookat(0)
|
||||
|
||||
# 主要语法
|
||||
`[scene]` ::= `[scene]` "." `[op]` |`[obj]`
|
||||
|
||||
`[obj]` ::= `[obj_ty]` | `[obj_ty]` "(" [args] ")"
|
||||
|
||||
`[op]` ::= `[op_ty]` | `[op_ty]` "(" `[args]` ")"
|
||||
|
||||
`[args]` ::= `[args]` "," `[arg]` | `[arg]`
|
||||
|
||||
其中 `obj_ty`、`op_ty` 分别为物体类型(如 `cube`、`sphere`、`torus` 等)与变换类型(如 `pos`、`rot`)。
|
||||
|
||||
# 支持的物体
|
||||
目前支持的物体有(不包含 alias):
|
||||
`cube`:可选参数长宽高
|
||||
`sphere`:可选参数半径
|
||||
`torus`:可选参数半径与粗细
|
||||
`cylinder`:可选参数半径与高度
|
||||
`capsule`:可选参数高度与半径
|
||||
|
||||
特殊物体:
|
||||
`mix`:混合两个物体
|
||||
`bool`:两个物体相交
|
||||
`minus`:两个物体相减
|
||||
`camera`:相机,可选参数焦距
|
||||
|
||||
# 支持的变换
|
||||
目前支持的变换有
|
||||
`pos`:位移
|
||||
`rot`:旋转(欧拉角 xyz)
|
||||
`color`:基础色
|
||||
`lookat`:朝向
|
||||
`rounded`:圆角
|
||||
|
||||
# 特殊说明
|
||||
`[op_ty]` 不包含 scale。非正交的变换会破坏 SDF 的性质。
|
||||
@ -2,14 +2,21 @@ from nonebot import on_command
|
||||
from nonebot.adapters import Message
|
||||
from nonebot_plugin_alconna import UniMessage
|
||||
from nonebot.params import CommandArg
|
||||
import render
|
||||
|
||||
import konabot.plugins.marchtoy.gl_render as render
|
||||
# import konabot.plugins.marchtoy.cpu_render as render
|
||||
import io
|
||||
cmd_marchtoy = on_command("march", priority=10, block=True)
|
||||
cmd_marchtoy = on_command("march")
|
||||
@cmd_marchtoy.handle()
|
||||
async def _(args: Message = CommandArg()):
|
||||
if cmd := args.extract_plain_text():
|
||||
img = await render.render(cmd, 256, 256)
|
||||
buffer = io.BytesIO()
|
||||
img.save(buffer, format="PNG")
|
||||
buffer.seek(0)
|
||||
await cmd_marchtoy.send(await UniMessage().image(raw=buffer).export())
|
||||
try:
|
||||
img = await render.render(cmd, (512, 512))
|
||||
buffer = io.BytesIO()
|
||||
# img.show()
|
||||
# img.save("/mnt/d/output.png", format="GIF")
|
||||
img.save(buffer, format="PNG")
|
||||
buffer.seek(0)
|
||||
await cmd_marchtoy.send(await UniMessage().image(raw=buffer).export())
|
||||
except Exception as e:
|
||||
await cmd_marchtoy.send(await UniMessage.text(f"cannot render: {e}").export())
|
||||
@ -13,14 +13,10 @@ example:
|
||||
march cube.pos(0, 0, 0).color(red) cam(1.0).pos(1, 1, 1).lookat(0, 0, 0)
|
||||
"""
|
||||
|
||||
import pathlib
|
||||
from dataclasses import dataclass
|
||||
import re
|
||||
import numpy as np
|
||||
import regex as re
|
||||
from dataclasses import dataclass
|
||||
from konabot.plugins.marchtoy.obj import Object, Camera, OBJECT_ENTRIES
|
||||
from konabot.plugins.marchtoy.op import OPERATION_ENTRIES
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class Command:
|
||||
@ -29,7 +25,7 @@ class Command:
|
||||
|
||||
|
||||
class CommandChainParser:
|
||||
CHAIN_PATTERN = r"^[a-zA-Z]+(\([^(]*\))?(\.[a-zA-Z]+(\([^(]*\))?)+"
|
||||
CHAIN_PATTERN = r"^(([a-zA-Z0-9\-+]+(?:\(([^()]*|(?1)+)(\s*\,\s*(?1))*\))?)(\.(?1))*)"
|
||||
|
||||
def __init__(self, _command_chain: str) -> None:
|
||||
self.command_chain = _command_chain
|
||||
@ -46,8 +42,10 @@ class CommandChainParser:
|
||||
|
||||
|
||||
class CommandParser:
|
||||
CMD_PATTERN = r"^[a-zA-Z]+(\([^(]*\))?"
|
||||
CMD_PATTERN = r"^[a-zA-Z]+(?:\(([0-9.\-+]+|(([a-zA-Z0-9]+(?:\(([^()]*|(?1)+)(\s*\,\s*(?1))*\))?)(\.(?1))*))(\s*\,\s*(?1))*\))?"
|
||||
ID_PATTERN = r"^[a-zA-Z]+(?=\(|\.|$)"
|
||||
ARG_PATTERN = CommandChainParser.CHAIN_PATTERN
|
||||
TRIM_PATTERN = r"^\s*\,\s*"
|
||||
|
||||
def __init__(self, _command: str) -> None:
|
||||
self.command = _command
|
||||
@ -59,91 +57,17 @@ class CommandParser:
|
||||
if query := re.match(CommandParser.CMD_PATTERN, self.command):
|
||||
cmd = query[0]
|
||||
if cmd_id_qry := re.match(CommandParser.ID_PATTERN, cmd):
|
||||
cmd_id = cmd_id_qry[0]
|
||||
id = cmd_id_qry[0]
|
||||
cmd_args = cmd[len(id) + 1 : -1] # .replace(" ", "").split(",")
|
||||
args: list[str] = []
|
||||
self.command = self.command[len(cmd) + 1 :]
|
||||
cmd_args = cmd[len(cmd_id) + 1 : -1].replace(" ", "").split(",")
|
||||
while "" in cmd_args:
|
||||
cmd_args.remove("")
|
||||
return Command(cmd_id, cmd_args)
|
||||
raise StopIteration
|
||||
|
||||
|
||||
class Scene:
|
||||
def __init__(self, _instruction: str) -> None:
|
||||
self.canvas_objs: list[tuple[Object, str]] = []
|
||||
self.camera: Camera = Camera()
|
||||
for raw_cmd in CommandChainParser(_instruction):
|
||||
cmd_queue = CommandParser(raw_cmd)
|
||||
cmd_obj = next(cmd_queue)
|
||||
obj_id, obj_args = cmd_obj.id, cmd_obj.args
|
||||
obj_instance: Optional[Object] = None
|
||||
if obj_id in OBJECT_ENTRIES:
|
||||
obj_cls = OBJECT_ENTRIES[obj_id]
|
||||
if not issubclass(obj_cls, Object):
|
||||
raise Exception(f"{obj_id} is not a subclass of Object.")
|
||||
obj_instance = obj_cls()
|
||||
try:
|
||||
if len(obj_args) != 0:
|
||||
obj_instance.parse_args(obj_args)
|
||||
except Exception as e:
|
||||
raise Exception(
|
||||
f"object {obj_id} failed to parse args passed in: {obj_args}"
|
||||
) from e
|
||||
else:
|
||||
raise Exception(f"{obj_id} is not a valid object type.")
|
||||
|
||||
if obj_instance != None:
|
||||
for cmd in cmd_queue:
|
||||
op_id, op_args = cmd.id, cmd.args
|
||||
if op_id in OPERATION_ENTRIES:
|
||||
op_func = OPERATION_ENTRIES[op_id]
|
||||
if not callable(op_func):
|
||||
raise Exception(f"{op_id} is not a valid operation.")
|
||||
op_func(obj_instance, op_args)
|
||||
else:
|
||||
raise Exception(f"{op_id} is not a valid operation.")
|
||||
|
||||
try:
|
||||
sdf_block = obj_instance.sdf_block()
|
||||
self.canvas_objs.append((obj_instance, sdf_block))
|
||||
except:
|
||||
if type(obj_instance) == Camera:
|
||||
self.camera = obj_instance
|
||||
|
||||
def __str__(self) -> str:
|
||||
return ", ".join([str(type(obj)) for obj in self.canvas_objs])
|
||||
|
||||
def compile(self) -> str:
|
||||
PATH = pathlib.Path(__file__).parent / "shaders" / "raymarch.sksl"
|
||||
with PATH.open(encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
sdf_block: str = ""
|
||||
color_block: str = ""
|
||||
|
||||
index = 0
|
||||
for canvas_item in self.canvas_objs:
|
||||
obj, sdf_expr = canvas_item
|
||||
sdf_block += f"float sd{index} = {sdf_expr};"
|
||||
sdf_block += f"if(sd{index} < qry.value)"
|
||||
sdf_block += "{" + f"qry.value = sd{index}; qry.obj_id = {index}; " + "}\n"
|
||||
color = obj.texture.color
|
||||
color_block += (
|
||||
f"if(obj_id == {index}) return float4("
|
||||
f"{color[0]}, {color[1]}, {color[2]}, {color[3]});\n"
|
||||
)
|
||||
index += 1
|
||||
|
||||
content = content.replace("<SDF_BLOCK>", sdf_block)
|
||||
content = content.replace("<COLOR_BLOCK>", color_block)
|
||||
|
||||
cam_pos = self.camera.transform.t[0:3, 3]
|
||||
cam_focus = self.camera.focus
|
||||
cam_dir = self.camera.transform.t @ np.array((0.0, 0.0, -1.0, 0.0))
|
||||
content = content.replace(
|
||||
"<CAM_POS>", f"{cam_pos[0]}, {cam_pos[1]}, {cam_pos[2]}"
|
||||
)
|
||||
content = content.replace("<CAM_FOCUS>", str(cam_focus))
|
||||
content = content.replace(
|
||||
"<CAM_DIR>", f"{cam_dir[0]}, {cam_dir[1]}, {cam_dir[2]}"
|
||||
)
|
||||
return content
|
||||
while cmd_arg_qry := re.match(CommandParser.ARG_PATTERN, cmd_args):
|
||||
arg = cmd_arg_qry[0]
|
||||
args.append(arg)
|
||||
cmd_args = cmd_args[len(arg) :]
|
||||
if trim_qry := re.match(CommandParser.TRIM_PATTERN, cmd_args):
|
||||
cmd_args = cmd_args[len(trim_qry[0]) :]
|
||||
# while "" in cmd_args:
|
||||
# cmd_args.remove("")
|
||||
return Command(id, args)
|
||||
raise StopIteration
|
||||
44
konabot/plugins/marchtoy/gl_render.py
Normal file
44
konabot/plugins/marchtoy/gl_render.py
Normal file
@ -0,0 +1,44 @@
|
||||
"""
|
||||
headless moderngl
|
||||
"""
|
||||
import pathlib
|
||||
import moderngl
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from konabot.plugins.marchtoy.scene import Scene
|
||||
from nonebot import logger
|
||||
|
||||
PATH = pathlib.Path(__file__).parent / "shaders"
|
||||
with (PATH / "vert.glsl").open(encoding='utf-8') as f:
|
||||
VS_SRC = f.read()
|
||||
|
||||
async def render(command: str, res: tuple[int, int]):
|
||||
fs = Scene(command).compile()
|
||||
try:
|
||||
ctx = moderngl.create_context(standalone=True)
|
||||
except:
|
||||
ctx = moderngl.create_context(standalone=True, backend="egl")
|
||||
ctx.gc_mode = "auto"
|
||||
try:
|
||||
program = ctx.program(
|
||||
vertex_shader=VS_SRC,
|
||||
fragment_shader=fs
|
||||
)
|
||||
except Exception as e:
|
||||
raise Exception(f"cannot compile glsl: {e}") from e
|
||||
try:
|
||||
uniform = program["u_resolution"]
|
||||
except Exception as e:
|
||||
raise Exception("无法获取 uniform,可能相机位于物体内部?")
|
||||
uniform.write(np.array(res, dtype=np.float32))
|
||||
vertices = np.array([-1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0], dtype=np.float32)
|
||||
indices = np.array([0, 1, 2, 1, 2, 3], dtype=np.int32)
|
||||
ibo = ctx.buffer(indices)
|
||||
vbo = ctx.buffer(vertices)
|
||||
vao = ctx.vertex_array(program, vbo, 'in_position', index_buffer = ibo)
|
||||
fbo = ctx.simple_framebuffer(res)
|
||||
fbo.use()
|
||||
fbo.clear(0.0, 0.0, 0.0, 0.0)
|
||||
vao.render(moderngl.TRIANGLES)
|
||||
return Image.frombytes('RGBA', fbo.size, fbo.read(components=4), 'raw', 'RGBA', 0, -1)
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
import numpy as np
|
||||
from konabot.plugins.marchtoy.texture import Texture
|
||||
from konabot.plugins.marchtoy.utilities import ArgParser, SkslFormatter
|
||||
|
||||
from konabot.plugins.marchtoy.utilities import ArgParser, Formatter
|
||||
from nonebot import logger
|
||||
from typing import Optional
|
||||
import re
|
||||
OBJECT_ENTRIES = {}
|
||||
|
||||
|
||||
def make_obj(name: str, aliases: list[str] = []):
|
||||
def make_obj(*name: str):
|
||||
def decorator(cls):
|
||||
OBJECT_ENTRIES[name] = cls
|
||||
for alias in aliases:
|
||||
# OBJECT_ENTRIES[name] = cls
|
||||
for alias in [*name]:
|
||||
OBJECT_ENTRIES[alias] = cls
|
||||
return cls
|
||||
|
||||
@ -21,7 +23,7 @@ class Transform:
|
||||
|
||||
@staticmethod
|
||||
def normalize(p: np.ndarray) -> np.ndarray:
|
||||
return p / np.sqrt(np.dot(p, p))
|
||||
return p / (np.linalg.norm(p) + 1e-8)
|
||||
|
||||
def translate(self, x: float, y: float, z: float):
|
||||
mat = np.identity(4, dtype=np.float32)
|
||||
@ -29,7 +31,7 @@ class Transform:
|
||||
self.t = mat @ self.t
|
||||
return self
|
||||
|
||||
# scale 会破坏 sdf 的性质,不应该使用
|
||||
# scale 会破坏 sdf 的性质 梯度大小会变 导致 overshoot 等问题
|
||||
def scale(self, x: float, y: float, z: float):
|
||||
mat = np.identity(4, dtype=np.float32)
|
||||
mat[0, 0], mat[1, 1], mat[2, 2] = x, y, z
|
||||
@ -37,6 +39,9 @@ class Transform:
|
||||
return self
|
||||
|
||||
def rotate(self, x: float, y: float, z: float):
|
||||
x = x / 180 * np.pi
|
||||
y = y / 180 * np.pi
|
||||
z = z / 180 * np.pi
|
||||
cx, sx = np.cos(x), np.sin(x)
|
||||
cy, sy = np.cos(y), np.sin(y)
|
||||
cz, sz = np.cos(z), np.sin(z)
|
||||
@ -65,7 +70,6 @@ class Transform:
|
||||
zaxis = Transform.normalize(q - p)
|
||||
xaxis = Transform.normalize(np.cross(zaxis, up))
|
||||
yaxis = Transform.normalize(np.cross(xaxis, zaxis))
|
||||
# 约定本地 -Z 为“朝前”,这样和 shader 中使用的相机射线方向保持一致。
|
||||
self.t[:3, 0] = xaxis
|
||||
self.t[:3, 1] = yaxis
|
||||
self.t[:3, 2] = -zaxis
|
||||
@ -73,52 +77,202 @@ class Transform:
|
||||
|
||||
def p_expr(self) -> str:
|
||||
inv = np.linalg.inv(self.t) # + 1e-5 * np.identity(4, dtype=np.float32))
|
||||
return f"({SkslFormatter.mat4(inv)} * float4(p, 1.0)).xyz"
|
||||
return f"({Formatter.to_vec4(inv)} * vec4(p, 1.0)).xyz"
|
||||
|
||||
|
||||
class Object:
|
||||
def __init__(self) -> None:
|
||||
self.transform: Transform = Transform()
|
||||
self.texture: Texture = Texture()
|
||||
self.round_corner: float = 0.0
|
||||
|
||||
def parse_args(self, args: list[str]):
|
||||
raise NotImplementedError
|
||||
|
||||
def sdf_block(self) -> str:
|
||||
def get_transformed(self, p: np.ndarray) -> np.ndarray:
|
||||
if p.shape != (3,):
|
||||
raise Exception(f"{p} is not a vec3")
|
||||
p = p.copy()
|
||||
inv = np.linalg.inv(self.transform.t)
|
||||
|
||||
return (inv @ np.array((*p, 1)))[:3]
|
||||
|
||||
def sdf_block_glsl(self) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@make_obj("cube")
|
||||
@make_obj("cube", "box")
|
||||
class Cube(Object):
|
||||
def __init__(self, _size: np.ndarray = np.array([1.0, 1.0, 1.0])) -> None:
|
||||
super().__init__()
|
||||
self.size = _size
|
||||
|
||||
def parse_args(self, args: list[str]):
|
||||
def parse_args(self, args: str):
|
||||
self.size = ArgParser.as_vec3(args)
|
||||
|
||||
def sdf_block(self) -> str:
|
||||
return f"sdCube({self.transform.p_expr()}, float3({self.size[0]}, {self.size[1]}, {self.size[2]}))"
|
||||
def sdf_block_glsl(self) -> str:
|
||||
return f"sdCube({self.transform.p_expr()}, vec3({self.size[0]}, {self.size[1]}, {self.size[2]}))"
|
||||
|
||||
|
||||
@make_obj("sphere")
|
||||
@make_obj("sphere", "ball")
|
||||
class Sphere(Object):
|
||||
def __init__(self, _radius: float = 1.0) -> None:
|
||||
super().__init__()
|
||||
self.radius = _radius
|
||||
|
||||
def parse_args(self, args: list[str]):
|
||||
def parse_args(self, args: str):
|
||||
self.radius = ArgParser.as_float(args)
|
||||
|
||||
def sdf_block(self) -> str:
|
||||
def sdf_block_glsl(self) -> str:
|
||||
return f"sdSphere({self.transform.p_expr()}, {self.radius})"
|
||||
|
||||
|
||||
@make_obj("camera", ["cam"])
|
||||
@make_obj("cylinder", "cyl")
|
||||
class Cylinder(Object):
|
||||
def __init__(self, _radius: float = 1.0, _height: float = 1.0) -> None:
|
||||
super().__init__()
|
||||
self.radius = _radius
|
||||
self.height = _height
|
||||
|
||||
def parse_args(self, args: str):
|
||||
param = ArgParser.as_vec2(args)
|
||||
self.radius = param[0]
|
||||
self.height = param[1]
|
||||
|
||||
def sdf_block_glsl(self) -> str:
|
||||
return (
|
||||
f"sdCappedCylinder({self.transform.p_expr()}, {self.radius}, {self.height})"
|
||||
)
|
||||
|
||||
@make_obj("torus")
|
||||
class Torus(Object):
|
||||
def __init__(self, _r1: float = 1.0, _r2: float = 0.4) -> None:
|
||||
super().__init__()
|
||||
self.r1 = _r1
|
||||
self.r2 = _r2
|
||||
|
||||
def parse_args(self, args: list[str]):
|
||||
param = ArgParser.as_vec2(args)
|
||||
self.r1 = param[0]
|
||||
self.r2 = param[1]
|
||||
|
||||
def sdf_block_glsl(self) -> str:
|
||||
return f"sdTorus({self.transform.p_expr()}, vec2({self.r1}, {self.r2}))"
|
||||
|
||||
|
||||
@make_obj("capsule", "pill")
|
||||
class Capsule(Object):
|
||||
def __init__(self, _h: float = 1.0, _r: float = 0.25) -> None:
|
||||
super().__init__()
|
||||
self.h = _h
|
||||
self.r = _r
|
||||
|
||||
def parse_args(self, args: list[str]):
|
||||
param = ArgParser.as_vec2(args)
|
||||
self.h = param[0]
|
||||
self.r = param[1]
|
||||
|
||||
def sdf_block_glsl(self) -> str:
|
||||
return f"sdVerticalCapsule({self.transform.p_expr()}, {self.h}, {self.r})"
|
||||
|
||||
@make_obj("smoothed", "mix")
|
||||
class Smoothed(Object):
|
||||
def __init__(self, _k: float = 12.00):
|
||||
super().__init__()
|
||||
self.block_a: str = "INF"
|
||||
self.block_b: str = "INF"
|
||||
self.k: float = _k
|
||||
|
||||
def parse_args(self, args: list[str]):
|
||||
from konabot.plugins.marchtoy.scene import Scene
|
||||
try:
|
||||
if not len(args) >= 2:
|
||||
raise Exception("expecting at least 2 args")
|
||||
scene_a = Scene(args[0])
|
||||
scene_b = Scene(args[1])
|
||||
self.block_a = scene_a.canvas_objs[0][1]
|
||||
self.block_b = scene_b.canvas_objs[0][1]
|
||||
if len(args) > 2:
|
||||
self.k = ArgParser.as_float(args[2], 0.25)
|
||||
except Exception as e:
|
||||
raise Exception(f"cannot build smoothed object over {args}: {e}")
|
||||
|
||||
|
||||
def sdf_block_glsl(self):
|
||||
return f"smin({self.block_a}, {self.block_b}, {self.k})"
|
||||
|
||||
@make_obj("intersect", "bool")
|
||||
class BoolIntersect(Object):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def parse_args(self, args: list[str]):
|
||||
from konabot.plugins.marchtoy.scene import Scene
|
||||
try:
|
||||
if not len(args) >= 2:
|
||||
raise Exception("expecting at least 2 args")
|
||||
scene_a = Scene(args[0])
|
||||
scene_b = Scene(args[1])
|
||||
self.block_a = scene_a.canvas_objs[0][1]
|
||||
self.block_b = scene_b.canvas_objs[0][1]
|
||||
if len(args) > 2:
|
||||
self.k = ArgParser.as_float(args[2], 0.25)
|
||||
except Exception as e:
|
||||
raise Exception(f"cannot build bool object over {args}: {e}")
|
||||
|
||||
def sdf_block_glsl(self):
|
||||
return f"max({self.block_a}, {self.block_b})"
|
||||
|
||||
@make_obj("substract", "minus")
|
||||
class BoolSubstract(Object):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def parse_args(self, args: list[str]):
|
||||
from konabot.plugins.marchtoy.scene import Scene
|
||||
try:
|
||||
if not len(args) >= 2:
|
||||
raise Exception("expecting at least 2 args")
|
||||
scene_a = Scene(args[0])
|
||||
scene_b = Scene(args[1])
|
||||
self.block_a = scene_a.canvas_objs[0][1]
|
||||
self.block_b = scene_b.canvas_objs[0][1]
|
||||
if len(args) > 2:
|
||||
self.k = ArgParser.as_float(args[2], 0.25)
|
||||
except Exception as e:
|
||||
raise Exception(f"cannot build bool object over {args}: {e}")
|
||||
|
||||
def sdf_block_glsl(self):
|
||||
return f"max({self.block_a}, -{self.block_b})"
|
||||
|
||||
@make_obj("add", "addition")
|
||||
class BoolAddition(Object):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def parse_args(self, args: list[str]):
|
||||
from konabot.plugins.marchtoy.scene import Scene
|
||||
try:
|
||||
if not len(args) >= 2:
|
||||
raise Exception("expecting at least 2 args")
|
||||
scene_a = Scene(args[0])
|
||||
scene_b = Scene(args[1])
|
||||
self.block_a = scene_a.canvas_objs[0][1]
|
||||
self.block_b = scene_b.canvas_objs[0][1]
|
||||
except Exception as e:
|
||||
raise Exception(f"cannot build bool object over {args}: {e}")
|
||||
|
||||
def sdf_block_glsl(self):
|
||||
return f"min({self.block_a}, {self.block_b})"
|
||||
|
||||
|
||||
|
||||
@make_obj("camera", "cam")
|
||||
class Camera(Object):
|
||||
def __init__(self, _focus: float = 1.0) -> None:
|
||||
def __init__(self, _focus: float = 2.0) -> None:
|
||||
super().__init__()
|
||||
self.focus = _focus
|
||||
# self.transform.translate(8.0, 8.0, 8.0).lookat(0.0, 0.0, 0.0)
|
||||
|
||||
def parse_args(self, args: list[str]):
|
||||
self.focus = ArgParser.as_float(args)
|
||||
|
||||
|
||||
@ -1,39 +1,37 @@
|
||||
from konabot.plugins.marchtoy.obj import Object
|
||||
import numpy as np
|
||||
from konabot.plugins.marchtoy.utilities import ArgParser
|
||||
|
||||
OPERATION_ENTRIES = {}
|
||||
|
||||
|
||||
def make_operation(name: str, aliases: list[str] = []):
|
||||
def make_op(*name: str):
|
||||
def decorator(op):
|
||||
OPERATION_ENTRIES[name] = op
|
||||
for alias in aliases:
|
||||
# OPERATION_ENTRIES[name] = op
|
||||
for alias in [*name]:
|
||||
OPERATION_ENTRIES[alias] = op
|
||||
return op
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
@make_operation("pos", ["translate", "position", "p"])
|
||||
@make_op("pos", "translate", "position", "p")
|
||||
def translate(obj: Object, args: list[str]):
|
||||
pos = ArgParser.as_vec3(args)
|
||||
obj.transform.translate(pos[0], pos[1], pos[2])
|
||||
|
||||
|
||||
@make_operation("rot", ["rotate", "r"])
|
||||
@make_op("rot", "rotate", "r")
|
||||
def rotate(obj: Object, args: list[str]):
|
||||
pos = ArgParser.as_vec3(args)
|
||||
obj.transform.rotate(pos[0], pos[1], pos[2])
|
||||
|
||||
|
||||
@make_operation("lookat", ["look", "l"])
|
||||
@make_op("lookat", "look", "l")
|
||||
def lookat(obj: Object, args: list[str]):
|
||||
pos = ArgParser.as_vec3(args)
|
||||
obj.transform.lookat(pos[0], pos[1], pos[2])
|
||||
|
||||
|
||||
@make_operation("color", ["col", "texture"])
|
||||
@make_op("color", "col", "texture")
|
||||
def color(obj: Object, args: list[str]):
|
||||
try:
|
||||
if len(args) == 1:
|
||||
@ -46,7 +44,13 @@ def color(obj: Object, args: list[str]):
|
||||
col = ArgParser.as_vec4(args)
|
||||
obj.texture.color = (col[0], col[1], col[2], col[3])
|
||||
else:
|
||||
raise Exception("unknown color")
|
||||
raise Exception("invalid argument number")
|
||||
|
||||
except:
|
||||
raise Exception("unknown color")
|
||||
|
||||
@make_op("rounded", "round_corner", "corner")
|
||||
def rounded(obj: Object, args: list[str]):
|
||||
if len(args) >= 1:
|
||||
obj.round_corner = ArgParser.as_float(args)
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
from konabot.plugins.marchtoy.command import Scene
|
||||
import skia
|
||||
import struct
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
# 暂时先照抄小帕的了,之后有空再单独封装一下
|
||||
|
||||
|
||||
async def render(cmd: str, width: int, height: int):
|
||||
scene = Scene(cmd)
|
||||
surface = skia.Surface(width, height)
|
||||
sksl_code = scene.compile()
|
||||
|
||||
runtime_effect = skia.RuntimeEffect.MakeForShader(scene.compile())
|
||||
if runtime_effect is None:
|
||||
raise Exception("cannot compile sksl shader")
|
||||
|
||||
uv_uniform = struct.pack("ff", float(width), float(height))
|
||||
uniform_data = skia.Data.MakeWithCopy(uv_uniform)
|
||||
shader = runtime_effect.makeShader(uniform_data, None, 0)
|
||||
canvas = surface.getCanvas()
|
||||
canvas.clear(skia.Color(0, 0, 0, 0))
|
||||
paint = skia.Paint()
|
||||
paint.setShader(shader)
|
||||
canvas.drawRect(skia.Rect.MakeWH(width, height), paint)
|
||||
image = surface.makeImageSnapshot()
|
||||
target_info = skia.ImageInfo.Make(
|
||||
image.width(),
|
||||
image.height(),
|
||||
skia.ColorType.kBGRA_8888_ColorType,
|
||||
skia.AlphaType.kPremul_AlphaType,
|
||||
)
|
||||
pixel_data = bytearray(
|
||||
image.width() * image.height() * 4
|
||||
) # 4 bytes per pixel (BGRA)
|
||||
success = image.readPixels(target_info, pixel_data, target_info.minRowBytes())
|
||||
img_array = np.frombuffer(pixel_data, dtype=np.uint8).reshape((height, width, 4))
|
||||
rgb_array = img_array[:, :, [2, 1, 0]]
|
||||
pil_img = Image.fromarray(rgb_array)
|
||||
return pil_img
|
||||
100
konabot/plugins/marchtoy/scene.py
Normal file
100
konabot/plugins/marchtoy/scene.py
Normal file
@ -0,0 +1,100 @@
|
||||
from typing import Optional
|
||||
import pathlib
|
||||
import numpy as np
|
||||
from nonebot import logger
|
||||
|
||||
PATH = pathlib.Path(__file__).parent / "shaders" / "frag.glsl"
|
||||
with PATH.open(encoding="utf-8") as f:
|
||||
FS_SRC = f.read()
|
||||
|
||||
class Scene:
|
||||
def __init__(self, _instruction: str) -> None:
|
||||
from konabot.plugins.marchtoy.command import CommandChainParser, CommandParser
|
||||
from konabot.plugins.marchtoy.op import OPERATION_ENTRIES
|
||||
from konabot.plugins.marchtoy.obj import Object, Camera, OBJECT_ENTRIES
|
||||
logger.info(f"building scene: {_instruction}")
|
||||
self.canvas_objs: list[tuple[Object, str]] = []
|
||||
self.camera: Camera = Camera(1.0 / np.tan(np.deg2rad(30.0)))
|
||||
self.camera.transform.translate(8.0, 8.0, 8.0).lookat(0.0, 0.0, 0.0)
|
||||
|
||||
for raw_cmd in CommandChainParser(_instruction):
|
||||
cmd_queue = CommandParser(raw_cmd)
|
||||
cmd_obj = next(cmd_queue)
|
||||
obj_id, obj_args = cmd_obj.id, cmd_obj.args
|
||||
logger.info(f"parsing object: {obj_id} with args {obj_args}")
|
||||
obj_instance: Optional[Object] = None
|
||||
if obj_id in OBJECT_ENTRIES:
|
||||
obj_cls = OBJECT_ENTRIES[obj_id]
|
||||
if not issubclass(obj_cls, Object):
|
||||
raise Exception(f"{obj_id} is not a subclass of Object.")
|
||||
obj_instance = obj_cls()
|
||||
try:
|
||||
if len(obj_args) != 0:
|
||||
obj_instance.parse_args(obj_args)
|
||||
except Exception as e:
|
||||
raise Exception(
|
||||
f"object {obj_id} failed to parse args passed in: {obj_args}.\n{e}"
|
||||
) from e
|
||||
else:
|
||||
raise Exception(f"{obj_id} is not a valid object type.")
|
||||
logger.info(f"parsed object {obj_id}({obj_args})")
|
||||
if obj_instance != None:
|
||||
for cmd in cmd_queue:
|
||||
op_id, op_args = cmd.id, cmd.args
|
||||
logger.info(f"parsing operation {op_id} with args {op_args}")
|
||||
if op_id in OPERATION_ENTRIES:
|
||||
op_func = OPERATION_ENTRIES[op_id]
|
||||
if not callable(op_func):
|
||||
raise Exception(f"{op_id} is not a valid operation.")
|
||||
op_func(obj_instance, op_args)
|
||||
else:
|
||||
raise Exception(f"{op_id} is not a valid operation.")
|
||||
logger.info(f"parsed operation {op_id}({op_args})")
|
||||
|
||||
try:
|
||||
sdf_block = obj_instance.sdf_block_glsl()
|
||||
self.canvas_objs.append((obj_instance, sdf_block))
|
||||
logger.info(f"parsed sdf {sdf_block}")
|
||||
except:
|
||||
# logger.info(f"parsed camera")
|
||||
if type(obj_instance) == Camera:
|
||||
self.camera = obj_instance
|
||||
|
||||
def __str__(self) -> str:
|
||||
return ", ".join([str(type(obj)) for obj in self.canvas_objs])
|
||||
|
||||
def compile(self) -> str:
|
||||
|
||||
sdf_block: str = ""
|
||||
color_block: str = ""
|
||||
|
||||
index = 0
|
||||
for canvas_item in self.canvas_objs:
|
||||
obj, sdf_expr = canvas_item
|
||||
round_corner = f"- {obj.round_corner}" if obj.round_corner > 1e-8 else ""
|
||||
logger.info(round_corner)
|
||||
sdf_block += f"float sd{index} = {sdf_expr}{round_corner};"
|
||||
sdf_block += f"if(sd{index} < qry.value)"
|
||||
sdf_block += "{" + f"qry.value = sd{index}; qry.obj_id = {index}; " + "}\n"
|
||||
color = obj.texture.color
|
||||
color_block += (
|
||||
f"if(obj_id == {index}) return vec4("
|
||||
f"{color[0]}, {color[1]}, {color[2]}, {color[3]});\n"
|
||||
)
|
||||
index += 1
|
||||
|
||||
content = FS_SRC
|
||||
content = content.replace("<SDF_BLOCK>", sdf_block)
|
||||
content = content.replace("<COLOR_BLOCK>", color_block)
|
||||
|
||||
cam_pos = self.camera.transform.t[0:3, 3]
|
||||
cam_focus = self.camera.focus
|
||||
cam_dir = self.camera.transform.t @ np.array((0.0, 0.0, -1.0, 0.0))
|
||||
content = content.replace(
|
||||
"<CAM_POS>", f"{cam_pos[0]}, {cam_pos[1]}, {cam_pos[2]}"
|
||||
)
|
||||
content = content.replace("<CAM_FOCUS>", str(cam_focus))
|
||||
content = content.replace(
|
||||
"<CAM_DIR>", f"{cam_dir[0]}, {cam_dir[1]}, {cam_dir[2]}"
|
||||
)
|
||||
return content
|
||||
168
konabot/plugins/marchtoy/shaders/frag.glsl
Normal file
168
konabot/plugins/marchtoy/shaders/frag.glsl
Normal file
@ -0,0 +1,168 @@
|
||||
#version 330
|
||||
const float EPS = 0.001;
|
||||
const int MAX_ITER = 128;
|
||||
const float INF = 1e10;
|
||||
const float PI = 3.14159;
|
||||
|
||||
uniform vec2 u_resolution;
|
||||
out vec4 fragColor;
|
||||
|
||||
struct sdQuery {
|
||||
float value;
|
||||
int obj_id;
|
||||
};
|
||||
|
||||
float sdCube(vec3 p, vec3 b) {
|
||||
p = abs(p) - b;
|
||||
return length(max(p, 0.0)) + min(max(p.x, max(p.y, p.z)), 0.0);
|
||||
}
|
||||
|
||||
float sdSphere(vec3 p, float r) {
|
||||
return length(p) - r;
|
||||
}
|
||||
|
||||
float sdCappedCylinder( vec3 p, float r, float h )
|
||||
{
|
||||
vec2 d = abs(vec2(length(p.xz),p.y)) - vec2(r,h);
|
||||
return min(max(d.x,d.y),0.0) + length(max(d,0.0));
|
||||
}
|
||||
|
||||
float sdVerticalCapsule( vec3 p, float h, float r )
|
||||
{
|
||||
p.y -= clamp( p.y, 0.0, h );
|
||||
return length( p ) - r;
|
||||
}
|
||||
float sdTorus( vec3 p, vec2 t )
|
||||
{
|
||||
vec2 q = vec2(length(p.xz)-t.x,p.y);
|
||||
return length(q)-t.y;
|
||||
}
|
||||
// float smin( float a, float b, float k )
|
||||
// {
|
||||
// k *= 1.0;
|
||||
// float r = exp2(-a/k) + exp2(-b/k);
|
||||
// return -k*log2(r);
|
||||
// }
|
||||
|
||||
float smin( float a, float b, float k )
|
||||
{
|
||||
k *= 2.0;
|
||||
float x = (b-a)/k;
|
||||
float g = 0.5*(x+sqrt(x*x+1.0));
|
||||
return b - k * g;
|
||||
}
|
||||
|
||||
sdQuery sd(vec3 p) {
|
||||
sdQuery qry;
|
||||
qry.value = INF;
|
||||
qry.obj_id = -1;
|
||||
<SDF_BLOCK>
|
||||
return qry;
|
||||
}
|
||||
|
||||
vec3 nrm(vec3 p) {
|
||||
vec2 d = vec2(EPS, 0.0);
|
||||
return normalize(vec3(
|
||||
sd(p + d.xyy).value - sd(p - d.xyy).value,
|
||||
sd(p + d.yxy).value - sd(p - d.yxy).value,
|
||||
sd(p + d.yyx).value - sd(p - d.yyx).value
|
||||
));
|
||||
}
|
||||
|
||||
float saturate(float x) {
|
||||
return clamp(x, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float ggx_distribution(vec3 n, vec3 h, float roughness) {
|
||||
float alpha = roughness * roughness;
|
||||
float alpha2 = alpha * alpha;
|
||||
float NdotH = saturate(dot(n, h));
|
||||
float denom = NdotH * NdotH * (alpha2 - 1.0) + 1.0;
|
||||
return alpha2 / max(PI * denom * denom, EPS);
|
||||
}
|
||||
|
||||
float geometry_schlick_ggx(float NdotX, float roughness) {
|
||||
float r = roughness + 1.0;
|
||||
float k = r * r / 8.0;
|
||||
return NdotX / max(NdotX * (1.0 - k) + k, EPS);
|
||||
}
|
||||
|
||||
float geometry_smith(vec3 n, vec3 v, vec3 l, float roughness) {
|
||||
float NdotV = saturate(dot(n, v));
|
||||
float NdotL = saturate(dot(n, l));
|
||||
return geometry_schlick_ggx(NdotV, roughness) * geometry_schlick_ggx(NdotL, roughness);
|
||||
}
|
||||
|
||||
vec3 fresnel_schlick(vec3 f0, float cos_theta) {
|
||||
return f0 + (1.0 - f0) * pow(1.0 - saturate(cos_theta), 5.0);
|
||||
}
|
||||
|
||||
vec3 tonemap_aces(vec3 c) {
|
||||
const float a = 2.51;
|
||||
const float b = 0.03;
|
||||
const float c1 = 2.43;
|
||||
const float d = 0.59;
|
||||
const float e = 0.14;
|
||||
return clamp((c * (a * c + b)) / (c * (c1 * c + d) + e), 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec4 materialColor(int obj_id) {
|
||||
<COLOR_BLOCK>
|
||||
return vec4(1.0);
|
||||
}
|
||||
|
||||
vec4 color(vec3 p, vec3 r, int obj_id) {
|
||||
vec3 light_col = vec3(1.0);
|
||||
vec4 albedo = materialColor(obj_id);
|
||||
vec3 N = nrm(p);
|
||||
vec3 V = normalize(-r);
|
||||
vec3 L = normalize(vec3(0.5, 0.8, -0.6));
|
||||
vec3 H = normalize(V + L);
|
||||
float roughness = 0.45;
|
||||
float metallic = 0.02;
|
||||
float NdotL = saturate(dot(N, L));
|
||||
float NdotV = saturate(dot(N, V));
|
||||
float D = ggx_distribution(N, H, roughness);
|
||||
float G = geometry_smith(N, V, L, roughness);
|
||||
vec3 F0 = mix(vec3(0.04), albedo.rgb, metallic);
|
||||
vec3 F = fresnel_schlick(F0, dot(V, H));
|
||||
vec3 kD = (1.0 - F) * (1.0 - metallic);
|
||||
vec3 diffuse = kD * albedo.rgb / PI;
|
||||
vec3 specular = D * G * F / max(4.0 * NdotL * NdotV, EPS);
|
||||
|
||||
float hemi = N.y * 0.5 + 0.5;
|
||||
vec3 sky_ambient = vec3(0.60, 0.72, 0.92);
|
||||
vec3 ground_ambient = vec3(0.18, 0.16, 0.14);
|
||||
vec3 ambient = mix(ground_ambient, sky_ambient, hemi) * (diffuse + 0.25 * F0) * 0.35;
|
||||
|
||||
vec3 col = ambient + (diffuse + specular) * light_col * NdotL;
|
||||
col = tonemap_aces(col);
|
||||
col = pow(col, vec3(1.0 / 2.2));
|
||||
return vec4(col, 1.0);
|
||||
}
|
||||
|
||||
vec4 march(vec3 p, vec3 r) {
|
||||
sdQuery qry;
|
||||
vec4 col = vec4(0.0);
|
||||
for(int i = 0; i < MAX_ITER; ++i) {
|
||||
qry = sd(p);
|
||||
if(qry.value < EPS){
|
||||
col = color(p, r, qry.obj_id);
|
||||
break;
|
||||
}
|
||||
p += qry.value * r;
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 uv = 2. * (gl_FragCoord.xy / u_resolution - .5) * vec2(u_resolution.x / u_resolution.y, 1.);
|
||||
vec3 c_p = vec3(<CAM_POS>);
|
||||
vec3 c_z = normalize(vec3(<CAM_DIR>));
|
||||
vec3 world_up = abs(c_z.y) > 0.999 ? vec3(0., 0., 1.) : vec3(0., 1., 0.);
|
||||
vec3 c_x = normalize(cross(c_z, world_up));
|
||||
vec3 c_y = normalize(cross(c_x, c_z));
|
||||
mat3 view = mat3(c_x, c_y, c_z);
|
||||
vec3 r = normalize(vec3(uv, <CAM_FOCUS>));
|
||||
fragColor = march(c_p, view * r);
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
uniform float2 u_resolution;
|
||||
|
||||
const float EPS = 0.001;
|
||||
const int MAX_ITER = 64;
|
||||
struct sdQuery {
|
||||
float value;
|
||||
int obj_id;
|
||||
};
|
||||
|
||||
float sdCube(float3 p, float3 b) {
|
||||
p = abs(p) - b;
|
||||
return length(max(p, 0.0)) + min(max(p.x, max(p.y, p.z)), 0.0);
|
||||
}
|
||||
|
||||
float sdSphere(float3 p, float r) {
|
||||
return length(p) - r;
|
||||
}
|
||||
|
||||
sdQuery sd(float3 p) {
|
||||
sdQuery qry;
|
||||
qry.value = 100000000.0;
|
||||
qry.obj_id = -1;
|
||||
<SDF_BLOCK>
|
||||
return qry;
|
||||
}
|
||||
|
||||
float3 nrm(float3 p) {
|
||||
float2 d = float2(EPS, 0.0);
|
||||
return normalize(float3(
|
||||
sd(p + d.xyy).value - sd(p - d.xyy).value,
|
||||
sd(p + d.yxy).value - sd(p - d.yxy).value,
|
||||
sd(p + d.yyx).value - sd(p - d.yyx).value
|
||||
));
|
||||
}
|
||||
|
||||
float4 materialColor(int obj_id) {
|
||||
<COLOR_BLOCK>
|
||||
return float4(1.0);
|
||||
}
|
||||
|
||||
float4 color(float3 p, int obj_id) {
|
||||
float3 normal = nrm(p);
|
||||
float3 light_dir = normalize(float3(0.5, 0.8, -0.6));
|
||||
float light = 0.2 + 0.8 * max(dot(normal, light_dir), 0.0);
|
||||
float4 base = materialColor(obj_id);
|
||||
return float4(base.rgb * light, base.a);
|
||||
}
|
||||
|
||||
float4 march(float3 p, float3 r) {
|
||||
sdQuery qry;
|
||||
float4 col = float4(0.0);
|
||||
for(int i = 0; i < MAX_ITER; ++i) {
|
||||
qry = sd(p);
|
||||
if(qry.value < EPS){
|
||||
col = color(p, qry.obj_id);
|
||||
break;
|
||||
}
|
||||
p += qry.value * r;
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
||||
half4 main(float2 fragCoord) {
|
||||
float2 uv = -2. * (fragCoord / u_resolution - .5) * float2(u_resolution.x / u_resolution.y, 1.);
|
||||
float3 c_p = float3(<CAM_POS>);
|
||||
float3 c_z = normalize(float3(<CAM_DIR>));
|
||||
float3 world_up = abs(c_z.y) > 0.999 ? float3(0., 0., 1.) : float3(0., 1., 0.);
|
||||
float3 c_x = normalize(cross(c_z, world_up));
|
||||
float3 c_y = normalize(cross(c_x, c_z));
|
||||
float3x3 view = float3x3(c_x, c_y, c_z);
|
||||
float3 r = normalize(float3(uv, <CAM_FOCUS>));
|
||||
return half4(march(c_p, view * r));
|
||||
}
|
||||
6
konabot/plugins/marchtoy/shaders/vert.glsl
Normal file
6
konabot/plugins/marchtoy/shaders/vert.glsl
Normal file
@ -0,0 +1,6 @@
|
||||
#version 330
|
||||
in vec2 in_position;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(in_position, 0.0, 1.0);
|
||||
}
|
||||
@ -1,13 +1,54 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
COLORS = {
|
||||
"red": (1.0, 0.0, 0.0, 1.0),
|
||||
"green": (0.0, 1.0, 0.0, 1.0),
|
||||
"blue": (0.0, 0.0, 1.0, 1.0),
|
||||
"white": (1.0, 1.0, 1.0, 1.0),
|
||||
"black": (0.0, 0.0, 0.0, 1.0),
|
||||
"gray": (0.5, 0.5, 0.5, 1.0),
|
||||
"light_gray": (0.75, 0.75, 0.75, 1.0),
|
||||
"dark_gray": (0.25, 0.25, 0.25, 1.0),
|
||||
"dark_red": (0.5, 0.0, 0.0, 1.0),
|
||||
"crimson": (0.86, 0.08, 0.24, 1.0),
|
||||
"pink": (1.0, 0.75, 0.8, 1.0),
|
||||
"hot_pink": (1.0, 0.41, 0.71, 1.0),
|
||||
"orange_red": (1.0, 0.27, 0.0, 1.0),
|
||||
"orange": (1.0, 0.65, 0.0, 1.0),
|
||||
"gold": (1.0, 0.84, 0.0, 1.0),
|
||||
"yellow": (1.0, 1.0, 0.0, 1.0),
|
||||
"light_yellow": (1.0, 1.0, 0.88, 1.0),
|
||||
"khaki": (0.94, 0.90, 0.55, 1.0),
|
||||
"light_green": (0.56, 0.93, 0.56, 1.0),
|
||||
"lime": (0.0, 1.0, 0.0, 1.0),
|
||||
"forest_green": (0.13, 0.55, 0.13, 1.0),
|
||||
"dark_green": (0.0, 0.39, 0.0, 1.0),
|
||||
"olive": (0.5, 0.5, 0.0, 1.0),
|
||||
"teal": (0.0, 0.5, 0.5, 1.0),
|
||||
"light_blue": (0.68, 0.85, 0.9, 1.0),
|
||||
"sky_blue": (0.53, 0.81, 0.92, 1.0),
|
||||
"cyan": (0.0, 1.0, 1.0, 1.0),
|
||||
"navy": (0.0, 0.0, 0.5, 1.0),
|
||||
"royal_blue": (0.25, 0.41, 0.88, 1.0),
|
||||
"steel_blue": (0.27, 0.51, 0.71, 1.0),
|
||||
"purple": (0.5, 0.0, 0.5, 1.0),
|
||||
"magenta": (1.0, 0.0, 1.0, 1.0),
|
||||
"violet": (0.93, 0.51, 0.93, 1.0),
|
||||
"lavender": (0.90, 0.90, 0.98, 1.0),
|
||||
"indigo": (0.29, 0.0, 0.51, 1.0),
|
||||
"brown": (0.65, 0.16, 0.16, 1.0),
|
||||
"saddle_brown": (0.55, 0.27, 0.07, 1.0),
|
||||
"chocolate": (0.82, 0.41, 0.12, 1.0),
|
||||
"tan": (0.82, 0.71, 0.55, 1.0),
|
||||
"beige": (0.96, 0.96, 0.86, 1.0),
|
||||
"coral": (1.0, 0.5, 0.31, 1.0),
|
||||
"salmon": (0.98, 0.5, 0.45, 1.0),
|
||||
"turquoise": (0.25, 0.88, 0.82, 1.0),
|
||||
"aqua": (0.0, 1.0, 1.0, 1.0),
|
||||
"plum": (0.87, 0.63, 0.87, 1.0),
|
||||
"wheat": (0.96, 0.87, 0.70, 1.0),
|
||||
}
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Texture:
|
||||
color: tuple[float, float, float, float] = (1.0, 1.0, 1.0, 1.0)
|
||||
|
||||
@ -1,28 +1,58 @@
|
||||
import numpy as np
|
||||
import regex as re
|
||||
from konabot.plugins.marchtoy.texture import COLORS
|
||||
|
||||
|
||||
class SkslFormatter:
|
||||
class Formatter:
|
||||
@staticmethod
|
||||
def mat4(m: np.ndarray) -> str:
|
||||
def to_vec4(m: np.ndarray) -> str:
|
||||
if m.shape != (4, 4):
|
||||
m = np.identity(4)
|
||||
v_0 = ", ".join([str(x) for x in m[0]])
|
||||
v_1 = ", ".join([str(x) for x in m[1]])
|
||||
v_2 = ", ".join([str(x) for x in m[2]])
|
||||
v_3 = ", ".join([str(x) for x in m[3]])
|
||||
return f"float4x4(float4({v_0}), float4({v_1}), float4({v_2}), float4({v_3}))"
|
||||
v_0 = ", ".join([str(x) for x in m[:, 0]])
|
||||
v_1 = ", ".join([str(x) for x in m[:, 1]])
|
||||
v_2 = ", ".join([str(x) for x in m[:, 2]])
|
||||
v_3 = ", ".join([str(x) for x in m[:, 3]])
|
||||
return f"mat4(vec4({v_0}), vec4({v_1}), vec4({v_2}), vec4({v_3}))"
|
||||
|
||||
"""
|
||||
TODO: 除零出现 nan 情况的单独处理
|
||||
"""
|
||||
class ArgParser:
|
||||
@staticmethod
|
||||
def as_float(args: list[str], default: float = 0.0) -> float:
|
||||
def to_params(args: str, delim: str = ',') -> list[str]:
|
||||
raise DeprecationWarning
|
||||
_params = args.replace(" ", "").split(delim)
|
||||
params: list[str] = []
|
||||
# 还是避免 while 为好
|
||||
for param in _params:
|
||||
if param != "":
|
||||
params.append(param)
|
||||
return params
|
||||
|
||||
@staticmethod
|
||||
def as_float(args: list[str] | str, default: float = 0.0) -> float:
|
||||
try:
|
||||
if len(args) >= 1:
|
||||
if isinstance(args, list) and len(args) >= 1:
|
||||
x = float(args[0])
|
||||
return x
|
||||
elif isinstance(args, str):
|
||||
x = float(args)
|
||||
return x
|
||||
except:
|
||||
# raise Exception(f"cannot parse {args}")
|
||||
return default
|
||||
|
||||
@staticmethod
|
||||
def as_vec2(
|
||||
args: list[str], default: np.ndarray = np.array((0.0, 0.0))
|
||||
) -> np.ndarray:
|
||||
try:
|
||||
if len(args) == 1:
|
||||
x = float(args[0])
|
||||
return np.array((x, x))
|
||||
elif len(args) == 2:
|
||||
x = float(args[0])
|
||||
y = float(args[1])
|
||||
return np.array((x, y))
|
||||
except:
|
||||
raise Exception(f"cannot parse {args}")
|
||||
return default
|
||||
@ -40,9 +70,9 @@ class ArgParser:
|
||||
y = float(args[1])
|
||||
z = float(args[2])
|
||||
return np.array((x, y, z))
|
||||
return default
|
||||
except:
|
||||
raise Exception(f"cannot parse {args}")
|
||||
return default
|
||||
|
||||
@staticmethod
|
||||
def as_vec4(
|
||||
@ -66,4 +96,4 @@ class ArgParser:
|
||||
def as_literal_color(args: list[str], default=np.array((1.0, 1.0, 1.0, 1.0))):
|
||||
if len(args) == 1 and args[0] in COLORS:
|
||||
return np.array(COLORS[args[0]])
|
||||
return default
|
||||
return default
|
||||
@ -77,7 +77,7 @@ async def get_permission(
|
||||
perm: str,
|
||||
event: Event,
|
||||
):
|
||||
data = await pm.check_has_permission_info(ec, perm)
|
||||
data = await pm.get_permission_info(ec, perm)
|
||||
|
||||
obj_s = f"{ec[0].platform}.{ec[0].entity_type}.{ec[0].external_id}"
|
||||
|
||||
|
||||
@ -41,6 +41,7 @@ bin_path: Path | None = None
|
||||
|
||||
@arti_typst_linux.on_finished
|
||||
async def _(downloaded: bool):
|
||||
logger.debug("安装好了 Linux 版本的 Typst")
|
||||
global bin_path
|
||||
|
||||
tar_path = arti_typst_linux.target
|
||||
@ -71,6 +72,7 @@ async def _(downloaded: bool):
|
||||
|
||||
@arti_typst_windows.on_finished
|
||||
async def _(downloaded: bool):
|
||||
logger.debug("安装好了 Windows 版本的 Typst")
|
||||
global bin_path
|
||||
zip_path = arti_typst_windows.target
|
||||
bin_path = BINARY_PATH / "typst.exe"
|
||||
@ -160,6 +162,7 @@ async def _(
|
||||
# 对于本地机器,一般不会在应用启动时自动下载,这里再保证存在
|
||||
await ensure_artifact(arti_typst_linux)
|
||||
await ensure_artifact(arti_typst_windows)
|
||||
|
||||
if bin_path is None or not bin_path.exists():
|
||||
logger.warning("当前环境不存在 Typst,但仍然调用了")
|
||||
return
|
||||
|
||||
280
poetry.lock
generated
280
poetry.lock
generated
@ -1709,6 +1709,85 @@ type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "glcontext"
|
||||
version = "3.0.0"
|
||||
description = "Portable Headless OpenGL Context"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "glcontext-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b154c25a57e16dbb073478d0cbe2c0090649d135c4c9f87e753b6181b97ec848"},
|
||||
{file = "glcontext-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fa5a14778d13ecf4a0dd60a7825427bf6ac0383eacb3b8b1a9c23e4976aa0c8c"},
|
||||
{file = "glcontext-3.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c229290a3a33004a59799b94a50bc5e6f8addd1b5bc5039ef9e78f86d888b31"},
|
||||
{file = "glcontext-3.0.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1445a03d8795113034e1f9ffa662f795df65ae69ee21b26ed3b1d66100ba3f8c"},
|
||||
{file = "glcontext-3.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:09247011c09c37b8d30eca9aa24659288de2febaeaa6a817b33b1498b5ef164c"},
|
||||
{file = "glcontext-3.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c8c1223f1cbcfc0b88428e1717baca829ee863ed5d88e9b5574c7ed6598249cd"},
|
||||
{file = "glcontext-3.0.0-cp310-cp310-win32.whl", hash = "sha256:c13dedb3636328b133c4d53c047ce69040ae784095e8f239432ad74d6f921712"},
|
||||
{file = "glcontext-3.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:4817f4cd52c7fe5410c92ca12b6712435548918719373882ade76f8f75d80abd"},
|
||||
{file = "glcontext-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3a9e56fa3597cc709cfd0fdf2ae682cda36510a13faac2b3142f401e823b64f4"},
|
||||
{file = "glcontext-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a0484308af75e04b0e56066dc2324a8fb9f1443b76ddb98833439982322b2a39"},
|
||||
{file = "glcontext-3.0.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:983231394396aa2a1e2b96df49404cc8f8aa729d462ed40e605a74b079c46342"},
|
||||
{file = "glcontext-3.0.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fa413f4420abff2bbb5aa5770a3e1deffcdc13e0ef2f459b145fa79c36909e7"},
|
||||
{file = "glcontext-3.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7d0ac35ac07fc91eccea093beb9d1c1a4eae250bc33836047deff01a3b5f4757"},
|
||||
{file = "glcontext-3.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7145d17a70adc5784ca59ebbe19a56435ba21816070b8b433f43aa2dfb8be71a"},
|
||||
{file = "glcontext-3.0.0-cp311-cp311-win32.whl", hash = "sha256:b31808ca2517fedcac8ca5b296ff46c8af012911eaa2080889a1f244d329ef9a"},
|
||||
{file = "glcontext-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:ef4b4ec35e2b720f4cd250bb92cf6417add445490bf780345596da5a796a0e6f"},
|
||||
{file = "glcontext-3.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:848f870a2bc72a29de7ab6756b9e8f2e6ce052e17873ebc6b3f25129b6e0d58a"},
|
||||
{file = "glcontext-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b3b12a66f57379566dd4d36899ac265abdbe040f3fc3293f50cd6678a1dcc9b"},
|
||||
{file = "glcontext-3.0.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:449eaefd89c0519900715b8363ead59ac4aa32457722ca521ce01297441edb34"},
|
||||
{file = "glcontext-3.0.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04921720740438ceea8fb8a38b5665963520c7c8f27bef03df8aeb3ea3cfbfb6"},
|
||||
{file = "glcontext-3.0.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:25538bdb106f673638d70e8a16a0c037a92a24c4cf40a05f0d3fa14b483d6194"},
|
||||
{file = "glcontext-3.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d11f7701b900a5a34c994e1d91c547be1cc469b73f881471460fd905f69f9e4c"},
|
||||
{file = "glcontext-3.0.0-cp312-cp312-win32.whl", hash = "sha256:5d2b567eaf34adb016aadce81fd2f1d4c8e4a39e3d6f2a395ce528e2a350dd3f"},
|
||||
{file = "glcontext-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:e80bb37ba727bd20c192f2754aea40c437a7665005c1001c10752f91913964e9"},
|
||||
{file = "glcontext-3.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5bd37089570d3cdb01c6c0b315c49ce8a4dcdab2c431f5ba9f37a8b633cebfdf"},
|
||||
{file = "glcontext-3.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:857fd83e60f15580afd369dfb651a10d84a70ec35995622d253551bfb3ff9477"},
|
||||
{file = "glcontext-3.0.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93fda9b378ce6d91f366e83e71ebdafdd167280a9834d1d6341ce6457c4e42ed"},
|
||||
{file = "glcontext-3.0.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89ad50d34aa62f03f6aaf6ae39fc27afd1b0eaefb0281aac51f686dc5672d473"},
|
||||
{file = "glcontext-3.0.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2634d5e9647a6d7b0c5a5c0c57e91ac98aa79759bffb42459af4374b049fab01"},
|
||||
{file = "glcontext-3.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0140c5df37cb48271527355062d35589dc3e1e7e73b51adf9962ed5048115f69"},
|
||||
{file = "glcontext-3.0.0-cp313-cp313-win32.whl", hash = "sha256:6678e0552b516fa8fe62f500ef2b953bec991e82a003be2a9840d16556d03d2e"},
|
||||
{file = "glcontext-3.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:18aa4b1df50e8c8ea39bd0f775f39bcc987521f92c4ed019ec7d70078471354d"},
|
||||
{file = "glcontext-3.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:17a1339db9c1df55eb0b7341dd3da1e45c9992d59aa3a72afefd5bd43d588c92"},
|
||||
{file = "glcontext-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9ca980d9ac22045ef2489cac8cf3800748b1baa716f74a53705003405664950c"},
|
||||
{file = "glcontext-3.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5a7fb8ab69fc4f076282622e94284ea4cbf7022a1f6ed50938d076b982f653e"},
|
||||
{file = "glcontext-3.0.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5825e1df53bdf941c3a5ea5ff6d1869491dfeb9f2c30d97f45bbbcb12d91dc1f"},
|
||||
{file = "glcontext-3.0.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f3be86fa6587eca16f3fe2b46ee72e2a188fdccedafff0de7515b1d5e72f265e"},
|
||||
{file = "glcontext-3.0.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b4a06207c487f0aa79e49bf1c19d0f2633ff1e1889704196993d24b342763344"},
|
||||
{file = "glcontext-3.0.0-cp38-cp38-win32.whl", hash = "sha256:3eb55b653fc00a4ec415acacbbf1e8f03ee10b5a685f63fce43ad75b4cef5d4e"},
|
||||
{file = "glcontext-3.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:7e51f04bb3a3a12147106036676a237c5405297a95b8209f7686d624f013bc17"},
|
||||
{file = "glcontext-3.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7043f59d126feb26896a6419ebfeecc78c07ffefced2a2f59104dd7a2f71ebb5"},
|
||||
{file = "glcontext-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4a2972f92da9a6fb06860e117de05f5b8adc2e2d827bbc0ccc7acbe7325acd1e"},
|
||||
{file = "glcontext-3.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb2eb1c455d589005567b36642db8059b31bb1752f0525c6dbe70ceeeb0131d5"},
|
||||
{file = "glcontext-3.0.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdd81e8b580e43c1fe1c48c0fc3909e6c1f37ee4cfd8c990c03b2df3b463bd37"},
|
||||
{file = "glcontext-3.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:64eb425f0f0c54c60527e1b112465d4d69b010af42a3b0e69f22317fffd8faea"},
|
||||
{file = "glcontext-3.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:def2b9956fbd3da94b4cadeb85d947a6321582ddfea2e58c4134178bfabce0f8"},
|
||||
{file = "glcontext-3.0.0-cp39-cp39-win32.whl", hash = "sha256:cfdc763adffcd20509b9e6ac964a9abf7b2a898bb32d7bd4efce9db8af2caaf5"},
|
||||
{file = "glcontext-3.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:12c2abef8efabb8ab7e35d16785968de888ae7349d1b83c765080f35fcd3c6e5"},
|
||||
{file = "glcontext-3.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f4e285e5d40e7a9bafeb0651c3d8f1a7c822525dec7091e97109ba87a70dbd1e"},
|
||||
{file = "glcontext-3.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:96d1bbe62c5bc5315ca2f84a2abae6fa7b7d645dd368415a0cd1ee5ba6f3f8f7"},
|
||||
{file = "glcontext-3.0.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a190b1cdb39110c7b56c33857dbff493a364633bfd0ff402a1ce205956c94ca"},
|
||||
{file = "glcontext-3.0.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d467cce2dac8c3928847e90310eb6bdfdfa69f8df39b76a74734046faa15e688"},
|
||||
{file = "glcontext-3.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2b0c5240125d75498a8f14596484233e4effe98a6a035f566872dd2fdf472ceb"},
|
||||
{file = "glcontext-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d89a6bcf0129f27594c07eb9aafc33389e9dd66f344fe1e255fe297bbc123317"},
|
||||
{file = "glcontext-3.0.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:d9b0bd64b01be0ecad521ca4869153893ed10f8c9043dcd8d1a81c8f686008c9"},
|
||||
{file = "glcontext-3.0.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ba5af7ca9309bb42b89cf25f576cc28ae36671be01ecdfce264308a007880ac"},
|
||||
{file = "glcontext-3.0.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb413b6bf3b2ded2e5bf4235b75eb9ac9d36361af38393c53c689dfcc096eba"},
|
||||
{file = "glcontext-3.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:dfea7fc7b22afce49027d8470a84f9c7c6f06a09b43f6606030b53b3240df0d1"},
|
||||
{file = "glcontext-3.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cf5af894228b4357b088a6e26761438d799c2af907e33f17935072fe46903c2d"},
|
||||
{file = "glcontext-3.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1712d7a7216b687b181291098e5117e5fae1f1df466583b4290dc2e80d9a72c8"},
|
||||
{file = "glcontext-3.0.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07980350ba2aac9f793185f90c4f561ae1aa03cc11b58cce4b51e20a70e8c6e3"},
|
||||
{file = "glcontext-3.0.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed5135fdb0da5e0decea1cb26ca10a279188aa0bc4462f1c77e214d6f956a710"},
|
||||
{file = "glcontext-3.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c3648e13478d77128a74dd25baa98faf9ddb9cbcba5af39775ef3a496f71fd10"},
|
||||
{file = "glcontext-3.0.0.tar.gz", hash = "sha256:57168edcd38df2fc0d70c318edf6f7e59091fba1cd3dadb289d0aa50449211ef"},
|
||||
]
|
||||
|
||||
[package.source]
|
||||
type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "3.4.0"
|
||||
@ -2534,6 +2613,76 @@ type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "moderngl"
|
||||
version = "5.12.0"
|
||||
description = "ModernGL: High performance rendering for Python 3"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "moderngl-5.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:105cdee2ee29a7e6ebbc76cf178941503656a185c6945933bc7ef395ba8e65a7"},
|
||||
{file = "moderngl-5.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:18deb8bebd0a4277d92c76dbedf8e4b4b68bf0a8a878404c6b26aed750890d3b"},
|
||||
{file = "moderngl-5.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f0c4f7c42425177168938386a4fabd734ca3bbb5de2d1fd1176cfa6f980fc13"},
|
||||
{file = "moderngl-5.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:878cdf593204d85c020305f21d306f979353a67c932b9df58ea936f6e5ad13e7"},
|
||||
{file = "moderngl-5.12.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:00d94f9cb485d87c85088edad624201e152d8ac401793a024b16cd9e2bc4dbf6"},
|
||||
{file = "moderngl-5.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:edd91057b8d76beebac0d7b0c741466ee8f37eaf3c07856785c2872afe0b35ac"},
|
||||
{file = "moderngl-5.12.0-cp310-cp310-win32.whl", hash = "sha256:3d066eae2eb44e81bd7addf565adebc041bdee119e7ac6e4f95831d6f327a938"},
|
||||
{file = "moderngl-5.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:76d966194d51852f48c42a6e786a4520f1e1be5f93e2626423d673663422d559"},
|
||||
{file = "moderngl-5.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28cdba5dcf2d03c89bb25dc3b2f5770ac4104470ed5bbe680a15494fa52a537d"},
|
||||
{file = "moderngl-5.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dad93893e3fcb2410bfd31e854f20e1370b4fbafa07a737f1046f5fbd29ba0f4"},
|
||||
{file = "moderngl-5.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fc0f8788bc84433d2124e9a4893adbe40f93c7d213abb8ad7b909540cb0161f"},
|
||||
{file = "moderngl-5.12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6efd3fe0d2c9652af21e2c1f5a936a2b971abac5bdd777da7182a54962466cab"},
|
||||
{file = "moderngl-5.12.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6f3bd2d534fc081cde30545b84ebca63aef847ba8bd533217b9a37f565614ade"},
|
||||
{file = "moderngl-5.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:eaa3de9446c6febec4d5f888e6f1a4e9398bc5a5ea70b1570ea447213641d4a6"},
|
||||
{file = "moderngl-5.12.0-cp311-cp311-win32.whl", hash = "sha256:9fdb76f1fd890db67727c8cdee4db2ee6319068c7ce92be0308366f8745e28ab"},
|
||||
{file = "moderngl-5.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:0c210e8d52a60025f6586ca015c39feb1e57e6dc792c3ff44800f6493a541b1a"},
|
||||
{file = "moderngl-5.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2750547707c1ec3790dfbeb9c90fb808672ff13f61cac392c706ba09fda10db0"},
|
||||
{file = "moderngl-5.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5c2a5fe06c7021183d9274df798f25516409c8d55898c324dae8a0b2de10144"},
|
||||
{file = "moderngl-5.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6c4972f3ddd10a3de6c30311da2c25bc493d023796e16c5d4e0f8bd6d5770be"},
|
||||
{file = "moderngl-5.12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d497ec6a3f6afa9ebd0be816d9bfe2fe20fec2105acfb88d956619c3ed8eb4"},
|
||||
{file = "moderngl-5.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2f3d240e9bc5d83257378bae59f8f35638b89d22bb003cf674b88fd7932161ce"},
|
||||
{file = "moderngl-5.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6fa667d560d842e778e2a5968305fb78f9781616a11b1b93acd2562f97262ccf"},
|
||||
{file = "moderngl-5.12.0-cp312-cp312-win32.whl", hash = "sha256:0a02fddd54dccee1ca6060bfed75a2e6a17dd3ee06920fac418506d8a8233849"},
|
||||
{file = "moderngl-5.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:8698a59ad03539a2982125b7998efc1c107ba31d5d03437b6fcd72cb2c226922"},
|
||||
{file = "moderngl-5.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f6efb432f5164f871471d1da36e3a4be9dc3efd7a1e48d0ac6b751e556af5d02"},
|
||||
{file = "moderngl-5.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9b09d8d15b2eaab41c8646a664429ec86af225fa25096758497cd212489d2e1e"},
|
||||
{file = "moderngl-5.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:071042dd4846e58cbe204cf49341b62cd209fdcb6d48018feb5a61c66707fcb2"},
|
||||
{file = "moderngl-5.12.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91db8302ac7f5d7a82a967388677e1378ff078f1e16d05da37ce77f4633b93b1"},
|
||||
{file = "moderngl-5.12.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:51971d65ec96a212a814350c8b324ae0754353e1b61826d1a06aa2d060df170e"},
|
||||
{file = "moderngl-5.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d56827360c19e831e986243b5daaf6a51006f1ec0d5372084ad446308763d19f"},
|
||||
{file = "moderngl-5.12.0-cp313-cp313-win32.whl", hash = "sha256:caa432c12b138a6c9571719075c4d103bdc2504cd31aeda38a00ad10fcf268cb"},
|
||||
{file = "moderngl-5.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:e34d1cd38f7998258f76a08bb5e87f351ec653b7ea1928b2711f8719c10cefd1"},
|
||||
{file = "moderngl-5.12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b0712fcce0ebbee962f5e93628118aedcb568d56b5c59f2e9aac43ea57190219"},
|
||||
{file = "moderngl-5.12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:861aae4a38da0f5d82dc2d5ece0f0a6d799809c362343cd1a447ab840a68370f"},
|
||||
{file = "moderngl-5.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:726a025ab9822c295369a9ddb1bfaf4930f9645b7a958b74dfcd6a969d7052cf"},
|
||||
{file = "moderngl-5.12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29a181bae8bde003016fee671b93c2faa3e1460033033e2a832ec9187aa73efb"},
|
||||
{file = "moderngl-5.12.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:8b35c17d5497f19c524068f9337cbe5e0e0e5662150b12fa95618665130bbf16"},
|
||||
{file = "moderngl-5.12.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:74e5b7df5614f3291d197139a888c967aa29c348e13ebd28ce2a55bf03baed3d"},
|
||||
{file = "moderngl-5.12.0-cp38-cp38-win32.whl", hash = "sha256:8dc1bacc24840e5bc562e79be65dc506d6c5a7d40ecac01a062f86d013c890af"},
|
||||
{file = "moderngl-5.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:cbd822cf3707fe955cfd940ec68b900519e2c43a5ef8085de5b0c983b4142c8b"},
|
||||
{file = "moderngl-5.12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:49a6e27abafacef104c7ca336f6790f91c69617a1d752ead4a017b706d632b55"},
|
||||
{file = "moderngl-5.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7146c6fec3b2d7e8d11fa2504046b186d396520c0c2f7ef3aed40d8456dbebfc"},
|
||||
{file = "moderngl-5.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28b9eb3574ffc6e303173ca0a419b63d8b12cd67f924289c02d127c4d17cdca5"},
|
||||
{file = "moderngl-5.12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6948237626f9f0d9f931faa3b123d53613d5723679bc70b8db2590924795203f"},
|
||||
{file = "moderngl-5.12.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3c6b4342b7508d75744f1091868cf184cae0be85d37be858fba32eb20d799695"},
|
||||
{file = "moderngl-5.12.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:13fec30855d346c4e69eff437e56f2bdd9953d22e80b7c5a319bccac7024e463"},
|
||||
{file = "moderngl-5.12.0-cp39-cp39-win32.whl", hash = "sha256:878f249505785cde8cc39d6016e62e74b46acbf3bb6d5a86341c86a7da7e7531"},
|
||||
{file = "moderngl-5.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:e801cd0d35b4e3e99fc6a6f15eb193ce907bfa78127afa5825f1fad24c700c0e"},
|
||||
{file = "moderngl-5.12.0.tar.gz", hash = "sha256:52936a98ccb2f2e1d6e3cb18528b2919f6831e7e3f924e788b5873badce5129b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
glcontext = ">=3.0.0"
|
||||
|
||||
[package.extras]
|
||||
headless = ["glcontext (>=3.0.0)"]
|
||||
|
||||
[package.source]
|
||||
type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "msgpack"
|
||||
version = "1.1.2"
|
||||
@ -4251,6 +4400,135 @@ type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "2026.4.4"
|
||||
description = "Alternative regular expression module, to replace re."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "regex-2026.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:74fa82dcc8143386c7c0392e18032009d1db715c25f4ba22d23dc2e04d02a20f"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a85b620a388d6c9caa12189233109e236b3da3deffe4ff11b84ae84e218a274f"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2895506ebe32cc63eeed8f80e6eae453171cfccccab35b70dc3129abec35a5b8"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6780f008ee81381c737634e75c24e5a6569cc883c4f8e37a37917ee79efcafd9"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:88e9b048345c613f253bea4645b2fe7e579782b82cac99b1daad81e29cc2ed8e"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:be061028481186ba62a0f4c5f1cc1e3d5ab8bce70c89236ebe01023883bc903b"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d2228c02b368d69b724c36e96d3d1da721561fb9cc7faa373d7bf65e07d75cb5"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0540e5b733618a2f84e9cb3e812c8afa82e151ca8e19cf6c4e95c5a65198236f"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cf9b1b2e692d4877880388934ac746c99552ce6bf40792a767fd42c8c99f136d"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:011bb48bffc1b46553ac704c975b3348717f4e4aa7a67522b51906f99da1820c"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8512fcdb43f1bf18582698a478b5ab73f9c1667a5b7548761329ef410cd0a760"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:867bddc63109a0276f5a31999e4c8e0eb7bbbad7d6166e28d969a2c1afeb97f9"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1b9a00b83f3a40e09859c78920571dcb83293c8004079653dd22ec14bbfa98c7"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e355be718caf838aa089870259cf1776dc2a4aa980514af9d02c59544d9a8b22"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-win32.whl", hash = "sha256:33bfda9684646d323414df7abe5692c61d297dbb0530b28ec66442e768813c59"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:0709f22a56798457ae317bcce42aacee33c680068a8f14097430d9f9ba364bee"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:ee9627de8587c1a22201cb16d0296ab92b4df5cdcb5349f4e9744d61db7c7c98"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4c36a85b00fadb85db9d9e90144af0a980e1a3d2ef9cd0f8a5bef88054657c6"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dcb5453ecf9cd58b562967badd1edbf092b0588a3af9e32ee3d05c985077ce87"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6aa809ed4dc3706cc38594d67e641601bd2f36d5555b2780ff074edfcb136cf8"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33424f5188a7db12958246a54f59a435b6cb62c5cf9c8d71f7cc49475a5fdada"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d346fccdde28abba117cc9edc696b9518c3307fbfcb689e549d9b5979018c6d"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:415a994b536440f5011aa77e50a4274d15da3245e876e5c7f19da349caaedd87"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:21e5eb86179b4c67b5759d452ea7c48eb135cd93308e7a260aa489ed2eb423a4"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:312ec9dd1ae7d96abd8c5a36a552b2139931914407d26fba723f9e53c8186f86"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0d2b28aa1354c7cd7f71b7658c4326f7facac106edd7f40eda984424229fd59"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:349d7310eddff40429a099c08d995c6d4a4bfaf3ff40bd3b5e5cb5a5a3c7d453"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:e7ab63e9fe45a9ec3417509e18116b367e89c9ceb6219222a3396fa30b147f80"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fe896e07a5a2462308297e515c0054e9ec2dd18dfdc9427b19900b37dfe6f40b"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:eb59c65069498dbae3c0ef07bbe224e1eaa079825a437fb47a479f0af11f774f"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-win32.whl", hash = "sha256:2a5d273181b560ef8397c8825f2b9d57013de744da9e8257b8467e5da8599351"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:9542ccc1e689e752594309444081582f7be2fdb2df75acafea8a075108566735"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:b5f9fb784824a042be3455b53d0b112655686fdb7a91f88f095f3fee1e2a2a54"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c07ab8794fa929e58d97a0e1796b8b76f70943fa39df225ac9964615cf1f9d52"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2c785939dc023a1ce4ec09599c032cc9933d258a998d16ca6f2b596c010940eb"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b1ce5c81c9114f1ce2f9288a51a8fd3aeea33a0cc440c415bf02da323aa0a76"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:760ef21c17d8e6a4fe8cf406a97cf2806a4df93416ccc82fc98d25b1c20425be"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7088fcdcb604a4417c208e2169715800d28838fefd7455fbe40416231d1d47c1"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:07edca1ba687998968f7db5bc355288d0c6505caa7374f013d27356d93976d13"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f657a7c1c6ec51b5e0ba97c9817d06b84ea5fa8d82e43b9405de0defdc2b9"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2b69102a743e7569ebee67e634a69c4cb7e59d6fa2e1aa7d3bdbf3f61435f62d"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dac006c8b6dda72d86ea3d1333d45147de79a3a3f26f10c1cf9287ca4ca0ac3"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:50a766ee2010d504554bfb5f578ed2e066898aa26411d57e6296230627cdefa0"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9e2f5217648f68e3028c823df58663587c1507a5ba8419f4fdfc8a461be76043"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39d8de85a08e32632974151ba59c6e9140646dcc36c80423962b1c5c0a92e244"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55d9304e0e7178dfb1e106c33edf834097ddf4a890e2f676f6c5118f84390f73"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-win32.whl", hash = "sha256:04bb679bc0bde8a7bfb71e991493d47314e7b98380b083df2447cda4b6edb60f"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:db0ac18435a40a2543dbb3d21e161a6c78e33e8159bd2e009343d224bb03bb1b"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:4ce255cc05c1947a12989c6db801c96461947adb7a59990f1360b5983fab4983"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:62f5519042c101762509b1d717b45a69c0139d60414b3c604b81328c01bd1943"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3790ba9fb5dd76715a7afe34dbe603ba03f8820764b1dc929dd08106214ed031"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8fae3c6e795d7678963f2170152b0d892cf6aee9ee8afc8c45e6be38d5107fe7"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:298c3ec2d53225b3bf91142eb9691025bab610e0c0c51592dde149db679b3d17"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e9638791082eaf5b3ac112c587518ee78e083a11c4b28012d8fe2a0f536dfb17"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae3e764bd4c5ff55035dc82a8d49acceb42a5298edf6eb2fc4d328ee5dd7afae"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffa81f81b80047ba89a3c69ae6a0f78d06f4a42ce5126b0eb2a0a10ad44e0b2e"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f56ebf9d70305307a707911b88469213630aba821e77de7d603f9d2f0730687d"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:773d1dfd652bbffb09336abf890bfd64785c7463716bf766d0eb3bc19c8b7f27"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d51d20befd5275d092cdffba57ded05f3c436317ee56466c8928ac32d960edaf"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:0a51cdb3c1e9161154f976cb2bef9894bc063ac82f31b733087ffb8e880137d0"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ae5266a82596114e41fb5302140e9630204c1b5f325c770bec654b95dd54b0aa"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c882cd92ec68585e9c1cf36c447ec846c0d94edd706fe59e0c198e65822fd23b"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-win32.whl", hash = "sha256:05568c4fbf3cb4fa9e28e3af198c40d3237cf6041608a9022285fe567ec3ad62"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:3384df51ed52db0bea967e21458ab0a414f67cdddfd94401688274e55147bb81"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:acd38177bd2c8e69a411d6521760806042e244d0ef94e2dd03ecdaa8a3c99427"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f94a11a9d05afcfcfa640e096319720a19cc0c9f7768e1a61fceee6a3afc6c7c"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:36bcb9d6d1307ab629edc553775baada2aefa5c50ccc0215fbfd2afcfff43141"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261c015b3e2ed0919157046d768774ecde57f03d8fa4ba78d29793447f70e717"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c228cf65b4a54583763645dcd73819b3b381ca8b4bb1b349dee1c135f4112c07"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dd2630faeb6876fb0c287f664d93ddce4d50cd46c6e88e60378c05c9047e08ca"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a50ab11b7779b849472337191f3a043e27e17f71555f98d0092fa6d73364520"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0734f63afe785138549fbe822a8cfeaccd1bae814c5057cc0ed5b9f2de4fc883"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4ee50606cb1967db7e523224e05f32089101945f859928e65657a2cbb3d278b"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6c1818f37be3ca02dcb76d63f2c7aaba4b0dc171b579796c6fbe00148dfec6b1"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f5bfc2741d150d0be3e4a0401a5c22b06e60acb9aa4daa46d9e79a6dcd0f135b"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:504ffa8a03609a087cad81277a629b6ce884b51a24bd388a7980ad61748618ff"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:70aadc6ff12e4b444586e57fc30771f86253f9f0045b29016b9605b4be5f7dfb"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f4f83781191007b6ef43b03debc35435f10cad9b96e16d147efe84a1d48bdde4"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-win32.whl", hash = "sha256:e014a797de43d1847df957c0a2a8e861d1c17547ee08467d1db2c370b7568baa"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:b15b88b0d52b179712632832c1d6e58e5774f93717849a41096880442da41ab0"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:586b89cdadf7d67bf86ae3342a4dcd2b8d70a832d90c18a0ae955105caf34dbe"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2da82d643fa698e5e5210e54af90181603d5853cf469f5eedf9bfc8f59b4b8c7"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:54a1189ad9d9357760557c91103d5e421f0a2dabe68a5cdf9103d0dcf4e00752"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:76d67d5afb1fe402d10a6403bae668d000441e2ab115191a804287d53b772951"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e7cd3e4ee8d80447a83bbc9ab0c8459781fa77087f856c3e740d7763be0df27f"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e19e18c568d2866d8b6a6dfad823db86193503f90823a8f66689315ba28fbe8"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7698a6f38730fd1385d390d1ed07bb13dce39aa616aca6a6d89bea178464b9a4"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:173a66f3651cdb761018078e2d9487f4cf971232c990035ec0eb1cdc6bf929a9"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa7922bbb2cc84fa062d37723f199d4c0cd200245ce269c05db82d904db66b83"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:59f67cd0a0acaf0e564c20bbd7f767286f23e91e2572c5703bf3e56ea7557edb"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:475e50f3f73f73614f7cba5524d6de49dee269df00272a1b85e3d19f6d498465"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a1c0c7d67b64d85ac2e1879923bad2f08a08f3004055f2f406ef73c850114bd4"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:1371c2ccbb744d66ee63631cc9ca12aa233d5749972626b68fe1a649dd98e566"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59968142787042db793348a3f5b918cf24ced1f23247328530e063f89c128a95"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-win32.whl", hash = "sha256:59efe72d37fd5a91e373e5146f187f921f365f4abc1249a5ab446a60f30dd5f8"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:e0aab3ff447845049d676827d2ff714aab4f73f340e155b7de7458cf53baa5a4"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:a7a5bb6aa0cf62208bb4fa079b0c756734f8ad0e333b425732e8609bd51ee22f"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:97850d0638391bdc7d35dc1c1039974dcb921eaafa8cc935ae4d7f272b1d60b3"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ee7337f88f2a580679f7bbfe69dc86c043954f9f9c541012f49abc554a962f2e"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7429f4e6192c11d659900c0648ba8776243bf396ab95558b8c51a345afeddde6"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4f10fbd5dd13dcf4265b4cc07d69ca70280742870c97ae10093e3d66000359"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a152560af4f9742b96f3827090f866eeec5becd4765c8e0d3473d9d280e76a5a"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54170b3e95339f415d54651f97df3bff7434a663912f9358237941bbf9143f55"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:07f190d65f5a72dcb9cf7106bfc3d21e7a49dd2879eda2207b683f32165e4d99"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9a2741ce5a29d3c84b0b94261ba630ab459a1b847a0d6beca7d62d188175c790"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b26c30df3a28fd9793113dac7385a4deb7294a06c0f760dd2b008bd49a9139bc"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:421439d1bee44b19f4583ccf42670ca464ffb90e9fdc38d37f39d1ddd1e44f1f"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b40379b53ecbc747fd9bdf4a0ea14eb8188ca1bd0f54f78893a39024b28f4863"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:08c55c13d2eef54f73eeadc33146fb0baaa49e7335eb1aff6ae1324bf0ddbe4a"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9776b85f510062f5a75ef112afe5f494ef1635607bf1cc220c1391e9ac2f5e81"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-win32.whl", hash = "sha256:385edaebde5db5be103577afc8699fea73a0e36a734ba24870be7ffa61119d74"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:5d354b18839328927832e2fa5f7c95b7a3ccc39e7a681529e1685898e6436d45"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:af0384cb01a33600c49505c27c6c57ab0b27bf84a74e28524c92ca897ebdac9d"},
|
||||
{file = "regex-2026.4.4.tar.gz", hash = "sha256:e08270659717f6973523ce3afbafa53515c4dc5dcad637dc215b6fd50f689423"},
|
||||
]
|
||||
|
||||
[package.source]
|
||||
type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.33.1"
|
||||
@ -5376,4 +5654,4 @@ reference = "mirrors"
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.12,<4.0"
|
||||
content-hash = "cb843d7ddb9458a0c0fd43f254b0362a05c0a017cf1df1d855d34caa98f4d10b"
|
||||
content-hash = "91e6623a2ea57cf1189d99cfc085abb3e5fe46f7fd1d68390292ab8d5f0fa304"
|
||||
|
||||
@ -39,6 +39,8 @@ dependencies = [
|
||||
"aiosignal (>=1.4.0,<2.0.0)",
|
||||
"pytest-mock (>=3.15.1,<4.0.0)",
|
||||
"skia-python (>=144.0.post2,<145.0)",
|
||||
"moderngl (>=5.12.0,<6.0.0)",
|
||||
"regex (>=2026.4.4,<2027.0.0)",
|
||||
]
|
||||
|
||||
[tool.poetry]
|
||||
|
||||
Reference in New Issue
Block a user