diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index 29e7fff..e3c2e1c 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -13,7 +13,7 @@ on:
# 设置 GITHUB_TOKEN 的权限,以允许部署到 GitHub Pages
permissions:
- contents: read
+ contents: write
pages: write
id-token: write
@@ -42,7 +42,9 @@ jobs:
- name: Setup API markdown
run: |-
python -m pip install pydantic
- python docs/mkdoc.py
+ python liteyuki_autodoc mbcp -o docs/api -l zh-Hans
+ python liteyuki_autodoc mbcp -o docs/en/api -l en
+ python liteyuki_autodoc mbcp -o docs/ja/api -l ja
- name: 安装 pnpm
uses: pnpm/action-setup@v2
@@ -67,5 +69,5 @@ jobs:
uses: JamesIves/github-pages-deploy-action@v4
with:
# 这是文档部署到的分支名称
- branch: gh-pages
+ branch: docs
folder: docs/.vitepress/dist
\ No newline at end of file
diff --git a/README.md b/README.md
index 2a00fd1..38edea8 100644
--- a/README.md
+++ b/README.md
@@ -10,5 +10,4 @@ A Minecraft particle production library
## 示例
- [【特效红石音乐】童话镇~「总有一条蜿蜒在童话镇里...」](https://www.bilibili.com/video/BV1xE4m1d72j)
-- [【特效红石音乐】使一颗心免于哀伤 If I Can Stop One Heart From Breaking「崩坏:星穹铁道 EP」](https://www.bilibili.com/video/BV1B1421t7i3)
-
+- [【特效红石音乐】使一颗心免于哀伤 If I Can Stop One Heart From Breaking「崩坏:星穹铁道 EP」](https://www.bilibili.com/video/BV1B1421t7i3)
\ No newline at end of file
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
deleted file mode 100644
index 72532fd..0000000
--- a/docs/.vitepress/config.mts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { defineConfig } from 'vitepress'
-
-// https://vitepress.dev/reference/site-config
-export default defineConfig({
- title: "MBCP docs",
- description: "MBCP library docs",
- locales: {
- root: {
- label: '简体中文',
- lang: 'zh-CN'
- },
- en: {
- label: 'English',
- lang: 'en-US'
- }
- },
- themeConfig: {
- // https://vitepress.dev/reference/default-theme-config
- nav: [
- { text: 'Home', link: '/' },
- { text: 'Examples', link: '/markdown-examples' }
- ],
-
- sidebar: [
- {
- text: 'Examples',
- items: [
- { text: 'Markdown Examples', link: '/markdown-examples' },
- { text: 'Runtime API Examples', link: '/api-examples' }
- ]
- }
- ],
-
- socialLinks: [
- { icon: 'github', link: 'https://github.com/snowykami/mbcp' }
- ]
- },
- srcDir: '.'
-})
diff --git a/docs/.vitepress/config/common.ts b/docs/.vitepress/config/common.ts
index 68cac75..7d91400 100644
--- a/docs/.vitepress/config/common.ts
+++ b/docs/.vitepress/config/common.ts
@@ -1,8 +1,23 @@
import {defineConfig} from 'vitepress'
+import AutoSidebarPlugin from 'vitepress-auto-sidebar-plugin'
+
export const common = defineConfig({
title: "MBCP docs",
description: "MBCP library docs",
+ vite: {
+ plugins: [
+ AutoSidebarPlugin({
+ // 如果不指定 `srcDir`,则默认使用 `vitepress` 的 `srcDir`
+ ignoreList: [
+ 'README.md'
+ ],
+ title: {
+ mode: text => text.toLowerCase()
+ }
+ }),
+ ],
+ },
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
socialLinks: [
diff --git a/docs/.vitepress/config/zh.ts b/docs/.vitepress/config/zh.ts
index 3b98a70..8c5003f 100644
--- a/docs/.vitepress/config/zh.ts
+++ b/docs/.vitepress/config/zh.ts
@@ -1,10 +1,21 @@
import {defineConfig} from 'vitepress'
export const zh = defineConfig({
-
lang: "zh-Hans",
description: "一个用于Minecraft粒子计算和生成的库",
themeConfig: {
+ nav: [
+ {text: '快速开始', link: '/guide'},
+ {text: 'API文档', link: '/api/'},
+ {text: '实例', link: '/demo/'},
+ ],
+ // sidebar: {
+ // '/api/': {
+ // base: '/api/',
+ // items: [
+ // ]
+ // }
+ // }
},
})
\ No newline at end of file
diff --git a/docs/api/index.md b/docs/api/index.md
index f28b6d1..6d251e8 100644
--- a/docs/api/index.md
+++ b/docs/api/index.md
@@ -1,6 +1,3 @@
---
-title: mbcp.\n\ninit\n\n
-order: 1
-icon: laptop-code
-category: API
----
\ No newline at end of file
+title: mbcp
+---
diff --git a/docs/api/mp_math/angle.md b/docs/api/mp_math/angle.md
index a88b240..042a44d 100644
--- a/docs/api/mp_math/angle.md
+++ b/docs/api/mp_math/angle.md
@@ -1,26 +1,25 @@
---
-title: mbcp.mp\nmath.angle
-order: 1
-icon: laptop-code
-category: API
+title: mbcp.mp_math.angle
---
+### ***class*** `Angle`
### ***class*** `AnyAngle`
+- #### *def* `__init__(self, value: float, is_radian: bool = False)`
-### ***def*** `__init__(self, value: float, is_radian: bool) -> None`
+任意角度。
- 任意角度。
+参数:
-Args:
+value: 角度或弧度值
- value: 角度或弧度值
+is_radian: 是否为弧度,默认为否
- is_radian: 是否为弧度,默认为否
+- #
-源代码
+源码
```python
def __init__(self, value: float, is_radian: bool=False):
@@ -37,17 +36,20 @@ def __init__(self, value: float, is_radian: bool=False):
```
-### ***@property***
-### ***def*** `complementary(self: Any) -> 'AnyAngle'`
+- #### `@property`
+- #### *def* `complementary(self)`
- 余角:两角的和为90°。
-Returns:
+余角:两角的和为90°。
- 余角
+返回:
+余角
+
+
+- #
-源代码
+源码
```python
@property
@@ -61,17 +63,20 @@ def complementary(self) -> 'AnyAngle':
```
-### ***@property***
-### ***def*** `supplementary(self: Any) -> 'AnyAngle'`
+- #### `@property`
+- #### *def* `supplementary(self)`
- 补角:两角的和为180°。
-Returns:
+补角:两角的和为180°。
- 补角
+返回:
+补角
+
+
+- #
-源代码
+源码
```python
@property
@@ -85,17 +90,20 @@ def supplementary(self) -> 'AnyAngle':
```
-### ***@property***
-### ***def*** `degree(self: Any) -> float`
+- #### `@property`
+- #### *def* `degree(self)`
- 角度。
-Returns:
+角度。
- 弧度
+返回:
+弧度
+
+
+- #
-源代码
+源码
```python
@property
@@ -109,17 +117,20 @@ def degree(self) -> float:
```
-### ***@property***
-### ***def*** `minimum_positive(self: Any) -> 'AnyAngle'`
+- #### `@property`
+- #### *def* `minimum_positive(self)`
- 最小正角。
-Returns:
+最小正角。
- 最小正角度
+返回:
+最小正角度
+
+
+- #
-源代码
+源码
```python
@property
@@ -133,17 +144,20 @@ def minimum_positive(self) -> 'AnyAngle':
```
-### ***@property***
-### ***def*** `maximum_negative(self: Any) -> 'AnyAngle'`
+- #### `@property`
+- #### *def* `maximum_negative(self)`
- 最大负角。
-Returns:
+最大负角。
- 最大负角度
+返回:
+最大负角度
+
+
+- #
-源代码
+源码
```python
@property
@@ -157,17 +171,20 @@ def maximum_negative(self) -> 'AnyAngle':
```
-### ***@property***
-### ***def*** `sin(self: Any) -> float`
+- #### `@property`
+- #### *def* `sin(self)`
- 正弦值。
-Returns:
+正弦值。
- 正弦值
+返回:
+正弦值
+
+
+- #
-源代码
+源码
```python
@property
@@ -181,17 +198,20 @@ def sin(self) -> float:
```
-### ***@property***
-### ***def*** `cos(self: Any) -> float`
+- #### `@property`
+- #### *def* `cos(self)`
- 余弦值。
-Returns:
+余弦值。
- 余弦值
+返回:
+余弦值
+
+
+- #
-源代码
+源码
```python
@property
@@ -205,17 +225,20 @@ def cos(self) -> float:
```
-### ***@property***
-### ***def*** `tan(self: Any) -> float`
+- #### `@property`
+- #### *def* `tan(self)`
- 正切值。
-Returns:
+正切值。
- 正切值
+返回:
+正切值
+
+
+- #
-源代码
+源码
```python
@property
@@ -229,17 +252,20 @@ def tan(self) -> float:
```
-### ***@property***
-### ***def*** `cot(self: Any) -> float`
+- #### `@property`
+- #### *def* `cot(self)`
- 余切值。
-Returns:
+余切值。
- 余切值
+返回:
+余切值
+
+
+- #
-源代码
+源码
```python
@property
@@ -253,17 +279,20 @@ def cot(self) -> float:
```
-### ***@property***
-### ***def*** `sec(self: Any) -> float`
+- #### `@property`
+- #### *def* `sec(self)`
- 正割值。
-Returns:
+正割值。
- 正割值
+返回:
+正割值
+
+
+- #
-源代码
+源码
```python
@property
@@ -277,17 +306,20 @@ def sec(self) -> float:
```
-### ***@property***
-### ***def*** `csc(self: Any) -> float`
+- #### `@property`
+- #### *def* `csc(self)`
- 余割值。
-Returns:
+余割值。
- 余割值
+返回:
+余割值
+
+
+- #
-源代码
+源码
```python
@property
@@ -301,3 +333,117 @@ def csc(self) -> float:
```
+- #### *def* `__add__(self, other: 'AnyAngle')`
+
+- #
+
+源码
+
+```python
+def __add__(self, other: 'AnyAngle') -> 'AnyAngle':
+ return AnyAngle(self.radian + other.radian, is_radian=True)
+```
+
+
+- #### *def* `__eq__(self, other)`
+
+- #
+
+源码
+
+```python
+def __eq__(self, other):
+ return approx(self.radian, other.radian)
+```
+
+
+- #### *def* `__sub__(self, other: 'AnyAngle')`
+
+- #
+
+源码
+
+```python
+def __sub__(self, other: 'AnyAngle') -> 'AnyAngle':
+ return AnyAngle(self.radian - other.radian, is_radian=True)
+```
+
+
+- #### *def* `__mul__(self, other: float)`
+
+- #
+
+源码
+
+```python
+def __mul__(self, other: float) -> 'AnyAngle':
+ return AnyAngle(self.radian * other, is_radian=True)
+```
+
+
+- #### *def* `__repr__(self)`
+
+- #
+
+源码
+
+```python
+def __repr__(self):
+ return f'AnyAngle({self.radian}, is_radian=True)'
+```
+
+
+- #### *def* `__str__(self)`
+
+- #
+
+源码
+
+```python
+def __str__(self):
+ return f'AnyAngle({self.degree}° or {self.radian} rad)'
+```
+
+
+- #### `@overload`
+- #### *def* `__truediv__(self, other: float)`
+
+- #
+
+源码
+
+```python
+@overload
+def __truediv__(self, other: float) -> 'AnyAngle':
+ ...
+```
+
+
+- #### `@overload`
+- #### *def* `__truediv__(self, other: 'AnyAngle')`
+
+- #
+
+源码
+
+```python
+@overload
+def __truediv__(self, other: 'AnyAngle') -> float:
+ ...
+```
+
+
+- #### *def* `__truediv__(self, other)`
+
+- #
+
+源码
+
+```python
+def __truediv__(self, other):
+ if isinstance(other, AnyAngle):
+ return self.radian / other.radian
+ return AnyAngle(self.radian / other, is_radian=True)
+```
+
+
diff --git a/docs/api/mp_math/const.md b/docs/api/mp_math/const.md
index 5e9f287..888edf3 100644
--- a/docs/api/mp_math/const.md
+++ b/docs/api/mp_math/const.md
@@ -1,31 +1,15 @@
---
-title: mbcp.mp\nmath.const
-order: 1
-icon: laptop-code
-category: API
+title: mbcp.mp_math.const
---
-
### ***var*** `PI = math.pi`
-
-
### ***var*** `E = math.e`
-
-
### ***var*** `GOLDEN_RATIO = (1 + math.sqrt(5)) / 2`
-
-
### ***var*** `GAMMA = 0.5772156649015329`
-
-
### ***var*** `EPSILON = 0.0001`
-
-
### ***var*** `APPROX = 0.001`
-
-
diff --git a/docs/api/mp_math/equation.md b/docs/api/mp_math/equation.md
index cbdba56..a051e91 100644
--- a/docs/api/mp_math/equation.md
+++ b/docs/api/mp_math/equation.md
@@ -1,32 +1,25 @@
---
-title: mbcp.mp\nmath.equation
-order: 1
-icon: laptop-code
-category: API
+title: mbcp.mp_math.equation
---
+### ***var*** `result_func = get_partial_derivative_func(result_func, v, epsilon)`
+
+### *def* `get_partial_derivative_func(func: MultiVarsFunc = EPSILON)`
-### ***def*** `get_partial_derivative_func(func: MultiVarsFunc, var: int | tuple[int, ...], epsilon: Number) -> MultiVarsFunc`
求N元函数一阶偏导函数。这玩意不太稳定,慎用。
-Args:
+参数:
- func: 函数
+func: 函数
- var: 变量位置,可为整数(一阶偏导)或整数元组(高阶偏导)
+var: 变量位置,可为整数(一阶偏导)或整数元组(高阶偏导)
- epsilon: 偏移量
+epsilon: 偏移量
-Returns:
- 偏导函数
-
-Raises:
-
- ValueError: 无效变量类型
-源代码
+源码
```python
def get_partial_derivative_func(func: MultiVarsFunc, var: int | tuple[int, ...], epsilon: Number=EPSILON) -> MultiVarsFunc:
@@ -63,12 +56,11 @@ def get_partial_derivative_func(func: MultiVarsFunc, var: int | tuple[int, ...],
```
-### ***def*** `partial_derivative_func() -> Var`
-
+### *def* `partial_derivative_func()`
-源代码
+源码
```python
def partial_derivative_func(*args: Var) -> Var:
@@ -80,12 +72,11 @@ def partial_derivative_func(*args: Var) -> Var:
```
-### ***def*** `high_order_partial_derivative_func() -> Var`
-
+### *def* `high_order_partial_derivative_func()`
-源代码
+源码
```python
def high_order_partial_derivative_func(*args: Var) -> Var:
@@ -98,28 +89,32 @@ def high_order_partial_derivative_func(*args: Var) -> Var:
### ***class*** `CurveEquation`
+- #### *def* `__init__(self, x_func: OneVarFunc, y_func: OneVarFunc, z_func: OneVarFunc)`
-### ***def*** `__init__(self, x_func: OneVarFunc, y_func: OneVarFunc, z_func: OneVarFunc) -> None`
+曲线方程。
- 曲线方程。
+参数:
-:param x_func:
+x_func: x函数
-:param y_func:
+y_func: y函数
-:param z_func:
+z_func: z函数
+
+- #
-源代码
+源码
```python
def __init__(self, x_func: OneVarFunc, y_func: OneVarFunc, z_func: OneVarFunc):
"""
曲线方程。
- :param x_func:
- :param y_func:
- :param z_func:
+ Args:
+ x_func: x函数
+ y_func: y函数
+ z_func: z函数
"""
self.x_func = x_func
self.y_func = y_func
@@ -127,19 +122,48 @@ def __init__(self, x_func: OneVarFunc, y_func: OneVarFunc, z_func: OneVarFunc):
```
-### ***var*** `args_list_plus = list(args)`
+- #### *def* `__call__(self)`
+计算曲线上的点。
-### ***var*** `args_list_minus = list(args)`
+参数:
+
+*t:
+
+参数:
+- #
+
+源码
-### ***var*** `result_func = func`
+```python
+def __call__(self, *t: Var) -> Point3 | tuple[Point3, ...]:
+ """
+ 计算曲线上的点。
+ Args:
+ *t:
+ 参数
+ Returns:
+ """
+ if len(t) == 1:
+ return Point3(self.x_func(t[0]), self.y_func(t[0]), self.z_func(t[0]))
+ else:
+ return tuple([Point3(x, y, z) for x, y, z in zip(self.x_func(t), self.y_func(t), self.z_func(t))])
+```
+
+- #### *def* `__str__(self)`
-### ***var*** `result_func = get_partial_derivative_func(result_func, v, epsilon)`
-
+- #
+
+源码
+```python
+def __str__(self):
+ return 'CurveEquation()'
+```
+
diff --git a/docs/api/mp_math/index.md b/docs/api/mp_math/index.md
index 21d2307..6e3261c 100644
--- a/docs/api/mp_math/index.md
+++ b/docs/api/mp_math/index.md
@@ -1,7 +1,3 @@
---
-title: mbcp.mp\nmath.\n\ninit\n\n
-order: 1
-icon: laptop-code
-category: API
+title: mbcp.mp_math
---
-
diff --git a/docs/api/mp_math/line.md b/docs/api/mp_math/line.md
index 3ae3642..e564aa8 100644
--- a/docs/api/mp_math/line.md
+++ b/docs/api/mp_math/line.md
@@ -1,26 +1,23 @@
---
-title: mbcp.mp\nmath.line
-order: 1
-icon: laptop-code
-category: API
+title: mbcp.mp_math.line
---
-
### ***class*** `Line3`
+- #### *def* `__init__(self, point: 'Point3', direction: 'Vector3')`
-### ***def*** `__init__(self, point: 'Point3', direction: 'Vector3') -> None`
+三维空间中的直线。由一个点和一个方向向量确定。
- 三维空间中的直线。由一个点和一个方向向量确定。
+参数:
-Args:
+point: 直线上的一点
- point: 直线上的一点
+direction: 直线的方向向量
- direction: 直线的方向向量
+- #
-源代码
+源码
```python
def __init__(self, point: 'Point3', direction: 'Vector3'):
@@ -35,22 +32,21 @@ def __init__(self, point: 'Point3', direction: 'Vector3'):
```
-### ***def*** `approx(self, other: 'Line3', epsilon: float) -> bool`
+- #### *def* `approx(self, other: 'Line3', epsilon: float = APPROX)`
- 判断两条直线是否近似相等。
-Args:
+判断两条直线是否近似相等。
- other: 另一条直线
+参数:
- epsilon: 误差
+other: 另一条直线
-Returns:
+epsilon: 误差
- 是否近似相等
+- #
-源代码
+源码
```python
def approx(self, other: 'Line3', epsilon: float=APPROX) -> bool:
@@ -66,24 +62,19 @@ def approx(self, other: 'Line3', epsilon: float=APPROX) -> bool:
```
-### ***def*** `cal_angle(self, other: 'Line3') -> 'AnyAngle'`
+- #### *def* `cal_angle(self, other: 'Line3')`
- 计算直线和直线之间的夹角。
-Args:
+计算直线和直线之间的夹角。
- other: 另一条直线
+参数:
-Returns:
+other: 另一条直线
- 夹角弧度
-
-Raises:
-
- TypeError: 不支持的类型
+- #
-源代码
+源码
```python
def cal_angle(self, other: 'Line3') -> 'AnyAngle':
@@ -100,26 +91,19 @@ def cal_angle(self, other: 'Line3') -> 'AnyAngle':
```
-### ***def*** `cal_distance(self, other: 'Line3 | Point3') -> float`
-
- 计算直线和直线或点之间的距离。
-
-Args:
-
- other: 平行直线或点
+- #### *def* `cal_distance(self, other: 'Line3 | Point3')`
+计算直线和直线或点之间的距离。
-Returns:
+参数:
- 距离
+other: 平行直线或点
-Raises:
-
- TypeError: 不支持的类型
+- #
-源代码
+源码
```python
def cal_distance(self, other: 'Line3 | Point3') -> float:
@@ -149,26 +133,19 @@ def cal_distance(self, other: 'Line3 | Point3') -> float:
```
-### ***def*** `cal_intersection(self, other: 'Line3') -> 'Point3'`
+- #### *def* `cal_intersection(self, other: 'Line3')`
- 计算两条直线的交点。
-Args:
+计算两条直线的交点。
- other: 另一条直线
+参数:
-Returns:
+other: 另一条直线
- 交点
-
-Raises:
-
- ValueError: 直线平行
-
- ValueError: 直线不共面
+- #
-源代码
+源码
```python
def cal_intersection(self, other: 'Line3') -> 'Point3':
@@ -190,20 +167,19 @@ def cal_intersection(self, other: 'Line3') -> 'Point3':
```
-### ***def*** `cal_perpendicular(self, point: 'Point3') -> 'Line3'`
+- #### *def* `cal_perpendicular(self, point: 'Point3')`
- 计算直线经过指定点p的垂线。
-Args:
+计算直线经过指定点p的垂线。
- point: 指定点
+参数:
-Returns:
+point: 指定点
- 垂线
+- #
-源代码
+源码
```python
def cal_perpendicular(self, point: 'Point3') -> 'Line3':
@@ -218,20 +194,19 @@ def cal_perpendicular(self, point: 'Point3') -> 'Line3':
```
-### ***def*** `get_point(self, t: RealNumber) -> 'Point3'`
+- #### *def* `get_point(self, t: RealNumber)`
- 获取直线上的点。同一条直线,但起始点和方向向量不同,则同一个t对应的点不同。
-Args:
+获取直线上的点。同一条直线,但起始点和方向向量不同,则同一个t对应的点不同。
- t: 参数t
+参数:
-Returns:
+t: 参数t
- 点
+- #
-源代码
+源码
```python
def get_point(self, t: RealNumber) -> 'Point3':
@@ -246,16 +221,19 @@ def get_point(self, t: RealNumber) -> 'Point3':
```
-### ***def*** `get_parametric_equations(self) -> tuple[OneSingleVarFunc, OneSingleVarFunc, OneSingleVarFunc]`
+- #### *def* `get_parametric_equations(self)`
- 获取直线的参数方程。
-Returns:
+获取直线的参数方程。
- x(t), y(t), z(t)
+返回:
+x(t), y(t), z(t)
+
+
+- #
-源代码
+源码
```python
def get_parametric_equations(self) -> tuple[OneSingleVarFunc, OneSingleVarFunc, OneSingleVarFunc]:
@@ -268,22 +246,21 @@ def get_parametric_equations(self) -> tuple[OneSingleVarFunc, OneSingleVarFunc,
```
-### ***def*** `is_approx_parallel(self, other: 'Line3', epsilon: float) -> bool`
+- #### *def* `is_approx_parallel(self, other: 'Line3', epsilon: float = 1e-06)`
- 判断两条直线是否近似平行。
-Args:
+判断两条直线是否近似平行。
- other: 另一条直线
+参数:
- epsilon: 误差
+other: 另一条直线
-Returns:
+epsilon: 误差
- 是否近似平行
+- #
-源代码
+源码
```python
def is_approx_parallel(self, other: 'Line3', epsilon: float=1e-06) -> bool:
@@ -299,20 +276,19 @@ def is_approx_parallel(self, other: 'Line3', epsilon: float=1e-06) -> bool:
```
-### ***def*** `is_parallel(self, other: 'Line3') -> bool`
+- #### *def* `is_parallel(self, other: 'Line3')`
- 判断两条直线是否平行。
-Args:
+判断两条直线是否平行。
- other: 另一条直线
+参数:
-Returns:
+other: 另一条直线
- 是否平行
+- #
-源代码
+源码
```python
def is_parallel(self, other: 'Line3') -> bool:
@@ -327,20 +303,19 @@ def is_parallel(self, other: 'Line3') -> bool:
```
-### ***def*** `is_collinear(self, other: 'Line3') -> bool`
+- #### *def* `is_collinear(self, other: 'Line3')`
- 判断两条直线是否共线。
-Args:
+判断两条直线是否共线。
- other: 另一条直线
+参数:
-Returns:
+other: 另一条直线
- 是否共线
+- #
-源代码
+源码
```python
def is_collinear(self, other: 'Line3') -> bool:
@@ -355,20 +330,19 @@ def is_collinear(self, other: 'Line3') -> bool:
```
-### ***def*** `is_point_on(self, point: 'Point3') -> bool`
+- #### *def* `is_point_on(self, point: 'Point3')`
- 判断点是否在直线上。
-Args:
+判断点是否在直线上。
- point: 点
+参数:
-Returns:
+point: 点
- 是否在直线上
+- #
-源代码
+源码
```python
def is_point_on(self, point: 'Point3') -> bool:
@@ -383,22 +357,20 @@ def is_point_on(self, point: 'Point3') -> bool:
```
-### ***def*** `is_coplanar(self, other: 'Line3') -> bool`
+- #### *def* `is_coplanar(self, other: 'Line3')`
- 判断两条直线是否共面。
+判断两条直线是否共面。
充要条件:两直线方向向量的叉乘与两直线上任意一点的向量的点积为0。
-Args:
+参数:
- other: 另一条直线
+other: 另一条直线
-Returns:
-
- 是否共面
+- #
-源代码
+源码
```python
def is_coplanar(self, other: 'Line3') -> bool:
@@ -414,18 +386,18 @@ def is_coplanar(self, other: 'Line3') -> bool:
```
-### ***def*** `simplify(self) -> None`
+- #### *def* `simplify(self)`
- 简化直线方程,等价相等。
+简化直线方程,等价相等。
自体简化,不返回值。
-
-
按照可行性一次对x y z 化 0 处理,并对向量单位化
+
+- #
-源代码
+源码
```python
def simplify(self):
@@ -445,23 +417,22 @@ def simplify(self):
```
-### ***@classmethod***
-### ***def*** `from_two_points(cls: Any, p1: 'Point3', p2: 'Point3') -> 'Line3'`
+- #### `@classmethod`
+- #### *def* `from_two_points(cls, p1: 'Point3', p2: 'Point3')`
- 工厂函数 由两点构造直线。
-Args:
+工厂函数 由两点构造直线。
- p1: 点1
+参数:
- p2: 点2
+p1: 点1
-Returns:
+p2: 点2
- 直线
+- #
-源代码
+源码
```python
@classmethod
@@ -479,11 +450,107 @@ def from_two_points(cls, p1: 'Point3', p2: 'Point3') -> 'Line3':
```
-### ***var*** `direction = p2 - p1`
+- #### *def* `__and__(self, other: 'Line3')`
+
+
+计算两条直线点集合的交集。重合线返回自身,平行线返回None,交线返回交点。
+
+参数:
+
+other: 另一条直线
+
+
+- #
+
+源码
+
+```python
+def __and__(self, other: 'Line3') -> 'Line3 | Point3 | None':
+ """
+ 计算两条直线点集合的交集。重合线返回自身,平行线返回None,交线返回交点。
+ Args:
+ other: 另一条直线
+ Returns:
+ 交点
+ """
+ if self.is_collinear(other):
+ return self
+ elif self.is_parallel(other) or not self.is_coplanar(other):
+ return None
+ else:
+ return self.cal_intersection(other)
+```
+
+
+- #### *def* `__eq__(self, other)`
+
+
+判断两条直线是否等价。
+
+v1 // v2 ∧ (p1 - p2) // v1
+
+参数:
+
+other:
+
+
+- #
+
+源码
+
+```python
+def __eq__(self, other) -> bool:
+ """
+ 判断两条直线是否等价。
+
+ v1 // v2 ∧ (p1 - p2) // v1
+ Args:
+ other:
+
+ Returns:
+
+ """
+ return self.direction.is_parallel(other.direction) and (self.point - other.point).is_parallel(self.direction)
+```
+
+
+- #### *def* `__str__(self)`
-### ***var*** `s = 'Line3: '`
+- #
+
+源码
+
+```python
+def __str__(self):
+ """
+ 返回点向式(x-x0)
+ Returns:
+
+ """
+ s = 'Line3: '
+ if self.direction.x != 0:
+ s += f'(x{sign_format(-self.point.x)})/{self.direction.x}'
+ if self.direction.y != 0:
+ s += f' = (y{sign_format(-self.point.y)})/{self.direction.y}'
+ if self.direction.z != 0:
+ s += f' = (z{sign_format(-self.point.z)})/{self.direction.z}'
+ return s
+```
+
+
+- #### *def* `__repr__(self)`
+
+- #
+
+源码
+
+```python
+def __repr__(self):
+ return f'Line3({self.point}, {self.direction})'
+```
+
diff --git a/docs/api/mp_math/mp_math_typing.md b/docs/api/mp_math/mp_math_typing.md
index b1bb7d5..b166130 100644
--- a/docs/api/mp_math/mp_math_typing.md
+++ b/docs/api/mp_math/mp_math_typing.md
@@ -1,15 +1,37 @@
---
-title: mbcp.mp\nmath.mp\nmath\ntyping
-order: 1
-icon: laptop-code
-category: API
+title: mbcp.mp_math.mp_math_typing
---
+### ***var*** `RealNumber: TypeAlias = int | float`
+
+### ***var*** `Number: TypeAlias = RealNumber | complex`
### ***var*** `SingleVar = TypeVar('SingleVar', bound=Number)`
-
-
### ***var*** `ArrayVar = TypeVar('ArrayVar', bound=Iterable[Number])`
+### ***var*** `Var: TypeAlias = SingleVar | ArrayVar`
+### ***var*** `OneSingleVarFunc: TypeAlias = Callable[[SingleVar], SingleVar]`
+
+### ***var*** `OneArrayFunc: TypeAlias = Callable[[ArrayVar], ArrayVar]`
+
+### ***var*** `OneVarFunc: TypeAlias = OneSingleVarFunc | OneArrayFunc`
+
+### ***var*** `TwoSingleVarsFunc: TypeAlias = Callable[[SingleVar, SingleVar], SingleVar]`
+
+### ***var*** `TwoArraysFunc: TypeAlias = Callable[[ArrayVar, ArrayVar], ArrayVar]`
+
+### ***var*** `TwoVarsFunc: TypeAlias = TwoSingleVarsFunc | TwoArraysFunc`
+
+### ***var*** `ThreeSingleVarsFunc: TypeAlias = Callable[[SingleVar, SingleVar, SingleVar], SingleVar]`
+
+### ***var*** `ThreeArraysFunc: TypeAlias = Callable[[ArrayVar, ArrayVar, ArrayVar], ArrayVar]`
+
+### ***var*** `ThreeVarsFunc: TypeAlias = ThreeSingleVarsFunc | ThreeArraysFunc`
+
+### ***var*** `MultiSingleVarsFunc: TypeAlias = Callable[..., SingleVar]`
+
+### ***var*** `MultiArraysFunc: TypeAlias = Callable[..., ArrayVar]`
+
+### ***var*** `MultiVarsFunc: TypeAlias = MultiSingleVarsFunc | MultiArraysFunc`
diff --git a/docs/api/mp_math/plane.md b/docs/api/mp_math/plane.md
index 3832cd3..9a598b7 100644
--- a/docs/api/mp_math/plane.md
+++ b/docs/api/mp_math/plane.md
@@ -1,30 +1,47 @@
---
-title: mbcp.mp\nmath.plane
-order: 1
-icon: laptop-code
-category: API
+title: mbcp.mp_math.plane
---
+### ***var*** `k = other.a / self.a`
+
+### ***var*** `A = np.array([[self.b, self.c], [other.b, other.c]])`
+
+### ***var*** `B = np.array([-self.d, -other.d])`
+
+### ***var*** `v2 = l2.get_point(1) - l1.point`
+
+### ***var*** `k = other.b / self.b`
+
+### ***var*** `A = np.array([[self.a, self.c], [other.a, other.c]])`
+
+### ***var*** `B = np.array([-self.d, -other.d])`
+
+### ***var*** `k = other.c / self.c`
+
+### ***var*** `A = np.array([[self.a, self.b], [other.a, other.b]])`
+
+### ***var*** `B = np.array([-self.d, -other.d])`
### ***class*** `Plane3`
+- #### *def* `__init__(self, a: float, b: float, c: float, d: float)`
-### ***def*** `__init__(self, a: float, b: float, c: float, d: float) -> None`
+平面方程:ax + by + cz + d = 0
- 平面方程:ax + by + cz + d = 0
+参数:
-Args:
+a:
- a:
+b:
- b:
+c:
- c:
+d:
- d:
+- #
-源代码
+源码
```python
def __init__(self, a: float, b: float, c: float, d: float):
@@ -43,32 +60,26 @@ def __init__(self, a: float, b: float, c: float, d: float):
```
-### ***def*** `approx(self, other: 'Plane3', epsilon: float) -> bool`
-
- 判断两个平面是否近似相等。
-
-Args:
-
- other:
-
- epsilon:
+- #### *def* `approx(self, other: 'Plane3')`
+判断两个平面是否近似相等。
-Returns:
+参数:
- 是否近似相等
+other:
+
+- #
-源代码
+源码
```python
-def approx(self, other: 'Plane3', epsilon: float=APPROX) -> bool:
+def approx(self, other: 'Plane3') -> bool:
"""
判断两个平面是否近似相等。
Args:
other:
- epsilon:
Returns:
是否近似相等
@@ -87,24 +98,19 @@ def approx(self, other: 'Plane3', epsilon: float=APPROX) -> bool:
```
-### ***def*** `cal_angle(self, other: 'Line3 | Plane3') -> 'AnyAngle'`
+- #### *def* `cal_angle(self, other: 'Line3 | Plane3')`
- 计算平面与平面之间的夹角。
-Args:
+计算平面与平面之间的夹角。
- other: 另一个平面
+参数:
-Returns:
+other: 另一个平面
- 夹角弧度
-
-Raises:
-
- TypeError: 不支持的类型
+- #
-源代码
+源码
```python
def cal_angle(self, other: 'Line3 | Plane3') -> 'AnyAngle':
@@ -126,24 +132,19 @@ def cal_angle(self, other: 'Line3 | Plane3') -> 'AnyAngle':
```
-### ***def*** `cal_distance(self, other: 'Plane3 | Point3') -> float`
+- #### *def* `cal_distance(self, other: 'Plane3 | Point3')`
- 计算平面与平面或点之间的距离。
-Args:
+计算平面与平面或点之间的距离。
- other: 另一个平面或点
+参数:
-Returns:
+other: 另一个平面或点
- 距离
-
-Raises:
-
- TypeError: 不支持的类型
+- #
-源代码
+源码
```python
def cal_distance(self, other: 'Plane3 | Point3') -> float:
@@ -165,22 +166,19 @@ def cal_distance(self, other: 'Plane3 | Point3') -> float:
```
-### ***def*** `cal_intersection_line3(self, other: 'Plane3') -> 'Line3'`
+- #### *def* `cal_intersection_line3(self, other: 'Plane3')`
- 计算两平面的交线。该方法有问题,待修复。
-Args:
+计算两平面的交线。该方法有问题,待修复。
- other: 另一个平面
+参数:
-Returns:
+other: 另一个平面
- 交线
-
-Raises:
+- #
-源代码
+源码
```python
def cal_intersection_line3(self, other: 'Plane3') -> 'Line3':
@@ -212,24 +210,19 @@ def cal_intersection_line3(self, other: 'Plane3') -> 'Line3':
```
-### ***def*** `cal_intersection_point3(self, other: 'Line3') -> 'Point3'`
+- #### *def* `cal_intersection_point3(self, other: 'Line3')`
- 计算平面与直线的交点。
-Args:
+计算平面与直线的交点。
- other: 不与平面平行或在平面上的直线
+参数:
-Returns:
+other: 不与平面平行或在平面上的直线
- 交点
-
-Raises:
-
- ValueError: 平面与直线平行或重合
+- #
-源代码
+源码
```python
def cal_intersection_point3(self, other: 'Line3') -> 'Point3':
@@ -250,20 +243,19 @@ def cal_intersection_point3(self, other: 'Line3') -> 'Point3':
```
-### ***def*** `cal_parallel_plane3(self, point: 'Point3') -> 'Plane3'`
+- #### *def* `cal_parallel_plane3(self, point: 'Point3')`
- 计算平行于该平面且过指定点的平面。
-Args:
+计算平行于该平面且过指定点的平面。
- point: 指定点
+参数:
-Returns:
+point: 指定点
- 平面
+- #
-源代码
+源码
```python
def cal_parallel_plane3(self, point: 'Point3') -> 'Plane3':
@@ -278,20 +270,19 @@ def cal_parallel_plane3(self, point: 'Point3') -> 'Plane3':
```
-### ***def*** `is_parallel(self, other: 'Plane3') -> bool`
+- #### *def* `is_parallel(self, other: 'Plane3')`
- 判断两个平面是否平行。
-Args:
+判断两个平面是否平行。
- other: 另一个平面
+参数:
-Returns:
+other: 另一个平面
- 是否平行
+- #
-源代码
+源码
```python
def is_parallel(self, other: 'Plane3') -> bool:
@@ -306,17 +297,20 @@ def is_parallel(self, other: 'Plane3') -> bool:
```
-### ***@property***
-### ***def*** `normal(self: Any) -> 'Vector3'`
+- #### `@property`
+- #### *def* `normal(self)`
- 平面的法向量。
-Returns:
+平面的法向量。
- 法向量
+返回:
+法向量
+
+
+- #
-源代码
+源码
```python
@property
@@ -330,23 +324,22 @@ def normal(self) -> 'Vector3':
```
-### ***@classmethod***
-### ***def*** `from_point_and_normal(cls: Any, point: 'Point3', normal: 'Vector3') -> 'Plane3'`
+- #### `@classmethod`
+- #### *def* `from_point_and_normal(cls, point: 'Point3', normal: 'Vector3')`
- 工厂函数 由点和法向量构造平面(点法式构造)。
-Args:
+工厂函数 由点和法向量构造平面(点法式构造)。
- point: 平面上的一点
+参数:
- normal: 法向量
+point: 平面上的一点
-Returns:
+normal: 法向量
- 平面
+- #
-源代码
+源码
```python
@classmethod
@@ -365,25 +358,24 @@ def from_point_and_normal(cls, point: 'Point3', normal: 'Vector3') -> 'Plane3':
```
-### ***@classmethod***
-### ***def*** `from_three_points(cls: Any, p1: 'Point3', p2: 'Point3', p3: 'Point3') -> 'Plane3'`
+- #### `@classmethod`
+- #### *def* `from_three_points(cls, p1: 'Point3', p2: 'Point3', p3: 'Point3')`
- 工厂函数 由三点构造平面。
-Args:
+工厂函数 由三点构造平面。
- p1: 点1
+参数:
- p2: 点2
+p1: 点1
- p3: 点3
+p2: 点2
-Returns:
+p3: 点3
- 平面
+- #
-源代码
+源码
```python
@classmethod
@@ -404,23 +396,22 @@ def from_three_points(cls, p1: 'Point3', p2: 'Point3', p3: 'Point3') -> 'Plane3'
```
-### ***@classmethod***
-### ***def*** `from_two_lines(cls: Any, l1: 'Line3', l2: 'Line3') -> 'Plane3'`
+- #### `@classmethod`
+- #### *def* `from_two_lines(cls, l1: 'Line3', l2: 'Line3')`
- 工厂函数 由两直线构造平面。
-Args:
+工厂函数 由两直线构造平面。
- l1: 直线1
+参数:
- l2: 直线2
+l1: 直线1
-Returns:
+l2: 直线2
- 平面
+- #
-源代码
+源码
```python
@classmethod
@@ -441,23 +432,22 @@ def from_two_lines(cls, l1: 'Line3', l2: 'Line3') -> 'Plane3':
```
-### ***@classmethod***
-### ***def*** `from_point_and_line(cls: Any, point: 'Point3', line: 'Line3') -> 'Plane3'`
+- #### `@classmethod`
+- #### *def* `from_point_and_line(cls, point: 'Point3', line: 'Line3')`
- 工厂函数 由点和直线构造平面。
-Args:
+工厂函数 由点和直线构造平面。
- point: 面上一点
+参数:
- line: 面上直线,不包含点
+point: 面上一点
-Returns:
+line: 面上直线,不包含点
- 平面
+- #
-源代码
+源码
```python
@classmethod
@@ -474,79 +464,124 @@ def from_point_and_line(cls, point: 'Point3', line: 'Line3') -> 'Plane3':
```
-### ***var*** `direction = self.normal.cross(other.normal)`
+- #### *def* `__repr__(self)`
+
+- #
+
+源码
+
+```python
+def __repr__(self):
+ return f'Plane3({self.a}, {self.b}, {self.c}, {self.d})'
+```
+
+
+- #### *def* `__str__(self)`
+
+- #
+
+源码
+
+```python
+def __str__(self):
+ s = 'Plane3: '
+ if self.a != 0:
+ s += f'{sign(self.a, only_neg=True)}{abs(self.a)}x'
+ if self.b != 0:
+ s += f' {sign(self.b)} {abs(self.b)}y'
+ if self.c != 0:
+ s += f' {sign(self.c)} {abs(self.c)}z'
+ if self.d != 0:
+ s += f' {sign(self.d)} {abs(self.d)}'
+ return s + ' = 0'
+```
+
+
+- #### `@overload`
+- #### *def* `__and__(self, other: 'Line3')`
+
+- #
+
+源码
+
+```python
+@overload
+def __and__(self, other: 'Line3') -> 'Point3 | None':
+ ...
+```
+
+
+- #### `@overload`
+- #### *def* `__and__(self, other: 'Plane3')`
+
+- #
+
+源码
+
+```python
+@overload
+def __and__(self, other: 'Plane3') -> 'Line3 | None':
+ ...
+```
+
+
+- #### *def* `__and__(self, other)`
+取两平面的交集(人话:交线)
-### ***var*** `t = -(self.a * other.point.x + self.b * other.point.y + self.c * other.point.z + self.d) / (self.a * other.direction.x + self.b * other.direction.y + self.c * other.direction.z)`
+参数:
+
+other:
+- #
+
+源码
-### ***var*** `d = -a * point.x - b * point.y - c * point.z`
+```python
+def __and__(self, other):
+ """
+ 取两平面的交集(人话:交线)
+ Args:
+ other:
+ Returns:
+ 不平行平面的交线,平面平行返回None
+ """
+ if isinstance(other, Plane3):
+ if self.normal.is_parallel(other.normal):
+ return None
+ return self.cal_intersection_line3(other)
+ elif isinstance(other, Line3):
+ if self.normal @ other.direction == 0:
+ return None
+ return self.cal_intersection_point3(other)
+ else:
+ raise TypeError(f"unsupported operand type(s) for &: 'Plane3' and '{type(other)}'")
+```
+
+- #### *def* `__eq__(self, other)`
+- #
+
+源码
-### ***var*** `v1 = p2 - p1`
+```python
+def __eq__(self, other) -> bool:
+ return self.approx(other)
+```
+
+- #### *def* `__rand__(self, other: 'Line3')`
+- #
+
+源码
-### ***var*** `v2 = p3 - p1`
-
-
-
-### ***var*** `normal = v1.cross(v2)`
-
-
-
-### ***var*** `v1 = l1.direction`
-
-
-
-### ***var*** `v2 = l2.point - l1.point`
-
-
-
-### ***var*** `s = 'Plane3: '`
-
-
-
-### ***var*** `k = other.a / self.a`
-
-
-
-### ***var*** `A = np.array([[self.b, self.c], [other.b, other.c]])`
-
-
-
-### ***var*** `B = np.array([-self.d, -other.d])`
-
-
-
-### ***var*** `v2 = l2.get_point(1) - l1.point`
-
-
-
-### ***var*** `k = other.b / self.b`
-
-
-
-### ***var*** `A = np.array([[self.a, self.c], [other.a, other.c]])`
-
-
-
-### ***var*** `B = np.array([-self.d, -other.d])`
-
-
-
-### ***var*** `k = other.c / self.c`
-
-
-
-### ***var*** `A = np.array([[self.a, self.b], [other.a, other.b]])`
-
-
-
-### ***var*** `B = np.array([-self.d, -other.d])`
-
-
+```python
+def __rand__(self, other: 'Line3') -> 'Point3':
+ return self.cal_intersection_point3(other)
+```
+
diff --git a/docs/api/mp_math/point.md b/docs/api/mp_math/point.md
index 53e79f7..f040766 100644
--- a/docs/api/mp_math/point.md
+++ b/docs/api/mp_math/point.md
@@ -1,28 +1,25 @@
---
-title: mbcp.mp\nmath.point
-order: 1
-icon: laptop-code
-category: API
+title: mbcp.mp_math.point
---
-
### ***class*** `Point3`
+- #### *def* `__init__(self, x: float, y: float, z: float)`
-### ***def*** `__init__(self, x: float, y: float, z: float) -> None`
+笛卡尔坐标系中的点。
- 笛卡尔坐标系中的点。
+参数:
-Args:
+x: x 坐标
- x: x 坐标
+y: y 坐标
- y: y 坐标
+z: z 坐标
- z: z 坐标
+- #
-源代码
+源码
```python
def __init__(self, x: float, y: float, z: float):
@@ -39,24 +36,21 @@ def __init__(self, x: float, y: float, z: float):
```
-### ***def*** `approx(self, other: 'Point3', epsilon: float) -> bool`
-
- 判断两个点是否近似相等。
-
-Args:
-
- other:
-
- epsilon:
+- #### *def* `approx(self, other: 'Point3', epsilon: float = APPROX)`
+判断两个点是否近似相等。
-Returns:
+参数:
- 是否近似相等
+other:
+epsilon:
+
+
+- #
-源代码
+源码
```python
def approx(self, other: 'Point3', epsilon: float=APPROX) -> bool:
@@ -73,3 +67,97 @@ def approx(self, other: 'Point3', epsilon: float=APPROX) -> bool:
```
+- #### *def* `__str__(self)`
+
+- #
+
+源码
+
+```python
+def __str__(self):
+ return f'Point3({self.x}, {self.y}, {self.z})'
+```
+
+
+- #### `@overload`
+- #### *def* `__add__(self, other: 'Vector3')`
+
+- #
+
+源码
+
+```python
+@overload
+def __add__(self, other: 'Vector3') -> 'Point3':
+ ...
+```
+
+
+- #### `@overload`
+- #### *def* `__add__(self, other: 'Point3')`
+
+- #
+
+源码
+
+```python
+@overload
+def __add__(self, other: 'Point3') -> 'Point3':
+ ...
+```
+
+
+- #### *def* `__add__(self, other)`
+
+
+P + V -> P
+P + P -> P
+
+参数:
+
+other:
+
+
+- #
+
+源码
+
+```python
+def __add__(self, other):
+ """
+ P + V -> P
+ P + P -> P
+ Args:
+ other:
+ Returns:
+ """
+ return Point3(self.x + other.x, self.y + other.y, self.z + other.z)
+```
+
+
+- #### *def* `__eq__(self, other)`
+
+
+判断两个点是否相等。
+
+参数:
+
+other:
+
+
+- #
+
+源码
+
+```python
+def __eq__(self, other):
+ """
+ 判断两个点是否相等。
+ Args:
+ other:
+ Returns:
+ """
+ return approx(self.x, other.x) and approx(self.y, other.y) and approx(self.z, other.z)
+```
+
+
diff --git a/docs/api/mp_math/segment.md b/docs/api/mp_math/segment.md
index 7da8df6..13a897a 100644
--- a/docs/api/mp_math/segment.md
+++ b/docs/api/mp_math/segment.md
@@ -1,24 +1,19 @@
---
-title: mbcp.mp\nmath.segment
-order: 1
-icon: laptop-code
-category: API
+title: mbcp.mp_math.segment
---
-
### ***class*** `Segment3`
+- #### *def* `__init__(self, p1: 'Point3', p2: 'Point3')`
-### ***def*** `__init__(self, p1: 'Point3', p2: 'Point3') -> None`
-
- 三维空间中的线段。
-
+三维空间中的线段。
:param p1:
-
:param p2:
+
+- #
-源代码
+源码
```python
def __init__(self, p1: 'Point3', p2: 'Point3'):
@@ -38,3 +33,27 @@ def __init__(self, p1: 'Point3', p2: 'Point3'):
```
+- #### *def* `__repr__(self)`
+
+- #
+
+源码
+
+```python
+def __repr__(self):
+ return f'Segment3({self.p1}, {self.p2})'
+```
+
+
+- #### *def* `__str__(self)`
+
+- #
+
+源码
+
+```python
+def __str__(self):
+ return f'Segment3({self.p1} -> {self.p2})'
+```
+
+
diff --git a/docs/api/mp_math/utils.md b/docs/api/mp_math/utils.md
index 93cc597..9406a53 100644
--- a/docs/api/mp_math/utils.md
+++ b/docs/api/mp_math/utils.md
@@ -1,30 +1,23 @@
---
-title: mbcp.mp\nmath.utils
-order: 1
-icon: laptop-code
-category: API
+title: mbcp.mp_math.utils
---
+### *def* `clamp()`
-### ***def*** `clamp(x: float, min_: float, max_: float) -> float`
区间截断函数。
-Args:
+参数:
- x:
+x:
- min_:
+min_:
- max_:
+max_:
-Returns:
-
- 限制后的值
-
-源代码
+源码
```python
def clamp(x: float, min_: float, max_: float) -> float:
@@ -42,26 +35,23 @@ def clamp(x: float, min_: float, max_: float) -> float:
```
-### ***def*** `approx(x: float, y: float, epsilon: float) -> bool`
+### *def* `approx(x: float = 0.0, y: float = APPROX)`
+
判断两个数是否近似相等。或包装一个实数,用于判断是否近似于0。
-Args:
+参数:
- x:
+x:
- y:
+y:
- epsilon:
+epsilon:
-Returns:
-
- 是否近似相等
-
-源代码
+源码
```python
def approx(x: float, y: float=0.0, epsilon: float=APPROX) -> bool:
@@ -79,22 +69,21 @@ def approx(x: float, y: float=0.0, epsilon: float=APPROX) -> bool:
```
-### ***def*** `sign(x: float, only_neg: bool) -> str`
+### *def* `sign(x: float = False)`
+
获取数的符号。
-Args:
+参数:
- x: 数
+x: 数
- only_neg: 是否只返回负数的符号
+only_neg: 是否只返回负数的符号
-Returns:
- 符号 + - ""
-源代码
+源码
```python
def sign(x: float, only_neg: bool=False) -> str:
@@ -114,28 +103,24 @@ def sign(x: float, only_neg: bool=False) -> str:
```
-### ***def*** `sign_format(x: float, only_neg: bool) -> str`
+### *def* `sign_format(x: float = False)`
+
格式化符号数
-
-1 -> -1
-
1 -> +1
-
0 -> ""
-Args:
+参数:
- x: 数
+x: 数
- only_neg: 是否只返回负数的符号
+only_neg: 是否只返回负数的符号
-Returns:
- 符号 + - ""
-源代码
+源码
```python
def sign_format(x: float, only_neg: bool=False) -> str:
@@ -160,18 +145,11 @@ def sign_format(x: float, only_neg: bool=False) -> str:
### ***class*** `Approx`
-用于近似比较对象
-
-
-
-已实现对象 实数 Vector3 Point3 Plane3 Line3
-
-### ***def*** `__init__(self, value: RealNumber) -> None`
-
-
+- #### *def* `__init__(self, value: RealNumber)`
+- #
-源代码
+源码
```python
def __init__(self, value: RealNumber):
@@ -179,12 +157,32 @@ def __init__(self, value: RealNumber):
```
-### ***def*** `raise_type_error(self, other: Any) -> None`
-
-
+- #### *def* `__eq__(self, other)`
+- #
-源代码
+源码
+
+```python
+def __eq__(self, other):
+ if isinstance(self.value, (float, int)):
+ if isinstance(other, (float, int)):
+ return abs(self.value - other) < APPROX
+ else:
+ self.raise_type_error(other)
+ elif isinstance(self.value, Vector3):
+ if isinstance(other, (Vector3, Point3, Plane3, Line3)):
+ return all([approx(self.value.x, other.x), approx(self.value.y, other.y), approx(self.value.z, other.z)])
+ else:
+ self.raise_type_error(other)
+```
+
+
+- #### *def* `raise_type_error(self, other)`
+
+- #
+
+源码
```python
def raise_type_error(self, other):
@@ -192,3 +190,15 @@ def raise_type_error(self, other):
```
+- #### *def* `__ne__(self, other)`
+
+- #
+
+源码
+
+```python
+def __ne__(self, other):
+ return not self.__eq__(other)
+```
+
+
diff --git a/docs/api/mp_math/vector.md b/docs/api/mp_math/vector.md
index 73d7f22..988ae57 100644
--- a/docs/api/mp_math/vector.md
+++ b/docs/api/mp_math/vector.md
@@ -1,28 +1,33 @@
---
-title: mbcp.mp\nmath.vector
-order: 1
-icon: laptop-code
-category: API
+title: mbcp.mp_math.vector
---
+### ***var*** `zero_vector3 = Vector3(0, 0, 0)`
+
+### ***var*** `x_axis = Vector3(1, 0, 0)`
+
+### ***var*** `y_axis = Vector3(0, 1, 0)`
+
+### ***var*** `z_axis = Vector3(0, 0, 1)`
### ***class*** `Vector3`
+- #### *def* `__init__(self, x: float, y: float, z: float)`
-### ***def*** `__init__(self, x: float, y: float, z: float) -> None`
+3维向量
- 3维向量
+参数:
-Args:
+x: x轴分量
- x: x轴分量
+y: y轴分量
- y: y轴分量
+z: z轴分量
- z: z轴分量
+- #
-源代码
+源码
```python
def __init__(self, x: float, y: float, z: float):
@@ -39,24 +44,21 @@ def __init__(self, x: float, y: float, z: float):
```
-### ***def*** `approx(self, other: 'Vector3', epsilon: float) -> bool`
-
- 判断两个向量是否近似相等。
-
-Args:
-
- other:
-
- epsilon:
+- #### *def* `approx(self, other: 'Vector3', epsilon: float = APPROX)`
+判断两个向量是否近似相等。
-Returns:
+参数:
- 是否近似相等
+other:
+epsilon:
+
+
+- #
-源代码
+源码
```python
def approx(self, other: 'Vector3', epsilon: float=APPROX) -> bool:
@@ -73,20 +75,19 @@ def approx(self, other: 'Vector3', epsilon: float=APPROX) -> bool:
```
-### ***def*** `cal_angle(self, other: 'Vector3') -> 'AnyAngle'`
+- #### *def* `cal_angle(self, other: 'Vector3')`
- 计算两个向量之间的夹角。
-Args:
+计算两个向量之间的夹角。
- other: 另一个向量
+参数:
-Returns:
+other: 另一个向量
- 夹角
+- #
-源代码
+源码
```python
def cal_angle(self, other: 'Vector3') -> 'AnyAngle':
@@ -101,44 +102,19 @@ def cal_angle(self, other: 'Vector3') -> 'AnyAngle':
```
-### ***def*** `cross(self, other: 'Vector3') -> 'Vector3'`
-
- 向量积 叉乘:v1 cross v2 -> v3
+- #### *def* `cross(self, other: 'Vector3')`
+向量积 叉乘:v1 cross v2 -> v3
叉乘为0,则两向量平行。
-
其余结果的模为平行四边形的面积。
-返回如下行列式的结果:
-
-
-
-``i j k``
-
-
-
-``x1 y1 z1``
-
-
-
-``x2 y2 z2``
-
-
-
-Args:
-
- other:
-
-Returns:
-
- 行列式的结果
-
+- #
-源代码
+源码
```python
def cross(self, other: 'Vector3') -> 'Vector3':
@@ -165,22 +141,21 @@ def cross(self, other: 'Vector3') -> 'Vector3':
```
-### ***def*** `is_approx_parallel(self, other: 'Vector3', epsilon: float) -> bool`
+- #### *def* `is_approx_parallel(self, other: 'Vector3', epsilon: float = APPROX)`
- 判断两个向量是否近似平行。
-Args:
+判断两个向量是否近似平行。
- other: 另一个向量
+参数:
- epsilon: 允许的误差
+other: 另一个向量
-Returns:
+epsilon: 允许的误差
- 是否近似平行
+- #
-源代码
+源码
```python
def is_approx_parallel(self, other: 'Vector3', epsilon: float=APPROX) -> bool:
@@ -196,20 +171,19 @@ def is_approx_parallel(self, other: 'Vector3', epsilon: float=APPROX) -> bool:
```
-### ***def*** `is_parallel(self, other: 'Vector3') -> bool`
+- #### *def* `is_parallel(self, other: 'Vector3')`
- 判断两个向量是否平行。
-Args:
+判断两个向量是否平行。
- other: 另一个向量
+参数:
-Returns:
+other: 另一个向量
- 是否平行
+- #
-源代码
+源码
```python
def is_parallel(self, other: 'Vector3') -> bool:
@@ -224,16 +198,17 @@ def is_parallel(self, other: 'Vector3') -> bool:
```
-### ***def*** `normalize(self) -> None`
-
- 将向量归一化。
+- #### *def* `normalize(self)`
+将向量归一化。
自体归一化,不返回值。
+
+- #
-源代码
+源码
```python
def normalize(self):
@@ -249,15 +224,16 @@ def normalize(self):
```
-### ***@property***
-### ***def*** `np_array(self: Any) -> 'np.ndarray'`
+- #### `@property`
+- #### *def* `np_array(self)`
- 返回numpy数组
-Returns:
+
+
+- #
-源代码
+源码
```python
@property
@@ -270,17 +246,20 @@ def np_array(self) -> 'np.ndarray':
```
-### ***@property***
-### ***def*** `length(self: Any) -> float`
+- #### `@property`
+- #### *def* `length(self)`
- 向量的模。
-Returns:
+向量的模。
- 模
+返回:
+模
+
+
+- #
-源代码
+源码
```python
@property
@@ -294,17 +273,20 @@ def length(self) -> float:
```
-### ***@property***
-### ***def*** `unit(self: Any) -> 'Vector3'`
+- #### `@property`
+- #### *def* `unit(self)`
- 获取该向量的单位向量。
-Returns:
+获取该向量的单位向量。
- 单位向量
+返回:
+单位向量
+
+
+- #
-源代码
+源码
```python
@property
@@ -318,23 +300,372 @@ def unit(self) -> 'Vector3':
```
-### ***var*** `zero_vector3 = Vector3(0, 0, 0)`
+- #### *def* `__abs__(self)`
+
+- #
+
+源码
+
+```python
+def __abs__(self):
+ return self.length
+```
+
+
+- #### `@overload`
+- #### *def* `__add__(self, other: 'Vector3')`
+
+- #
+
+源码
+
+```python
+@overload
+def __add__(self, other: 'Vector3') -> 'Vector3':
+ ...
+```
+
+
+- #### `@overload`
+- #### *def* `__add__(self, other: 'Point3')`
+
+- #
+
+源码
+
+```python
+@overload
+def __add__(self, other: 'Point3') -> 'Point3':
+ ...
+```
+
+
+- #### *def* `__add__(self, other)`
+V + P -> P
-### ***var*** `x_axis = Vector3(1, 0, 0)`
+V + V -> V
+
+参数:
+
+other:
+- #
+
+源码
-### ***var*** `y_axis = Vector3(0, 1, 0)`
+```python
+def __add__(self, other):
+ """
+ V + P -> P
+
+ V + V -> V
+ Args:
+ other:
+ Returns:
+
+ """
+ if isinstance(other, Vector3):
+ return Vector3(self.x + other.x, self.y + other.y, self.z + other.z)
+ elif isinstance(other, Point3):
+ return Point3(self.x + other.x, self.y + other.y, self.z + other.z)
+ else:
+ raise TypeError(f"unsupported operand type(s) for +: 'Vector3' and '{type(other)}'")
+```
+
+
+- #### *def* `__eq__(self, other)`
+判断两个向量是否相等。
-### ***var*** `z_axis = Vector3(0, 0, 1)`
+参数:
+
+other:
+- #
+
+源码
-### ***var*** `length = self.length`
+```python
+def __eq__(self, other):
+ """
+ 判断两个向量是否相等。
+ Args:
+ other:
+ Returns:
+ 是否相等
+ """
+ return approx(self.x, other.x) and approx(self.y, other.y) and approx(self.z, other.z)
+```
+
+
+- #### *def* `__radd__(self, other: 'Point3')`
+P + V -> P
+
+别去点那边实现了。
+:param other:
+:return:
+
+
+- #
+
+源码
+
+```python
+def __radd__(self, other: 'Point3') -> 'Point3':
+ """
+ P + V -> P
+
+ 别去点那边实现了。
+ :param other:
+ :return:
+ """
+ return Point3(self.x + other.x, self.y + other.y, self.z + other.z)
+```
+
+
+- #### `@overload`
+- #### *def* `__sub__(self, other: 'Vector3')`
+
+- #
+
+源码
+
+```python
+@overload
+def __sub__(self, other: 'Vector3') -> 'Vector3':
+ ...
+```
+
+
+- #### `@overload`
+- #### *def* `__sub__(self, other: 'Point3')`
+
+- #
+
+源码
+
+```python
+@overload
+def __sub__(self, other: 'Point3') -> 'Point3':
+ ...
+```
+
+
+- #### *def* `__sub__(self, other)`
+
+
+V - P -> P
+
+V - V -> V
+
+参数:
+
+other:
+
+
+- #
+
+源码
+
+```python
+def __sub__(self, other):
+ """
+ V - P -> P
+
+ V - V -> V
+ Args:
+ other:
+ Returns:
+ """
+ if isinstance(other, Vector3):
+ return Vector3(self.x - other.x, self.y - other.y, self.z - other.z)
+ elif isinstance(other, Point3):
+ return Point3(self.x - other.x, self.y - other.y, self.z - other.z)
+ else:
+ raise TypeError(f'unsupported operand type(s) for -: "Vector3" and "{type(other)}"')
+```
+
+
+- #### *def* `__rsub__(self, other: 'Point3')`
+
+
+P - V -> P
+
+参数:
+
+other:
+
+
+- #
+
+源码
+
+```python
+def __rsub__(self, other: 'Point3'):
+ """
+ P - V -> P
+ Args:
+ other:
+ Returns:
+
+ """
+ if isinstance(other, Point3):
+ return Point3(other.x - self.x, other.y - self.y, other.z - self.z)
+ else:
+ raise TypeError(f"unsupported operand type(s) for -: '{type(other)}' and 'Vector3'")
+```
+
+
+- #### `@overload`
+- #### *def* `__mul__(self, other: 'Vector3')`
+
+- #
+
+源码
+
+```python
+@overload
+def __mul__(self, other: 'Vector3') -> 'Vector3':
+ ...
+```
+
+
+- #### `@overload`
+- #### *def* `__mul__(self, other: RealNumber)`
+
+- #
+
+源码
+
+```python
+@overload
+def __mul__(self, other: RealNumber) -> 'Vector3':
+ ...
+```
+
+
+- #### *def* `__mul__(self, other: 'int | float | Vector3')`
+
+
+数组运算 非点乘。点乘使用@,叉乘使用cross。
+
+参数:
+
+other:
+
+
+- #
+
+源码
+
+```python
+def __mul__(self, other: 'int | float | Vector3') -> 'Vector3':
+ """
+ 数组运算 非点乘。点乘使用@,叉乘使用cross。
+ Args:
+ other:
+
+ Returns:
+ """
+ if isinstance(other, Vector3):
+ return Vector3(self.x * other.x, self.y * other.y, self.z * other.z)
+ elif isinstance(other, (float, int)):
+ return Vector3(self.x * other, self.y * other, self.z * other)
+ else:
+ raise TypeError(f"unsupported operand type(s) for *: 'Vector3' and '{type(other)}'")
+```
+
+
+- #### *def* `__rmul__(self, other: 'RealNumber')`
+
+- #
+
+源码
+
+```python
+def __rmul__(self, other: 'RealNumber') -> 'Vector3':
+ return self.__mul__(other)
+```
+
+
+- #### *def* `__matmul__(self, other: 'Vector3')`
+
+
+点乘。
+
+参数:
+
+other:
+
+
+- #
+
+源码
+
+```python
+def __matmul__(self, other: 'Vector3') -> 'RealNumber':
+ """
+ 点乘。
+ Args:
+ other:
+ Returns:
+ """
+ return self.x * other.x + self.y * other.y + self.z * other.z
+```
+
+
+- #### *def* `__truediv__(self, other: RealNumber)`
+
+- #
+
+源码
+
+```python
+def __truediv__(self, other: RealNumber) -> 'Vector3':
+ return Vector3(self.x / other, self.y / other, self.z / other)
+```
+
+
+- #### *def* `__neg__(self)`
+
+- #
+
+源码
+
+```python
+def __neg__(self):
+ return Vector3(-self.x, -self.y, -self.z)
+```
+
+
+- #### *def* `__repr__(self)`
+
+- #
+
+源码
+
+```python
+def __repr__(self):
+ return f'Vector3({self.x}, {self.y}, {self.z})'
+```
+
+
+- #### *def* `__str__(self)`
+
+- #
+
+源码
+
+```python
+def __str__(self):
+ return f'Vector3({self.x}, {self.y}, {self.z})'
+```
+
diff --git a/docs/api/particle/index.md b/docs/api/particle/index.md
index 1669e90..4b514d5 100644
--- a/docs/api/particle/index.md
+++ b/docs/api/particle/index.md
@@ -1,7 +1,3 @@
---
-title: mbcp.particle.\n\ninit\n\n
-order: 1
-icon: laptop-code
-category: API
+title: mbcp.particle
---
-
diff --git a/docs/api/presets/index.md b/docs/api/presets/index.md
index 85b4964..472366f 100644
--- a/docs/api/presets/index.md
+++ b/docs/api/presets/index.md
@@ -1,7 +1,3 @@
---
-title: mbcp.presets.\n\ninit\n\n
-order: 1
-icon: laptop-code
-category: API
+title: mbcp.presets
---
-
diff --git a/docs/api/presets/model/index.md b/docs/api/presets/model/index.md
index 5986544..fee2a50 100644
--- a/docs/api/presets/model/index.md
+++ b/docs/api/presets/model/index.md
@@ -1,26 +1,24 @@
---
-title: mbcp.presets.model.\n\ninit\n\n
-order: 1
-icon: laptop-code
-category: API
+title: mbcp.presets.model
---
+### ***class*** `GeometricModels`
+
+- #### `@staticmethod`
+- #### *def* `sphere(radius: float, density: float)`
-### ***def*** `sphere(radius: float, density: float) -> None`
生成球体上的点集。
-Args:
+参数:
- radius:
+radius:
- density:
+density:
-Returns:
-
- List[Point3]: 球体上的点集。
+- #
-源代码
+源码
```python
@staticmethod
@@ -44,75 +42,3 @@ def sphere(radius: float, density: float):
```
-### ***class*** `GeometricModels`
-
-
-
-### ***@staticmethod***
-### ***def*** `sphere(radius: float, density: float) -> None`
-
- 生成球体上的点集。
-
-Args:
-
- radius:
-
- density:
-
-Returns:
-
- List[Point3]: 球体上的点集。
-
-
-源代码
-
-```python
-@staticmethod
-def sphere(radius: float, density: float):
- """
- 生成球体上的点集。
- Args:
- radius:
- density:
- Returns:
- List[Point3]: 球体上的点集。
- """
- area = 4 * np.pi * radius ** 2
- num = int(area * density)
- phi_list = np.arccos([clamp(-1 + (2.0 * _ - 1.0) / num, -1, 1) for _ in range(num)])
- theta_list = np.sqrt(num * np.pi) * phi_list
- x_array = radius * np.sin(phi_list) * np.cos(theta_list)
- y_array = radius * np.sin(phi_list) * np.sin(theta_list)
- z_array = radius * np.cos(phi_list)
- return [Point3(x_array[i], y_array[i], z_array[i]) for i in range(num)]
-```
-
-
-### ***var*** `area = 4 * np.pi * radius ** 2`
-
-
-
-### ***var*** `num = int(area * density)`
-
-
-
-### ***var*** `phi_list = np.arccos([clamp(-1 + (2.0 * _ - 1.0) / num, -1, 1) for _ in range(num)])`
-
-
-
-### ***var*** `theta_list = np.sqrt(num * np.pi) * phi_list`
-
-
-
-### ***var*** `x_array = radius * np.sin(phi_list) * np.cos(theta_list)`
-
-
-
-### ***var*** `y_array = radius * np.sin(phi_list) * np.sin(theta_list)`
-
-
-
-### ***var*** `z_array = radius * np.cos(phi_list)`
-
-
-
diff --git a/docs/package.json b/docs/package.json
index 5ecc3a5..ae45d0a 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -1,6 +1,7 @@
{
"devDependencies": {
- "vitepress": "^1.3.4"
+ "vitepress": "^1.3.4",
+ "vitepress-auto-sidebar-plugin": "^0.2.4"
},
"scripts": {
"docs:dev": "vitepress dev",
diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml
index 841c47c..f876605 100644
--- a/docs/pnpm-lock.yaml
+++ b/docs/pnpm-lock.yaml
@@ -8,6 +8,9 @@ devDependencies:
vitepress:
specifier: ^1.3.4
version: 1.3.4(@algolia/client-search@5.1.1)(search-insights@2.17.0)
+ vitepress-auto-sidebar-plugin:
+ specifier: ^0.2.4
+ version: 0.2.4(vite@5.4.2)(vitepress@1.3.4)
packages:
@@ -470,6 +473,27 @@ packages:
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
dev: true
+ /@nodelib/fs.scandir@2.1.5:
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+ dev: true
+
+ /@nodelib/fs.stat@2.0.5:
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+ dev: true
+
+ /@nodelib/fs.walk@1.2.8:
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.17.1
+ dev: true
+
/@rollup/rollup-android-arm-eabi@4.21.1:
resolution: {integrity: sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==}
cpu: [arm]
@@ -856,10 +880,28 @@ packages:
'@algolia/transporter': 4.24.0
dev: true
+ /argparse@1.0.10:
+ resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+ dependencies:
+ sprintf-js: 1.0.3
+ dev: true
+
/birpc@0.2.17:
resolution: {integrity: sha512-+hkTxhot+dWsLpp3gia5AkVHIsKlZybNT5gIYiDlNzJrmYPcTM9k5/w2uaj3IPpd7LlEYpmCj4Jj1nC41VhDFg==}
dev: true
+ /braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+ dependencies:
+ fill-range: 7.1.1
+ dev: true
+
+ /consola@3.2.3:
+ resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==}
+ engines: {node: ^14.18.0 || >=16.10.0}
+ dev: true
+
/copy-anything@3.0.5:
resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
engines: {node: '>=12.13'}
@@ -907,10 +949,47 @@ packages:
'@esbuild/win32-x64': 0.21.5
dev: true
+ /esprima@4.0.1:
+ resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+ engines: {node: '>=4'}
+ hasBin: true
+ dev: true
+
/estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
dev: true
+ /extend-shallow@2.0.1:
+ resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ is-extendable: 0.1.1
+ dev: true
+
+ /fast-glob@3.3.2:
+ resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
+ engines: {node: '>=8.6.0'}
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+ dev: true
+
+ /fastq@1.17.1:
+ resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
+ dependencies:
+ reusify: 1.0.4
+ dev: true
+
+ /fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+ dependencies:
+ to-regex-range: 5.0.1
+ dev: true
+
/focus-trap@7.5.4:
resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==}
dependencies:
@@ -925,15 +1004,67 @@ packages:
dev: true
optional: true
+ /glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+ dependencies:
+ is-glob: 4.0.3
+ dev: true
+
+ /gray-matter@4.0.3:
+ resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==}
+ engines: {node: '>=6.0'}
+ dependencies:
+ js-yaml: 3.14.1
+ kind-of: 6.0.3
+ section-matter: 1.0.0
+ strip-bom-string: 1.0.0
+ dev: true
+
/hookable@5.5.3:
resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
dev: true
+ /is-extendable@0.1.1:
+ resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ is-extglob: 2.1.1
+ dev: true
+
+ /is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+ dev: true
+
/is-what@4.1.16:
resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
engines: {node: '>=12.13'}
dev: true
+ /js-yaml@3.14.1:
+ resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
+ hasBin: true
+ dependencies:
+ argparse: 1.0.10
+ esprima: 4.0.1
+ dev: true
+
+ /kind-of@6.0.3:
+ resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
/magic-string@0.30.11:
resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==}
dependencies:
@@ -944,6 +1075,19 @@ packages:
resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==}
dev: true
+ /merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+ dev: true
+
+ /micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+ dev: true
+
/minisearch@7.1.0:
resolution: {integrity: sha512-tv7c/uefWdEhcu6hvrfTihflgeEi2tN6VV7HJnCjK6VxM75QQJh4t9FwJCsA2EsRS8LCnu3W87CuGPWMocOLCA==}
dev: true
@@ -958,6 +1102,10 @@ packages:
hasBin: true
dev: true
+ /pathe@1.1.2:
+ resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
+ dev: true
+
/perfect-debounce@1.0.0:
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
dev: true
@@ -966,6 +1114,11 @@ packages:
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
dev: true
+ /picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+ dev: true
+
/postcss@8.4.41:
resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==}
engines: {node: ^10 || ^12 || >=14}
@@ -979,6 +1132,15 @@ packages:
resolution: {integrity: sha512-kKYfePf9rzKnxOAKDpsWhg/ysrHPqT+yQ7UW4JjdnqjFIeNUnNcEJvhuA8fDenxAGWzUqtd51DfVg7xp/8T9NA==}
dev: true
+ /queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+ dev: true
+
+ /reusify@1.0.4:
+ resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+ dev: true
+
/rfdc@1.4.1:
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
dev: true
@@ -1009,10 +1171,24 @@ packages:
fsevents: 2.3.3
dev: true
+ /run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+ dependencies:
+ queue-microtask: 1.2.3
+ dev: true
+
/search-insights@2.17.0:
resolution: {integrity: sha512-AskayU3QNsXQzSL6v4LTYST7NNfs2HWyHHB+sdORP9chsytAhro5XRfToAMI/LAVYgNbzowVZTMfBRodgbUHKg==}
dev: true
+ /section-matter@1.0.0:
+ resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
+ engines: {node: '>=4'}
+ dependencies:
+ extend-shallow: 2.0.1
+ kind-of: 6.0.3
+ dev: true
+
/shiki@1.14.1:
resolution: {integrity: sha512-FujAN40NEejeXdzPt+3sZ3F2dx1U24BY2XTY01+MG8mbxCiA2XukXdcbyMyLAHJ/1AUUnQd1tZlvIjefWWEJeA==}
dependencies:
@@ -1030,6 +1206,15 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
+ /sprintf-js@1.0.3:
+ resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+ dev: true
+
+ /strip-bom-string@1.0.0:
+ resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
/superjson@2.2.1:
resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==}
engines: {node: '>=16'}
@@ -1046,6 +1231,13 @@ packages:
engines: {node: '>=4'}
dev: true
+ /to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+ dependencies:
+ is-number: 7.0.0
+ dev: true
+
/vite@5.4.2:
resolution: {integrity: sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -1084,6 +1276,21 @@ packages:
fsevents: 2.3.3
dev: true
+ /vitepress-auto-sidebar-plugin@0.2.4(vite@5.4.2)(vitepress@1.3.4):
+ resolution: {integrity: sha512-s2EcnBAi6zTDKSODTKl1dtmq/0Wl/WSVT0QWS4/6Q8j2+LIL9s4tV6swVhffrGii/zQGouIlMyQflqDFGGZJIw==}
+ peerDependencies:
+ vite: ^5.2.9
+ vitepress: ^1.1.0
+ dependencies:
+ consola: 3.2.3
+ fast-glob: 3.3.2
+ gray-matter: 4.0.3
+ pathe: 1.1.2
+ perfect-debounce: 1.0.0
+ vite: 5.4.2
+ vitepress: 1.3.4(@algolia/client-search@5.1.1)(search-insights@2.17.0)
+ dev: true
+
/vitepress@1.3.4(@algolia/client-search@5.1.1)(search-insights@2.17.0):
resolution: {integrity: sha512-I1/F6OW1xl3kW4PaIMC6snxjWgf3qfziq2aqsDoFc/Gt41WbcRv++z8zjw8qGRIJ+I4bUW7ZcKFDHHN/jkH9DQ==}
hasBin: true
diff --git a/liteyuki_autodoc/__init__.py b/liteyuki_autodoc/__init__.py
new file mode 100644
index 0000000..31a9761
--- /dev/null
+++ b/liteyuki_autodoc/__init__.py
@@ -0,0 +1,10 @@
+# -*- 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
+"""
\ No newline at end of file
diff --git a/liteyuki_autodoc/__main__.py b/liteyuki_autodoc/__main__.py
new file mode 100644
index 0000000..dff40b1
--- /dev/null
+++ b/liteyuki_autodoc/__main__.py
@@ -0,0 +1,46 @@
+# -*- 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 liteyuki_autodoc.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()
diff --git a/liteyuki_autodoc/docstring/__init__.py b/liteyuki_autodoc/docstring/__init__.py
new file mode 100644
index 0000000..323b116
--- /dev/null
+++ b/liteyuki_autodoc/docstring/__init__.py
@@ -0,0 +1,10 @@
+# -*- 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
+"""
\ No newline at end of file
diff --git a/liteyuki_autodoc/docstring/docstring.py b/liteyuki_autodoc/docstring/docstring.py
new file mode 100644
index 0000000..f1e9855
--- /dev/null
+++ b/liteyuki_autodoc/docstring/docstring.py
@@ -0,0 +1,150 @@
+# -*- 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 liteyuki_autodoc.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
+ if is_classmethod:
+ PREFIX = " -"
+ ret = ""
+ ret += self.desc + "\n\n"
+ if self.args:
+ ret += PREFIX + f"{get_text(lang, 'docstring.args')}:\n\n"
+ for arg in self.args:
+ ret += PREFIX + f"{arg.name}: {arg.type} {arg.desc}\n\n"
+ if self.attrs:
+ ret += PREFIX + f"{get_text(lang, 'docstring.attrs')}:\n\n"
+ for attr in self.attrs:
+ ret += PREFIX + f"{attr.name}: {attr.type} {attr.desc}\n\n"
+ if self.return_:
+ ret += PREFIX + f"{get_text(lang, 'docstring.return')}:\n\n"
+ ret += PREFIX + f"{self.return_.desc}\n\n"
+ if self.raise_:
+ ret += PREFIX + f"{get_text(lang, 'docstring.raises')}:\n\n"
+ for exception in self.raise_:
+ ret += PREFIX + f"{exception.name} {exception.desc}\n\n"
+ if self.example:
+ ret += PREFIX + f"{get_text(lang, 'docstring.example')}:\n\n"
+ for example in self.example:
+ ret += PREFIX + f"{example.desc}\n Input: {example.input}\n Output: {example.output}\n\n"
+ return ret
+
+ def __str__(self):
+ return self.desc
diff --git a/liteyuki_autodoc/docstring/parser.py b/liteyuki_autodoc/docstring/parser.py
new file mode 100644
index 0000000..35d05c3
--- /dev/null
+++ b/liteyuki_autodoc/docstring/parser.py
@@ -0,0 +1,178 @@
+"""
+Google docstring parser for Python.
+"""
+from typing import Optional
+
+from liteyuki_autodoc.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 match_token(self) -> Optional[str]:
+ for token in self._tokens:
+ if self.lines[self.lineno][self.char:].startswith(token):
+ self.char += len(token)
+ 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:
+ """
+ while self.lineno + 1 < len(self.lines):
+ line = self.lines[self.lineno + 1]
+
+ if line.startswith(" " * self.indent):
+ line = line[self.indent:]
+ else:
+ return None
+ if not line:
+ return None
+ self.lineno += 1
+ return line
+
+ 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}")
diff --git a/liteyuki_autodoc/i18n.py b/liteyuki_autodoc/i18n.py
new file mode 100644
index 0000000..3430a0a
--- /dev/null
+++ b/liteyuki_autodoc/i18n.py
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+"""
+Internationalization module.
+"""
+from typing import Optional, TypeAlias
+
+NestedDict: TypeAlias = dict[str, 'str | NestedDict']
+
+i18n_dict: dict[str, NestedDict] = {
+ "en" : {
+ "docstring": {
+ "args" : "Args",
+ "return" : "Return",
+ "attribute": "Attribute",
+ "raises" : "Raises",
+ "example" : "Examples",
+ "yields" : "Yields",
+ },
+ "src": "Source code",
+ },
+ "zh-Hans": {
+ "docstring": {
+ "args" : "参数",
+ "return" : "返回",
+ "attribute": "属性",
+ "raises" : "引发",
+ "example" : "示例",
+ "yields" : "产出",
+ },
+ "src": "源码",
+ },
+ "zh-Hant": {
+ "docstring": {
+ "args" : "參數",
+ "return" : "返回",
+ "attribute": "屬性",
+ "raises" : "引發",
+ "example" : "示例",
+ "yields" : "產出",
+ },
+ "src": "源碼",
+ },
+ "ja" : {
+ "docstring": {
+ "args" : "引数",
+ "return" : "戻り値",
+ "attribute": "属性",
+ "raises" : "例外",
+ "example" : "例",
+ "yields" : "生成",
+ },
+ "src": "ソースコード",
+ },
+}
+
+
+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
diff --git a/liteyuki_autodoc/output.py b/liteyuki_autodoc/output.py
new file mode 100644
index 0000000..0d9a2d5
--- /dev/null
+++ b/liteyuki_autodoc/output.py
@@ -0,0 +1,107 @@
+# -*- 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 liteyuki_autodoc.style.markdown import generate
+from liteyuki_autodoc.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)
diff --git a/liteyuki_autodoc/style/__init__.py b/liteyuki_autodoc/style/__init__.py
new file mode 100644
index 0000000..fb9c65f
--- /dev/null
+++ b/liteyuki_autodoc/style/__init__.py
@@ -0,0 +1,10 @@
+# -*- 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
+"""
\ No newline at end of file
diff --git a/liteyuki_autodoc/style/markdown.py b/liteyuki_autodoc/style/markdown.py
new file mode 100644
index 0000000..730becf
--- /dev/null
+++ b/liteyuki_autodoc/style/markdown.py
@@ -0,0 +1,61 @@
+# -*- 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 liteyuki_autodoc.syntax.astparser import AstParser
+from liteyuki_autodoc.syntax.node import *
+from liteyuki_autodoc.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
+ """
+ print(parser.variables)
+ 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 var in parser.variables:
+ if var.type == TypeHint.NO_TYPEHINT:
+ md += f"### ***var*** `{var.name} = {var.value}`\n\n"
+ else:
+ md += f"### ***var*** `{var.name}: {var.type} = {var.value}`\n\n"
+
+ for func in parser.functions:
+
+ md += func.markdown(lang)
+
+ for cls in parser.classes:
+ md += f"### ***class*** `{cls.name}`\n\n"
+ for mtd in cls.methods:
+
+ md += mtd.markdown(lang, 2, True)
+
+ for attr in cls.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
diff --git a/liteyuki_autodoc/syntax/astparser.py b/liteyuki_autodoc/syntax/astparser.py
new file mode 100644
index 0000000..c94e240
--- /dev/null
+++ b/liteyuki_autodoc/syntax/astparser.py
@@ -0,0 +1,198 @@
+# -*- 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
+
+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()
+
+ 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=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=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=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_=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()
+ ))
+ 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=ast.unparse(arg.annotation).strip() if arg.annotation else TypeHint.NO_TYPEHINT,
+ )
+ for arg in node.args.posonlyargs
+ ],
+ args=[
+ ArgNode(
+ name=arg.arg,
+ type=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=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_=ast.unparse(node.returns).strip() if node.returns else TypeHint.NO_TYPEHINT,
+ 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_variable(node):
+ # print("变量不在模块级别", ast.unparse(node))
+ continue
+ else:
+ print("变量在模块级别", ast.unparse(node))
+ 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
+ ))
+ elif 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()
+ ))
+
+ 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):
+ 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
diff --git a/liteyuki_autodoc/syntax/node.py b/liteyuki_autodoc/syntax/node.py
new file mode 100644
index 0000000..aecb3a5
--- /dev/null
+++ b/liteyuki_autodoc/syntax/node.py
@@ -0,0 +1,258 @@
+# -*- 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 liteyuki_autodoc.docstring.docstring import Docstring
+from liteyuki_autodoc.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
+
+
+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 = Field(TypeHint.NO_RETURN, alias="return")
+ decorators: list[str] = []
+ src: str
+ is_async: bool = False
+
+ 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, is_classmethod: bool = False) -> str:
+ """
+ Args:
+ indent: int
+ The number of spaces to indent the markdown.
+ is_classmethod: bool
+ lang: str
+ The language of the
+ Returns:
+ markdown style document
+ """
+ self.complete_default_args()
+ PREFIX = "" * indent
+ if is_classmethod:
+ PREFIX = "- #"
+
+ md = ""
+
+ # 装饰器部分
+ if len(self.decorators) > 0:
+ for decorator in self.decorators:
+ md += PREFIX + f"### `@{decorator}`\n"
+
+ if self.is_async:
+ md += PREFIX + "### *async def* "
+ else:
+ md += PREFIX + "### *def* "
+
+ md += f"`{self.name}(" # 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)
+
+ md += ", ".join(args) + ")"
+
+ 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\n{get_text(lang, 'src')}
\n\n```python\n{self.src}\n```\n \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.
+ inherit: list["ClassNode"] = []
+ The classes that the class inherits from
+ """
+ name: str
+ docs: Optional[Docstring] = None
+ attrs: list[AttrNode] = []
+ methods: list[FunctionNode] = []
+ inherit: list["ClassNode"] = []
diff --git a/make_docs.py b/make_docs.py
new file mode 100644
index 0000000..54a1301
--- /dev/null
+++ b/make_docs.py
@@ -0,0 +1,10 @@
+# -*- 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
+"""
\ No newline at end of file
diff --git a/mbcp/mp_math/angle.py b/mbcp/mp_math/angle.py
index 2cd02ec..a7fc861 100644
--- a/mbcp/mp_math/angle.py
+++ b/mbcp/mp_math/angle.py
@@ -15,7 +15,11 @@ from .const import PI # type: ignore
from .utils import approx
-class AnyAngle:
+class Angle:
+ ...
+
+
+class AnyAngle(Angle):
def __init__(self, value: float, is_radian: bool = False):
"""
任意角度。
diff --git a/mbcp/mp_math/equation.py b/mbcp/mp_math/equation.py
index 9352af0..e6e5ce8 100644
--- a/mbcp/mp_math/equation.py
+++ b/mbcp/mp_math/equation.py
@@ -18,9 +18,10 @@ class CurveEquation:
def __init__(self, x_func: OneVarFunc, y_func: OneVarFunc, z_func: OneVarFunc):
"""
曲线方程。
- :param x_func:
- :param y_func:
- :param z_func:
+ Args:
+ x_func: x函数
+ y_func: y函数
+ z_func: z函数
"""
self.x_func = x_func
self.y_func = y_func
@@ -31,7 +32,7 @@ class CurveEquation:
计算曲线上的点。
Args:
*t:
-
+ 参数
Returns:
"""
diff --git a/mbcp/mp_math/plane.py b/mbcp/mp_math/plane.py
index e87deff..dc260cb 100644
--- a/mbcp/mp_math/plane.py
+++ b/mbcp/mp_math/plane.py
@@ -32,12 +32,11 @@ class Plane3:
self.c = c
self.d = d
- def approx(self, other: 'Plane3', epsilon: float = APPROX) -> bool:
+ def approx(self, other: 'Plane3') -> bool:
"""
判断两个平面是否近似相等。
Args:
other:
- epsilon:
Returns:
是否近似相等
diff --git a/mbcp/mp_math/point.py b/mbcp/mp_math/point.py
index 7a1bed6..35facf1 100644
--- a/mbcp/mp_math/point.py
+++ b/mbcp/mp_math/point.py
@@ -62,15 +62,15 @@ class Point3:
"""
return approx(self.x, other.x) and approx(self.y, other.y) and approx(self.z, other.z)
- def __sub__(self, other: "Point3") -> "Vector3":
- """
- P - P -> V
-
- P - V -> P 已在 :class:`Vector3` 中实现
- Args:
- other:
- Returns:
-
- """
- from .vector import Vector3
- return Vector3(self.x - other.x, self.y - other.y, self.z - other.z)
+ # def __sub__(self, other: "Point3") -> "Vector3":
+ # """
+ # P - P -> V
+ #
+ # P - V -> P 已在 :class:`Vector3` 中实现
+ # Args:
+ # other:
+ # Returns:
+ #
+ # """
+ # from .vector import Vector3
+ # return Vector3(self.x - other.x, self.y - other.y, self.z - other.z)
diff --git a/docs/mkdoc.py b/mkdoc.py
similarity index 96%
rename from docs/mkdoc.py
rename to mkdoc.py
index 2bd10ee..72bf946 100644
--- a/docs/mkdoc.py
+++ b/mkdoc.py
@@ -323,19 +323,13 @@ def generate_docs(module_folder: str, output_dir: str, with_top: bool = False, l
# 生成markdown
- if "README" in abs_md_path:
+ if "index" in abs_md_path:
front_matter = {
- "title" : module_info.module_path.replace(".__init__", "").replace("_", "\\n"),
- "index" : "true",
- "icon" : "laptop-code",
- "category": "API"
+ "title" : module_info.module_path.replace(".__init__", ""),
}
else:
front_matter = {
- "title" : module_info.module_path.replace("_", "\\n"),
- "order" : "1",
- "icon" : "laptop-code",
- "category": "API"
+ "title" : module_info.module_path
}
md_content = generate_markdown(module_info, front_matter)