diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml
index f859671..bc000eb 100644
--- a/.github/workflows/pytest.yml
+++ b/.github/workflows/pytest.yml
@@ -31,7 +31,8 @@ jobs:
- name: Test with pytest
run: |
- pdm run pytest --junitxml=report/report.xml
+ pdm run pytest --junitxml=report/report.xml.
+ 0
- name: Archive Pytest test report
uses: actions/upload-artifact@v3
diff --git a/.gitignore b/.gitignore
index ef5ce8f..a8698e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,10 @@
.pdm-build
.pdm-python
pdm.lock
-.pdm-build/
\ No newline at end of file
+.pdm-build/
+
+# node.js
+node_modules/
+
+docs/.vitepress/dist
+docs/.vitepress/cache
\ No newline at end of file
diff --git a/README.md b/README.md
index 9fba32b..2a00fd1 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,14 @@
-# mcpe
-MinecraftParticleEffect
+# More Basic Change Particle - 更多基础变换粒子
+A Minecraft particle production library
+
+## 介绍
+该库用于一些Minecraft粒子特效的计算和制作,集成了常用的numpy,sympy,scipy等科学计算库
+
+## 文档
+[MBCP docs](https://mbcp.liteyuki.icu)
+
+## 示例
+- [【特效红石音乐】童话镇~「总有一条蜿蜒在童话镇里...」](https://www.bilibili.com/video/BV1xE4m1d72j)
+
+- [【特效红石音乐】使一颗心免于哀伤 If I Can Stop One Heart From Breaking「崩坏:星穹铁道 EP」](https://www.bilibili.com/video/BV1B1421t7i3)
+
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
new file mode 100644
index 0000000..75104cd
--- /dev/null
+++ b/docs/.vitepress/config.mts
@@ -0,0 +1,28 @@
+import { defineConfig } from 'vitepress'
+
+// https://vitepress.dev/reference/site-config
+export default defineConfig({
+ title: "MBCP docs",
+ description: "MBCP library docs",
+ 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/vuejs/vitepress' }
+ ]
+ }
+})
diff --git a/docs/api-examples.md b/docs/api-examples.md
new file mode 100644
index 0000000..6bd8bb5
--- /dev/null
+++ b/docs/api-examples.md
@@ -0,0 +1,49 @@
+---
+outline: deep
+---
+
+# Runtime API Examples
+
+This page demonstrates usage of some of the runtime APIs provided by VitePress.
+
+The main `useData()` API can be used to access site, theme, and page data for the current page. It works in both `.md` and `.vue` files:
+
+```md
+
+
+## Results
+
+### Theme Data
+
{{ theme }}
+
+### Page Data
+{{ page }}
+
+### Page Frontmatter
+{{ frontmatter }}
+```
+
+
+
+## Results
+
+### Theme Data
+{{ theme }}
+
+### Page Data
+{{ page }}
+
+### Page Frontmatter
+{{ frontmatter }}
+
+## More
+
+Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata).
diff --git a/docs/api/indedx.md b/docs/api/indedx.md
new file mode 100644
index 0000000..daef73b
--- /dev/null
+++ b/docs/api/indedx.md
@@ -0,0 +1,7 @@
+---
+title: mbcp.\n\ninit\n\n
+order: 1
+icon: laptop-code
+category: API
+---
+
diff --git a/docs/api/mp_math/angle.md b/docs/api/mp_math/angle.md
new file mode 100644
index 0000000..a88b240
--- /dev/null
+++ b/docs/api/mp_math/angle.md
@@ -0,0 +1,303 @@
+---
+title: mbcp.mp\nmath.angle
+order: 1
+icon: laptop-code
+category: API
+---
+
+### ***class*** `AnyAngle`
+
+
+
+### ***def*** `__init__(self, value: float, is_radian: bool) -> None`
+
+ 任意角度。
+
+Args:
+
+ value: 角度或弧度值
+
+ is_radian: 是否为弧度,默认为否
+
+
+源代码
+
+```python
+def __init__(self, value: float, is_radian: bool=False):
+ """
+ 任意角度。
+ Args:
+ value: 角度或弧度值
+ is_radian: 是否为弧度,默认为否
+ """
+ if is_radian:
+ self.radian = value
+ else:
+ self.radian = value * PI / 180
+```
+
+
+### ***@property***
+### ***def*** `complementary(self: Any) -> 'AnyAngle'`
+
+ 余角:两角的和为90°。
+
+Returns:
+
+ 余角
+
+
+源代码
+
+```python
+@property
+def complementary(self) -> 'AnyAngle':
+ """
+ 余角:两角的和为90°。
+ Returns:
+ 余角
+ """
+ return AnyAngle(PI / 2 - self.minimum_positive.radian, is_radian=True)
+```
+
+
+### ***@property***
+### ***def*** `supplementary(self: Any) -> 'AnyAngle'`
+
+ 补角:两角的和为180°。
+
+Returns:
+
+ 补角
+
+
+源代码
+
+```python
+@property
+def supplementary(self) -> 'AnyAngle':
+ """
+ 补角:两角的和为180°。
+ Returns:
+ 补角
+ """
+ return AnyAngle(PI - self.minimum_positive.radian, is_radian=True)
+```
+
+
+### ***@property***
+### ***def*** `degree(self: Any) -> float`
+
+ 角度。
+
+Returns:
+
+ 弧度
+
+
+源代码
+
+```python
+@property
+def degree(self) -> float:
+ """
+ 角度。
+ Returns:
+ 弧度
+ """
+ return self.radian * 180 / PI
+```
+
+
+### ***@property***
+### ***def*** `minimum_positive(self: Any) -> 'AnyAngle'`
+
+ 最小正角。
+
+Returns:
+
+ 最小正角度
+
+
+源代码
+
+```python
+@property
+def minimum_positive(self) -> 'AnyAngle':
+ """
+ 最小正角。
+ Returns:
+ 最小正角度
+ """
+ return AnyAngle(self.radian % (2 * PI))
+```
+
+
+### ***@property***
+### ***def*** `maximum_negative(self: Any) -> 'AnyAngle'`
+
+ 最大负角。
+
+Returns:
+
+ 最大负角度
+
+
+源代码
+
+```python
+@property
+def maximum_negative(self) -> 'AnyAngle':
+ """
+ 最大负角。
+ Returns:
+ 最大负角度
+ """
+ return AnyAngle(-self.radian % (2 * PI), is_radian=True)
+```
+
+
+### ***@property***
+### ***def*** `sin(self: Any) -> float`
+
+ 正弦值。
+
+Returns:
+
+ 正弦值
+
+
+源代码
+
+```python
+@property
+def sin(self) -> float:
+ """
+ 正弦值。
+ Returns:
+ 正弦值
+ """
+ return math.sin(self.radian)
+```
+
+
+### ***@property***
+### ***def*** `cos(self: Any) -> float`
+
+ 余弦值。
+
+Returns:
+
+ 余弦值
+
+
+源代码
+
+```python
+@property
+def cos(self) -> float:
+ """
+ 余弦值。
+ Returns:
+ 余弦值
+ """
+ return math.cos(self.radian)
+```
+
+
+### ***@property***
+### ***def*** `tan(self: Any) -> float`
+
+ 正切值。
+
+Returns:
+
+ 正切值
+
+
+源代码
+
+```python
+@property
+def tan(self) -> float:
+ """
+ 正切值。
+ Returns:
+ 正切值
+ """
+ return math.tan(self.radian)
+```
+
+
+### ***@property***
+### ***def*** `cot(self: Any) -> float`
+
+ 余切值。
+
+Returns:
+
+ 余切值
+
+
+源代码
+
+```python
+@property
+def cot(self) -> float:
+ """
+ 余切值。
+ Returns:
+ 余切值
+ """
+ return 1 / math.tan(self.radian)
+```
+
+
+### ***@property***
+### ***def*** `sec(self: Any) -> float`
+
+ 正割值。
+
+Returns:
+
+ 正割值
+
+
+源代码
+
+```python
+@property
+def sec(self) -> float:
+ """
+ 正割值。
+ Returns:
+ 正割值
+ """
+ return 1 / math.cos(self.radian)
+```
+
+
+### ***@property***
+### ***def*** `csc(self: Any) -> float`
+
+ 余割值。
+
+Returns:
+
+ 余割值
+
+
+源代码
+
+```python
+@property
+def csc(self) -> float:
+ """
+ 余割值。
+ Returns:
+ 余割值
+ """
+ return 1 / math.sin(self.radian)
+```
+
+
diff --git a/docs/api/mp_math/const.md b/docs/api/mp_math/const.md
new file mode 100644
index 0000000..5e9f287
--- /dev/null
+++ b/docs/api/mp_math/const.md
@@ -0,0 +1,31 @@
+---
+title: mbcp.mp\nmath.const
+order: 1
+icon: laptop-code
+category: API
+---
+
+### ***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
new file mode 100644
index 0000000..cbdba56
--- /dev/null
+++ b/docs/api/mp_math/equation.md
@@ -0,0 +1,145 @@
+---
+title: mbcp.mp\nmath.equation
+order: 1
+icon: laptop-code
+category: API
+---
+
+### ***def*** `get_partial_derivative_func(func: MultiVarsFunc, var: int | tuple[int, ...], epsilon: Number) -> MultiVarsFunc`
+
+求N元函数一阶偏导函数。这玩意不太稳定,慎用。
+
+Args:
+
+ func: 函数
+
+ var: 变量位置,可为整数(一阶偏导)或整数元组(高阶偏导)
+
+ epsilon: 偏移量
+
+Returns:
+
+ 偏导函数
+
+Raises:
+
+ ValueError: 无效变量类型
+
+
+源代码
+
+```python
+def get_partial_derivative_func(func: MultiVarsFunc, var: int | tuple[int, ...], epsilon: Number=EPSILON) -> MultiVarsFunc:
+ """
+ 求N元函数一阶偏导函数。这玩意不太稳定,慎用。
+ Args:
+ func: 函数
+ var: 变量位置,可为整数(一阶偏导)或整数元组(高阶偏导)
+ epsilon: 偏移量
+ Returns:
+ 偏导函数
+ Raises:
+ ValueError: 无效变量类型
+ """
+ if isinstance(var, int):
+
+ def partial_derivative_func(*args: Var) -> Var:
+ args_list_plus = list(args)
+ args_list_plus[var] += epsilon
+ args_list_minus = list(args)
+ args_list_minus[var] -= epsilon
+ return (func(*args_list_plus) - func(*args_list_minus)) / (2 * epsilon)
+ return partial_derivative_func
+ elif isinstance(var, tuple):
+
+ def high_order_partial_derivative_func(*args: Var) -> Var:
+ result_func = func
+ for v in var:
+ result_func = get_partial_derivative_func(result_func, v, epsilon)
+ return result_func(*args)
+ return high_order_partial_derivative_func
+ else:
+ raise ValueError('Invalid var type')
+```
+
+
+### ***def*** `partial_derivative_func() -> Var`
+
+
+
+
+源代码
+
+```python
+def partial_derivative_func(*args: Var) -> Var:
+ args_list_plus = list(args)
+ args_list_plus[var] += epsilon
+ args_list_minus = list(args)
+ args_list_minus[var] -= epsilon
+ return (func(*args_list_plus) - func(*args_list_minus)) / (2 * epsilon)
+```
+
+
+### ***def*** `high_order_partial_derivative_func() -> Var`
+
+
+
+
+源代码
+
+```python
+def high_order_partial_derivative_func(*args: Var) -> Var:
+ result_func = func
+ for v in var:
+ result_func = get_partial_derivative_func(result_func, v, epsilon)
+ return result_func(*args)
+```
+
+
+### ***class*** `CurveEquation`
+
+
+
+### ***def*** `__init__(self, x_func: OneVarFunc, y_func: OneVarFunc, z_func: OneVarFunc) -> None`
+
+ 曲线方程。
+
+:param x_func:
+
+:param y_func:
+
+:param z_func:
+
+
+源代码
+
+```python
+def __init__(self, x_func: OneVarFunc, y_func: OneVarFunc, z_func: OneVarFunc):
+ """
+ 曲线方程。
+ :param x_func:
+ :param y_func:
+ :param z_func:
+ """
+ self.x_func = x_func
+ self.y_func = y_func
+ self.z_func = z_func
+```
+
+
+### ***var*** `args_list_plus = list(args)`
+
+
+
+### ***var*** `args_list_minus = list(args)`
+
+
+
+### ***var*** `result_func = func`
+
+
+
+### ***var*** `result_func = get_partial_derivative_func(result_func, v, epsilon)`
+
+
+
diff --git a/docs/api/mp_math/indedx.md b/docs/api/mp_math/indedx.md
new file mode 100644
index 0000000..21d2307
--- /dev/null
+++ b/docs/api/mp_math/indedx.md
@@ -0,0 +1,7 @@
+---
+title: mbcp.mp\nmath.\n\ninit\n\n
+order: 1
+icon: laptop-code
+category: API
+---
+
diff --git a/docs/api/mp_math/line.md b/docs/api/mp_math/line.md
new file mode 100644
index 0000000..3ae3642
--- /dev/null
+++ b/docs/api/mp_math/line.md
@@ -0,0 +1,489 @@
+---
+title: mbcp.mp\nmath.line
+order: 1
+icon: laptop-code
+category: API
+---
+
+### ***class*** `Line3`
+
+
+
+### ***def*** `__init__(self, point: 'Point3', direction: 'Vector3') -> None`
+
+ 三维空间中的直线。由一个点和一个方向向量确定。
+
+Args:
+
+ point: 直线上的一点
+
+ direction: 直线的方向向量
+
+
+源代码
+
+```python
+def __init__(self, point: 'Point3', direction: 'Vector3'):
+ """
+ 三维空间中的直线。由一个点和一个方向向量确定。
+ Args:
+ point: 直线上的一点
+ direction: 直线的方向向量
+ """
+ self.point = point
+ self.direction = direction
+```
+
+
+### ***def*** `approx(self, other: 'Line3', epsilon: float) -> bool`
+
+ 判断两条直线是否近似相等。
+
+Args:
+
+ other: 另一条直线
+
+ epsilon: 误差
+
+Returns:
+
+ 是否近似相等
+
+
+源代码
+
+```python
+def approx(self, other: 'Line3', epsilon: float=APPROX) -> bool:
+ """
+ 判断两条直线是否近似相等。
+ Args:
+ other: 另一条直线
+ epsilon: 误差
+ Returns:
+ 是否近似相等
+ """
+ return self.is_approx_parallel(other, epsilon) and (self.point - other.point).is_approx_parallel(self.direction, epsilon)
+```
+
+
+### ***def*** `cal_angle(self, other: 'Line3') -> 'AnyAngle'`
+
+ 计算直线和直线之间的夹角。
+
+Args:
+
+ other: 另一条直线
+
+Returns:
+
+ 夹角弧度
+
+Raises:
+
+ TypeError: 不支持的类型
+
+
+源代码
+
+```python
+def cal_angle(self, other: 'Line3') -> 'AnyAngle':
+ """
+ 计算直线和直线之间的夹角。
+ Args:
+ other: 另一条直线
+ Returns:
+ 夹角弧度
+ Raises:
+ TypeError: 不支持的类型
+ """
+ return self.direction.cal_angle(other.direction)
+```
+
+
+### ***def*** `cal_distance(self, other: 'Line3 | Point3') -> float`
+
+ 计算直线和直线或点之间的距离。
+
+Args:
+
+ other: 平行直线或点
+
+
+
+Returns:
+
+ 距离
+
+Raises:
+
+ TypeError: 不支持的类型
+
+
+源代码
+
+```python
+def cal_distance(self, other: 'Line3 | Point3') -> float:
+ """
+ 计算直线和直线或点之间的距离。
+ Args:
+ other: 平行直线或点
+
+ Returns:
+ 距离
+ Raises:
+ TypeError: 不支持的类型
+ """
+ if isinstance(other, Line3):
+ if self == other:
+ return 0
+ elif self.is_parallel(other):
+ return (other.point - self.point).cross(self.direction).length / self.direction.length
+ elif not self.is_coplanar(other):
+ return abs(self.direction.cross(other.direction) @ (self.point - other.point) / self.direction.cross(other.direction).length)
+ else:
+ return 0
+ elif isinstance(other, Point3):
+ return (other - self.point).cross(self.direction).length / self.direction.length
+ else:
+ raise TypeError('Unsupported type.')
+```
+
+
+### ***def*** `cal_intersection(self, other: 'Line3') -> 'Point3'`
+
+ 计算两条直线的交点。
+
+Args:
+
+ other: 另一条直线
+
+Returns:
+
+ 交点
+
+Raises:
+
+ ValueError: 直线平行
+
+ ValueError: 直线不共面
+
+
+源代码
+
+```python
+def cal_intersection(self, other: 'Line3') -> 'Point3':
+ """
+ 计算两条直线的交点。
+ Args:
+ other: 另一条直线
+ Returns:
+ 交点
+ Raises:
+ ValueError: 直线平行
+ ValueError: 直线不共面
+ """
+ if self.is_parallel(other):
+ raise ValueError('Lines are parallel and do not intersect.')
+ if not self.is_coplanar(other):
+ raise ValueError('Lines are not coplanar and do not intersect.')
+ return self.point + self.direction.cross(other.direction) @ other.direction.cross(self.point - other.point) / self.direction.cross(other.direction).length ** 2 * self.direction
+```
+
+
+### ***def*** `cal_perpendicular(self, point: 'Point3') -> 'Line3'`
+
+ 计算直线经过指定点p的垂线。
+
+Args:
+
+ point: 指定点
+
+Returns:
+
+ 垂线
+
+
+源代码
+
+```python
+def cal_perpendicular(self, point: 'Point3') -> 'Line3':
+ """
+ 计算直线经过指定点p的垂线。
+ Args:
+ point: 指定点
+ Returns:
+ 垂线
+ """
+ return Line3(point, self.direction.cross(point - self.point))
+```
+
+
+### ***def*** `get_point(self, t: RealNumber) -> 'Point3'`
+
+ 获取直线上的点。同一条直线,但起始点和方向向量不同,则同一个t对应的点不同。
+
+Args:
+
+ t: 参数t
+
+Returns:
+
+ 点
+
+
+源代码
+
+```python
+def get_point(self, t: RealNumber) -> 'Point3':
+ """
+ 获取直线上的点。同一条直线,但起始点和方向向量不同,则同一个t对应的点不同。
+ Args:
+ t: 参数t
+ Returns:
+ 点
+ """
+ return self.point + t * self.direction
+```
+
+
+### ***def*** `get_parametric_equations(self) -> tuple[OneSingleVarFunc, OneSingleVarFunc, OneSingleVarFunc]`
+
+ 获取直线的参数方程。
+
+Returns:
+
+ x(t), y(t), z(t)
+
+
+源代码
+
+```python
+def get_parametric_equations(self) -> tuple[OneSingleVarFunc, OneSingleVarFunc, OneSingleVarFunc]:
+ """
+ 获取直线的参数方程。
+ Returns:
+ x(t), y(t), z(t)
+ """
+ return (lambda t: self.point.x + self.direction.x * t, lambda t: self.point.y + self.direction.y * t, lambda t: self.point.z + self.direction.z * t)
+```
+
+
+### ***def*** `is_approx_parallel(self, other: 'Line3', epsilon: float) -> bool`
+
+ 判断两条直线是否近似平行。
+
+Args:
+
+ other: 另一条直线
+
+ epsilon: 误差
+
+Returns:
+
+ 是否近似平行
+
+
+源代码
+
+```python
+def is_approx_parallel(self, other: 'Line3', epsilon: float=1e-06) -> bool:
+ """
+ 判断两条直线是否近似平行。
+ Args:
+ other: 另一条直线
+ epsilon: 误差
+ Returns:
+ 是否近似平行
+ """
+ return self.direction.is_approx_parallel(other.direction, epsilon)
+```
+
+
+### ***def*** `is_parallel(self, other: 'Line3') -> bool`
+
+ 判断两条直线是否平行。
+
+Args:
+
+ other: 另一条直线
+
+Returns:
+
+ 是否平行
+
+
+源代码
+
+```python
+def is_parallel(self, other: 'Line3') -> bool:
+ """
+ 判断两条直线是否平行。
+ Args:
+ other: 另一条直线
+ Returns:
+ 是否平行
+ """
+ return self.direction.is_parallel(other.direction)
+```
+
+
+### ***def*** `is_collinear(self, other: 'Line3') -> bool`
+
+ 判断两条直线是否共线。
+
+Args:
+
+ other: 另一条直线
+
+Returns:
+
+ 是否共线
+
+
+源代码
+
+```python
+def is_collinear(self, other: 'Line3') -> bool:
+ """
+ 判断两条直线是否共线。
+ Args:
+ other: 另一条直线
+ Returns:
+ 是否共线
+ """
+ return self.is_parallel(other) and (self.point - other.point).is_parallel(self.direction)
+```
+
+
+### ***def*** `is_point_on(self, point: 'Point3') -> bool`
+
+ 判断点是否在直线上。
+
+Args:
+
+ point: 点
+
+Returns:
+
+ 是否在直线上
+
+
+源代码
+
+```python
+def is_point_on(self, point: 'Point3') -> bool:
+ """
+ 判断点是否在直线上。
+ Args:
+ point: 点
+ Returns:
+ 是否在直线上
+ """
+ return (point - self.point).is_parallel(self.direction)
+```
+
+
+### ***def*** `is_coplanar(self, other: 'Line3') -> bool`
+
+ 判断两条直线是否共面。
+
+充要条件:两直线方向向量的叉乘与两直线上任意一点的向量的点积为0。
+
+Args:
+
+ other: 另一条直线
+
+Returns:
+
+ 是否共面
+
+
+源代码
+
+```python
+def is_coplanar(self, other: 'Line3') -> bool:
+ """
+ 判断两条直线是否共面。
+ 充要条件:两直线方向向量的叉乘与两直线上任意一点的向量的点积为0。
+ Args:
+ other: 另一条直线
+ Returns:
+ 是否共面
+ """
+ return self.direction.cross(other.direction) @ (self.point - other.point) == 0
+```
+
+
+### ***def*** `simplify(self) -> None`
+
+ 简化直线方程,等价相等。
+
+自体简化,不返回值。
+
+
+
+按照可行性一次对x y z 化 0 处理,并对向量单位化
+
+
+源代码
+
+```python
+def simplify(self):
+ """
+ 简化直线方程,等价相等。
+ 自体简化,不返回值。
+
+ 按照可行性一次对x y z 化 0 处理,并对向量单位化
+ """
+ self.direction.normalize()
+ if self.direction.x == 0:
+ self.point.x = 0
+ if self.direction.y == 0:
+ self.point.y = 0
+ if self.direction.z == 0:
+ self.point.z = 0
+```
+
+
+### ***@classmethod***
+### ***def*** `from_two_points(cls: Any, p1: 'Point3', p2: 'Point3') -> 'Line3'`
+
+ 工厂函数 由两点构造直线。
+
+Args:
+
+ p1: 点1
+
+ p2: 点2
+
+Returns:
+
+ 直线
+
+
+源代码
+
+```python
+@classmethod
+def from_two_points(cls, p1: 'Point3', p2: 'Point3') -> 'Line3':
+ """
+ 工厂函数 由两点构造直线。
+ Args:
+ p1: 点1
+ p2: 点2
+ Returns:
+ 直线
+ """
+ direction = p2 - p1
+ return cls(p1, direction)
+```
+
+
+### ***var*** `direction = p2 - p1`
+
+
+
+### ***var*** `s = 'Line3: '`
+
+
+
diff --git a/docs/api/mp_math/mp_math_typing.md b/docs/api/mp_math/mp_math_typing.md
new file mode 100644
index 0000000..b1bb7d5
--- /dev/null
+++ b/docs/api/mp_math/mp_math_typing.md
@@ -0,0 +1,15 @@
+---
+title: mbcp.mp\nmath.mp\nmath\ntyping
+order: 1
+icon: laptop-code
+category: API
+---
+
+### ***var*** `SingleVar = TypeVar('SingleVar', bound=Number)`
+
+
+
+### ***var*** `ArrayVar = TypeVar('ArrayVar', bound=Iterable[Number])`
+
+
+
diff --git a/docs/api/mp_math/plane.md b/docs/api/mp_math/plane.md
new file mode 100644
index 0000000..3832cd3
--- /dev/null
+++ b/docs/api/mp_math/plane.md
@@ -0,0 +1,552 @@
+---
+title: mbcp.mp\nmath.plane
+order: 1
+icon: laptop-code
+category: API
+---
+
+### ***class*** `Plane3`
+
+
+
+### ***def*** `__init__(self, a: float, b: float, c: float, d: float) -> None`
+
+ 平面方程:ax + by + cz + d = 0
+
+Args:
+
+ a:
+
+ b:
+
+ c:
+
+ d:
+
+
+源代码
+
+```python
+def __init__(self, a: float, b: float, c: float, d: float):
+ """
+ 平面方程:ax + by + cz + d = 0
+ Args:
+ a:
+ b:
+ c:
+ d:
+ """
+ self.a = a
+ self.b = b
+ self.c = c
+ self.d = d
+```
+
+
+### ***def*** `approx(self, other: 'Plane3', epsilon: float) -> bool`
+
+ 判断两个平面是否近似相等。
+
+Args:
+
+ other:
+
+ epsilon:
+
+
+
+Returns:
+
+ 是否近似相等
+
+
+源代码
+
+```python
+def approx(self, other: 'Plane3', epsilon: float=APPROX) -> bool:
+ """
+ 判断两个平面是否近似相等。
+ Args:
+ other:
+ epsilon:
+
+ Returns:
+ 是否近似相等
+ """
+ if self.a != 0:
+ k = other.a / self.a
+ return approx(other.b, self.b * k) and approx(other.c, self.c * k) and approx(other.d, self.d * k)
+ elif self.b != 0:
+ k = other.b / self.b
+ return approx(other.a, self.a * k) and approx(other.c, self.c * k) and approx(other.d, self.d * k)
+ elif self.c != 0:
+ k = other.c / self.c
+ return approx(other.a, self.a * k) and approx(other.b, self.b * k) and approx(other.d, self.d * k)
+ else:
+ return False
+```
+
+
+### ***def*** `cal_angle(self, other: 'Line3 | Plane3') -> 'AnyAngle'`
+
+ 计算平面与平面之间的夹角。
+
+Args:
+
+ other: 另一个平面
+
+Returns:
+
+ 夹角弧度
+
+Raises:
+
+ TypeError: 不支持的类型
+
+
+源代码
+
+```python
+def cal_angle(self, other: 'Line3 | Plane3') -> 'AnyAngle':
+ """
+ 计算平面与平面之间的夹角。
+ Args:
+ other: 另一个平面
+ Returns:
+ 夹角弧度
+ Raises:
+ TypeError: 不支持的类型
+ """
+ if isinstance(other, Line3):
+ return self.normal.cal_angle(other.direction).complementary
+ elif isinstance(other, Plane3):
+ return AnyAngle(math.acos(self.normal @ other.normal / (self.normal.length * other.normal.length)), is_radian=True)
+ else:
+ raise TypeError(f'Unsupported type: {type(other)}')
+```
+
+
+### ***def*** `cal_distance(self, other: 'Plane3 | Point3') -> float`
+
+ 计算平面与平面或点之间的距离。
+
+Args:
+
+ other: 另一个平面或点
+
+Returns:
+
+ 距离
+
+Raises:
+
+ TypeError: 不支持的类型
+
+
+源代码
+
+```python
+def cal_distance(self, other: 'Plane3 | Point3') -> float:
+ """
+ 计算平面与平面或点之间的距离。
+ Args:
+ other: 另一个平面或点
+ Returns:
+ 距离
+ Raises:
+ TypeError: 不支持的类型
+ """
+ if isinstance(other, Plane3):
+ return 0
+ elif isinstance(other, Point3):
+ return abs(self.a * other.x + self.b * other.y + self.c * other.z + self.d) / (self.a ** 2 + self.b ** 2 + self.c ** 2) ** 0.5
+ else:
+ raise TypeError(f'Unsupported type: {type(other)}')
+```
+
+
+### ***def*** `cal_intersection_line3(self, other: 'Plane3') -> 'Line3'`
+
+ 计算两平面的交线。该方法有问题,待修复。
+
+Args:
+
+ other: 另一个平面
+
+Returns:
+
+ 交线
+
+Raises:
+
+
+源代码
+
+```python
+def cal_intersection_line3(self, other: 'Plane3') -> 'Line3':
+ """
+ 计算两平面的交线。该方法有问题,待修复。
+ Args:
+ other: 另一个平面
+ Returns:
+ 交线
+ Raises:
+ """
+ if self.normal.is_parallel(other.normal):
+ raise ValueError('Planes are parallel and have no intersection.')
+ direction = self.normal.cross(other.normal)
+ x, y, z = (0, 0, 0)
+ if self.a != 0 and other.a != 0:
+ A = np.array([[self.b, self.c], [other.b, other.c]])
+ B = np.array([-self.d, -other.d])
+ y, z = np.linalg.solve(A, B)
+ elif self.b != 0 and other.b != 0:
+ A = np.array([[self.a, self.c], [other.a, other.c]])
+ B = np.array([-self.d, -other.d])
+ x, z = np.linalg.solve(A, B)
+ elif self.c != 0 and other.c != 0:
+ A = np.array([[self.a, self.b], [other.a, other.b]])
+ B = np.array([-self.d, -other.d])
+ x, y = np.linalg.solve(A, B)
+ return Line3(Point3(x, y, z), direction)
+```
+
+
+### ***def*** `cal_intersection_point3(self, other: 'Line3') -> 'Point3'`
+
+ 计算平面与直线的交点。
+
+Args:
+
+ other: 不与平面平行或在平面上的直线
+
+Returns:
+
+ 交点
+
+Raises:
+
+ ValueError: 平面与直线平行或重合
+
+
+源代码
+
+```python
+def cal_intersection_point3(self, other: 'Line3') -> 'Point3':
+ """
+ 计算平面与直线的交点。
+ Args:
+ other: 不与平面平行或在平面上的直线
+ Returns:
+ 交点
+ Raises:
+ ValueError: 平面与直线平行或重合
+ """
+ if self.normal @ other.direction == 0:
+ raise ValueError('The plane and the line are parallel or coincident.')
+ x, y, z = other.get_parametric_equations()
+ 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)
+ return Point3(x(t), y(t), z(t))
+```
+
+
+### ***def*** `cal_parallel_plane3(self, point: 'Point3') -> 'Plane3'`
+
+ 计算平行于该平面且过指定点的平面。
+
+Args:
+
+ point: 指定点
+
+Returns:
+
+ 平面
+
+
+源代码
+
+```python
+def cal_parallel_plane3(self, point: 'Point3') -> 'Plane3':
+ """
+ 计算平行于该平面且过指定点的平面。
+ Args:
+ point: 指定点
+ Returns:
+ 平面
+ """
+ return Plane3.from_point_and_normal(point, self.normal)
+```
+
+
+### ***def*** `is_parallel(self, other: 'Plane3') -> bool`
+
+ 判断两个平面是否平行。
+
+Args:
+
+ other: 另一个平面
+
+Returns:
+
+ 是否平行
+
+
+源代码
+
+```python
+def is_parallel(self, other: 'Plane3') -> bool:
+ """
+ 判断两个平面是否平行。
+ Args:
+ other: 另一个平面
+ Returns:
+ 是否平行
+ """
+ return self.normal.is_parallel(other.normal)
+```
+
+
+### ***@property***
+### ***def*** `normal(self: Any) -> 'Vector3'`
+
+ 平面的法向量。
+
+Returns:
+
+ 法向量
+
+
+源代码
+
+```python
+@property
+def normal(self) -> 'Vector3':
+ """
+ 平面的法向量。
+ Returns:
+ 法向量
+ """
+ return Vector3(self.a, self.b, self.c)
+```
+
+
+### ***@classmethod***
+### ***def*** `from_point_and_normal(cls: Any, point: 'Point3', normal: 'Vector3') -> 'Plane3'`
+
+ 工厂函数 由点和法向量构造平面(点法式构造)。
+
+Args:
+
+ point: 平面上的一点
+
+ normal: 法向量
+
+Returns:
+
+ 平面
+
+
+源代码
+
+```python
+@classmethod
+def from_point_and_normal(cls, point: 'Point3', normal: 'Vector3') -> 'Plane3':
+ """
+ 工厂函数 由点和法向量构造平面(点法式构造)。
+ Args:
+ point: 平面上的一点
+ normal: 法向量
+ Returns:
+ 平面
+ """
+ a, b, c = (normal.x, normal.y, normal.z)
+ d = -a * point.x - b * point.y - c * point.z
+ return cls(a, b, c, d)
+```
+
+
+### ***@classmethod***
+### ***def*** `from_three_points(cls: Any, p1: 'Point3', p2: 'Point3', p3: 'Point3') -> 'Plane3'`
+
+ 工厂函数 由三点构造平面。
+
+Args:
+
+ p1: 点1
+
+ p2: 点2
+
+ p3: 点3
+
+Returns:
+
+ 平面
+
+
+源代码
+
+```python
+@classmethod
+def from_three_points(cls, p1: 'Point3', p2: 'Point3', p3: 'Point3') -> 'Plane3':
+ """
+ 工厂函数 由三点构造平面。
+ Args:
+ p1: 点1
+ p2: 点2
+ p3: 点3
+ Returns:
+ 平面
+ """
+ v1 = p2 - p1
+ v2 = p3 - p1
+ normal = v1.cross(v2)
+ return cls.from_point_and_normal(p1, normal)
+```
+
+
+### ***@classmethod***
+### ***def*** `from_two_lines(cls: Any, l1: 'Line3', l2: 'Line3') -> 'Plane3'`
+
+ 工厂函数 由两直线构造平面。
+
+Args:
+
+ l1: 直线1
+
+ l2: 直线2
+
+Returns:
+
+ 平面
+
+
+源代码
+
+```python
+@classmethod
+def from_two_lines(cls, l1: 'Line3', l2: 'Line3') -> 'Plane3':
+ """
+ 工厂函数 由两直线构造平面。
+ Args:
+ l1: 直线1
+ l2: 直线2
+ Returns:
+ 平面
+ """
+ v1 = l1.direction
+ v2 = l2.point - l1.point
+ if v2 == zero_vector3:
+ v2 = l2.get_point(1) - l1.point
+ return cls.from_point_and_normal(l1.point, v1.cross(v2))
+```
+
+
+### ***@classmethod***
+### ***def*** `from_point_and_line(cls: Any, point: 'Point3', line: 'Line3') -> 'Plane3'`
+
+ 工厂函数 由点和直线构造平面。
+
+Args:
+
+ point: 面上一点
+
+ line: 面上直线,不包含点
+
+Returns:
+
+ 平面
+
+
+源代码
+
+```python
+@classmethod
+def from_point_and_line(cls, point: 'Point3', line: 'Line3') -> 'Plane3':
+ """
+ 工厂函数 由点和直线构造平面。
+ Args:
+ point: 面上一点
+ line: 面上直线,不包含点
+ Returns:
+ 平面
+ """
+ return cls.from_point_and_normal(point, line.direction)
+```
+
+
+### ***var*** `direction = self.normal.cross(other.normal)`
+
+
+
+### ***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)`
+
+
+
+### ***var*** `d = -a * point.x - b * point.y - c * point.z`
+
+
+
+### ***var*** `v1 = p2 - p1`
+
+
+
+### ***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])`
+
+
+
diff --git a/docs/api/mp_math/point.md b/docs/api/mp_math/point.md
new file mode 100644
index 0000000..53e79f7
--- /dev/null
+++ b/docs/api/mp_math/point.md
@@ -0,0 +1,75 @@
+---
+title: mbcp.mp\nmath.point
+order: 1
+icon: laptop-code
+category: API
+---
+
+### ***class*** `Point3`
+
+
+
+### ***def*** `__init__(self, x: float, y: float, z: float) -> None`
+
+ 笛卡尔坐标系中的点。
+
+Args:
+
+ x: x 坐标
+
+ y: y 坐标
+
+ z: z 坐标
+
+
+源代码
+
+```python
+def __init__(self, x: float, y: float, z: float):
+ """
+ 笛卡尔坐标系中的点。
+ Args:
+ x: x 坐标
+ y: y 坐标
+ z: z 坐标
+ """
+ self.x = x
+ self.y = y
+ self.z = z
+```
+
+
+### ***def*** `approx(self, other: 'Point3', epsilon: float) -> bool`
+
+ 判断两个点是否近似相等。
+
+Args:
+
+ other:
+
+ epsilon:
+
+
+
+Returns:
+
+ 是否近似相等
+
+
+源代码
+
+```python
+def approx(self, other: 'Point3', epsilon: float=APPROX) -> bool:
+ """
+ 判断两个点是否近似相等。
+ Args:
+ other:
+ epsilon:
+
+ Returns:
+ 是否近似相等
+ """
+ return all([abs(self.x - other.x) < epsilon, abs(self.y - other.y) < epsilon, abs(self.z - other.z) < epsilon])
+```
+
+
diff --git a/docs/api/mp_math/segment.md b/docs/api/mp_math/segment.md
new file mode 100644
index 0000000..7da8df6
--- /dev/null
+++ b/docs/api/mp_math/segment.md
@@ -0,0 +1,40 @@
+---
+title: mbcp.mp\nmath.segment
+order: 1
+icon: laptop-code
+category: API
+---
+
+### ***class*** `Segment3`
+
+
+
+### ***def*** `__init__(self, p1: 'Point3', p2: 'Point3') -> None`
+
+ 三维空间中的线段。
+
+:param p1:
+
+:param p2:
+
+
+源代码
+
+```python
+def __init__(self, p1: 'Point3', p2: 'Point3'):
+ """
+ 三维空间中的线段。
+ :param p1:
+ :param p2:
+ """
+ self.p1 = p1
+ self.p2 = p2
+ '方向向量'
+ self.direction = self.p2 - self.p1
+ '长度'
+ self.length = self.direction.length
+ '中心点'
+ self.midpoint = Point3((self.p1.x + self.p2.x) / 2, (self.p1.y + self.p2.y) / 2, (self.p1.z + self.p2.z) / 2)
+```
+
+
diff --git a/docs/api/mp_math/utils.md b/docs/api/mp_math/utils.md
new file mode 100644
index 0000000..93cc597
--- /dev/null
+++ b/docs/api/mp_math/utils.md
@@ -0,0 +1,194 @@
+---
+title: mbcp.mp\nmath.utils
+order: 1
+icon: laptop-code
+category: API
+---
+
+### ***def*** `clamp(x: float, min_: float, max_: float) -> float`
+
+区间截断函数。
+
+Args:
+
+ x:
+
+ min_:
+
+ max_:
+
+
+
+Returns:
+
+ 限制后的值
+
+
+源代码
+
+```python
+def clamp(x: float, min_: float, max_: float) -> float:
+ """
+ 区间截断函数。
+ Args:
+ x:
+ min_:
+ max_:
+
+ Returns:
+ 限制后的值
+ """
+ return max(min(x, max_), min_)
+```
+
+
+### ***def*** `approx(x: float, y: float, epsilon: float) -> bool`
+
+判断两个数是否近似相等。或包装一个实数,用于判断是否近似于0。
+
+Args:
+
+ x:
+
+ y:
+
+ epsilon:
+
+
+
+Returns:
+
+ 是否近似相等
+
+
+源代码
+
+```python
+def approx(x: float, y: float=0.0, epsilon: float=APPROX) -> bool:
+ """
+ 判断两个数是否近似相等。或包装一个实数,用于判断是否近似于0。
+ Args:
+ x:
+ y:
+ epsilon:
+
+ Returns:
+ 是否近似相等
+ """
+ return abs(x - y) < epsilon
+```
+
+
+### ***def*** `sign(x: float, only_neg: bool) -> str`
+
+获取数的符号。
+
+Args:
+
+ x: 数
+
+ only_neg: 是否只返回负数的符号
+
+Returns:
+
+ 符号 + - ""
+
+
+源代码
+
+```python
+def sign(x: float, only_neg: bool=False) -> str:
+ """获取数的符号。
+ Args:
+ x: 数
+ only_neg: 是否只返回负数的符号
+ Returns:
+ 符号 + - ""
+ """
+ if x > 0:
+ return '+' if not only_neg else ''
+ elif x < 0:
+ return '-'
+ else:
+ return ''
+```
+
+
+### ***def*** `sign_format(x: float, only_neg: bool) -> str`
+
+格式化符号数
+
+-1 -> -1
+
+1 -> +1
+
+0 -> ""
+
+Args:
+
+ x: 数
+
+ only_neg: 是否只返回负数的符号
+
+Returns:
+
+ 符号 + - ""
+
+
+源代码
+
+```python
+def sign_format(x: float, only_neg: bool=False) -> str:
+ """格式化符号数
+ -1 -> -1
+ 1 -> +1
+ 0 -> ""
+ Args:
+ x: 数
+ only_neg: 是否只返回负数的符号
+ Returns:
+ 符号 + - ""
+ """
+ if x > 0:
+ return f'+{x}' if not only_neg else f'{x}'
+ elif x < 0:
+ return f'-{abs(x)}'
+ else:
+ return ''
+```
+
+
+### ***class*** `Approx`
+
+用于近似比较对象
+
+
+
+已实现对象 实数 Vector3 Point3 Plane3 Line3
+
+### ***def*** `__init__(self, value: RealNumber) -> None`
+
+
+
+
+源代码
+
+```python
+def __init__(self, value: RealNumber):
+ self.value = value
+```
+
+
+### ***def*** `raise_type_error(self, other: Any) -> None`
+
+
+
+
+源代码
+
+```python
+def raise_type_error(self, other):
+ raise TypeError(f'Unsupported type: {type(self.value)} and {type(other)}')
+```
+
+
diff --git a/docs/api/mp_math/vector.md b/docs/api/mp_math/vector.md
new file mode 100644
index 0000000..73d7f22
--- /dev/null
+++ b/docs/api/mp_math/vector.md
@@ -0,0 +1,340 @@
+---
+title: mbcp.mp\nmath.vector
+order: 1
+icon: laptop-code
+category: API
+---
+
+### ***class*** `Vector3`
+
+
+
+### ***def*** `__init__(self, x: float, y: float, z: float) -> None`
+
+ 3维向量
+
+Args:
+
+ x: x轴分量
+
+ y: y轴分量
+
+ z: z轴分量
+
+
+源代码
+
+```python
+def __init__(self, x: float, y: float, z: float):
+ """
+ 3维向量
+ Args:
+ x: x轴分量
+ y: y轴分量
+ z: z轴分量
+ """
+ self.x = x
+ self.y = y
+ self.z = z
+```
+
+
+### ***def*** `approx(self, other: 'Vector3', epsilon: float) -> bool`
+
+ 判断两个向量是否近似相等。
+
+Args:
+
+ other:
+
+ epsilon:
+
+
+
+Returns:
+
+ 是否近似相等
+
+
+源代码
+
+```python
+def approx(self, other: 'Vector3', epsilon: float=APPROX) -> bool:
+ """
+ 判断两个向量是否近似相等。
+ Args:
+ other:
+ epsilon:
+
+ Returns:
+ 是否近似相等
+ """
+ return all([abs(self.x - other.x) < epsilon, abs(self.y - other.y) < epsilon, abs(self.z - other.z) < epsilon])
+```
+
+
+### ***def*** `cal_angle(self, other: 'Vector3') -> 'AnyAngle'`
+
+ 计算两个向量之间的夹角。
+
+Args:
+
+ other: 另一个向量
+
+Returns:
+
+ 夹角
+
+
+源代码
+
+```python
+def cal_angle(self, other: 'Vector3') -> 'AnyAngle':
+ """
+ 计算两个向量之间的夹角。
+ Args:
+ other: 另一个向量
+ Returns:
+ 夹角
+ """
+ return AnyAngle(math.acos(self @ other / (self.length * other.length)), is_radian=True)
+```
+
+
+### ***def*** `cross(self, other: 'Vector3') -> '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':
+ """
+ 向量积 叉乘:v1 cross v2 -> v3
+
+ 叉乘为0,则两向量平行。
+ 其余结果的模为平行四边形的面积。
+
+ 返回如下行列式的结果:
+
+ ``i j k``
+
+ ``x1 y1 z1``
+
+ ``x2 y2 z2``
+
+ Args:
+ other:
+ Returns:
+ 行列式的结果
+ """
+ return Vector3(self.y * other.z - self.z * other.y, self.z * other.x - self.x * other.z, self.x * other.y - self.y * other.x)
+```
+
+
+### ***def*** `is_approx_parallel(self, other: 'Vector3', epsilon: float) -> bool`
+
+ 判断两个向量是否近似平行。
+
+Args:
+
+ other: 另一个向量
+
+ epsilon: 允许的误差
+
+Returns:
+
+ 是否近似平行
+
+
+源代码
+
+```python
+def is_approx_parallel(self, other: 'Vector3', epsilon: float=APPROX) -> bool:
+ """
+ 判断两个向量是否近似平行。
+ Args:
+ other: 另一个向量
+ epsilon: 允许的误差
+ Returns:
+ 是否近似平行
+ """
+ return self.cross(other).length < epsilon
+```
+
+
+### ***def*** `is_parallel(self, other: 'Vector3') -> bool`
+
+ 判断两个向量是否平行。
+
+Args:
+
+ other: 另一个向量
+
+Returns:
+
+ 是否平行
+
+
+源代码
+
+```python
+def is_parallel(self, other: 'Vector3') -> bool:
+ """
+ 判断两个向量是否平行。
+ Args:
+ other: 另一个向量
+ Returns:
+ 是否平行
+ """
+ return self.cross(other).approx(zero_vector3)
+```
+
+
+### ***def*** `normalize(self) -> None`
+
+ 将向量归一化。
+
+
+
+自体归一化,不返回值。
+
+
+源代码
+
+```python
+def normalize(self):
+ """
+ 将向量归一化。
+
+ 自体归一化,不返回值。
+ """
+ length = self.length
+ self.x /= length
+ self.y /= length
+ self.z /= length
+```
+
+
+### ***@property***
+### ***def*** `np_array(self: Any) -> 'np.ndarray'`
+
+ 返回numpy数组
+
+Returns:
+
+
+源代码
+
+```python
+@property
+def np_array(self) -> 'np.ndarray':
+ """
+ 返回numpy数组
+ Returns:
+ """
+ return np.array([self.x, self.y, self.z])
+```
+
+
+### ***@property***
+### ***def*** `length(self: Any) -> float`
+
+ 向量的模。
+
+Returns:
+
+ 模
+
+
+源代码
+
+```python
+@property
+def length(self) -> float:
+ """
+ 向量的模。
+ Returns:
+ 模
+ """
+ return math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)
+```
+
+
+### ***@property***
+### ***def*** `unit(self: Any) -> 'Vector3'`
+
+ 获取该向量的单位向量。
+
+Returns:
+
+ 单位向量
+
+
+源代码
+
+```python
+@property
+def unit(self) -> 'Vector3':
+ """
+ 获取该向量的单位向量。
+ Returns:
+ 单位向量
+ """
+ return self / self.length
+```
+
+
+### ***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)`
+
+
+
+### ***var*** `length = self.length`
+
+
+
diff --git a/docs/api/particle/indedx.md b/docs/api/particle/indedx.md
new file mode 100644
index 0000000..1669e90
--- /dev/null
+++ b/docs/api/particle/indedx.md
@@ -0,0 +1,7 @@
+---
+title: mbcp.particle.\n\ninit\n\n
+order: 1
+icon: laptop-code
+category: API
+---
+
diff --git a/docs/api/presets/indedx.md b/docs/api/presets/indedx.md
new file mode 100644
index 0000000..85b4964
--- /dev/null
+++ b/docs/api/presets/indedx.md
@@ -0,0 +1,7 @@
+---
+title: mbcp.presets.\n\ninit\n\n
+order: 1
+icon: laptop-code
+category: API
+---
+
diff --git a/docs/api/presets/model/indedx.md b/docs/api/presets/model/indedx.md
new file mode 100644
index 0000000..5986544
--- /dev/null
+++ b/docs/api/presets/model/indedx.md
@@ -0,0 +1,118 @@
+---
+title: mbcp.presets.model.\n\ninit\n\n
+order: 1
+icon: laptop-code
+category: API
+---
+
+### ***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)]
+```
+
+
+### ***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/index.md b/docs/index.md
new file mode 100644
index 0000000..51d63d6
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,25 @@
+---
+# https://vitepress.dev/reference/default-theme-home-page
+layout: home
+
+hero:
+ name: "MBCP docs"
+ text: "More basic change particle"
+ tagline: 用于几何运算和粒子制作的库
+ actions:
+ - theme: brand
+ text: 快速开始
+ link: /guide
+ - theme: alt
+ text: API
+ link: api
+
+features:
+ - title: 高可用性
+ details: 通过简单的接口,实现了大部分几何运算和粒子制作的需求
+ - title: 高集成度
+ details: 对numpy
、scipy
及sumpy
进行了封装和集成,使脚本编写像使用Geogebra一样简单
+ - title: 内置预设
+ details: 提供了大量的预设,包括常见的几何图形、粒子效果等,方便快速制作
+---
+
diff --git a/docs/markdown-examples.md b/docs/markdown-examples.md
new file mode 100644
index 0000000..f9258a5
--- /dev/null
+++ b/docs/markdown-examples.md
@@ -0,0 +1,85 @@
+# Markdown Extension Examples
+
+This page demonstrates some of the built-in markdown extensions provided by VitePress.
+
+## Syntax Highlighting
+
+VitePress provides Syntax Highlighting powered by [Shiki](https://github.com/shikijs/shiki), with additional features like line-highlighting:
+
+**Input**
+
+````md
+```js{4}
+export default {
+ data () {
+ return {
+ msg: 'Highlighted!'
+ }
+ }
+}
+```
+````
+
+**Output**
+
+```js{4}
+export default {
+ data () {
+ return {
+ msg: 'Highlighted!'
+ }
+ }
+}
+```
+
+## Custom Containers
+
+**Input**
+
+```md
+::: info
+This is an info box.
+:::
+
+::: tip
+This is a tip.
+:::
+
+::: warning
+This is a warning.
+:::
+
+::: danger
+This is a dangerous warning.
+:::
+
+::: details
+This is a details block.
+:::
+```
+
+**Output**
+
+::: info
+This is an info box.
+:::
+
+::: tip
+This is a tip.
+:::
+
+::: warning
+This is a warning.
+:::
+
+::: danger
+This is a dangerous warning.
+:::
+
+::: details
+This is a details block.
+:::
+
+## More
+
+Check out the documentation for the [full list of markdown extensions](https://vitepress.dev/guide/markdown).
diff --git a/docs/mkdoc.py b/docs/mkdoc.py
new file mode 100644
index 0000000..67083da
--- /dev/null
+++ b/docs/mkdoc.py
@@ -0,0 +1,352 @@
+# -*- coding: utf-8 -*-
+"""
+MkDocs文档生成脚本
+用法 python docs/mkdoc.py
+"""
+
+import ast
+import os
+import shutil
+from typing import Any
+from enum import Enum
+from pydantic import BaseModel
+
+NO_TYPE_ANY = "Any"
+NO_TYPE_HINT = "NoTypeHint"
+
+
+class DefType(Enum):
+ FUNCTION = "function"
+ METHOD = "method"
+ STATIC_METHOD = "staticmethod"
+ CLASS_METHOD = "classmethod"
+ PROPERTY = "property"
+
+
+class FunctionInfo(BaseModel):
+ name: str
+ args: list[tuple[str, str]]
+ return_type: str
+ docstring: str
+ source_code: str = ""
+
+ type: DefType
+ """若为类中def,则有"""
+ is_async: bool
+
+
+class AttributeInfo(BaseModel):
+ name: str
+ type: str
+ value: Any = None
+ docstring: str = ""
+
+
+class ClassInfo(BaseModel):
+ name: str
+ docstring: str
+ methods: list[FunctionInfo]
+ attributes: list[AttributeInfo]
+ inherit: list[str]
+
+
+class ModuleInfo(BaseModel):
+ module_path: str
+ """点分割模块路径 例如 liteyuki.bot"""
+
+ functions: list[FunctionInfo]
+ classes: list[ClassInfo]
+ attributes: list[AttributeInfo]
+ docstring: str
+
+
+def get_relative_path(base_path: str, target_path: str) -> str:
+ """
+ 获取相对路径
+ Args:
+ base_path: 基础路径
+ target_path: 目标路径
+ """
+ return os.path.relpath(target_path, base_path)
+
+
+def write_to_files(file_data: dict[str, str]):
+ """
+ 输出文件
+ Args:
+ file_data: 文件数据 相对路径
+ """
+
+ for rp, data in file_data.items():
+
+ if not os.path.exists(os.path.dirname(rp)):
+ os.makedirs(os.path.dirname(rp))
+ with open(rp, 'w', encoding='utf-8') as f:
+ f.write(data)
+
+
+def get_file_list(module_folder: str):
+ file_list = []
+ for root, dirs, files in os.walk(module_folder):
+ for file in files:
+ if file.endswith((".py", ".pyi")):
+ file_list.append(os.path.join(root, file))
+ return file_list
+
+
+def get_module_info_normal(file_path: str, ignore_private: bool = True) -> ModuleInfo:
+ """
+ 获取函数和类
+ Args:
+ file_path: Python 文件路径
+ ignore_private: 忽略私有函数和类
+ Returns:
+ 模块信息
+ """
+
+ with open(file_path, 'r', encoding='utf-8') as file:
+ file_content = file.read()
+ tree = ast.parse(file_content)
+
+ dot_sep_module_path = file_path.replace(os.sep, '.').replace(".py", "").replace(".pyi", "")
+ module_docstring = ast.get_docstring(tree)
+
+ module_info = ModuleInfo(
+ module_path=dot_sep_module_path,
+ functions=[],
+ classes=[],
+ attributes=[],
+ docstring=module_docstring if module_docstring else ""
+ )
+
+ for node in ast.walk(tree):
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
+ # 模块函数 且不在类中 若ignore_private=True则忽略私有函数
+ if not any(isinstance(parent, ast.ClassDef) for parent in ast.iter_child_nodes(node)) and (not ignore_private or not node.name.startswith('_')):
+
+ # 判断第一个参数是否为self或cls,后期用其他办法优化
+ if node.args.args:
+ first_arg = node.args.args[0]
+ if first_arg.arg in ("self", "cls"):
+ continue
+
+ function_docstring = ast.get_docstring(node)
+
+ func_info = FunctionInfo(
+ name=node.name,
+ args=[(arg.arg, ast.unparse(arg.annotation) if arg.annotation else NO_TYPE_ANY) for arg in node.args.args],
+ return_type=ast.unparse(node.returns) if node.returns else "None",
+ docstring=function_docstring if function_docstring else "",
+ type=DefType.FUNCTION,
+ is_async=isinstance(node, ast.AsyncFunctionDef),
+ source_code=ast.unparse(node)
+ )
+ module_info.functions.append(func_info)
+
+ elif isinstance(node, ast.ClassDef):
+ # 模块类
+ class_docstring = ast.get_docstring(node)
+
+ class_info = ClassInfo(
+ name=node.name,
+ docstring=class_docstring if class_docstring else "",
+ methods=[],
+ attributes=[],
+ inherit=[ast.unparse(base) for base in node.bases]
+ )
+
+ for class_node in node.body:
+ # methods [instance, static, class, property],保留__init__方法
+ if isinstance(class_node, ast.FunctionDef) and (not ignore_private or not class_node.name.startswith('_') or class_node.name == "__init__"):
+ method_docstring = ast.get_docstring(class_node)
+ def_type = DefType.METHOD
+ if class_node.decorator_list:
+ if any(isinstance(decorator, ast.Name) and decorator.id == "staticmethod" for decorator in class_node.decorator_list):
+ def_type = DefType.STATIC_METHOD
+ elif any(isinstance(decorator, ast.Name) and decorator.id == "classmethod" for decorator in class_node.decorator_list):
+ def_type = DefType.CLASS_METHOD
+ elif any(isinstance(decorator, ast.Name) and decorator.id == "property" for decorator in class_node.decorator_list):
+ def_type = DefType.PROPERTY
+ class_info.methods.append(FunctionInfo(
+ name=class_node.name,
+ args=[(arg.arg, ast.unparse(arg.annotation) if arg.annotation else NO_TYPE_ANY) for arg in class_node.args.args],
+ return_type=ast.unparse(class_node.returns) if class_node.returns else "None",
+ docstring=method_docstring if method_docstring else "",
+ type=def_type,
+ is_async=isinstance(class_node, ast.AsyncFunctionDef),
+ source_code=ast.unparse(class_node)
+ ))
+ # attributes
+ elif isinstance(class_node, ast.Assign):
+ for target in class_node.targets:
+ if isinstance(target, ast.Name):
+ class_info.attributes.append(AttributeInfo(
+ name=target.id,
+ type=ast.unparse(class_node.value)
+ ))
+ module_info.classes.append(class_info)
+
+ elif isinstance(node, ast.Assign):
+ # 检查是否在类或函数中
+ if not any(isinstance(parent, (ast.ClassDef, ast.FunctionDef)) for parent in ast.iter_child_nodes(node)):
+ # 模块属性变量
+ for target in node.targets:
+ if isinstance(target, ast.Name) and (not ignore_private or not target.id.startswith('_')):
+ attr_type = NO_TYPE_HINT
+ if isinstance(node.value, ast.AnnAssign) and node.value.annotation:
+ attr_type = ast.unparse(node.value.annotation)
+ module_info.attributes.append(AttributeInfo(
+ name=target.id,
+ type=attr_type,
+ value=ast.unparse(node.value) if node.value else None
+ ))
+
+ return module_info
+
+
+def generate_markdown(module_info: ModuleInfo, front_matter=None, lang: str = "zh-CN") -> str:
+ """
+ 生成模块的Markdown
+ 你可在此自定义生成的Markdown格式
+ Args:
+ module_info: 模块信息
+ front_matter: 自定义选项title, index, icon, category
+ lang: 语言
+ Returns:
+ Markdown 字符串
+ """
+
+ content = ""
+
+ front_matter = "---\n" + "\n".join([f"{k}: {v}" for k, v in front_matter.items()]) + "\n---\n\n"
+
+ content += front_matter
+
+ # 模块函数
+ for func in module_info.functions:
+ args_with_type = [f"{arg[0]}: {arg[1]}" if arg[1] else arg[0] for arg in func.args]
+ content += f"### ***{'async ' if func.is_async else ''}def*** `{func.name}({', '.join(args_with_type)}) -> {func.return_type}`\n\n"
+
+ func.docstring = func.docstring.replace("\n", "\n\n")
+ content += f"{func.docstring}\n\n"
+
+ # 函数源代码可展开区域
+ content += f"\n源代码
\n\n```python\n{func.source_code}\n```\n \n\n"
+
+ # 类
+ for cls in module_info.classes:
+ if cls.inherit:
+ inherit = f"({', '.join(cls.inherit)})" if cls.inherit else ""
+ content += f"### ***class*** `{cls.name}{inherit}`\n\n"
+ else:
+ content += f"### ***class*** `{cls.name}`\n\n"
+
+ cls.docstring = cls.docstring.replace("\n", "\n\n")
+ content += f"{cls.docstring}\n\n"
+ for method in cls.methods:
+ # 类函数
+
+ if method.type != DefType.METHOD:
+ args_with_type = [f"{arg[0]}: {arg[1]}" if arg[1] else arg[0] for arg in method.args]
+ content += f"### ***@{method.type.value}***\n"
+ else:
+ # self不加类型提示
+ args_with_type = [f"{arg[0]}: {arg[1]}" if arg[1] and arg[0] != "self" else arg[0] for arg in method.args]
+ content += f"### ***{'async ' if method.is_async else ''}def*** `{method.name}({', '.join(args_with_type)}) -> {method.return_type}`\n\n"
+
+ method.docstring = method.docstring.replace("\n", "\n\n")
+ content += f" {method.docstring}\n\n"
+ # 函数源代码可展开区域
+
+ if lang == "zh-CN":
+ TEXT_SOURCE_CODE = "源代码"
+ else:
+ TEXT_SOURCE_CODE = "Source Code"
+
+ content += f"\n{TEXT_SOURCE_CODE}
\n\n```python\n{method.source_code}\n```\n \n\n"
+ for attr in cls.attributes:
+ content += f"### ***attr*** `{attr.name}: {attr.type}`\n\n"
+
+ # 模块属性
+ for attr in module_info.attributes:
+ if attr.type == NO_TYPE_HINT:
+ content += f"### ***var*** `{attr.name} = {attr.value}`\n\n"
+ else:
+ content += f"### ***var*** `{attr.name}: {attr.type} = {attr.value}`\n\n"
+
+ attr.docstring = attr.docstring.replace("\n", "\n\n")
+ content += f"{attr.docstring}\n\n"
+
+ return content
+
+
+def generate_docs(module_folder: str, output_dir: str, with_top: bool = False, lang: str = "zh-CN", ignored_paths=None):
+ """
+ 生成文档
+ Args:
+ module_folder: 模块文件夹
+ output_dir: 输出文件夹
+ with_top: 是否包含顶层文件夹 False时例如docs/api/module_a, docs/api/module_b, True时例如docs/api/module/module_a.md, docs/api/module/module_b.md
+ ignored_paths: 忽略的路径
+ lang: 语言
+ """
+ if ignored_paths is None:
+ ignored_paths = []
+ file_data: dict[str, str] = {} # 路径 -> 字串
+
+ file_list = get_file_list(module_folder)
+
+ # 清理输出目录
+ shutil.rmtree(output_dir, ignore_errors=True)
+ os.mkdir(output_dir)
+
+ replace_data = {
+ "__init__": "indedx",
+ ".py" : ".md",
+ }
+
+ for pyfile_path in file_list:
+ if any(ignored_path.replace("\\", "/") in pyfile_path.replace("\\", "/") for ignored_path in ignored_paths):
+ continue
+
+ no_module_name_pyfile_path = get_relative_path(module_folder, pyfile_path) # 去头路径
+
+ # markdown相对路径
+ rel_md_path = pyfile_path if with_top else no_module_name_pyfile_path
+ for rk, rv in replace_data.items():
+ rel_md_path = rel_md_path.replace(rk, rv)
+
+ abs_md_path = os.path.join(output_dir, rel_md_path)
+
+ # 获取模块信息
+ module_info = get_module_info_normal(pyfile_path)
+
+ # 生成markdown
+
+ if "README" in abs_md_path:
+ front_matter = {
+ "title" : module_info.module_path.replace(".__init__", "").replace("_", "\\n"),
+ "index" : "true",
+ "icon" : "laptop-code",
+ "category": "API"
+ }
+ else:
+ front_matter = {
+ "title" : module_info.module_path.replace("_", "\\n"),
+ "order" : "1",
+ "icon" : "laptop-code",
+ "category": "API"
+ }
+
+ md_content = generate_markdown(module_info, front_matter)
+ print(f"Generate {pyfile_path} -> {abs_md_path}")
+ file_data[abs_md_path] = md_content
+
+ write_to_files(file_data)
+
+
+# 入口脚本
+if __name__ == '__main__':
+ # 这里填入你的模块路径
+ generate_docs('mbcp', 'docs/api', with_top=False, ignored_paths=["liteyuki/plugins"], lang="zh-CN")
+ # generate_docs('mbcp', 'docs/en/api', with_top=False, ignored_paths=["liteyuki/plugins"], lang="en")
diff --git a/docs/package.json b/docs/package.json
new file mode 100644
index 0000000..a65c678
--- /dev/null
+++ b/docs/package.json
@@ -0,0 +1,10 @@
+{
+ "devDependencies": {
+ "vitepress": "^1.3.4"
+ },
+ "scripts": {
+ "docs:dev": "vitepress dev",
+ "docs:build": "vitepress build",
+ "docs:preview": "vitepress preview"
+ }
+}
\ No newline at end of file
diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml
new file mode 100644
index 0000000..841c47c
--- /dev/null
+++ b/docs/pnpm-lock.yaml
@@ -0,0 +1,1172 @@
+lockfileVersion: '6.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+devDependencies:
+ vitepress:
+ specifier: ^1.3.4
+ version: 1.3.4(@algolia/client-search@5.1.1)(search-insights@2.17.0)
+
+packages:
+
+ /@algolia/autocomplete-core@1.9.3(@algolia/client-search@5.1.1)(algoliasearch@4.24.0)(search-insights@2.17.0):
+ resolution: {integrity: sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==}
+ dependencies:
+ '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@5.1.1)(algoliasearch@4.24.0)(search-insights@2.17.0)
+ '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@5.1.1)(algoliasearch@4.24.0)
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - algoliasearch
+ - search-insights
+ dev: true
+
+ /@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@5.1.1)(algoliasearch@4.24.0)(search-insights@2.17.0):
+ resolution: {integrity: sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==}
+ peerDependencies:
+ search-insights: '>= 1 < 3'
+ dependencies:
+ '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@5.1.1)(algoliasearch@4.24.0)
+ search-insights: 2.17.0
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - algoliasearch
+ dev: true
+
+ /@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@5.1.1)(algoliasearch@4.24.0):
+ resolution: {integrity: sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==}
+ peerDependencies:
+ '@algolia/client-search': '>= 4.9.1 < 6'
+ algoliasearch: '>= 4.9.1 < 6'
+ dependencies:
+ '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@5.1.1)(algoliasearch@4.24.0)
+ '@algolia/client-search': 5.1.1
+ algoliasearch: 4.24.0
+ dev: true
+
+ /@algolia/autocomplete-shared@1.9.3(@algolia/client-search@5.1.1)(algoliasearch@4.24.0):
+ resolution: {integrity: sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==}
+ peerDependencies:
+ '@algolia/client-search': '>= 4.9.1 < 6'
+ algoliasearch: '>= 4.9.1 < 6'
+ dependencies:
+ '@algolia/client-search': 5.1.1
+ algoliasearch: 4.24.0
+ dev: true
+
+ /@algolia/cache-browser-local-storage@4.24.0:
+ resolution: {integrity: sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww==}
+ dependencies:
+ '@algolia/cache-common': 4.24.0
+ dev: true
+
+ /@algolia/cache-common@4.24.0:
+ resolution: {integrity: sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g==}
+ dev: true
+
+ /@algolia/cache-in-memory@4.24.0:
+ resolution: {integrity: sha512-gDrt2so19jW26jY3/MkFg5mEypFIPbPoXsQGQWAi6TrCPsNOSEYepBMPlucqWigsmEy/prp5ug2jy/N3PVG/8w==}
+ dependencies:
+ '@algolia/cache-common': 4.24.0
+ dev: true
+
+ /@algolia/client-account@4.24.0:
+ resolution: {integrity: sha512-adcvyJ3KjPZFDybxlqnf+5KgxJtBjwTPTeyG2aOyoJvx0Y8dUQAEOEVOJ/GBxX0WWNbmaSrhDURMhc+QeevDsA==}
+ dependencies:
+ '@algolia/client-common': 4.24.0
+ '@algolia/client-search': 4.24.0
+ '@algolia/transporter': 4.24.0
+ dev: true
+
+ /@algolia/client-analytics@4.24.0:
+ resolution: {integrity: sha512-y8jOZt1OjwWU4N2qr8G4AxXAzaa8DBvyHTWlHzX/7Me1LX8OayfgHexqrsL4vSBcoMmVw2XnVW9MhL+Y2ZDJXg==}
+ dependencies:
+ '@algolia/client-common': 4.24.0
+ '@algolia/client-search': 4.24.0
+ '@algolia/requester-common': 4.24.0
+ '@algolia/transporter': 4.24.0
+ dev: true
+
+ /@algolia/client-common@4.24.0:
+ resolution: {integrity: sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==}
+ dependencies:
+ '@algolia/requester-common': 4.24.0
+ '@algolia/transporter': 4.24.0
+ dev: true
+
+ /@algolia/client-common@5.1.1:
+ resolution: {integrity: sha512-jkQNQbGY+XQB3Eln7wqqdUZKBzG8lETcsaUk5gcMc6iIwyN/qW0v0fhpKPH+Kli+BImLxo0CWk12CvVvx2exWA==}
+ engines: {node: '>= 14.0.0'}
+ dev: true
+
+ /@algolia/client-personalization@4.24.0:
+ resolution: {integrity: sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w==}
+ dependencies:
+ '@algolia/client-common': 4.24.0
+ '@algolia/requester-common': 4.24.0
+ '@algolia/transporter': 4.24.0
+ dev: true
+
+ /@algolia/client-search@4.24.0:
+ resolution: {integrity: sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==}
+ dependencies:
+ '@algolia/client-common': 4.24.0
+ '@algolia/requester-common': 4.24.0
+ '@algolia/transporter': 4.24.0
+ dev: true
+
+ /@algolia/client-search@5.1.1:
+ resolution: {integrity: sha512-SFpb3FI/VouGou/vpuS7qeCA5Y/KpV42P6CEA/1MZQtl/xJkl6PVjikb+Q9YadeHi2jtDV/aQ6PyiVDnX4PQcw==}
+ engines: {node: '>= 14.0.0'}
+ dependencies:
+ '@algolia/client-common': 5.1.1
+ '@algolia/requester-browser-xhr': 5.1.1
+ '@algolia/requester-node-http': 5.1.1
+ dev: true
+
+ /@algolia/logger-common@4.24.0:
+ resolution: {integrity: sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA==}
+ dev: true
+
+ /@algolia/logger-console@4.24.0:
+ resolution: {integrity: sha512-X4C8IoHgHfiUROfoRCV+lzSy+LHMgkoEEU1BbKcsfnV0i0S20zyy0NLww9dwVHUWNfPPxdMU+/wKmLGYf96yTg==}
+ dependencies:
+ '@algolia/logger-common': 4.24.0
+ dev: true
+
+ /@algolia/recommend@4.24.0:
+ resolution: {integrity: sha512-P9kcgerfVBpfYHDfVZDvvdJv0lEoCvzNlOy2nykyt5bK8TyieYyiD0lguIJdRZZYGre03WIAFf14pgE+V+IBlw==}
+ dependencies:
+ '@algolia/cache-browser-local-storage': 4.24.0
+ '@algolia/cache-common': 4.24.0
+ '@algolia/cache-in-memory': 4.24.0
+ '@algolia/client-common': 4.24.0
+ '@algolia/client-search': 4.24.0
+ '@algolia/logger-common': 4.24.0
+ '@algolia/logger-console': 4.24.0
+ '@algolia/requester-browser-xhr': 4.24.0
+ '@algolia/requester-common': 4.24.0
+ '@algolia/requester-node-http': 4.24.0
+ '@algolia/transporter': 4.24.0
+ dev: true
+
+ /@algolia/requester-browser-xhr@4.24.0:
+ resolution: {integrity: sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==}
+ dependencies:
+ '@algolia/requester-common': 4.24.0
+ dev: true
+
+ /@algolia/requester-browser-xhr@5.1.1:
+ resolution: {integrity: sha512-NXmN1ujJCj5GlJQaMK6DbdiXdcf6nhRef/X40lu9TYi71q9xTo/5RPMI0K2iOp6g07S26BrXFOz6RSV3Ny4LLw==}
+ engines: {node: '>= 14.0.0'}
+ dependencies:
+ '@algolia/client-common': 5.1.1
+ dev: true
+
+ /@algolia/requester-common@4.24.0:
+ resolution: {integrity: sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==}
+ dev: true
+
+ /@algolia/requester-node-http@4.24.0:
+ resolution: {integrity: sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==}
+ dependencies:
+ '@algolia/requester-common': 4.24.0
+ dev: true
+
+ /@algolia/requester-node-http@5.1.1:
+ resolution: {integrity: sha512-xwrgnNTIzgxDEx6zuCKSKTPzQLA8fL/WZiVB6fRpIu5agLMjoAi0cWA5YSDbo+2FFxqVgLqKY/Jz6mKmWtY15Q==}
+ engines: {node: '>= 14.0.0'}
+ dependencies:
+ '@algolia/client-common': 5.1.1
+ dev: true
+
+ /@algolia/transporter@4.24.0:
+ resolution: {integrity: sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==}
+ dependencies:
+ '@algolia/cache-common': 4.24.0
+ '@algolia/logger-common': 4.24.0
+ '@algolia/requester-common': 4.24.0
+ dev: true
+
+ /@babel/helper-string-parser@7.24.8:
+ resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@babel/helper-validator-identifier@7.24.7:
+ resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@babel/parser@7.25.4:
+ resolution: {integrity: sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+ dependencies:
+ '@babel/types': 7.25.4
+ dev: true
+
+ /@babel/types@7.25.4:
+ resolution: {integrity: sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-string-parser': 7.24.8
+ '@babel/helper-validator-identifier': 7.24.7
+ to-fast-properties: 2.0.0
+ dev: true
+
+ /@docsearch/css@3.6.1:
+ resolution: {integrity: sha512-VtVb5DS+0hRIprU2CO6ZQjK2Zg4QU5HrDM1+ix6rT0umsYvFvatMAnf97NHZlVWDaaLlx7GRfR/7FikANiM2Fg==}
+ dev: true
+
+ /@docsearch/js@3.6.1(@algolia/client-search@5.1.1)(search-insights@2.17.0):
+ resolution: {integrity: sha512-erI3RRZurDr1xES5hvYJ3Imp7jtrXj6f1xYIzDzxiS7nNBufYWPbJwrmMqWC5g9y165PmxEmN9pklGCdLi0Iqg==}
+ dependencies:
+ '@docsearch/react': 3.6.1(@algolia/client-search@5.1.1)(search-insights@2.17.0)
+ preact: 10.23.2
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - '@types/react'
+ - react
+ - react-dom
+ - search-insights
+ dev: true
+
+ /@docsearch/react@3.6.1(@algolia/client-search@5.1.1)(search-insights@2.17.0):
+ resolution: {integrity: sha512-qXZkEPvybVhSXj0K7U3bXc233tk5e8PfhoZ6MhPOiik/qUQxYC+Dn9DnoS7CxHQQhHfCvTiN0eY9M12oRghEXw==}
+ peerDependencies:
+ '@types/react': '>= 16.8.0 < 19.0.0'
+ react: '>= 16.8.0 < 19.0.0'
+ react-dom: '>= 16.8.0 < 19.0.0'
+ search-insights: '>= 1 < 3'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ search-insights:
+ optional: true
+ dependencies:
+ '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@5.1.1)(algoliasearch@4.24.0)(search-insights@2.17.0)
+ '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@5.1.1)(algoliasearch@4.24.0)
+ '@docsearch/css': 3.6.1
+ algoliasearch: 4.24.0
+ search-insights: 2.17.0
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ dev: true
+
+ /@esbuild/aix-ppc64@0.21.5:
+ resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [aix]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/android-arm64@0.21.5:
+ resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/android-arm@0.21.5:
+ resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/android-x64@0.21.5:
+ resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/darwin-arm64@0.21.5:
+ resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/darwin-x64@0.21.5:
+ resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/freebsd-arm64@0.21.5:
+ resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/freebsd-x64@0.21.5:
+ resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-arm64@0.21.5:
+ resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-arm@0.21.5:
+ resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-ia32@0.21.5:
+ resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-loong64@0.21.5:
+ resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
+ engines: {node: '>=12'}
+ cpu: [loong64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-mips64el@0.21.5:
+ resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-ppc64@0.21.5:
+ resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-riscv64@0.21.5:
+ resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-s390x@0.21.5:
+ resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-x64@0.21.5:
+ resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/netbsd-x64@0.21.5:
+ resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/openbsd-x64@0.21.5:
+ resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/sunos-x64@0.21.5:
+ resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-arm64@0.21.5:
+ resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-ia32@0.21.5:
+ resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-x64@0.21.5:
+ resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@jridgewell/sourcemap-codec@1.5.0:
+ resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+ dev: true
+
+ /@rollup/rollup-android-arm-eabi@4.21.1:
+ resolution: {integrity: sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==}
+ cpu: [arm]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-android-arm64@4.21.1:
+ resolution: {integrity: sha512-t1lLYn4V9WgnIFHXy1d2Di/7gyzBWS8G5pQSXdZqfrdCGTwi1VasRMSS81DTYb+avDs/Zz4A6dzERki5oRYz1g==}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-darwin-arm64@4.21.1:
+ resolution: {integrity: sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-darwin-x64@4.21.1:
+ resolution: {integrity: sha512-dO0BIz/+5ZdkLZrVgQrDdW7m2RkrLwYTh2YMFG9IpBtlC1x1NPNSXkfczhZieOlOLEqgXOFH3wYHB7PmBtf+Bg==}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-arm-gnueabihf@4.21.1:
+ resolution: {integrity: sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-arm-musleabihf@4.21.1:
+ resolution: {integrity: sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==}
+ cpu: [arm]
+ os: [linux]
+ libc: [musl]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-arm64-gnu@4.21.1:
+ resolution: {integrity: sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-arm64-musl@4.21.1:
+ resolution: {integrity: sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-powerpc64le-gnu@4.21.1:
+ resolution: {integrity: sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-riscv64-gnu@4.21.1:
+ resolution: {integrity: sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-s390x-gnu@4.21.1:
+ resolution: {integrity: sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-x64-gnu@4.21.1:
+ resolution: {integrity: sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-x64-musl@4.21.1:
+ resolution: {integrity: sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-win32-arm64-msvc@4.21.1:
+ resolution: {integrity: sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-win32-ia32-msvc@4.21.1:
+ resolution: {integrity: sha512-tNg+jJcKR3Uwe4L0/wY3Ro0H+u3nrb04+tcq1GSYzBEmKLeOQF2emk1whxlzNqb6MMrQ2JOcQEpuuiPLyRcSIw==}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-win32-x64-msvc@4.21.1:
+ resolution: {integrity: sha512-xGiIH95H1zU7naUyTKEyOA/I0aexNMUdO9qRv0bLKN3qu25bBdrxZHqA3PTJ24YNN/GdMzG4xkDcd/GvjuhfLg==}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@shikijs/core@1.14.1:
+ resolution: {integrity: sha512-KyHIIpKNaT20FtFPFjCQB5WVSTpLR/n+jQXhWHWVUMm9MaOaG9BGOG0MSyt7yA4+Lm+4c9rTc03tt3nYzeYSfw==}
+ dependencies:
+ '@types/hast': 3.0.4
+ dev: true
+
+ /@shikijs/transformers@1.14.1:
+ resolution: {integrity: sha512-JJqL8QBVCJh3L61jqqEXgFq1cTycwjcGj7aSmqOEsbxnETM9hRlaB74QuXvY/fVJNjbNt8nvWo0VwAXKvMSLRg==}
+ dependencies:
+ shiki: 1.14.1
+ dev: true
+
+ /@types/estree@1.0.5:
+ resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
+ dev: true
+
+ /@types/hast@3.0.4:
+ resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+ dependencies:
+ '@types/unist': 3.0.3
+ dev: true
+
+ /@types/linkify-it@5.0.0:
+ resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
+ dev: true
+
+ /@types/markdown-it@14.1.2:
+ resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
+ dependencies:
+ '@types/linkify-it': 5.0.0
+ '@types/mdurl': 2.0.0
+ dev: true
+
+ /@types/mdurl@2.0.0:
+ resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
+ dev: true
+
+ /@types/unist@3.0.3:
+ resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+ dev: true
+
+ /@types/web-bluetooth@0.0.20:
+ resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
+ dev: true
+
+ /@vitejs/plugin-vue@5.1.2(vite@5.4.2)(vue@3.4.38):
+ resolution: {integrity: sha512-nY9IwH12qeiJqumTCLJLE7IiNx7HZ39cbHaysEUd+Myvbz9KAqd2yq+U01Kab1R/H1BmiyM2ShTYlNH32Fzo3A==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ peerDependencies:
+ vite: ^5.0.0
+ vue: ^3.2.25
+ dependencies:
+ vite: 5.4.2
+ vue: 3.4.38
+ dev: true
+
+ /@vue/compiler-core@3.4.38:
+ resolution: {integrity: sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==}
+ dependencies:
+ '@babel/parser': 7.25.4
+ '@vue/shared': 3.4.38
+ entities: 4.5.0
+ estree-walker: 2.0.2
+ source-map-js: 1.2.0
+ dev: true
+
+ /@vue/compiler-dom@3.4.38:
+ resolution: {integrity: sha512-Osc/c7ABsHXTsETLgykcOwIxFktHfGSUDkb05V61rocEfsFDcjDLH/IHJSNJP+/Sv9KeN2Lx1V6McZzlSb9EhQ==}
+ dependencies:
+ '@vue/compiler-core': 3.4.38
+ '@vue/shared': 3.4.38
+ dev: true
+
+ /@vue/compiler-sfc@3.4.38:
+ resolution: {integrity: sha512-s5QfZ+9PzPh3T5H4hsQDJtI8x7zdJaew/dCGgqZ2630XdzaZ3AD8xGZfBqpT8oaD/p2eedd+pL8tD5vvt5ZYJQ==}
+ dependencies:
+ '@babel/parser': 7.25.4
+ '@vue/compiler-core': 3.4.38
+ '@vue/compiler-dom': 3.4.38
+ '@vue/compiler-ssr': 3.4.38
+ '@vue/shared': 3.4.38
+ estree-walker: 2.0.2
+ magic-string: 0.30.11
+ postcss: 8.4.41
+ source-map-js: 1.2.0
+ dev: true
+
+ /@vue/compiler-ssr@3.4.38:
+ resolution: {integrity: sha512-YXznKFQ8dxYpAz9zLuVvfcXhc31FSPFDcqr0kyujbOwNhlmaNvL2QfIy+RZeJgSn5Fk54CWoEUeW+NVBAogGaw==}
+ dependencies:
+ '@vue/compiler-dom': 3.4.38
+ '@vue/shared': 3.4.38
+ dev: true
+
+ /@vue/devtools-api@7.3.9:
+ resolution: {integrity: sha512-D+GTYtFg68bqSu66EugQUydsOqaDlPLNmYw5oYk8k81uBu9/bVTUrqlAJrAA9Am7MXhKz2gWdDkopY6sOBf/Bg==}
+ dependencies:
+ '@vue/devtools-kit': 7.3.9
+ dev: true
+
+ /@vue/devtools-kit@7.3.9:
+ resolution: {integrity: sha512-Gr17nA+DaQzqyhNx1DUJr1CJRzTRfbIuuC80ZgU8MD/qNO302tv9la+ROi+Uaw+ULVwU9T71GnwLy4n8m9Lspg==}
+ dependencies:
+ '@vue/devtools-shared': 7.3.9
+ birpc: 0.2.17
+ hookable: 5.5.3
+ mitt: 3.0.1
+ perfect-debounce: 1.0.0
+ speakingurl: 14.0.1
+ superjson: 2.2.1
+ dev: true
+
+ /@vue/devtools-shared@7.3.9:
+ resolution: {integrity: sha512-CdfMRZKXyI8vw+hqOcQIiLihB6Hbbi7WNZGp7LsuH1Qe4aYAFmTaKjSciRZ301oTnwmU/knC/s5OGuV6UNiNoA==}
+ dependencies:
+ rfdc: 1.4.1
+ dev: true
+
+ /@vue/reactivity@3.4.38:
+ resolution: {integrity: sha512-4vl4wMMVniLsSYYeldAKzbk72+D3hUnkw9z8lDeJacTxAkXeDAP1uE9xr2+aKIN0ipOL8EG2GPouVTH6yF7Gnw==}
+ dependencies:
+ '@vue/shared': 3.4.38
+ dev: true
+
+ /@vue/runtime-core@3.4.38:
+ resolution: {integrity: sha512-21z3wA99EABtuf+O3IhdxP0iHgkBs1vuoCAsCKLVJPEjpVqvblwBnTj42vzHRlWDCyxu9ptDm7sI2ZMcWrQqlA==}
+ dependencies:
+ '@vue/reactivity': 3.4.38
+ '@vue/shared': 3.4.38
+ dev: true
+
+ /@vue/runtime-dom@3.4.38:
+ resolution: {integrity: sha512-afZzmUreU7vKwKsV17H1NDThEEmdYI+GCAK/KY1U957Ig2NATPVjCROv61R19fjZNzMmiU03n79OMnXyJVN0UA==}
+ dependencies:
+ '@vue/reactivity': 3.4.38
+ '@vue/runtime-core': 3.4.38
+ '@vue/shared': 3.4.38
+ csstype: 3.1.3
+ dev: true
+
+ /@vue/server-renderer@3.4.38(vue@3.4.38):
+ resolution: {integrity: sha512-NggOTr82FbPEkkUvBm4fTGcwUY8UuTsnWC/L2YZBmvaQ4C4Jl/Ao4HHTB+l7WnFCt5M/dN3l0XLuyjzswGYVCA==}
+ peerDependencies:
+ vue: 3.4.38
+ dependencies:
+ '@vue/compiler-ssr': 3.4.38
+ '@vue/shared': 3.4.38
+ vue: 3.4.38
+ dev: true
+
+ /@vue/shared@3.4.38:
+ resolution: {integrity: sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==}
+ dev: true
+
+ /@vueuse/core@11.0.3(vue@3.4.38):
+ resolution: {integrity: sha512-RENlh64+SYA9XMExmmH1a3TPqeIuJBNNB/63GT35MZI+zpru3oMRUA6cEFr9HmGqEgUisurwGwnIieF6qu3aXw==}
+ dependencies:
+ '@types/web-bluetooth': 0.0.20
+ '@vueuse/metadata': 11.0.3
+ '@vueuse/shared': 11.0.3(vue@3.4.38)
+ vue-demi: 0.14.10(vue@3.4.38)
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+ - vue
+ dev: true
+
+ /@vueuse/integrations@11.0.3(focus-trap@7.5.4)(vue@3.4.38):
+ resolution: {integrity: sha512-w6CDisaxs19S5Fd+NPPLFaA3GoX5gxuxrbTTBu0EYap7oH13w75L6C/+7e9mcoF9akhcR6GyYajwVMQEjdapJg==}
+ peerDependencies:
+ async-validator: ^4
+ axios: ^1
+ change-case: ^5
+ drauu: ^0.4
+ focus-trap: ^7
+ fuse.js: ^7
+ idb-keyval: ^6
+ jwt-decode: ^4
+ nprogress: ^0.2
+ qrcode: ^1.5
+ sortablejs: ^1
+ universal-cookie: ^7
+ peerDependenciesMeta:
+ async-validator:
+ optional: true
+ axios:
+ optional: true
+ change-case:
+ optional: true
+ drauu:
+ optional: true
+ focus-trap:
+ optional: true
+ fuse.js:
+ optional: true
+ idb-keyval:
+ optional: true
+ jwt-decode:
+ optional: true
+ nprogress:
+ optional: true
+ qrcode:
+ optional: true
+ sortablejs:
+ optional: true
+ universal-cookie:
+ optional: true
+ dependencies:
+ '@vueuse/core': 11.0.3(vue@3.4.38)
+ '@vueuse/shared': 11.0.3(vue@3.4.38)
+ focus-trap: 7.5.4
+ vue-demi: 0.14.10(vue@3.4.38)
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+ - vue
+ dev: true
+
+ /@vueuse/metadata@11.0.3:
+ resolution: {integrity: sha512-+FtbO4SD5WpsOcQTcC0hAhNlOid6QNLzqedtquTtQ+CRNBoAt9GuV07c6KNHK1wCmlq8DFPwgiLF2rXwgSHX5Q==}
+ dev: true
+
+ /@vueuse/shared@11.0.3(vue@3.4.38):
+ resolution: {integrity: sha512-0rY2m6HS5t27n/Vp5cTDsKTlNnimCqsbh/fmT2LgE+aaU42EMfXo8+bNX91W9I7DDmxfuACXMmrd7d79JxkqWA==}
+ dependencies:
+ vue-demi: 0.14.10(vue@3.4.38)
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+ - vue
+ dev: true
+
+ /algoliasearch@4.24.0:
+ resolution: {integrity: sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g==}
+ dependencies:
+ '@algolia/cache-browser-local-storage': 4.24.0
+ '@algolia/cache-common': 4.24.0
+ '@algolia/cache-in-memory': 4.24.0
+ '@algolia/client-account': 4.24.0
+ '@algolia/client-analytics': 4.24.0
+ '@algolia/client-common': 4.24.0
+ '@algolia/client-personalization': 4.24.0
+ '@algolia/client-search': 4.24.0
+ '@algolia/logger-common': 4.24.0
+ '@algolia/logger-console': 4.24.0
+ '@algolia/recommend': 4.24.0
+ '@algolia/requester-browser-xhr': 4.24.0
+ '@algolia/requester-common': 4.24.0
+ '@algolia/requester-node-http': 4.24.0
+ '@algolia/transporter': 4.24.0
+ dev: true
+
+ /birpc@0.2.17:
+ resolution: {integrity: sha512-+hkTxhot+dWsLpp3gia5AkVHIsKlZybNT5gIYiDlNzJrmYPcTM9k5/w2uaj3IPpd7LlEYpmCj4Jj1nC41VhDFg==}
+ dev: true
+
+ /copy-anything@3.0.5:
+ resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
+ engines: {node: '>=12.13'}
+ dependencies:
+ is-what: 4.1.16
+ dev: true
+
+ /csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+ dev: true
+
+ /entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+ dev: true
+
+ /esbuild@0.21.5:
+ resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
+ engines: {node: '>=12'}
+ hasBin: true
+ requiresBuild: true
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.21.5
+ '@esbuild/android-arm': 0.21.5
+ '@esbuild/android-arm64': 0.21.5
+ '@esbuild/android-x64': 0.21.5
+ '@esbuild/darwin-arm64': 0.21.5
+ '@esbuild/darwin-x64': 0.21.5
+ '@esbuild/freebsd-arm64': 0.21.5
+ '@esbuild/freebsd-x64': 0.21.5
+ '@esbuild/linux-arm': 0.21.5
+ '@esbuild/linux-arm64': 0.21.5
+ '@esbuild/linux-ia32': 0.21.5
+ '@esbuild/linux-loong64': 0.21.5
+ '@esbuild/linux-mips64el': 0.21.5
+ '@esbuild/linux-ppc64': 0.21.5
+ '@esbuild/linux-riscv64': 0.21.5
+ '@esbuild/linux-s390x': 0.21.5
+ '@esbuild/linux-x64': 0.21.5
+ '@esbuild/netbsd-x64': 0.21.5
+ '@esbuild/openbsd-x64': 0.21.5
+ '@esbuild/sunos-x64': 0.21.5
+ '@esbuild/win32-arm64': 0.21.5
+ '@esbuild/win32-ia32': 0.21.5
+ '@esbuild/win32-x64': 0.21.5
+ dev: true
+
+ /estree-walker@2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+ dev: true
+
+ /focus-trap@7.5.4:
+ resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==}
+ dependencies:
+ tabbable: 6.2.0
+ dev: true
+
+ /fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /hookable@5.5.3:
+ resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
+ dev: true
+
+ /is-what@4.1.16:
+ resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
+ engines: {node: '>=12.13'}
+ dev: true
+
+ /magic-string@0.30.11:
+ resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==}
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.0
+ dev: true
+
+ /mark.js@8.11.1:
+ resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==}
+ dev: true
+
+ /minisearch@7.1.0:
+ resolution: {integrity: sha512-tv7c/uefWdEhcu6hvrfTihflgeEi2tN6VV7HJnCjK6VxM75QQJh4t9FwJCsA2EsRS8LCnu3W87CuGPWMocOLCA==}
+ dev: true
+
+ /mitt@3.0.1:
+ resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
+ dev: true
+
+ /nanoid@3.3.7:
+ resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+ dev: true
+
+ /perfect-debounce@1.0.0:
+ resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
+ dev: true
+
+ /picocolors@1.0.1:
+ resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
+ dev: true
+
+ /postcss@8.4.41:
+ resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==}
+ engines: {node: ^10 || ^12 || >=14}
+ dependencies:
+ nanoid: 3.3.7
+ picocolors: 1.0.1
+ source-map-js: 1.2.0
+ dev: true
+
+ /preact@10.23.2:
+ resolution: {integrity: sha512-kKYfePf9rzKnxOAKDpsWhg/ysrHPqT+yQ7UW4JjdnqjFIeNUnNcEJvhuA8fDenxAGWzUqtd51DfVg7xp/8T9NA==}
+ dev: true
+
+ /rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+ dev: true
+
+ /rollup@4.21.1:
+ resolution: {integrity: sha512-ZnYyKvscThhgd3M5+Qt3pmhO4jIRR5RGzaSovB6Q7rGNrK5cUncrtLmcTTJVSdcKXyZjW8X8MB0JMSuH9bcAJg==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+ dependencies:
+ '@types/estree': 1.0.5
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.21.1
+ '@rollup/rollup-android-arm64': 4.21.1
+ '@rollup/rollup-darwin-arm64': 4.21.1
+ '@rollup/rollup-darwin-x64': 4.21.1
+ '@rollup/rollup-linux-arm-gnueabihf': 4.21.1
+ '@rollup/rollup-linux-arm-musleabihf': 4.21.1
+ '@rollup/rollup-linux-arm64-gnu': 4.21.1
+ '@rollup/rollup-linux-arm64-musl': 4.21.1
+ '@rollup/rollup-linux-powerpc64le-gnu': 4.21.1
+ '@rollup/rollup-linux-riscv64-gnu': 4.21.1
+ '@rollup/rollup-linux-s390x-gnu': 4.21.1
+ '@rollup/rollup-linux-x64-gnu': 4.21.1
+ '@rollup/rollup-linux-x64-musl': 4.21.1
+ '@rollup/rollup-win32-arm64-msvc': 4.21.1
+ '@rollup/rollup-win32-ia32-msvc': 4.21.1
+ '@rollup/rollup-win32-x64-msvc': 4.21.1
+ fsevents: 2.3.3
+ dev: true
+
+ /search-insights@2.17.0:
+ resolution: {integrity: sha512-AskayU3QNsXQzSL6v4LTYST7NNfs2HWyHHB+sdORP9chsytAhro5XRfToAMI/LAVYgNbzowVZTMfBRodgbUHKg==}
+ dev: true
+
+ /shiki@1.14.1:
+ resolution: {integrity: sha512-FujAN40NEejeXdzPt+3sZ3F2dx1U24BY2XTY01+MG8mbxCiA2XukXdcbyMyLAHJ/1AUUnQd1tZlvIjefWWEJeA==}
+ dependencies:
+ '@shikijs/core': 1.14.1
+ '@types/hast': 3.0.4
+ dev: true
+
+ /source-map-js@1.2.0:
+ resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /speakingurl@14.0.1:
+ resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /superjson@2.2.1:
+ resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==}
+ engines: {node: '>=16'}
+ dependencies:
+ copy-anything: 3.0.5
+ dev: true
+
+ /tabbable@6.2.0:
+ resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
+ dev: true
+
+ /to-fast-properties@2.0.0:
+ resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /vite@5.4.2:
+ resolution: {integrity: sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || >=20.0.0
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.4.0
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ dependencies:
+ esbuild: 0.21.5
+ postcss: 8.4.41
+ rollup: 4.21.1
+ optionalDependencies:
+ fsevents: 2.3.3
+ 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
+ peerDependencies:
+ markdown-it-mathjax3: ^4
+ postcss: ^8
+ peerDependenciesMeta:
+ markdown-it-mathjax3:
+ optional: true
+ postcss:
+ optional: true
+ dependencies:
+ '@docsearch/css': 3.6.1
+ '@docsearch/js': 3.6.1(@algolia/client-search@5.1.1)(search-insights@2.17.0)
+ '@shikijs/core': 1.14.1
+ '@shikijs/transformers': 1.14.1
+ '@types/markdown-it': 14.1.2
+ '@vitejs/plugin-vue': 5.1.2(vite@5.4.2)(vue@3.4.38)
+ '@vue/devtools-api': 7.3.9
+ '@vue/shared': 3.4.38
+ '@vueuse/core': 11.0.3(vue@3.4.38)
+ '@vueuse/integrations': 11.0.3(focus-trap@7.5.4)(vue@3.4.38)
+ focus-trap: 7.5.4
+ mark.js: 8.11.1
+ minisearch: 7.1.0
+ shiki: 1.14.1
+ vite: 5.4.2
+ vue: 3.4.38
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - '@types/node'
+ - '@types/react'
+ - '@vue/composition-api'
+ - async-validator
+ - axios
+ - change-case
+ - drauu
+ - fuse.js
+ - idb-keyval
+ - jwt-decode
+ - less
+ - lightningcss
+ - nprogress
+ - qrcode
+ - react
+ - react-dom
+ - sass
+ - sass-embedded
+ - search-insights
+ - sortablejs
+ - stylus
+ - sugarss
+ - terser
+ - typescript
+ - universal-cookie
+ dev: true
+
+ /vue-demi@0.14.10(vue@3.4.38):
+ resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
+ engines: {node: '>=12'}
+ hasBin: true
+ requiresBuild: true
+ peerDependencies:
+ '@vue/composition-api': ^1.0.0-rc.1
+ vue: ^3.0.0-0 || ^2.6.0
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+ dependencies:
+ vue: 3.4.38
+ dev: true
+
+ /vue@3.4.38:
+ resolution: {integrity: sha512-f0ZgN+mZ5KFgVv9wz0f4OgVKukoXtS3nwET4c2vLBGQR50aI8G0cqbFtLlX9Yiyg3LFGBitruPHt2PxwTduJEw==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ '@vue/compiler-dom': 3.4.38
+ '@vue/compiler-sfc': 3.4.38
+ '@vue/runtime-dom': 3.4.38
+ '@vue/server-renderer': 3.4.38(vue@3.4.38)
+ '@vue/shared': 3.4.38
+ dev: true
diff --git a/tests/test_angle.py b/tests/test_angle.py
index 6ed71f8..f0943e8 100644
--- a/tests/test_angle.py
+++ b/tests/test_angle.py
@@ -16,7 +16,7 @@ from tests.answer import output_ans
class TestAngle:
def test_radian_to_degree(self):
angle = AnyAngle(1, is_radian=True)
- output_ans(190 / PI, angle.degree, question="弧度转角度1")
+ output_ans(180 / PI, angle.degree, question="弧度转角度1")
angle = AnyAngle(2, is_radian=True)
output_ans(360 / PI, angle.degree, question="弧度转角度2")