mirror of
https://github.com/TriM-Organization/LiteyukiBot-TriM.git
synced 2024-11-25 16:45:03 +08:00
420 lines
12 KiB
Python
420 lines
12 KiB
Python
import json
|
||
from typing import List, Any
|
||
|
||
from PIL import Image
|
||
from arclet.alconna import Alconna
|
||
from nb_cli import run_sync
|
||
from nonebot import on_command
|
||
from nonebot_plugin_alconna import on_alconna, Alconna, Subcommand, Args, MultiVar, Arparma, UniMessage
|
||
from pydantic import BaseModel
|
||
|
||
from .canvas import *
|
||
from ...utils.base.resource import get_path
|
||
|
||
resolution = 256
|
||
|
||
|
||
class Entrance(BaseModel):
|
||
identifier: str
|
||
size: tuple[int, int]
|
||
dest: List[str]
|
||
|
||
|
||
class Station(BaseModel):
|
||
identifier: str
|
||
chineseName: str
|
||
englishName: str
|
||
position: tuple[int, int]
|
||
|
||
|
||
class Line(BaseModel):
|
||
identifier: str
|
||
chineseName: str
|
||
englishName: str
|
||
color: Any
|
||
stations: List["Station"]
|
||
|
||
|
||
font_light = get_path("templates/fonts/MiSans/MiSans-Light.woff2")
|
||
font_bold = get_path("templates/fonts/MiSans/MiSans-Bold.woff2")
|
||
|
||
@run_sync
|
||
def generate_entrance_sign(name: str, aliases: List[str], lineInfo: List[Line], entranceIdentifier: str, ratio: tuple[int | float, int | float],
|
||
reso: int = resolution):
|
||
"""
|
||
Generates an entrance sign for the ride.
|
||
"""
|
||
width, height = ratio[0] * reso, ratio[1] * reso
|
||
baseCanvas = Canvas(Image.new("RGBA", (width, height), Color.WHITE))
|
||
# 加黑色图框
|
||
baseCanvas.outline = Img(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 1),
|
||
parent_point=(0, 0),
|
||
point=(0, 0),
|
||
img=Shape.rectangle(
|
||
size=(width, height),
|
||
fillet=0,
|
||
fill=(0, 0, 0, 0),
|
||
width=15,
|
||
outline=Color.BLACK
|
||
)
|
||
)
|
||
|
||
baseCanvas.contentPanel = Panel(
|
||
uv_size=(width, height),
|
||
box_size=(width - 28, height - 28),
|
||
parent_point=(0.5, 0.5),
|
||
point=(0.5, 0.5),
|
||
)
|
||
|
||
linePanelHeight = 0.7 * ratio[1]
|
||
linePanelWidth = linePanelHeight * 1.3
|
||
|
||
# 画线路面板部分
|
||
|
||
for i, line in enumerate(lineInfo):
|
||
linePanel = baseCanvas.contentPanel.__dict__[f"Line_{i}_Panel"] = Panel(
|
||
uv_size=ratio,
|
||
box_size=(linePanelWidth, linePanelHeight),
|
||
parent_point=(i * linePanelWidth / ratio[0], 1),
|
||
point=(0, 1),
|
||
)
|
||
|
||
linePanel.colorCube = Img(
|
||
uv_size=(1, 1),
|
||
box_size=(0.15, 1),
|
||
parent_point=(0.125, 1),
|
||
point=(0, 1),
|
||
img=Shape.rectangle(
|
||
size=(100, 100),
|
||
fillet=0,
|
||
fill=line.color,
|
||
),
|
||
keep_ratio=False
|
||
)
|
||
|
||
textPanel = linePanel.TextPanel = Panel(
|
||
uv_size=(1, 1),
|
||
box_size=(0.625, 1),
|
||
parent_point=(1, 1),
|
||
point=(1, 1)
|
||
)
|
||
|
||
# 中文线路名
|
||
textPanel.namePanel = Panel(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 2 / 3),
|
||
parent_point=(0, 0),
|
||
point=(0, 0),
|
||
)
|
||
nameSize = baseCanvas.get_actual_pixel_size("contentPanel.Line_{}_Panel.TextPanel.namePanel".format(i))
|
||
textPanel.namePanel.text = Text(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 1),
|
||
parent_point=(0.5, 0.5),
|
||
point=(0.5, 0.5),
|
||
text=line.chineseName,
|
||
color=Color.BLACK,
|
||
font_size=int(nameSize[1] * 0.5),
|
||
force_size=True,
|
||
font=font_bold
|
||
|
||
)
|
||
|
||
# 英文线路名
|
||
textPanel.englishNamePanel = Panel(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 1 / 3),
|
||
parent_point=(0, 1),
|
||
point=(0, 1),
|
||
)
|
||
englishNameSize = baseCanvas.get_actual_pixel_size("contentPanel.Line_{}_Panel.TextPanel.englishNamePanel".format(i))
|
||
textPanel.englishNamePanel.text = Text(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 1),
|
||
parent_point=(0.5, 0.5),
|
||
point=(0.5, 0.5),
|
||
text=line.englishName,
|
||
color=Color.BLACK,
|
||
font_size=int(englishNameSize[1] * 0.6),
|
||
force_size=True,
|
||
font=font_light
|
||
)
|
||
|
||
# 画名称部分
|
||
namePanel = baseCanvas.contentPanel.namePanel = Panel(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 0.4),
|
||
parent_point=(0.5, 0),
|
||
point=(0.5, 0),
|
||
)
|
||
|
||
namePanel.text = Text(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 1),
|
||
parent_point=(0.5, 0.5),
|
||
point=(0.5, 0.5),
|
||
text=name,
|
||
color=Color.BLACK,
|
||
font_size=int(height * 0.3),
|
||
force_size=True,
|
||
font=font_bold
|
||
)
|
||
|
||
aliasesPanel = baseCanvas.contentPanel.aliasesPanel = Panel(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 0.5),
|
||
parent_point=(0.5, 1),
|
||
point=(0.5, 1),
|
||
|
||
)
|
||
for j, alias in enumerate(aliases):
|
||
aliasesPanel.__dict__[alias] = Text(
|
||
uv_size=(1, 1),
|
||
box_size=(0.35, 0.5),
|
||
parent_point=(0.5, 0.5 * j),
|
||
point=(0.5, 0),
|
||
text=alias,
|
||
color=Color.BLACK,
|
||
font_size=int(height * 0.15),
|
||
font=font_light
|
||
)
|
||
|
||
# 画入口标识
|
||
entrancePanel = baseCanvas.contentPanel.entrancePanel = Panel(
|
||
uv_size=(1, 1),
|
||
box_size=(0.2, 1),
|
||
parent_point=(1, 0.5),
|
||
point=(1, 0.5),
|
||
)
|
||
# 中文文本
|
||
entrancePanel.namePanel = Panel(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 0.5),
|
||
parent_point=(1, 0),
|
||
point=(1, 0),
|
||
)
|
||
entrancePanel.namePanel.text = Text(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 1),
|
||
parent_point=(0, 0.5),
|
||
point=(0, 0.5),
|
||
text=f"{entranceIdentifier}出入口",
|
||
color=Color.BLACK,
|
||
font_size=int(height * 0.2),
|
||
force_size=True,
|
||
font=font_bold
|
||
)
|
||
# 英文文本
|
||
entrancePanel.englishNamePanel = Panel(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 0.5),
|
||
parent_point=(1, 1),
|
||
point=(1, 1),
|
||
)
|
||
entrancePanel.englishNamePanel.text = Text(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 1),
|
||
parent_point=(0, 0.5),
|
||
point=(0, 0.5),
|
||
text=f"Entrance {entranceIdentifier}",
|
||
color=Color.BLACK,
|
||
font_size=int(height * 0.15),
|
||
force_size=True,
|
||
font=font_light
|
||
)
|
||
|
||
return baseCanvas.base_img.tobytes()
|
||
|
||
|
||
crt_alc = on_alconna(
|
||
Alconna(
|
||
"crt",
|
||
Subcommand(
|
||
"entrance",
|
||
Args["name", str]["lines", str, ""]["entrance", int, 1], # /crt entrance 璧山&Bishan 1号线&Line1&#ff0000,27号线&Line1&#ff0000 1A
|
||
)
|
||
)
|
||
)
|
||
|
||
|
||
@crt_alc.assign("entrance")
|
||
async def _(result: Arparma):
|
||
args = result.subcommands.get("entrance").args
|
||
name = args["name"]
|
||
lines = args["lines"]
|
||
entrance = args["entrance"]
|
||
line_info = []
|
||
for line in lines.split(","):
|
||
line_args = line.split("&")
|
||
line_info.append(Line(
|
||
identifier=1,
|
||
chineseName=line_args[0],
|
||
englishName=line_args[1],
|
||
color=line_args[2],
|
||
stations=[]
|
||
))
|
||
img_bytes = await generate_entrance_sign(
|
||
name=name,
|
||
aliases=name.split("&"),
|
||
lineInfo=line_info,
|
||
entranceIdentifier=entrance,
|
||
ratio=(8, 1),
|
||
reso=256,
|
||
)
|
||
await crt_alc.finish(
|
||
UniMessage.image(raw=img_bytes)
|
||
)
|
||
|
||
|
||
def generate_platform_line_pic(line: Line, station: Station, ratio=None, reso: int = resolution):
|
||
"""
|
||
生成站台线路图
|
||
:param line: 线路对象
|
||
:param station: 本站点对象
|
||
:param ratio: 比例
|
||
:param reso: 分辨率,1:reso
|
||
:return: 两个方向的站牌
|
||
"""
|
||
if ratio is None:
|
||
ratio = [4, 1]
|
||
width, height = ratio[0] * reso, ratio[1] * reso
|
||
baseCanvas = Canvas(Image.new("RGBA", (width, height), Color.YELLOW))
|
||
# 加黑色图框
|
||
baseCanvas.linePanel = Panel(
|
||
uv_size=(1, 1),
|
||
box_size=(0.8, 0.15),
|
||
parent_point=(0.5, 0.5),
|
||
point=(0.5, 0.5),
|
||
)
|
||
|
||
# 直线块
|
||
baseCanvas.linePanel.recLine = Img(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 1),
|
||
parent_point=(0.5, 0.5),
|
||
point=(0.5, 0.5),
|
||
img=Shape.rectangle(
|
||
size=(10, 10),
|
||
fill=line.color,
|
||
),
|
||
keep_ratio=False
|
||
)
|
||
# 灰色直线块
|
||
baseCanvas.linePanel.recLineGrey = Img(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 1),
|
||
parent_point=(0.5, 0.5),
|
||
point=(0.5, 0.5),
|
||
img=Shape.rectangle(
|
||
size=(10, 10),
|
||
fill=Color.GREY,
|
||
),
|
||
keep_ratio=False
|
||
)
|
||
# 生成各站圆点
|
||
outline_width = 40
|
||
circleForward = Shape.circular(
|
||
radius=200,
|
||
fill=Color.WHITE,
|
||
width=outline_width,
|
||
outline=line.color,
|
||
)
|
||
|
||
circleThisPanel = Canvas(Image.new("RGBA", (200, 200), (0, 0, 0, 0)))
|
||
circleThisPanel.circleOuter = Img(
|
||
uv_size=(1, 1),
|
||
box_size=(1, 1),
|
||
parent_point=(0.5, 0.5),
|
||
point=(0.5, 0.5),
|
||
img=Shape.circular(
|
||
radius=200,
|
||
fill=Color.WHITE,
|
||
width=outline_width,
|
||
outline=line.color,
|
||
),
|
||
)
|
||
circleThisPanel.circleOuter.circleInner = Img(
|
||
uv_size=(1, 1),
|
||
box_size=(0.7, 0.7),
|
||
parent_point=(0.5, 0.5),
|
||
point=(0.5, 0.5),
|
||
img=Shape.circular(
|
||
radius=200,
|
||
fill=line.color,
|
||
width=0,
|
||
outline=line.color,
|
||
),
|
||
)
|
||
|
||
circleThisPanel.export("a.png", alpha=True)
|
||
circleThis = circleThisPanel.base_img
|
||
|
||
circlePassed = Shape.circular(
|
||
radius=200,
|
||
fill=Color.WHITE,
|
||
width=outline_width,
|
||
outline=Color.GREY,
|
||
)
|
||
|
||
arrival = False
|
||
distance = 1 / (len(line.stations) - 1)
|
||
for i, sta in enumerate(line.stations):
|
||
box_size = (1.618, 1.618)
|
||
if sta.identifier == station.identifier:
|
||
arrival = True
|
||
baseCanvas.linePanel.recLine.__dict__["station_{}".format(sta.identifier)] = Img(
|
||
uv_size=(1, 1),
|
||
box_size=(1.8, 1.8),
|
||
parent_point=(distance * i, 0.5),
|
||
point=(0.5, 0.5),
|
||
img=circleThis,
|
||
keep_ratio=True
|
||
)
|
||
continue
|
||
if arrival:
|
||
# 后方站绘制
|
||
baseCanvas.linePanel.recLine.__dict__["station_{}".format(sta.identifier)] = Img(
|
||
uv_size=(1, 1),
|
||
box_size=box_size,
|
||
parent_point=(distance * i, 0.5),
|
||
point=(0.5, 0.5),
|
||
img=circleForward,
|
||
keep_ratio=True
|
||
)
|
||
else:
|
||
# 前方站绘制
|
||
baseCanvas.linePanel.recLine.__dict__["station_{}".format(sta.identifier)] = Img(
|
||
uv_size=(1, 1),
|
||
box_size=box_size,
|
||
parent_point=(distance * i, 0.5),
|
||
point=(0.5, 0.5),
|
||
img=circlePassed,
|
||
keep_ratio=True
|
||
)
|
||
return baseCanvas
|
||
|
||
|
||
def generate_platform_sign(name: str, aliases: List[str], lineInfo: List[Line], entranceIdentifier: str, ratio: tuple[int | float, int | float],
|
||
reso: int = resolution
|
||
):
|
||
pass
|
||
|
||
# def main():
|
||
# generate_entrance_sign(
|
||
# "璧山",
|
||
# aliases=["Bishan"],
|
||
# lineInfo=[
|
||
#
|
||
# Line(identifier="2", chineseName="1号线", englishName="Line 1", color=Color.RED, stations=[]),
|
||
# Line(identifier="3", chineseName="27号线", englishName="Line 27", color="#685bc7", stations=[]),
|
||
# Line(identifier="1", chineseName="璧铜线", englishName="BT Line", color="#685BC7", stations=[]),
|
||
# ],
|
||
# entranceIdentifier="1",
|
||
# ratio=(8, 1)
|
||
# )
|
||
#
|
||
#
|
||
# main()
|