📝 构建文档测试

This commit is contained in:
远野千束 2024-08-29 15:05:02 +08:00
parent 86b8a4c922
commit 5763a84679
46 changed files with 312 additions and 2081 deletions

View File

@ -39,7 +39,7 @@ jobs:
- name: Setup API markdown
run: |-
python -m pip install pydantic
python -m pip install litedoc
python -m litedoc mbcp -o docs/api -l zh-Hans
python -m litedoc mbcp -o docs/en/api -l en
python -m litedoc mbcp -o docs/ja/api -l ja

2
.gitignore vendored
View File

@ -12,3 +12,5 @@ node_modules/
docs/.vitepress/dist
docs/.vitepress/cache
litedoc/

View File

@ -7,13 +7,13 @@ title: mbcp.mp_math.equation
**说明**: 求N元函数一阶偏导函数。这玩意不太稳定慎用。
**返回**: 偏导函数
**参数**:
> - func: 函数
> - var: 变量位置,可为整数(一阶偏导)或整数元组(高阶偏导)
> - epsilon: 偏移量
**返回**: 偏导函数
**引发**:
> - ValueError 无效变量类型

View File

@ -35,12 +35,12 @@ def __init__(self, point: 'Point3', direction: 'Vector3'):
**说明**: 判断两条直线是否近似相等。
**返回**: 是否近似相等
**参数**:
> - other: 另一条直线
> - epsilon: 误差
**返回**: 是否近似相等
<details>
<summary> <b>源代码</b> </summary>
@ -65,11 +65,11 @@ def approx(self, other: 'Line3', epsilon: float=APPROX) -> bool:
**说明**: 计算直线和直线之间的夹角。
**返回**: 夹角弧度
**参数**:
> - other: 另一条直线
**返回**: 夹角弧度
**引发**:
> - TypeError 不支持的类型
@ -98,11 +98,11 @@ def cal_angle(self, other: 'Line3') -> 'AnyAngle':
**说明**: 计算直线和直线或点之间的距离。
**返回**: 距离
**参数**:
> - other: 平行直线或点
**返回**: 距离
**引发**:
> - TypeError 不支持的类型
@ -144,11 +144,11 @@ def cal_distance(self, other: 'Line3 | Point3') -> float:
**说明**: 计算两条直线的交点。
**返回**: 交点
**参数**:
> - other: 另一条直线
**返回**: 交点
**引发**:
> - ValueError 直线平行
> - ValueError 直线不共面
@ -183,11 +183,11 @@ def cal_intersection(self, other: 'Line3') -> 'Point3':
**说明**: 计算直线经过指定点p的垂线。
**返回**: 垂线
**参数**:
> - point: 指定点
**返回**: 垂线
<details>
<summary> <b>源代码</b> </summary>
@ -211,11 +211,11 @@ def cal_perpendicular(self, point: 'Point3') -> 'Line3':
**说明**: 获取直线上的点。同一条直线但起始点和方向向量不同则同一个t对应的点不同。
**返回**: 点
**参数**:
> - t: 参数t
**返回**: 点
<details>
<summary> <b>源代码</b> </summary>
@ -262,12 +262,12 @@ def get_parametric_equations(self) -> tuple[OneSingleVarFunc, OneSingleVarFunc,
**说明**: 判断两条直线是否近似平行。
**返回**: 是否近似平行
**参数**:
> - other: 另一条直线
> - epsilon: 误差
**返回**: 是否近似平行
<details>
<summary> <b>源代码</b> </summary>
@ -292,11 +292,11 @@ def is_approx_parallel(self, other: 'Line3', epsilon: float=1e-06) -> bool:
**说明**: 判断两条直线是否平行。
**返回**: 是否平行
**参数**:
> - other: 另一条直线
**返回**: 是否平行
<details>
<summary> <b>源代码</b> </summary>
@ -320,11 +320,11 @@ def is_parallel(self, other: 'Line3') -> bool:
**说明**: 判断两条直线是否共线。
**返回**: 是否共线
**参数**:
> - other: 另一条直线
**返回**: 是否共线
<details>
<summary> <b>源代码</b> </summary>
@ -348,11 +348,11 @@ def is_collinear(self, other: 'Line3') -> bool:
**说明**: 判断点是否在直线上。
**返回**: 是否在直线上
**参数**:
> - point: 点
**返回**: 是否在直线上
<details>
<summary> <b>源代码</b> </summary>
@ -377,11 +377,11 @@ def is_point_on(self, point: 'Point3') -> bool:
**说明**: 判断两条直线是否共面。
充要条件两直线方向向量的叉乘与两直线上任意一点的向量的点积为0。
**返回**: 是否共面
**参数**:
> - other: 另一条直线
**返回**: 是否共面
<details>
<summary> <b>源代码</b> </summary>
@ -438,12 +438,12 @@ def simplify(self):
**说明**: 工厂函数 由两点构造直线。
**返回**: 直线
**参数**:
> - p1: 点1
> - p2: 点2
**返回**: 直线
<details>
<summary> <b>源代码</b> </summary>
@ -470,11 +470,11 @@ def from_two_points(cls, p1: 'Point3', p2: 'Point3') -> 'Line3':
**说明**: 计算两条直线点集合的交集。重合线返回自身平行线返回None交线返回交点。
**返回**: 交点
**参数**:
> - other: 另一条直线
**返回**: 交点
<details>
<summary> <b>源代码</b> </summary>

View File

@ -41,11 +41,11 @@ def __init__(self, a: float, b: float, c: float, d: float):
**说明**: 判断两个平面是否近似相等。
**返回**: 是否近似相等
**参数**:
> - other: 另一个平面
**返回**: 是否近似相等
<details>
<summary> <b>源代码</b> </summary>
@ -79,11 +79,11 @@ def approx(self, other: 'Plane3') -> bool:
**说明**: 计算平面与平面之间的夹角。
**返回**: 夹角弧度
**参数**:
> - other: 另一个平面
**返回**: 夹角弧度
**引发**:
> - TypeError 不支持的类型
@ -117,11 +117,11 @@ def cal_angle(self, other: 'Line3 | Plane3') -> 'AnyAngle':
**说明**: 计算平面与平面或点之间的距离。
**返回**: 距离
**参数**:
> - other: 另一个平面或点
**返回**: 距离
**引发**:
> - TypeError 不支持的类型
@ -155,11 +155,11 @@ def cal_distance(self, other: 'Plane3 | Point3') -> float:
**说明**: 计算两平面的交线。
**返回**: 两平面的交线
**参数**:
> - other: 另一个平面
**返回**: 两平面的交线
<details>
<summary> <b>源代码</b> </summary>
@ -200,11 +200,11 @@ def cal_intersection_line3(self, other: 'Plane3') -> 'Line3':
**说明**: 计算平面与直线的交点。
**返回**: 交点
**参数**:
> - other: 不与平面平行或在平面上的直线
**返回**: 交点
**引发**:
> - ValueError 平面与直线平行或重合
@ -237,11 +237,11 @@ def cal_intersection_point3(self, other: 'Line3') -> 'Point3':
**说明**: 计算平行于该平面且过指定点的平面。
**返回**: 所求平面
**参数**:
> - point: 指定点
**返回**: 所求平面
<details>
<summary> <b>源代码</b> </summary>
@ -265,11 +265,11 @@ def cal_parallel_plane3(self, point: 'Point3') -> 'Plane3':
**说明**: 判断两个平面是否平行。
**返回**: 是否平行
**参数**:
> - other: 另一个平面
**返回**: 是否平行
<details>
<summary> <b>源代码</b> </summary>
@ -319,12 +319,12 @@ def normal(self) -> 'Vector3':
**说明**: 工厂函数 由点和法向量构造平面(点法式构造)。
**返回**: 平面
**参数**:
> - point: 平面上的一点
> - normal: 法向量
**返回**: 平面
<details>
<summary> <b>源代码</b> </summary>
@ -353,13 +353,13 @@ def from_point_and_normal(cls, point: 'Point3', normal: 'Vector3') -> 'Plane3':
**说明**: 工厂函数 由三点构造平面。
**返回**: 平面
**参数**:
> - p1: 点1
> - p2: 点2
> - p3: 点3
**返回**: 平面
<details>
<summary> <b>源代码</b> </summary>
@ -390,12 +390,12 @@ def from_three_points(cls, p1: 'Point3', p2: 'Point3', p3: 'Point3') -> 'Plane3'
**说明**: 工厂函数 由两直线构造平面。
**返回**: 平面
**参数**:
> - l1: 直线1
> - l2: 直线2
**返回**: 平面
<details>
<summary> <b>源代码</b> </summary>
@ -426,12 +426,12 @@ def from_two_lines(cls, l1: 'Line3', l2: 'Line3') -> 'Plane3':
**说明**: 工厂函数 由点和直线构造平面。
**返回**: 平面
**参数**:
> - point: 面上一点
> - line: 面上直线,不包含点
**返回**: 平面
<details>
<summary> <b>源代码</b> </summary>
@ -485,11 +485,11 @@ def __and__(self, other: 'Plane3') -> 'Line3 | None':
**说明**: 取两平面的交集(人话:交线)
**返回**: 不平行平面的交线平面平行返回None
**参数**:
> - other:
**返回**: 不平行平面的交线平面平行返回None
<details>
<summary> <b>源代码</b> </summary>

View File

@ -38,12 +38,12 @@ def __init__(self, x: float, y: float, z: float):
**说明**: 判断两个点是否近似相等。
**返回**: 是否近似相等
**参数**:
> - other:
> - epsilon:
**返回**: 是否近似相等
<details>
<summary> <b>源代码</b> </summary>

View File

@ -7,13 +7,13 @@ title: mbcp.mp_math.utils
**说明**: 区间限定函数
**返回**: 限制后的值
**参数**:
> - x: 待限定的值
> - min_: 最小值
> - max_: 最大值
**返回**: 限制后的值
<details>
<summary> <b>源代码</b> </summary>
@ -40,13 +40,13 @@ def clamp(x: float, min_: float, max_: float) -> float:
**说明**: 判断两个数是否近似相等。或包装一个实数用于判断是否近似于0。
**返回**: 是否近似相等
**参数**:
> - x: 数1
> - y: 数2
> - epsilon: 误差
**返回**: 是否近似相等
<details>
<summary> <b>源代码</b> </summary>
@ -72,12 +72,12 @@ def approx(x: float, y: float=0.0, epsilon: float=APPROX) -> bool:
**说明**: 获取数的符号。
**返回**: 符号 + - ""
**参数**:
> - x: 数
> - only_neg: 是否只返回负数的符号
**返回**: 符号 + - ""
<details>
<summary> <b>源代码</b> </summary>
@ -109,12 +109,12 @@ def sign(x: float, only_neg: bool=False) -> str:
1 -> +1
0 -> ""
**返回**: 符号 + - ""
**参数**:
> - x: 数
> - only_neg: 是否只返回负数的符号
**返回**: 符号 + - ""
<details>
<summary> <b>源代码</b> </summary>

View File

@ -38,12 +38,12 @@ def __init__(self, x: float, y: float, z: float):
**说明**: 判断两个向量是否近似相等。
**返回**: 是否近似相等
**参数**:
> - other:
> - epsilon:
**返回**: 是否近似相等
<details>
<summary> <b>源代码</b> </summary>
@ -69,11 +69,11 @@ def approx(self, other: 'Vector3', epsilon: float=APPROX) -> bool:
**说明**: 计算两个向量之间的夹角。
**返回**: 夹角
**参数**:
> - other: 另一个向量
**返回**: 夹角
<details>
<summary> <b>源代码</b> </summary>
@ -101,11 +101,11 @@ def cal_angle(self, other: 'Vector3') -> 'AnyAngle':
其余结果的模为平行四边形的面积。
**返回**: 行列式的结果
**参数**:
> - other:
**返回**: 行列式的结果
<details>
<summary> <b>源代码</b> </summary>
@ -141,12 +141,12 @@ def cross(self, other: 'Vector3') -> 'Vector3':
**说明**: 判断两个向量是否近似平行。
**返回**: 是否近似平行
**参数**:
> - other: 另一个向量
> - epsilon: 允许的误差
**返回**: 是否近似平行
<details>
<summary> <b>源代码</b> </summary>
@ -171,11 +171,11 @@ def is_approx_parallel(self, other: 'Vector3', epsilon: float=APPROX) -> bool:
**说明**: 判断两个向量是否平行。
**返回**: 是否平行
**参数**:
> - other: 另一个向量
**返回**: 是否平行
<details>
<summary> <b>源代码</b> </summary>
@ -370,11 +370,11 @@ def __add__(self, other):
**说明**: 判断两个向量是否相等。
**返回**: 是否相等
**参数**:
> - other:
**返回**: 是否相等
<details>
<summary> <b>源代码</b> </summary>

View File

@ -9,12 +9,12 @@ title: mbcp.presets.model
**说明**: 生成球体上的点集。
**返回**: List[Point3]: 球体上的点集。
**参数**:
> - radius:
> - density:
**返回**: List[Point3]: 球体上的点集。
<details>
<summary> <b>源代码</b> </summary>

View File

@ -7,13 +7,13 @@ title: mbcp.mp_math.equation
**Description**: 求N元函数一阶偏导函数。这玩意不太稳定慎用。
**Return**: 偏导函数
**Arguments**:
> - func: 函数
> - var: 变量位置,可为整数(一阶偏导)或整数元组(高阶偏导)
> - epsilon: 偏移量
**Return**: 偏导函数
**Raises**:
> - ValueError 无效变量类型

View File

@ -35,12 +35,12 @@ def __init__(self, point: 'Point3', direction: 'Vector3'):
**Description**: 判断两条直线是否近似相等。
**Return**: 是否近似相等
**Arguments**:
> - other: 另一条直线
> - epsilon: 误差
**Return**: 是否近似相等
<details>
<summary> <b>Source code</b> </summary>
@ -65,11 +65,11 @@ def approx(self, other: 'Line3', epsilon: float=APPROX) -> bool:
**Description**: 计算直线和直线之间的夹角。
**Return**: 夹角弧度
**Arguments**:
> - other: 另一条直线
**Return**: 夹角弧度
**Raises**:
> - TypeError 不支持的类型
@ -98,11 +98,11 @@ def cal_angle(self, other: 'Line3') -> 'AnyAngle':
**Description**: 计算直线和直线或点之间的距离。
**Return**: 距离
**Arguments**:
> - other: 平行直线或点
**Return**: 距离
**Raises**:
> - TypeError 不支持的类型
@ -144,11 +144,11 @@ def cal_distance(self, other: 'Line3 | Point3') -> float:
**Description**: 计算两条直线的交点。
**Return**: 交点
**Arguments**:
> - other: 另一条直线
**Return**: 交点
**Raises**:
> - ValueError 直线平行
> - ValueError 直线不共面
@ -183,11 +183,11 @@ def cal_intersection(self, other: 'Line3') -> 'Point3':
**Description**: 计算直线经过指定点p的垂线。
**Return**: 垂线
**Arguments**:
> - point: 指定点
**Return**: 垂线
<details>
<summary> <b>Source code</b> </summary>
@ -211,11 +211,11 @@ def cal_perpendicular(self, point: 'Point3') -> 'Line3':
**Description**: 获取直线上的点。同一条直线但起始点和方向向量不同则同一个t对应的点不同。
**Return**: 点
**Arguments**:
> - t: 参数t
**Return**: 点
<details>
<summary> <b>Source code</b> </summary>
@ -262,12 +262,12 @@ def get_parametric_equations(self) -> tuple[OneSingleVarFunc, OneSingleVarFunc,
**Description**: 判断两条直线是否近似平行。
**Return**: 是否近似平行
**Arguments**:
> - other: 另一条直线
> - epsilon: 误差
**Return**: 是否近似平行
<details>
<summary> <b>Source code</b> </summary>
@ -292,11 +292,11 @@ def is_approx_parallel(self, other: 'Line3', epsilon: float=1e-06) -> bool:
**Description**: 判断两条直线是否平行。
**Return**: 是否平行
**Arguments**:
> - other: 另一条直线
**Return**: 是否平行
<details>
<summary> <b>Source code</b> </summary>
@ -320,11 +320,11 @@ def is_parallel(self, other: 'Line3') -> bool:
**Description**: 判断两条直线是否共线。
**Return**: 是否共线
**Arguments**:
> - other: 另一条直线
**Return**: 是否共线
<details>
<summary> <b>Source code</b> </summary>
@ -348,11 +348,11 @@ def is_collinear(self, other: 'Line3') -> bool:
**Description**: 判断点是否在直线上。
**Return**: 是否在直线上
**Arguments**:
> - point: 点
**Return**: 是否在直线上
<details>
<summary> <b>Source code</b> </summary>
@ -377,11 +377,11 @@ def is_point_on(self, point: 'Point3') -> bool:
**Description**: 判断两条直线是否共面。
充要条件两直线方向向量的叉乘与两直线上任意一点的向量的点积为0。
**Return**: 是否共面
**Arguments**:
> - other: 另一条直线
**Return**: 是否共面
<details>
<summary> <b>Source code</b> </summary>
@ -438,12 +438,12 @@ def simplify(self):
**Description**: 工厂函数 由两点构造直线。
**Return**: 直线
**Arguments**:
> - p1: 点1
> - p2: 点2
**Return**: 直线
<details>
<summary> <b>Source code</b> </summary>
@ -470,11 +470,11 @@ def from_two_points(cls, p1: 'Point3', p2: 'Point3') -> 'Line3':
**Description**: 计算两条直线点集合的交集。重合线返回自身平行线返回None交线返回交点。
**Return**: 交点
**Arguments**:
> - other: 另一条直线
**Return**: 交点
<details>
<summary> <b>Source code</b> </summary>

View File

@ -41,11 +41,11 @@ def __init__(self, a: float, b: float, c: float, d: float):
**Description**: 判断两个平面是否近似相等。
**Return**: 是否近似相等
**Arguments**:
> - other: 另一个平面
**Return**: 是否近似相等
<details>
<summary> <b>Source code</b> </summary>
@ -79,11 +79,11 @@ def approx(self, other: 'Plane3') -> bool:
**Description**: 计算平面与平面之间的夹角。
**Return**: 夹角弧度
**Arguments**:
> - other: 另一个平面
**Return**: 夹角弧度
**Raises**:
> - TypeError 不支持的类型
@ -117,11 +117,11 @@ def cal_angle(self, other: 'Line3 | Plane3') -> 'AnyAngle':
**Description**: 计算平面与平面或点之间的距离。
**Return**: 距离
**Arguments**:
> - other: 另一个平面或点
**Return**: 距离
**Raises**:
> - TypeError 不支持的类型
@ -155,11 +155,11 @@ def cal_distance(self, other: 'Plane3 | Point3') -> float:
**Description**: 计算两平面的交线。
**Return**: 两平面的交线
**Arguments**:
> - other: 另一个平面
**Return**: 两平面的交线
<details>
<summary> <b>Source code</b> </summary>
@ -200,11 +200,11 @@ def cal_intersection_line3(self, other: 'Plane3') -> 'Line3':
**Description**: 计算平面与直线的交点。
**Return**: 交点
**Arguments**:
> - other: 不与平面平行或在平面上的直线
**Return**: 交点
**Raises**:
> - ValueError 平面与直线平行或重合
@ -237,11 +237,11 @@ def cal_intersection_point3(self, other: 'Line3') -> 'Point3':
**Description**: 计算平行于该平面且过指定点的平面。
**Return**: 所求平面
**Arguments**:
> - point: 指定点
**Return**: 所求平面
<details>
<summary> <b>Source code</b> </summary>
@ -265,11 +265,11 @@ def cal_parallel_plane3(self, point: 'Point3') -> 'Plane3':
**Description**: 判断两个平面是否平行。
**Return**: 是否平行
**Arguments**:
> - other: 另一个平面
**Return**: 是否平行
<details>
<summary> <b>Source code</b> </summary>
@ -319,12 +319,12 @@ def normal(self) -> 'Vector3':
**Description**: 工厂函数 由点和法向量构造平面(点法式构造)。
**Return**: 平面
**Arguments**:
> - point: 平面上的一点
> - normal: 法向量
**Return**: 平面
<details>
<summary> <b>Source code</b> </summary>
@ -353,13 +353,13 @@ def from_point_and_normal(cls, point: 'Point3', normal: 'Vector3') -> 'Plane3':
**Description**: 工厂函数 由三点构造平面。
**Return**: 平面
**Arguments**:
> - p1: 点1
> - p2: 点2
> - p3: 点3
**Return**: 平面
<details>
<summary> <b>Source code</b> </summary>
@ -390,12 +390,12 @@ def from_three_points(cls, p1: 'Point3', p2: 'Point3', p3: 'Point3') -> 'Plane3'
**Description**: 工厂函数 由两直线构造平面。
**Return**: 平面
**Arguments**:
> - l1: 直线1
> - l2: 直线2
**Return**: 平面
<details>
<summary> <b>Source code</b> </summary>
@ -426,12 +426,12 @@ def from_two_lines(cls, l1: 'Line3', l2: 'Line3') -> 'Plane3':
**Description**: 工厂函数 由点和直线构造平面。
**Return**: 平面
**Arguments**:
> - point: 面上一点
> - line: 面上直线,不包含点
**Return**: 平面
<details>
<summary> <b>Source code</b> </summary>
@ -485,11 +485,11 @@ def __and__(self, other: 'Plane3') -> 'Line3 | None':
**Description**: 取两平面的交集(人话:交线)
**Return**: 不平行平面的交线平面平行返回None
**Arguments**:
> - other:
**Return**: 不平行平面的交线平面平行返回None
<details>
<summary> <b>Source code</b> </summary>

View File

@ -38,12 +38,12 @@ def __init__(self, x: float, y: float, z: float):
**Description**: 判断两个点是否近似相等。
**Return**: 是否近似相等
**Arguments**:
> - other:
> - epsilon:
**Return**: 是否近似相等
<details>
<summary> <b>Source code</b> </summary>

View File

@ -7,13 +7,13 @@ title: mbcp.mp_math.utils
**Description**: 区间限定函数
**Return**: 限制后的值
**Arguments**:
> - x: 待限定的值
> - min_: 最小值
> - max_: 最大值
**Return**: 限制后的值
<details>
<summary> <b>Source code</b> </summary>
@ -40,13 +40,13 @@ def clamp(x: float, min_: float, max_: float) -> float:
**Description**: 判断两个数是否近似相等。或包装一个实数用于判断是否近似于0。
**Return**: 是否近似相等
**Arguments**:
> - x: 数1
> - y: 数2
> - epsilon: 误差
**Return**: 是否近似相等
<details>
<summary> <b>Source code</b> </summary>
@ -72,12 +72,12 @@ def approx(x: float, y: float=0.0, epsilon: float=APPROX) -> bool:
**Description**: 获取数的符号。
**Return**: 符号 + - ""
**Arguments**:
> - x: 数
> - only_neg: 是否只返回负数的符号
**Return**: 符号 + - ""
<details>
<summary> <b>Source code</b> </summary>
@ -109,12 +109,12 @@ def sign(x: float, only_neg: bool=False) -> str:
1 -> +1
0 -> ""
**Return**: 符号 + - ""
**Arguments**:
> - x: 数
> - only_neg: 是否只返回负数的符号
**Return**: 符号 + - ""
<details>
<summary> <b>Source code</b> </summary>

View File

@ -38,12 +38,12 @@ def __init__(self, x: float, y: float, z: float):
**Description**: 判断两个向量是否近似相等。
**Return**: 是否近似相等
**Arguments**:
> - other:
> - epsilon:
**Return**: 是否近似相等
<details>
<summary> <b>Source code</b> </summary>
@ -69,11 +69,11 @@ def approx(self, other: 'Vector3', epsilon: float=APPROX) -> bool:
**Description**: 计算两个向量之间的夹角。
**Return**: 夹角
**Arguments**:
> - other: 另一个向量
**Return**: 夹角
<details>
<summary> <b>Source code</b> </summary>
@ -101,11 +101,11 @@ def cal_angle(self, other: 'Vector3') -> 'AnyAngle':
其余结果的模为平行四边形的面积。
**Return**: 行列式的结果
**Arguments**:
> - other:
**Return**: 行列式的结果
<details>
<summary> <b>Source code</b> </summary>
@ -141,12 +141,12 @@ def cross(self, other: 'Vector3') -> 'Vector3':
**Description**: 判断两个向量是否近似平行。
**Return**: 是否近似平行
**Arguments**:
> - other: 另一个向量
> - epsilon: 允许的误差
**Return**: 是否近似平行
<details>
<summary> <b>Source code</b> </summary>
@ -171,11 +171,11 @@ def is_approx_parallel(self, other: 'Vector3', epsilon: float=APPROX) -> bool:
**Description**: 判断两个向量是否平行。
**Return**: 是否平行
**Arguments**:
> - other: 另一个向量
**Return**: 是否平行
<details>
<summary> <b>Source code</b> </summary>
@ -370,11 +370,11 @@ def __add__(self, other):
**Description**: 判断两个向量是否相等。
**Return**: 是否相等
**Arguments**:
> - other:
**Return**: 是否相等
<details>
<summary> <b>Source code</b> </summary>

View File

@ -9,12 +9,12 @@ title: mbcp.presets.model
**Description**: 生成球体上的点集。
**Return**: List[Point3]: 球体上的点集。
**Arguments**:
> - radius:
> - density:
**Return**: List[Point3]: 球体上的点集。
<details>
<summary> <b>Source code</b> </summary>

View File

@ -7,13 +7,13 @@ title: mbcp.mp_math.equation
**説明**: 求N元函数一阶偏导函数。这玩意不太稳定慎用。
**戻り値**: 偏导函数
**引数**:
> - func: 函数
> - var: 变量位置,可为整数(一阶偏导)或整数元组(高阶偏导)
> - epsilon: 偏移量
**戻り値**: 偏导函数
**例外**:
> - ValueError 无效变量类型

View File

@ -35,12 +35,12 @@ def __init__(self, point: 'Point3', direction: 'Vector3'):
**説明**: 判断两条直线是否近似相等。
**戻り値**: 是否近似相等
**引数**:
> - other: 另一条直线
> - epsilon: 误差
**戻り値**: 是否近似相等
<details>
<summary> <b>ソースコード</b> </summary>
@ -65,11 +65,11 @@ def approx(self, other: 'Line3', epsilon: float=APPROX) -> bool:
**説明**: 计算直线和直线之间的夹角。
**戻り値**: 夹角弧度
**引数**:
> - other: 另一条直线
**戻り値**: 夹角弧度
**例外**:
> - TypeError 不支持的类型
@ -98,11 +98,11 @@ def cal_angle(self, other: 'Line3') -> 'AnyAngle':
**説明**: 计算直线和直线或点之间的距离。
**戻り値**: 距离
**引数**:
> - other: 平行直线或点
**戻り値**: 距离
**例外**:
> - TypeError 不支持的类型
@ -144,11 +144,11 @@ def cal_distance(self, other: 'Line3 | Point3') -> float:
**説明**: 计算两条直线的交点。
**戻り値**: 交点
**引数**:
> - other: 另一条直线
**戻り値**: 交点
**例外**:
> - ValueError 直线平行
> - ValueError 直线不共面
@ -183,11 +183,11 @@ def cal_intersection(self, other: 'Line3') -> 'Point3':
**説明**: 计算直线经过指定点p的垂线。
**戻り値**: 垂线
**引数**:
> - point: 指定点
**戻り値**: 垂线
<details>
<summary> <b>ソースコード</b> </summary>
@ -211,11 +211,11 @@ def cal_perpendicular(self, point: 'Point3') -> 'Line3':
**説明**: 获取直线上的点。同一条直线但起始点和方向向量不同则同一个t对应的点不同。
**戻り値**: 点
**引数**:
> - t: 参数t
**戻り値**: 点
<details>
<summary> <b>ソースコード</b> </summary>
@ -262,12 +262,12 @@ def get_parametric_equations(self) -> tuple[OneSingleVarFunc, OneSingleVarFunc,
**説明**: 判断两条直线是否近似平行。
**戻り値**: 是否近似平行
**引数**:
> - other: 另一条直线
> - epsilon: 误差
**戻り値**: 是否近似平行
<details>
<summary> <b>ソースコード</b> </summary>
@ -292,11 +292,11 @@ def is_approx_parallel(self, other: 'Line3', epsilon: float=1e-06) -> bool:
**説明**: 判断两条直线是否平行。
**戻り値**: 是否平行
**引数**:
> - other: 另一条直线
**戻り値**: 是否平行
<details>
<summary> <b>ソースコード</b> </summary>
@ -320,11 +320,11 @@ def is_parallel(self, other: 'Line3') -> bool:
**説明**: 判断两条直线是否共线。
**戻り値**: 是否共线
**引数**:
> - other: 另一条直线
**戻り値**: 是否共线
<details>
<summary> <b>ソースコード</b> </summary>
@ -348,11 +348,11 @@ def is_collinear(self, other: 'Line3') -> bool:
**説明**: 判断点是否在直线上。
**戻り値**: 是否在直线上
**引数**:
> - point: 点
**戻り値**: 是否在直线上
<details>
<summary> <b>ソースコード</b> </summary>
@ -377,11 +377,11 @@ def is_point_on(self, point: 'Point3') -> bool:
**説明**: 判断两条直线是否共面。
充要条件两直线方向向量的叉乘与两直线上任意一点的向量的点积为0。
**戻り値**: 是否共面
**引数**:
> - other: 另一条直线
**戻り値**: 是否共面
<details>
<summary> <b>ソースコード</b> </summary>
@ -438,12 +438,12 @@ def simplify(self):
**説明**: 工厂函数 由两点构造直线。
**戻り値**: 直线
**引数**:
> - p1: 点1
> - p2: 点2
**戻り値**: 直线
<details>
<summary> <b>ソースコード</b> </summary>
@ -470,11 +470,11 @@ def from_two_points(cls, p1: 'Point3', p2: 'Point3') -> 'Line3':
**説明**: 计算两条直线点集合的交集。重合线返回自身平行线返回None交线返回交点。
**戻り値**: 交点
**引数**:
> - other: 另一条直线
**戻り値**: 交点
<details>
<summary> <b>ソースコード</b> </summary>

View File

@ -41,11 +41,11 @@ def __init__(self, a: float, b: float, c: float, d: float):
**説明**: 判断两个平面是否近似相等。
**戻り値**: 是否近似相等
**引数**:
> - other: 另一个平面
**戻り値**: 是否近似相等
<details>
<summary> <b>ソースコード</b> </summary>
@ -79,11 +79,11 @@ def approx(self, other: 'Plane3') -> bool:
**説明**: 计算平面与平面之间的夹角。
**戻り値**: 夹角弧度
**引数**:
> - other: 另一个平面
**戻り値**: 夹角弧度
**例外**:
> - TypeError 不支持的类型
@ -117,11 +117,11 @@ def cal_angle(self, other: 'Line3 | Plane3') -> 'AnyAngle':
**説明**: 计算平面与平面或点之间的距离。
**戻り値**: 距离
**引数**:
> - other: 另一个平面或点
**戻り値**: 距离
**例外**:
> - TypeError 不支持的类型
@ -155,11 +155,11 @@ def cal_distance(self, other: 'Plane3 | Point3') -> float:
**説明**: 计算两平面的交线。
**戻り値**: 两平面的交线
**引数**:
> - other: 另一个平面
**戻り値**: 两平面的交线
<details>
<summary> <b>ソースコード</b> </summary>
@ -200,11 +200,11 @@ def cal_intersection_line3(self, other: 'Plane3') -> 'Line3':
**説明**: 计算平面与直线的交点。
**戻り値**: 交点
**引数**:
> - other: 不与平面平行或在平面上的直线
**戻り値**: 交点
**例外**:
> - ValueError 平面与直线平行或重合
@ -237,11 +237,11 @@ def cal_intersection_point3(self, other: 'Line3') -> 'Point3':
**説明**: 计算平行于该平面且过指定点的平面。
**戻り値**: 所求平面
**引数**:
> - point: 指定点
**戻り値**: 所求平面
<details>
<summary> <b>ソースコード</b> </summary>
@ -265,11 +265,11 @@ def cal_parallel_plane3(self, point: 'Point3') -> 'Plane3':
**説明**: 判断两个平面是否平行。
**戻り値**: 是否平行
**引数**:
> - other: 另一个平面
**戻り値**: 是否平行
<details>
<summary> <b>ソースコード</b> </summary>
@ -319,12 +319,12 @@ def normal(self) -> 'Vector3':
**説明**: 工厂函数 由点和法向量构造平面(点法式构造)。
**戻り値**: 平面
**引数**:
> - point: 平面上的一点
> - normal: 法向量
**戻り値**: 平面
<details>
<summary> <b>ソースコード</b> </summary>
@ -353,13 +353,13 @@ def from_point_and_normal(cls, point: 'Point3', normal: 'Vector3') -> 'Plane3':
**説明**: 工厂函数 由三点构造平面。
**戻り値**: 平面
**引数**:
> - p1: 点1
> - p2: 点2
> - p3: 点3
**戻り値**: 平面
<details>
<summary> <b>ソースコード</b> </summary>
@ -390,12 +390,12 @@ def from_three_points(cls, p1: 'Point3', p2: 'Point3', p3: 'Point3') -> 'Plane3'
**説明**: 工厂函数 由两直线构造平面。
**戻り値**: 平面
**引数**:
> - l1: 直线1
> - l2: 直线2
**戻り値**: 平面
<details>
<summary> <b>ソースコード</b> </summary>
@ -426,12 +426,12 @@ def from_two_lines(cls, l1: 'Line3', l2: 'Line3') -> 'Plane3':
**説明**: 工厂函数 由点和直线构造平面。
**戻り値**: 平面
**引数**:
> - point: 面上一点
> - line: 面上直线,不包含点
**戻り値**: 平面
<details>
<summary> <b>ソースコード</b> </summary>
@ -485,11 +485,11 @@ def __and__(self, other: 'Plane3') -> 'Line3 | None':
**説明**: 取两平面的交集(人话:交线)
**戻り値**: 不平行平面的交线平面平行返回None
**引数**:
> - other:
**戻り値**: 不平行平面的交线平面平行返回None
<details>
<summary> <b>ソースコード</b> </summary>

View File

@ -38,12 +38,12 @@ def __init__(self, x: float, y: float, z: float):
**説明**: 判断两个点是否近似相等。
**戻り値**: 是否近似相等
**引数**:
> - other:
> - epsilon:
**戻り値**: 是否近似相等
<details>
<summary> <b>ソースコード</b> </summary>

View File

@ -7,13 +7,13 @@ title: mbcp.mp_math.utils
**説明**: 区间限定函数
**戻り値**: 限制后的值
**引数**:
> - x: 待限定的值
> - min_: 最小值
> - max_: 最大值
**戻り値**: 限制后的值
<details>
<summary> <b>ソースコード</b> </summary>
@ -40,13 +40,13 @@ def clamp(x: float, min_: float, max_: float) -> float:
**説明**: 判断两个数是否近似相等。或包装一个实数用于判断是否近似于0。
**戻り値**: 是否近似相等
**引数**:
> - x: 数1
> - y: 数2
> - epsilon: 误差
**戻り値**: 是否近似相等
<details>
<summary> <b>ソースコード</b> </summary>
@ -72,12 +72,12 @@ def approx(x: float, y: float=0.0, epsilon: float=APPROX) -> bool:
**説明**: 获取数的符号。
**戻り値**: 符号 + - ""
**引数**:
> - x: 数
> - only_neg: 是否只返回负数的符号
**戻り値**: 符号 + - ""
<details>
<summary> <b>ソースコード</b> </summary>
@ -109,12 +109,12 @@ def sign(x: float, only_neg: bool=False) -> str:
1 -> +1
0 -> ""
**戻り値**: 符号 + - ""
**引数**:
> - x: 数
> - only_neg: 是否只返回负数的符号
**戻り値**: 符号 + - ""
<details>
<summary> <b>ソースコード</b> </summary>

View File

@ -38,12 +38,12 @@ def __init__(self, x: float, y: float, z: float):
**説明**: 判断两个向量是否近似相等。
**戻り値**: 是否近似相等
**引数**:
> - other:
> - epsilon:
**戻り値**: 是否近似相等
<details>
<summary> <b>ソースコード</b> </summary>
@ -69,11 +69,11 @@ def approx(self, other: 'Vector3', epsilon: float=APPROX) -> bool:
**説明**: 计算两个向量之间的夹角。
**戻り値**: 夹角
**引数**:
> - other: 另一个向量
**戻り値**: 夹角
<details>
<summary> <b>ソースコード</b> </summary>
@ -101,11 +101,11 @@ def cal_angle(self, other: 'Vector3') -> 'AnyAngle':
其余结果的模为平行四边形的面积。
**戻り値**: 行列式的结果
**引数**:
> - other:
**戻り値**: 行列式的结果
<details>
<summary> <b>ソースコード</b> </summary>
@ -141,12 +141,12 @@ def cross(self, other: 'Vector3') -> 'Vector3':
**説明**: 判断两个向量是否近似平行。
**戻り値**: 是否近似平行
**引数**:
> - other: 另一个向量
> - epsilon: 允许的误差
**戻り値**: 是否近似平行
<details>
<summary> <b>ソースコード</b> </summary>
@ -171,11 +171,11 @@ def is_approx_parallel(self, other: 'Vector3', epsilon: float=APPROX) -> bool:
**説明**: 判断两个向量是否平行。
**戻り値**: 是否平行
**引数**:
> - other: 另一个向量
**戻り値**: 是否平行
<details>
<summary> <b>ソースコード</b> </summary>
@ -370,11 +370,11 @@ def __add__(self, other):
**説明**: 判断两个向量是否相等。
**戻り値**: 是否相等
**引数**:
> - other:
**戻り値**: 是否相等
<details>
<summary> <b>ソースコード</b> </summary>

View File

@ -9,12 +9,12 @@ title: mbcp.presets.model
**説明**: 生成球体上的点集。
**戻り値**: List[Point3]: 球体上的点集。
**引数**:
> - radius:
> - density:
**戻り値**: List[Point3]: 球体上的点集。
<details>
<summary> <b>ソースコード</b> </summary>

View File

@ -7,13 +7,13 @@ title: mbcp.mp_math.equation
**説明**: 求N元函数一阶偏导函数。这玩意不太稳定慎用。
**返回**: 偏导函数
**變數説明**:
> - func: 函数
> - var: 变量位置,可为整数(一阶偏导)或整数元组(高阶偏导)
> - epsilon: 偏移量
**返回**: 偏导函数
**抛出**:
> - ValueError 无效变量类型

View File

@ -35,12 +35,12 @@ def __init__(self, point: 'Point3', direction: 'Vector3'):
**説明**: 判断两条直线是否近似相等。
**返回**: 是否近似相等
**變數説明**:
> - other: 另一条直线
> - epsilon: 误差
**返回**: 是否近似相等
<details>
<summary> <b>源碼</b> </summary>
@ -65,11 +65,11 @@ def approx(self, other: 'Line3', epsilon: float=APPROX) -> bool:
**説明**: 计算直线和直线之间的夹角。
**返回**: 夹角弧度
**變數説明**:
> - other: 另一条直线
**返回**: 夹角弧度
**抛出**:
> - TypeError 不支持的类型
@ -98,11 +98,11 @@ def cal_angle(self, other: 'Line3') -> 'AnyAngle':
**説明**: 计算直线和直线或点之间的距离。
**返回**: 距离
**變數説明**:
> - other: 平行直线或点
**返回**: 距离
**抛出**:
> - TypeError 不支持的类型
@ -144,11 +144,11 @@ def cal_distance(self, other: 'Line3 | Point3') -> float:
**説明**: 计算两条直线的交点。
**返回**: 交点
**變數説明**:
> - other: 另一条直线
**返回**: 交点
**抛出**:
> - ValueError 直线平行
> - ValueError 直线不共面
@ -183,11 +183,11 @@ def cal_intersection(self, other: 'Line3') -> 'Point3':
**説明**: 计算直线经过指定点p的垂线。
**返回**: 垂线
**變數説明**:
> - point: 指定点
**返回**: 垂线
<details>
<summary> <b>源碼</b> </summary>
@ -211,11 +211,11 @@ def cal_perpendicular(self, point: 'Point3') -> 'Line3':
**説明**: 获取直线上的点。同一条直线但起始点和方向向量不同则同一个t对应的点不同。
**返回**: 点
**變數説明**:
> - t: 参数t
**返回**: 点
<details>
<summary> <b>源碼</b> </summary>
@ -262,12 +262,12 @@ def get_parametric_equations(self) -> tuple[OneSingleVarFunc, OneSingleVarFunc,
**説明**: 判断两条直线是否近似平行。
**返回**: 是否近似平行
**變數説明**:
> - other: 另一条直线
> - epsilon: 误差
**返回**: 是否近似平行
<details>
<summary> <b>源碼</b> </summary>
@ -292,11 +292,11 @@ def is_approx_parallel(self, other: 'Line3', epsilon: float=1e-06) -> bool:
**説明**: 判断两条直线是否平行。
**返回**: 是否平行
**變數説明**:
> - other: 另一条直线
**返回**: 是否平行
<details>
<summary> <b>源碼</b> </summary>
@ -320,11 +320,11 @@ def is_parallel(self, other: 'Line3') -> bool:
**説明**: 判断两条直线是否共线。
**返回**: 是否共线
**變數説明**:
> - other: 另一条直线
**返回**: 是否共线
<details>
<summary> <b>源碼</b> </summary>
@ -348,11 +348,11 @@ def is_collinear(self, other: 'Line3') -> bool:
**説明**: 判断点是否在直线上。
**返回**: 是否在直线上
**變數説明**:
> - point: 点
**返回**: 是否在直线上
<details>
<summary> <b>源碼</b> </summary>
@ -377,11 +377,11 @@ def is_point_on(self, point: 'Point3') -> bool:
**説明**: 判断两条直线是否共面。
充要条件两直线方向向量的叉乘与两直线上任意一点的向量的点积为0。
**返回**: 是否共面
**變數説明**:
> - other: 另一条直线
**返回**: 是否共面
<details>
<summary> <b>源碼</b> </summary>
@ -438,12 +438,12 @@ def simplify(self):
**説明**: 工厂函数 由两点构造直线。
**返回**: 直线
**變數説明**:
> - p1: 点1
> - p2: 点2
**返回**: 直线
<details>
<summary> <b>源碼</b> </summary>
@ -470,11 +470,11 @@ def from_two_points(cls, p1: 'Point3', p2: 'Point3') -> 'Line3':
**説明**: 计算两条直线点集合的交集。重合线返回自身平行线返回None交线返回交点。
**返回**: 交点
**變數説明**:
> - other: 另一条直线
**返回**: 交点
<details>
<summary> <b>源碼</b> </summary>

View File

@ -41,11 +41,11 @@ def __init__(self, a: float, b: float, c: float, d: float):
**説明**: 判断两个平面是否近似相等。
**返回**: 是否近似相等
**變數説明**:
> - other: 另一个平面
**返回**: 是否近似相等
<details>
<summary> <b>源碼</b> </summary>
@ -79,11 +79,11 @@ def approx(self, other: 'Plane3') -> bool:
**説明**: 计算平面与平面之间的夹角。
**返回**: 夹角弧度
**變數説明**:
> - other: 另一个平面
**返回**: 夹角弧度
**抛出**:
> - TypeError 不支持的类型
@ -117,11 +117,11 @@ def cal_angle(self, other: 'Line3 | Plane3') -> 'AnyAngle':
**説明**: 计算平面与平面或点之间的距离。
**返回**: 距离
**變數説明**:
> - other: 另一个平面或点
**返回**: 距离
**抛出**:
> - TypeError 不支持的类型
@ -155,11 +155,11 @@ def cal_distance(self, other: 'Plane3 | Point3') -> float:
**説明**: 计算两平面的交线。
**返回**: 两平面的交线
**變數説明**:
> - other: 另一个平面
**返回**: 两平面的交线
<details>
<summary> <b>源碼</b> </summary>
@ -200,11 +200,11 @@ def cal_intersection_line3(self, other: 'Plane3') -> 'Line3':
**説明**: 计算平面与直线的交点。
**返回**: 交点
**變數説明**:
> - other: 不与平面平行或在平面上的直线
**返回**: 交点
**抛出**:
> - ValueError 平面与直线平行或重合
@ -237,11 +237,11 @@ def cal_intersection_point3(self, other: 'Line3') -> 'Point3':
**説明**: 计算平行于该平面且过指定点的平面。
**返回**: 所求平面
**變數説明**:
> - point: 指定点
**返回**: 所求平面
<details>
<summary> <b>源碼</b> </summary>
@ -265,11 +265,11 @@ def cal_parallel_plane3(self, point: 'Point3') -> 'Plane3':
**説明**: 判断两个平面是否平行。
**返回**: 是否平行
**變數説明**:
> - other: 另一个平面
**返回**: 是否平行
<details>
<summary> <b>源碼</b> </summary>
@ -319,12 +319,12 @@ def normal(self) -> 'Vector3':
**説明**: 工厂函数 由点和法向量构造平面(点法式构造)。
**返回**: 平面
**變數説明**:
> - point: 平面上的一点
> - normal: 法向量
**返回**: 平面
<details>
<summary> <b>源碼</b> </summary>
@ -353,13 +353,13 @@ def from_point_and_normal(cls, point: 'Point3', normal: 'Vector3') -> 'Plane3':
**説明**: 工厂函数 由三点构造平面。
**返回**: 平面
**變數説明**:
> - p1: 点1
> - p2: 点2
> - p3: 点3
**返回**: 平面
<details>
<summary> <b>源碼</b> </summary>
@ -390,12 +390,12 @@ def from_three_points(cls, p1: 'Point3', p2: 'Point3', p3: 'Point3') -> 'Plane3'
**説明**: 工厂函数 由两直线构造平面。
**返回**: 平面
**變數説明**:
> - l1: 直线1
> - l2: 直线2
**返回**: 平面
<details>
<summary> <b>源碼</b> </summary>
@ -426,12 +426,12 @@ def from_two_lines(cls, l1: 'Line3', l2: 'Line3') -> 'Plane3':
**説明**: 工厂函数 由点和直线构造平面。
**返回**: 平面
**變數説明**:
> - point: 面上一点
> - line: 面上直线,不包含点
**返回**: 平面
<details>
<summary> <b>源碼</b> </summary>
@ -485,11 +485,11 @@ def __and__(self, other: 'Plane3') -> 'Line3 | None':
**説明**: 取两平面的交集(人话:交线)
**返回**: 不平行平面的交线平面平行返回None
**變數説明**:
> - other:
**返回**: 不平行平面的交线平面平行返回None
<details>
<summary> <b>源碼</b> </summary>

View File

@ -38,12 +38,12 @@ def __init__(self, x: float, y: float, z: float):
**説明**: 判断两个点是否近似相等。
**返回**: 是否近似相等
**變數説明**:
> - other:
> - epsilon:
**返回**: 是否近似相等
<details>
<summary> <b>源碼</b> </summary>

View File

@ -7,13 +7,13 @@ title: mbcp.mp_math.utils
**説明**: 区间限定函数
**返回**: 限制后的值
**變數説明**:
> - x: 待限定的值
> - min_: 最小值
> - max_: 最大值
**返回**: 限制后的值
<details>
<summary> <b>源碼</b> </summary>
@ -40,13 +40,13 @@ def clamp(x: float, min_: float, max_: float) -> float:
**説明**: 判断两个数是否近似相等。或包装一个实数用于判断是否近似于0。
**返回**: 是否近似相等
**變數説明**:
> - x: 数1
> - y: 数2
> - epsilon: 误差
**返回**: 是否近似相等
<details>
<summary> <b>源碼</b> </summary>
@ -72,12 +72,12 @@ def approx(x: float, y: float=0.0, epsilon: float=APPROX) -> bool:
**説明**: 获取数的符号。
**返回**: 符号 + - ""
**變數説明**:
> - x: 数
> - only_neg: 是否只返回负数的符号
**返回**: 符号 + - ""
<details>
<summary> <b>源碼</b> </summary>
@ -109,12 +109,12 @@ def sign(x: float, only_neg: bool=False) -> str:
1 -> +1
0 -> ""
**返回**: 符号 + - ""
**變數説明**:
> - x: 数
> - only_neg: 是否只返回负数的符号
**返回**: 符号 + - ""
<details>
<summary> <b>源碼</b> </summary>

View File

@ -38,12 +38,12 @@ def __init__(self, x: float, y: float, z: float):
**説明**: 判断两个向量是否近似相等。
**返回**: 是否近似相等
**變數説明**:
> - other:
> - epsilon:
**返回**: 是否近似相等
<details>
<summary> <b>源碼</b> </summary>
@ -69,11 +69,11 @@ def approx(self, other: 'Vector3', epsilon: float=APPROX) -> bool:
**説明**: 计算两个向量之间的夹角。
**返回**: 夹角
**變數説明**:
> - other: 另一个向量
**返回**: 夹角
<details>
<summary> <b>源碼</b> </summary>
@ -101,11 +101,11 @@ def cal_angle(self, other: 'Vector3') -> 'AnyAngle':
其余结果的模为平行四边形的面积。
**返回**: 行列式的结果
**變數説明**:
> - other:
**返回**: 行列式的结果
<details>
<summary> <b>源碼</b> </summary>
@ -141,12 +141,12 @@ def cross(self, other: 'Vector3') -> 'Vector3':
**説明**: 判断两个向量是否近似平行。
**返回**: 是否近似平行
**變數説明**:
> - other: 另一个向量
> - epsilon: 允许的误差
**返回**: 是否近似平行
<details>
<summary> <b>源碼</b> </summary>
@ -171,11 +171,11 @@ def is_approx_parallel(self, other: 'Vector3', epsilon: float=APPROX) -> bool:
**説明**: 判断两个向量是否平行。
**返回**: 是否平行
**變數説明**:
> - other: 另一个向量
**返回**: 是否平行
<details>
<summary> <b>源碼</b> </summary>
@ -370,11 +370,11 @@ def __add__(self, other):
**説明**: 判断两个向量是否相等。
**返回**: 是否相等
**變數説明**:
> - other:
**返回**: 是否相等
<details>
<summary> <b>源碼</b> </summary>

View File

@ -9,12 +9,12 @@ title: mbcp.presets.model
**説明**: 生成球体上的点集。
**返回**: List[Point3]: 球体上的点集。
**變數説明**:
> - radius:
> - density:
**返回**: List[Point3]: 球体上的点集。
<details>
<summary> <b>源碼</b> </summary>

View File

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Time : 2024/8/28 下午12:52
@Author : snowykami
@Email : snowykami@outlook.com
@File : __init__.py.py
@Software: PyCharm
"""

View File

@ -1,46 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Time : 2024/8/28 下午4:08
@Author : snowykami
@Email : snowykami@outlook.com
@File : __main__.py
@Software: PyCharm
"""
# command line tool
# args[0] path
# -o|--output output path
# -l|--lang zh-Hans en jp default zh-Hans
import argparse
import os
import sys
from litedoc.output import generate_from_module
def main():
parser = argparse.ArgumentParser(description="Generate documentation from Python modules.")
parser.add_argument("path", type=str, help="Path to the Python module or package.")
parser.add_argument("-o", "--output", default="doc-output", type=str, help="Output directory.")
parser.add_argument("-c", "--contain-top", action="store_true", help="Whether to contain top-level dir in output dir.")
parser.add_argument("-l", "--lang", nargs='+', default=["zh-Hans"], type=str, help="Languages of the document.")
args = parser.parse_args()
if not os.path.exists(args.path):
print(f"Error: The path {args.path} does not exist.")
sys.exit(1)
if not os.path.exists(args.output):
os.makedirs(args.output)
langs = args.lang
for lang in langs:
generate_from_module(args.path, args.output, with_top=args.contain_top, lang=lang)
if __name__ == '__main__':
main()

View File

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Time : 2024/8/28 下午1:46
@Author : snowykami
@Email : snowykami@outlook.com
@File : __init__.py.py
@Software: PyCharm
"""

View File

@ -1,154 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Time : 2024/8/28 下午1:46
@Author : snowykami
@Email : snowykami@outlook.com
@File : docstring.py
@Software: PyCharm
"""
from typing import Optional
from pydantic import BaseModel, Field
from litedoc.i18n import get_text
class Attr(BaseModel):
name: str
type: str = ""
desc: str = ""
class Args(BaseModel):
name: str
type: str = ""
desc: str = ""
class Return(BaseModel):
desc: str = ""
class Exception_(BaseModel):
name: str
desc: str = ""
class Raise(BaseModel):
exceptions: list[Exception_] = []
class Example(BaseModel):
desc: str = ""
input: str = ""
output: str = ""
class Docstring(BaseModel):
desc: str = ""
args: list[Args] = []
attrs: list[Attr] = []
return_: Optional[Return] = None
raise_: list[Exception_] = []
example: list[Example] = []
def add_desc(self, desc: str):
if self.desc == "":
self.desc = desc
else:
self.desc += "\n" + desc
def add_arg(self, name: str, type_: str = "", desc: str = ""):
self.args.append(Args(name=name, type=type_, desc=desc))
def add_attrs(self, name: str, type_: str = "", desc: str = ""):
self.attrs.append(Attr(name=name, type=type_, desc=desc))
def add_return(self, desc: str = ""):
self.return_ = Return(desc=desc)
def add_raise(self, name: str, desc: str = ""):
self.raise_.append(Exception_(name=name, desc=desc))
def add_example(self, desc: str = "", input_: str = "", output: str = ""):
self.example.append(Example(desc=desc, input=input_, output=output))
def reduction(self) -> str:
"""
通过解析结果还原docstring
Args:
Returns:
"""
ret = ""
ret += self.desc + "\n"
if self.args:
ret += "Args:\n"
for arg in self.args:
ret += f" {arg.name}: {arg.type}\n {arg.desc}\n"
if self.attrs:
ret += "Attributes:\n"
for attr in self.attrs:
ret += f" {attr.name}: {attr.type}\n {attr.desc}\n"
if self.return_:
ret += "Returns:\n"
ret += f" {self.return_.desc}\n"
if self.raise_:
ret += "Raises:\n"
for exception in self.raise_:
ret += f" {exception.name}\n {exception.desc}\n"
if self.example:
ret += "Examples:\n"
for example in self.example:
ret += f" {example.desc}\n Input: {example.input}\n Output: {example.output}\n"
return ret
def markdown(self, lang: str, indent: int = 4, is_classmethod: bool = False) -> str:
"""
生成markdown文档
Args:
is_classmethod:
lang:
indent:
Returns:
"""
PREFIX = "" * indent
ret = ""
# ret += self.desc + "\n\n"
# print(self.reduction())
# print(self.desc, self.return_)
# 单数属性
if self.desc:
ret += PREFIX + f"\n**{get_text(lang, 'desc')}**: {self.desc}\n"
if self.return_ is not None:
ret += PREFIX + f"\n**{get_text(lang, 'docstring.return')}**: {self.return_.desc}\n"
# 复数属性
if self.args:
ret += PREFIX + f"\n**{get_text(lang, 'docstring.args')}**:\n"
for arg in self.args:
ret += PREFIX + f"> - {arg.name}: {arg.type} {arg.desc}\n"
if self.attrs:
ret += PREFIX + f"\n**{get_text(lang, 'docstring.attrs')}**:\n"
for attr in self.attrs:
ret += PREFIX + f"> - {attr.name}: {attr.type} {attr.desc}\n"
if self.raise_:
ret += PREFIX + f"\n**{get_text(lang, 'docstring.raises')}**:\n"
for exception in self.raise_:
ret += PREFIX + f"> - {exception.name} {exception.desc}\n"
if self.example:
ret += PREFIX + f"\n**{get_text(lang, 'docstring.example')}**:\n"
for example in self.example:
ret += PREFIX + f" - {example.desc}\n> **{get_text(lang, 'docs.input')}**: {example.input}\n> **{get_text(lang, 'docs.output')}**: {example.output}\n"
return ret
def __str__(self):
return self.desc

View File

@ -1,192 +0,0 @@
"""
Google docstring parser for Python.
"""
from typing import Optional
from litedoc.docstring.docstring import Docstring
class Parser:
...
class GoogleDocstringParser(Parser):
_tokens = {
"Args" : "args",
"Arguments" : "args",
"参数" : "args",
"Return" : "return",
"Returns" : "return",
"返回" : "return",
"Attribute" : "attribute",
"Attributes" : "attribute",
"属性" : "attribute",
"Raises" : "raises",
"Raise" : "raises",
"引发" : "raises",
"Example" : "example",
"Examples" : "example",
"示例" : "example",
"Yields" : "yields",
"Yield" : "yields",
"产出" : "yields",
"Requires" : "requires",
"Require" : "requires",
"需要" : "requires",
"FrontMatter": "front_matter",
"前言" : "front_matter",
}
def __init__(self, docstring: str, indent: int = 4):
self.lines = docstring.splitlines()
self.indent = indent
self.lineno = 0 # Current line number
self.char = 0 # Current character position
self.docstring = Docstring()
def read_line(self, move: bool = True) -> str:
"""
每次读取一行
Args:
move: 是否移动指针
Returns:
"""
if self.lineno >= len(self.lines):
return ""
line = self.lines[self.lineno]
if move:
self.lineno += 1
return line
def match_token(self) -> Optional[str]:
"""
解析下一行的token
Returns:
"""
for token in self._tokens:
line = self.read_line(move=False)
if line.strip().startswith(token):
self.lineno += 1
return self._tokens[token]
return None
def parse_args(self):
"""
依次解析后面的参数行直到缩进小于等于当前行的缩进
"""
while line := self.match_next_line():
if ":" in line:
name, desc = line.split(":", 1)
self.docstring.add_arg(name.strip(), desc.strip())
else:
self.docstring.add_arg(line.strip())
def parse_return(self):
"""
解析返回值行
"""
if line := self.match_next_line():
self.docstring.add_return(line.strip())
def parse_raises(self):
"""
解析异常行
"""
while line := self.match_next_line():
if ":" in line:
name, desc = line.split(":", 1)
self.docstring.add_raise(name.strip(), desc.strip())
else:
self.docstring.add_raise(line.strip())
def parse_example(self):
"""
解析示例行
"""
while line := self.match_next_line():
if ":" in line:
name, desc = line.split(":", 1)
self.docstring.add_example(name.strip(), desc.strip())
else:
self.docstring.add_example(line.strip())
def parse_attrs(self):
"""
解析属性行
"""
while line := self.match_next_line():
if ":" in line:
name, desc = line.split(":", 1)
self.docstring.add_attrs(name.strip(), desc.strip())
else:
self.docstring.add_attrs(line.strip())
def match_next_line(self) -> Optional[str]:
"""
在一个子解析器中解析下一行直到缩进小于等于当前行的缩进
Returns:
"""
line = self.read_line(move=False)
if line.startswith(" " * self.indent):
self.lineno += 1
return line[self.indent:]
else:
return None
def parse(self) -> Docstring:
"""
逐行解析直到遇到EOS
最开始未解析到的内容全部加入desc
Returns:
"""
add_desc = True
while self.lineno < len(self.lines):
token = self.match_token()
if token is None and add_desc:
self.docstring.add_desc(self.lines[self.lineno].strip())
if token is not None:
add_desc = False
match token:
case "args":
self.parse_args()
case "return":
self.parse_return()
case "attribute":
self.parse_attrs()
case "raises":
self.parse_raises()
case "example":
self.parse_example()
case _:
self.lineno += 1
return self.docstring
class NumpyDocstringParser(Parser):
...
class ReStructuredParser(Parser):
...
def parse(docstring: str, parser: str = "google", indent: int = 4) -> Docstring:
if parser == "google":
return GoogleDocstringParser(docstring, indent).parse()
else:
raise ValueError(f"Unknown parser: {parser}")

View File

@ -1,131 +0,0 @@
# -*- coding: utf-8 -*-
"""
Internationalization module.
"""
from typing import Optional, TypeAlias
NestedDict: TypeAlias = dict[str, 'str | NestedDict']
i18n_dict: dict[str, NestedDict] = {
"en" : {
"docstring": {
"args" : "Arguments",
"return" : "Return",
"attribute": "Attribute",
"raises" : "Raises",
"example" : "Examples",
"yields" : "Yields",
},
"src": "Source code",
"desc": "Description",
"type": "Type",
},
"zh-Hans": {
"docstring": {
"args" : "参数",
"return" : "返回",
"attribute": "属性",
"raises" : "引发",
"example" : "示例",
"yields" : "产出",
},
"src": "源代码",
"desc": "说明",
"type": "类型",
},
"zh-Hant": {
"docstring": {
"args" : "變數説明",
"return" : "返回",
"attribute": "屬性",
"raises" : "抛出",
"example" : "範例",
"yields" : "產出",
},
"src": "源碼",
"desc": "説明",
"type": "類型",
},
"ja" : {
"docstring": {
"args" : "引数",
"return" : "戻り値",
"attribute": "属性",
"raises" : "例外",
"example" : "",
"yields" : "生成",
},
"src": "ソースコード",
"desc": "説明",
"type": "タイプ",
},
}
def flat_i18n_dict(data: dict[str, NestedDict]) -> dict[str, dict[str, str]]:
"""
Flatten i18n_dict.
Examples:
```python
{
"en": {
"docs": {
"key1": "val1",
"key2": "val2",
}
}
}
```
to
```python
{
"en": {
"docs.key1": "val1",
"docs.key2": "val2",
}
}
```
Returns:
"""
ret: dict[str, dict[str, str]] = {}
def _flat(_lang_data: NestedDict) -> dict[str, str]:
res = {}
for k, v in _lang_data.items():
if isinstance(v, dict):
for kk, vv in _flat(v).items():
res[f"{k}.{kk}"] = vv
else:
res[k] = v
return res
for lang, lang_data in data.items():
ret[lang] = _flat(lang_data)
return ret
i18n_flat_dict = flat_i18n_dict(i18n_dict)
def get_text(lang: str, key: str, default: Optional[str] = None, fallback: Optional[str] = "en") -> str:
"""
Get text from i18n_dict.
Args:
lang: language name
key: text key
default: default text, if None return fallback language or key
fallback: fallback language, priority is higher than default
Returns:
str: text
"""
if lang in i18n_flat_dict:
if key in i18n_flat_dict[lang]:
return i18n_flat_dict[lang][key]
if fallback is not None:
return i18n_flat_dict.get(fallback, {}).get(key, default or key)
else:
return default or key

View File

@ -1,107 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Time : 2024/8/28 下午3:59
@Author : snowykami
@Email : snowykami@outlook.com
@File : output.py
@Software: PyCharm
"""
import os.path
from litedoc.style.markdown import generate
from litedoc.syntax.astparser import AstParser
def write_to_file(content: str, output: str) -> None:
"""
Write content to file.
Args:
content: str, content to write.
output: str, path to output file.
"""
if not os.path.exists(os.path.dirname(output)):
os.makedirs(os.path.dirname(output))
with open(output, "w", encoding="utf-8") as f:
f.write(content)
def get_file_list(module_folder: str):
file_list = []
for root, dirs, files in os.walk(module_folder):
for file in files:
if file.endswith((".py", ".pyi")):
file_list.append(os.path.join(root, file))
return file_list
def get_relative_path(base_path: str, target_path: str) -> str:
"""
获取相对路径
Args:
base_path: 基础路径
target_path: 目标路径
"""
return os.path.relpath(target_path, base_path)
def generate_from_module(module_folder: str, output_dir: str, with_top: bool = False, lang: str = "zh-Hans", ignored_paths=None):
"""
生成文档
Args:
module_folder: 模块文件夹
output_dir: 输出文件夹
with_top: 是否包含顶层文件夹 False时例如docs/api/module_a, docs/api/module_b True时例如docs/api/module/module_a.md docs/api/module/module_b.md
ignored_paths: 忽略的路径
lang: 语言
"""
if ignored_paths is None:
ignored_paths = []
file_data: dict[str, str] = {} # 路径 -> 字串
file_list = get_file_list(module_folder)
# 清理输出目录
if not os.path.exists(output_dir):
os.makedirs(output_dir)
replace_data = {
"__init__": "index",
".py" : ".md",
}
for pyfile_path in file_list:
if any(ignored_path.replace("\\", "/") in pyfile_path.replace("\\", "/") for ignored_path in ignored_paths):
continue
no_module_name_pyfile_path = get_relative_path(module_folder, pyfile_path) # 去头路径
# markdown相对路径
rel_md_path = pyfile_path if with_top else no_module_name_pyfile_path
for rk, rv in replace_data.items():
rel_md_path = rel_md_path.replace(rk, rv)
abs_md_path = os.path.join(output_dir, rel_md_path)
# 获取模块信息
ast_parser = AstParser(open(pyfile_path, "r", encoding="utf-8").read())
# 生成markdown
front_matter = {
"title" : pyfile_path.replace("\\", "/").
replace("/", ".").
replace(".py", "").
replace(".__init__", ""),
}
md_content = generate(ast_parser, lang=lang, frontmatter=front_matter)
print(f"Generate {pyfile_path} -> {abs_md_path}")
file_data[abs_md_path] = md_content
for fn, content in file_data.items():
write_to_file(content, fn)

View File

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Time : 2024/8/28 下午3:39
@Author : snowykami
@Email : snowykami@outlook.com
@File : __init__.py.py
@Software: PyCharm
"""

View File

@ -1,59 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Time : 2024/8/28 下午3:39
@Author : snowykami
@Email : snowykami@outlook.com
@File : markdown.py
@Software: PyCharm
"""
from typing import Optional
from litedoc.syntax.astparser import AstParser
from litedoc.syntax.node import *
from litedoc.i18n import get_text
def generate(parser: AstParser, lang: str, frontmatter: Optional[dict] = None) -> str:
"""
Generate markdown style document from ast
You can modify this function to generate markdown style that enjoys you
Args:
parser:
lang: language
frontmatter:
Returns:
markdown style document
"""
if frontmatter is not None:
md = "---\n"
for k, v in frontmatter.items():
md += f"{k}: {v}\n"
md += "---\n"
else:
md = ""
# var > func > class
"""遍历函数"""
for func in parser.functions:
if func.name.startswith("_"):
continue
md += func.markdown(lang)
"""遍历类"""
for cls in parser.classes:
md += cls.markdown(lang)
"""遍历变量"""
for var in parser.variables:
md += f"### ***var*** `{var.name} = {var.value}`\n\n"
if var.type != TypeHint.NO_TYPEHINT:
md += f"- **{get_text(lang, 'type')}**: `{var.type}`\n\n"
if var.docs is not None:
md += f"- **{get_text(lang, 'desc')}**: {var.docs}\n\n"
return md

View File

@ -1,293 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Time : 2024/8/28 下午2:13
@Author : snowykami
@Email : snowykami@outlook.com
@File : astparser.py
@Software: PyCharm
"""
import ast
import inspect
from .node import *
from ..docstring.parser import parse
class AstParser:
def __init__(self, code: str):
self.code = code
self.tree = ast.parse(code)
self.classes: list[ClassNode] = []
self.functions: list[FunctionNode] = []
self.variables: list[AssignNode] = []
self.parse()
@staticmethod
def clear_quotes(s: str) -> str:
"""
去除类型注解中的引号
Args:
s:
Returns:
"""
return s.replace("'", "").replace('"', "")
def get_line_content(self, lineno: int, ignore_index_out: bool = True) -> str:
"""获取代码行内容
Args:
lineno: 行号
ignore_index_out: 是否忽略索引越界
Returns:
代码行内容
"""
if ignore_index_out:
if lineno < 1 or lineno > len(self.code.split("\n")):
return ""
return self.code.split("\n")[lineno - 1]
@staticmethod
def match_line_docs(linecontent: str) -> str:
"""匹配行内注释
Args:
linecontent: 行内容
Returns:
文档字符串
"""
in_string = False
string_char = ''
for i, char in enumerate(linecontent):
if char in ('"', "'"):
if in_string:
if char == string_char:
in_string = False
else:
in_string = True
string_char = char
elif char == '#' and not in_string:
return linecontent[i + 1:].strip()
return ""
def parse(self):
for node in ast.walk(self.tree):
if isinstance(node, ast.ClassDef):
if not self._is_module_level_class(node):
continue
class_node = ClassNode(
name=node.name,
docs=parse(ast.get_docstring(node)) if ast.get_docstring(node) else None,
inherits=[ast.unparse(base) for base in node.bases]
)
self.classes.append(class_node)
# 继续遍历类内部的函数
for sub_node in node.body:
if isinstance(sub_node, (ast.FunctionDef, ast.AsyncFunctionDef)):
class_node.methods.append(FunctionNode(
name=sub_node.name,
docs=parse(ast.get_docstring(sub_node)) if ast.get_docstring(sub_node) else None,
posonlyargs=[
ArgNode(
name=arg.arg,
type=self.clear_quotes(ast.unparse(arg.annotation).strip()) if arg.annotation else TypeHint.NO_TYPEHINT,
)
for arg in sub_node.args.posonlyargs
],
args=[
ArgNode(
name=arg.arg,
type=self.clear_quotes(ast.unparse(arg.annotation).strip()) if arg.annotation else TypeHint.NO_TYPEHINT,
)
for arg in sub_node.args.args
],
kwonlyargs=[
ArgNode(
name=arg.arg,
type=self.clear_quotes(ast.unparse(arg.annotation).strip()) if arg.annotation else TypeHint.NO_TYPEHINT,
)
for arg in sub_node.args.kwonlyargs
],
kw_defaults=[
ConstantNode(
value=ast.unparse(default).strip() if default else TypeHint.NO_DEFAULT
)
for default in sub_node.args.kw_defaults
],
defaults=[
ConstantNode(
value=ast.unparse(default).strip() if default else TypeHint.NO_DEFAULT
)
for default in sub_node.args.defaults
],
return_=self.clear_quotes(ast.unparse(sub_node.returns).strip()) if sub_node.returns else TypeHint.NO_RETURN,
decorators=[ast.unparse(decorator).strip() for decorator in sub_node.decorator_list],
is_async=isinstance(sub_node, ast.AsyncFunctionDef),
src=ast.unparse(sub_node).strip(),
is_classmethod=True
))
# elif isinstance(sub_node, (ast.Assign, ast.AnnAssign)):
# if isinstance(sub_node, ast.Assign):
# class_node.attrs.append(AttrNode(
# name=sub_node.targets[0].id, # type: ignore
# type=TypeHint.NO_TYPEHINT,
# value=ast.unparse(sub_node.value).strip()
# ))
# elif isinstance(sub_node, ast.AnnAssign):
# class_node.attrs.append(AttrNode(
# name=sub_node.target.id,
# type=ast.unparse(sub_node.annotation).strip(),
# value=ast.unparse(sub_node.value).strip() if sub_node.value else TypeHint.NO_DEFAULT
# ))
# else:
# raise ValueError(f"Unsupported node type: {type(sub_node)}")
elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
# 仅打印模块级别的函数
if not self._is_module_level_function(node):
continue
self.functions.append(FunctionNode(
name=node.name,
docs=parse(ast.get_docstring(node)) if ast.get_docstring(node) else None,
posonlyargs=[
ArgNode(
name=arg.arg,
type=self.clear_quotes(ast.unparse(arg.annotation).strip()) if arg.annotation else TypeHint.NO_TYPEHINT,
)
for arg in node.args.posonlyargs
],
args=[
ArgNode(
name=arg.arg,
type=self.clear_quotes(ast.unparse(arg.annotation).strip()) if arg.annotation else TypeHint.NO_TYPEHINT,
)
for arg, default in zip(node.args.args, node.args.defaults)
],
kwonlyargs=[
ArgNode(
name=arg.arg,
type=self.clear_quotes(ast.unparse(arg.annotation).strip()) if arg.annotation else TypeHint.NO_TYPEHINT,
)
for arg in node.args.kwonlyargs
],
kw_defaults=[
ConstantNode(
value=ast.unparse(default).strip() if default else TypeHint.NO_DEFAULT
)
for default in node.args.kw_defaults
],
defaults=[
ConstantNode(
value=ast.unparse(default).strip() if default else TypeHint.NO_DEFAULT
)
for default in node.args.defaults
],
return_=self.clear_quotes(ast.unparse(node.returns).strip()) if node.returns else TypeHint.NO_RETURN,
decorators=[ast.unparse(decorator).strip() for decorator in node.decorator_list],
is_async=isinstance(node, ast.AsyncFunctionDef),
src=ast.unparse(node).strip()
))
elif isinstance(node, (ast.Assign, ast.AnnAssign)):
if not self._is_module_level_variable2(node):
continue
else:
pass
lineno = node.lineno
prev_line = self.get_line_content(lineno - 1).strip()
curr_line = self.get_line_content(lineno).strip()
next_line = self.get_line_content(lineno + 1).strip()
# 获取文档字符串,优先检测下行"""
if next_line.startswith('"""'):
docs = next_line[3:-3]
elif prev_line.startswith('"""'):
docs = prev_line[3:-3]
else:
curr_docs = self.match_line_docs(curr_line)
if curr_docs:
docs = curr_docs
else:
docs = None
# if isinstance(node, ast.Assign):
# for target in node.targets:
# if isinstance(target, ast.Name):
# self.variables.append(AssignNode(
# name=target.id,
# value=ast.unparse(node.value).strip(),
# type=ast.unparse(node.annotation).strip() if isinstance(node, ast.AnnAssign) else TypeHint.NO_TYPEHINT
# ))
if isinstance(node, ast.AnnAssign):
self.variables.append(AssignNode(
name=node.target.id,
value=ast.unparse(node.value).strip() if node.value else TypeHint.NO_DEFAULT,
type=ast.unparse(node.annotation).strip(),
docs=docs
))
def _is_module_level_function(self, node: ast.FunctionDef | ast.AsyncFunctionDef):
for parent in ast.walk(self.tree):
if isinstance(parent, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
if node in parent.body:
return False
return True
def _is_module_level_class(self, node: ast.ClassDef):
for parent in ast.walk(self.tree):
if isinstance(parent, ast.ClassDef):
if node in parent.body:
return False
return True
def _is_module_level_variable(self, node: ast.Assign | ast.AnnAssign):
"""在类方法或函数内部的变量不会被记录"""
# for parent in ast.walk(self.tree):
# if isinstance(parent, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
# if node in parent.body:
# return False
# else:
# for sub_node in parent.body:
# if isinstance(sub_node, (ast.FunctionDef, ast.AsyncFunctionDef)):
# if node in sub_node.body:
# return False
# return True
# 递归检查
def _check(_node, _parent):
if isinstance(_parent, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
if _node in _parent.body:
return False
else:
for sub_node in _parent.body:
if isinstance(sub_node, (ast.FunctionDef, ast.AsyncFunctionDef)):
return _check(_node, sub_node)
return True
for parent in ast.walk(self.tree):
if not _check(node, parent):
return False
return True
def _is_module_level_variable2(self, node: ast.Assign | ast.AnnAssign) -> bool:
"""
检查变量是否在模块级别定义
"""
for parent in ast.walk(self.tree):
if isinstance(parent, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
if node in parent.body:
return False
return True
def __str__(self):
s = ""
for cls in self.classes:
s += f"class {cls.name}:\n"
for func in self.functions:
s += f"def {func.name}:\n"
for var in self.variables:
s += f"{var.name} = {var.value}\n"
return s

View File

@ -1,314 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Time : 2024/8/28 下午2:14
@Author : snowykami
@Email : snowykami@outlook.com
@File : node.py
@Software: PyCharm
"""
from typing import Literal, Optional
from enum import Enum
from pydantic import BaseModel, Field
from litedoc.docstring.docstring import Docstring
from litedoc.i18n import get_text
class TypeHint:
NO_TYPEHINT = "NO_TYPE_HINT"
NO_DEFAULT = "NO_DEFAULT"
NO_RETURN = "NO_RETURN"
class AssignNode(BaseModel):
"""
AssignNode is a pydantic model that represents an assignment.
Attributes:
name: str
The name of the assignment.
type: str = ""
The type of the assignment.
value: str
The value of the assignment.
"""
name: str
type: str = ""
value: str
docs: Optional[str] = ""
class ArgNode(BaseModel):
"""
ArgNode is a pydantic model that represents an argument.
Attributes:
name: str
The name of the argument.
type: str = ""
The type of the argument.
default: str = ""
The default value of the argument.
"""
name: str
type: str = TypeHint.NO_TYPEHINT
class AttrNode(BaseModel):
"""
AttrNode is a pydantic model that represents an attribute.
Attributes:
name: str
The name of the attribute.
type: str = ""
The type of the attribute.
value: str = ""
The value of the attribute
"""
name: str
type: str = ""
value: str = ""
class ImportNode(BaseModel):
"""
ImportNode is a pydantic model that represents an import statement.
Attributes:
name: str
The name of the import statement.
as_: str = ""
The alias of the import
"""
name: str
as_: str = ""
class ConstantNode(BaseModel):
"""
ConstantNode is a pydantic model that represents a constant.
Attributes:
value: str
The value of the constant.
"""
value: str
class FunctionNode(BaseModel):
"""
FunctionNode is a pydantic model that represents a function.
Attributes:
name: str
The name of the function.
docs: str = ""
The docstring of the function.
args: list[ArgNode] = []
The arguments of the function.
return_: ReturnNode = None
The return value of the function.
decorators: list[str] = []
The decorators of the function.
is_async: bool = False
Whether the function is asynchronous.
"""
name: str
docs: Optional[Docstring] = None
posonlyargs: list[ArgNode] = []
args: list[ArgNode] = []
kwonlyargs: list[ArgNode] = []
kw_defaults: list[ConstantNode] = []
defaults: list[ConstantNode] = []
return_: str = TypeHint.NO_RETURN
decorators: list[str] = []
src: str
is_async: bool = False
is_classmethod: bool = False
magic_methods: dict[str, str] = {
"__add__" : "+",
"__radd__" : "+",
"__sub__" : "-",
"__rsub__" : "-",
"__mul__" : "*",
"__rmul__" : "*",
"__matmul__" : "@",
"__rmatmul__": "@",
"__mod__" : "%",
"__truediv__": "/",
"__rtruediv__": "/",
"__neg__" : "-",
} # 魔术方法, 例如运算符
def is_private(self):
"""
Check if the function or method is private.
Returns:
bool: True if the function or method is private, False otherwise.
"""
return self.name.startswith("_")
def is_builtin(self):
"""
Check if the function or method is a builtin function or method.
Returns:
bool: True if the function or method is a builtin function or method, False otherwise.
"""
return self.name.startswith("__") and self.name.endswith("__")
def markdown(self, lang: str, indent: int = 0) -> str:
"""
Args:
indent: int
The number of spaces to indent the markdown.
lang: str
The language of the
Returns:
markdown style document
"""
self.complete_default_args()
PREFIX = "" * indent
# if is_classmethod:
# PREFIX = "- #"
func_type = "func" if not self.is_classmethod else "method"
md = ""
# 装饰器部分
if len(self.decorators) > 0:
for decorator in self.decorators:
md += PREFIX + f"### `@{decorator}`\n"
if self.is_async:
md += PREFIX + f"### *async {func_type}* "
else:
md += PREFIX + f"### *{func_type}* "
# code start
# 配对位置参数和位置参数默认值无默认值用TypeHint.NO_DEFAULT
args: list[str] = [] # 可直接", ".join(args)得到位置参数部分
arg_i = 0
if len(self.posonlyargs) > 0:
for arg in self.posonlyargs:
arg_text = f"{arg.name}"
if arg.type != TypeHint.NO_TYPEHINT:
arg_text += f": {arg.type}"
arg_default = self.defaults[arg_i].value
if arg_default != TypeHint.NO_DEFAULT:
arg_text += f" = {arg_default}"
args.append(arg_text)
arg_i += 1
# 加位置参数分割符 /
args.append("/")
for arg in self.args:
arg_text = f"{arg.name}"
if arg.type != TypeHint.NO_TYPEHINT:
arg_text += f": {arg.type}"
arg_default = self.defaults[arg_i].value
if arg_default != TypeHint.NO_DEFAULT:
arg_text += f" = {arg_default}"
args.append(arg_text)
arg_i += 1
if len(self.kwonlyargs) > 0:
# 加关键字参数分割符 *
args.append("*")
for arg, kw_default in zip(self.kwonlyargs, self.kw_defaults):
arg_text = f"{arg.name}"
if arg.type != TypeHint.NO_TYPEHINT:
arg_text += f": {arg.type}"
if kw_default.value != TypeHint.NO_DEFAULT:
arg_text += f" = {kw_default.value}"
args.append(arg_text)
"""魔法方法"""
if self.name in self.magic_methods:
if len(args) == 2:
md += f"`{args[0]} {self.magic_methods[self.name]} {args[1]}"
elif len(args) == 1:
md += f"`{self.magic_methods[self.name]} {args[0]}"
if self.return_ != TypeHint.NO_RETURN:
md += f" => {self.return_}"
else:
md += f"`{self.name}(" # code start
md += ", ".join(args) + ")"
if self.return_ != TypeHint.NO_RETURN:
md += f" -> {self.return_}"
md += "`\n\n" # code end
"""此处预留docstring"""
if self.docs is not None:
md += f"\n{self.docs.markdown(lang, indent)}\n"
else:
pass
# 源码展示
md += PREFIX + f"\n<details>\n<summary> <b>{get_text(lang, 'src')}</b> </summary>\n\n```python\n{self.src}\n```\n</details>\n\n"
return md
def complete_default_args(self):
"""
补全位置参数默认值用无默认值插入
Returns:
"""
num = len(self.args) + len(self.posonlyargs) - len(self.defaults)
self.defaults = [ConstantNode(value=TypeHint.NO_DEFAULT) for _ in range(num)] + self.defaults
def __str__(self):
return f"def {self.name}({', '.join([f'{arg.name}: {arg.type} = {arg.default}' for arg in self.args])}) -> {self.return_}"
class ClassNode(BaseModel):
"""
ClassNode is a pydantic model that represents a class.
Attributes:
name: str
The name of the class.
docs: str = ""
The docstring of the class.
attrs: list[AttrNode] = []
The attributes of the class.
methods: list[MethodNode] = []
The methods of the class.
inherits: list["ClassNode"] = []
The classes that the class inherits from
"""
name: str
docs: Optional[Docstring] = None
attrs: list[AttrNode] = []
methods: list[FunctionNode] = []
inherits: list[str] = []
def markdown(self, lang: str) -> str:
"""
返回类的markdown文档
Args:
lang: str
The language of the
Returns:
markdown style document
"""
hidden_methods = [
"__str__",
"__repr__",
]
md = ""
md += f"### **class** `{self.name}"
if len(self.inherits) > 0:
md += f"({', '.join([cls for cls in self.inherits])})"
md += "`\n"
for method in self.methods:
if method.name in hidden_methods:
continue
md += method.markdown(lang, 2)
for attr in self.attrs:
if attr.type == TypeHint.NO_TYPEHINT:
md += f"#### ***attr*** `{attr.name} = {attr.value}`\n\n"
else:
md += f"#### ***attr*** `{attr.name}: {attr.type} = {attr.value}`\n\n"
return md

View File

@ -1,47 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Time : 2024/8/29 下午12:02
@Author : snowykami
@Email : snowykami@outlook.com
@File : translator.py
@Software: PyCharm
"""
from typing import Optional
from translate import Translator # type: ignore
# 特殊映射语言
i18n_lang2googletrans_lang = {
"zh-Hans": "zh-cn",
"zh-Hant": "zh-tw",
"en" : "en",
}
def get_google_lang(lang: str) -> str:
"""
Get google translate language
Args:
lang: language
Returns:
google translate language
"""
return i18n_lang2googletrans_lang.get(lang, lang)
def translate(text: str, lang: str, source_lang: str) -> str:
"""
Translate text to target language
Args:
source_lang:
text: text
lang: target language
Returns:
translated text
"""
if lang == source_lang:
return text
google_lang = get_google_lang(lang)
return Translator(to_lang=google_lang, from_lang=source_lang).translate(text)

46
main.py
View File

@ -1,46 +0,0 @@
from typing import overload
class Vector:
def __init__(self, x: float, y: float, z: float):
"""
向量
Args:
x: x轴分量
y: y轴分量
z: z轴分量
"""
self.x = x
self.y = y
self.z = z
@overload
def __mul__(self, other: float) -> 'Vector':
...
@overload
def __mul__(self, other: 'Vector') -> float:
...
def __mul__(self, other):
"""
点乘和数乘
Args:
other:
Returns:
"""
if isinstance(other, (float, int)):
return Vector(self.x * other, self.y * other, self.z * other)
elif isinstance(other, Vector):
return self.x * other.x + self.y * other.y + self.z * other.z
else:
raise TypeError(f"unsupported operand type(s) for *: 'Vector' and '{type(other)}'")
def __rmul__(self, other: float) -> 'Vector':
return self.__mul__(other)
v: Vector = Vector(1, 2, 3) * 3.0
v2: Vector = 3.0 * Vector(1, 2, 3)
print(v, v2)

View File

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Time : 2024/8/28 下午12:50
@Author : snowykami
@Email : snowykami@outlook.com
@File : make_docs.py
@Software: PyCharm
"""

346
mkdoc.py
View File

@ -1,346 +0,0 @@
# -*- coding: utf-8 -*-
"""
MkDocs文档生成脚本
用法 python docs/mkdoc.py
"""
import ast
import os
import shutil
from typing import Any
from enum import Enum
from pydantic import BaseModel
NO_TYPE_ANY = "Any"
NO_TYPE_HINT = "NoTypeHint"
class DefType(Enum):
FUNCTION = "function"
METHOD = "method"
STATIC_METHOD = "staticmethod"
CLASS_METHOD = "classmethod"
PROPERTY = "property"
class FunctionInfo(BaseModel):
name: str
args: list[tuple[str, str]]
return_type: str
docstring: str
source_code: str = ""
type: DefType
"""若为类中def则有"""
is_async: bool
class AttributeInfo(BaseModel):
name: str
type: str
value: Any = None
docstring: str = ""
class ClassInfo(BaseModel):
name: str
docstring: str
methods: list[FunctionInfo]
attributes: list[AttributeInfo]
inherit: list[str]
class ModuleInfo(BaseModel):
module_path: str
"""点分割模块路径 例如 liteyuki.bot"""
functions: list[FunctionInfo]
classes: list[ClassInfo]
attributes: list[AttributeInfo]
docstring: str
def get_relative_path(base_path: str, target_path: str) -> str:
"""
获取相对路径
Args:
base_path: 基础路径
target_path: 目标路径
"""
return os.path.relpath(target_path, base_path)
def write_to_files(file_data: dict[str, str]):
"""
输出文件
Args:
file_data: 文件数据 相对路径
"""
for rp, data in file_data.items():
if not os.path.exists(os.path.dirname(rp)):
os.makedirs(os.path.dirname(rp))
with open(rp, 'w', encoding='utf-8') as f:
f.write(data)
def get_file_list(module_folder: str):
file_list = []
for root, dirs, files in os.walk(module_folder):
for file in files:
if file.endswith((".py", ".pyi")):
file_list.append(os.path.join(root, file))
return file_list
def get_module_info_normal(file_path: str, ignore_private: bool = True) -> ModuleInfo:
"""
获取函数和类
Args:
file_path: Python 文件路径
ignore_private: 忽略私有函数和类
Returns:
模块信息
"""
with open(file_path, 'r', encoding='utf-8') as file:
file_content = file.read()
tree = ast.parse(file_content)
dot_sep_module_path = file_path.replace(os.sep, '.').replace(".py", "").replace(".pyi", "")
module_docstring = ast.get_docstring(tree)
module_info = ModuleInfo(
module_path=dot_sep_module_path,
functions=[],
classes=[],
attributes=[],
docstring=module_docstring if module_docstring else ""
)
for node in ast.walk(tree):
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
# 模块函数 且不在类中 若ignore_private=True则忽略私有函数
if not any(isinstance(parent, ast.ClassDef) for parent in ast.iter_child_nodes(node)) and (not ignore_private or not node.name.startswith('_')):
# 判断第一个参数是否为self或cls后期用其他办法优化
if node.args.args:
first_arg = node.args.args[0]
if first_arg.arg in ("self", "cls"):
continue
function_docstring = ast.get_docstring(node)
func_info = FunctionInfo(
name=node.name,
args=[(arg.arg, ast.unparse(arg.annotation) if arg.annotation else NO_TYPE_ANY) for arg in node.args.args],
return_type=ast.unparse(node.returns) if node.returns else "None",
docstring=function_docstring if function_docstring else "",
type=DefType.FUNCTION,
is_async=isinstance(node, ast.AsyncFunctionDef),
source_code=ast.unparse(node)
)
module_info.functions.append(func_info)
elif isinstance(node, ast.ClassDef):
# 模块类
class_docstring = ast.get_docstring(node)
class_info = ClassInfo(
name=node.name,
docstring=class_docstring if class_docstring else "",
methods=[],
attributes=[],
inherit=[ast.unparse(base) for base in node.bases]
)
for class_node in node.body:
# methods [instance, static, class property]保留__init__方法
if isinstance(class_node, ast.FunctionDef) and (not ignore_private or not class_node.name.startswith('_') or class_node.name == "__init__"):
method_docstring = ast.get_docstring(class_node)
def_type = DefType.METHOD
if class_node.decorator_list:
if any(isinstance(decorator, ast.Name) and decorator.id == "staticmethod" for decorator in class_node.decorator_list):
def_type = DefType.STATIC_METHOD
elif any(isinstance(decorator, ast.Name) and decorator.id == "classmethod" for decorator in class_node.decorator_list):
def_type = DefType.CLASS_METHOD
elif any(isinstance(decorator, ast.Name) and decorator.id == "property" for decorator in class_node.decorator_list):
def_type = DefType.PROPERTY
class_info.methods.append(FunctionInfo(
name=class_node.name,
args=[(arg.arg, ast.unparse(arg.annotation) if arg.annotation else NO_TYPE_ANY) for arg in class_node.args.args],
return_type=ast.unparse(class_node.returns) if class_node.returns else "None",
docstring=method_docstring if method_docstring else "",
type=def_type,
is_async=isinstance(class_node, ast.AsyncFunctionDef),
source_code=ast.unparse(class_node)
))
# attributes
elif isinstance(class_node, ast.Assign):
for target in class_node.targets:
if isinstance(target, ast.Name):
class_info.attributes.append(AttributeInfo(
name=target.id,
type=ast.unparse(class_node.value)
))
module_info.classes.append(class_info)
elif isinstance(node, ast.Assign):
# 检查是否在类或函数中
if not any(isinstance(parent, (ast.ClassDef, ast.FunctionDef)) for parent in ast.iter_child_nodes(node)):
# 模块属性变量
for target in node.targets:
if isinstance(target, ast.Name) and (not ignore_private or not target.id.startswith('_')):
attr_type = NO_TYPE_HINT
if isinstance(node.value, ast.AnnAssign) and node.value.annotation:
attr_type = ast.unparse(node.value.annotation)
module_info.attributes.append(AttributeInfo(
name=target.id,
type=attr_type,
value=ast.unparse(node.value) if node.value else None
))
return module_info
def generate_markdown(module_info: ModuleInfo, front_matter=None, lang: str = "zh-CN") -> str:
"""
生成模块的Markdown
你可在此自定义生成的Markdown格式
Args:
module_info: 模块信息
front_matter: 自定义选项title, index, icon, category
lang: 语言
Returns:
Markdown 字符串
"""
content = ""
front_matter = "---\n" + "\n".join([f"{k}: {v}" for k, v in front_matter.items()]) + "\n---\n\n"
content += front_matter
# 模块函数
for func in module_info.functions:
args_with_type = [f"{arg[0]}: {arg[1]}" if arg[1] else arg[0] for arg in func.args]
content += f"### ***{'async ' if func.is_async else ''}def*** `{func.name}({', '.join(args_with_type)}) -> {func.return_type}`\n\n"
func.docstring = func.docstring.replace("\n", "\n\n")
content += f"{func.docstring}\n\n"
# 函数源代码可展开区域
content += f"<details>\n<summary>源代码</summary>\n\n```python\n{func.source_code}\n```\n</details>\n\n"
# 类
for cls in module_info.classes:
if cls.inherit:
inherit = f"({', '.join(cls.inherit)})" if cls.inherit else ""
content += f"### ***class*** `{cls.name}{inherit}`\n\n"
else:
content += f"### ***class*** `{cls.name}`\n\n"
cls.docstring = cls.docstring.replace("\n", "\n\n")
content += f"{cls.docstring}\n\n"
for method in cls.methods:
# 类函数
if method.type != DefType.METHOD:
args_with_type = [f"{arg[0]}: {arg[1]}" if arg[1] else arg[0] for arg in method.args]
content += f"### &emsp; ***@{method.type.value}***\n"
else:
# self不加类型提示
args_with_type = [f"{arg[0]}: {arg[1]}" if arg[1] and arg[0] != "self" else arg[0] for arg in method.args]
content += f"### &emsp; ***{'async ' if method.is_async else ''}def*** `{method.name}({', '.join(args_with_type)}) -> {method.return_type}`\n\n"
method.docstring = method.docstring.replace("\n", "\n\n")
content += f"&emsp;{method.docstring}\n\n"
# 函数源代码可展开区域
if lang == "zh-CN":
TEXT_SOURCE_CODE = "源代码"
else:
TEXT_SOURCE_CODE = "Source Code"
content += f"<details>\n<summary>{TEXT_SOURCE_CODE}</summary>\n\n```python\n{method.source_code}\n```\n</details>\n\n"
for attr in cls.attributes:
content += f"### &emsp; ***attr*** `{attr.name}: {attr.type}`\n\n"
# 模块属性
for attr in module_info.attributes:
if attr.type == NO_TYPE_HINT:
content += f"### ***var*** `{attr.name} = {attr.value}`\n\n"
else:
content += f"### ***var*** `{attr.name}: {attr.type} = {attr.value}`\n\n"
attr.docstring = attr.docstring.replace("\n", "\n\n")
content += f"{attr.docstring}\n\n"
return content
def generate_docs(module_folder: str, output_dir: str, with_top: bool = False, lang: str = "zh-CN", ignored_paths=None):
"""
生成文档
Args:
module_folder: 模块文件夹
output_dir: 输出文件夹
with_top: 是否包含顶层文件夹 False时例如docs/api/module_a, docs/api/module_b True时例如docs/api/module/module_a.md docs/api/module/module_b.md
ignored_paths: 忽略的路径
lang: 语言
"""
if ignored_paths is None:
ignored_paths = []
file_data: dict[str, str] = {} # 路径 -> 字串
file_list = get_file_list(module_folder)
# 清理输出目录
shutil.rmtree(output_dir, ignore_errors=True)
os.mkdir(output_dir)
replace_data = {
"__init__": "index",
".py" : ".md",
}
for pyfile_path in file_list:
if any(ignored_path.replace("\\", "/") in pyfile_path.replace("\\", "/") for ignored_path in ignored_paths):
continue
no_module_name_pyfile_path = get_relative_path(module_folder, pyfile_path) # 去头路径
# markdown相对路径
rel_md_path = pyfile_path if with_top else no_module_name_pyfile_path
for rk, rv in replace_data.items():
rel_md_path = rel_md_path.replace(rk, rv)
abs_md_path = os.path.join(output_dir, rel_md_path)
# 获取模块信息
module_info = get_module_info_normal(pyfile_path)
# 生成markdown
if "index" in abs_md_path:
front_matter = {
"title" : module_info.module_path.replace(".__init__", ""),
}
else:
front_matter = {
"title" : module_info.module_path
}
md_content = generate_markdown(module_info, front_matter)
print(f"Generate {pyfile_path} -> {abs_md_path}")
file_data[abs_md_path] = md_content
write_to_files(file_data)
# 入口脚本
if __name__ == '__main__':
# 这里填入你的模块路径
generate_docs('mbcp', 'docs/api', with_top=False, ignored_paths=["liteyuki/plugins"], lang="zh-CN")
# generate_docs('mbcp', 'docs/en/api', with_top=False, ignored_paths=["liteyuki/plugins"], lang="en")

View File

@ -26,3 +26,7 @@ distribution = true
dev = [
"translate>=3.6.1",
]
[[tool.pdm.source]]
name = "pypi"
url = "https://pypi.org/simple"
include_packages = ["litedoc"]