diff --git a/cMake/FreeCadMacros.cmake b/cMake/FreeCadMacros.cmake index f234416cd3..5a169f587d 100644 --- a/cMake/FreeCadMacros.cmake +++ b/cMake/FreeCadMacros.cmake @@ -156,13 +156,13 @@ macro(generate_from_py BASE_NAME) if(NOT EXISTS "${SOURCE_CPP_PATH}") # assures the source files are generated at least once message(STATUS "${SOURCE_CPP_PATH}") - execute_process(COMMAND "${PYTHON_EXECUTABLE}" "${TOOL_NATIVE_PATH}" --outputPath "${OUTPUT_NATIVE_PATH}" "${SOURCE_NATIVE_PATH}" + execute_process(COMMAND "${Python3_EXECUTABLE}" "${TOOL_NATIVE_PATH}" --outputPath "${OUTPUT_NATIVE_PATH}" "${SOURCE_NATIVE_PATH}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMAND_ERROR_IS_FATAL ANY ) endif() add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${BASE_NAME}_.h" "${CMAKE_CURRENT_BINARY_DIR}/${BASE_NAME}_.cpp" - COMMAND ${PYTHON_EXECUTABLE} "${TOOL_NATIVE_PATH}" --outputPath "${OUTPUT_NATIVE_PATH}" ${BASE_NAME}.pyi + COMMAND ${Python3_EXECUTABLE} "${TOOL_NATIVE_PATH}" --outputPath "${OUTPUT_NATIVE_PATH}" ${BASE_NAME}.pyi MAIN_DEPENDENCY "${BASE_NAME}.pyi" DEPENDS "${CMAKE_SOURCE_DIR}/src/Tools/bindings/templates/templateClassPyExport.py" diff --git a/src/Base/Axis.pyi b/src/Base/Axis.pyi new file mode 100644 index 0000000000..060d238276 --- /dev/null +++ b/src/Base/Axis.pyi @@ -0,0 +1,85 @@ +from Metadata import export +from PyObjectBase import PyObjectBase +from Vector import Vector +from Placement import Placement +from Axis import Axis +from typing import overload + +@export( + Constructor=True, + Delete=True, +) +class Axis(PyObjectBase): + """ + Base.Axis class. + + An Axis defines a direction and a position (base) in 3D space. + + The following constructors are supported: + + Axis() + Empty constructor. + + Axis(axis) + Copy constructor. + axis : Base.Axis + + Axis(base, direction) + Define from a position and a direction. + base : Base.Vector + direction : Base.Vector + """ + + Base: Vector = ... + """Base position vector of the Axis.""" + + Direction: Vector = ... + """Direction vector of the Axis.""" + + # fmt: off + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, axis: Axis) -> None: ... + @overload + def __init__(self, base: Vector, direction: Vector) -> None: ... + # fmt: on + + def copy(self) -> Axis: + """ + copy() -> Base.Axis + + Returns a copy of this Axis. + """ + ... + + def move(self, vector: Vector) -> None: + """ + move(vector) -> None + + Move the axis base along the given vector. + + vector : Base.Vector + Vector by which to move the axis. + """ + ... + + def multiply(self, placement: Placement) -> Axis: + """ + multiply(placement) -> Base.Axis + + Multiply this axis by a placement. + + placement : Base.Placement + Placement by which to multiply the axis. + """ + ... + + def reversed(self) -> Axis: + """ + reversed() -> Base.Axis + + Compute the reversed axis. This returns a new Base.Axis with + the original direction reversed. + """ + ... diff --git a/src/Base/BaseClass.pyi b/src/Base/BaseClass.pyi new file mode 100644 index 0000000000..15425dd6a4 --- /dev/null +++ b/src/Base/BaseClass.pyi @@ -0,0 +1,31 @@ +from Metadata import constmethod +from PyObjectBase import PyObjectBase +from typing import List, Final + +class BaseClass(PyObjectBase): + """ + This is the base class + + Author: Juergen Riegel (FreeCAD@juergen-riegel.net) + Licence: LGPL + """ + + TypeId: Final[str] = "" + """Is the type of the FreeCAD object with module domain""" + + Module: Final[str] = "" + """Module in which this class is defined""" + + @constmethod + def isDerivedFrom(self, typeName: str) -> bool: + """ + Returns true if given type is a father + """ + ... + + @constmethod + def getAllDerivedFrom(self) -> List[object]: + """ + Returns all descendants + """ + ... diff --git a/src/Base/BoundBox.pyi b/src/Base/BoundBox.pyi new file mode 100644 index 0000000000..368a29eb2b --- /dev/null +++ b/src/Base/BoundBox.pyi @@ -0,0 +1,370 @@ +from Metadata import export, constmethod +from PyObjectBase import PyObjectBase +from Vector import Vector +from Matrix import Matrix +from typing import overload, Any, Final, Tuple, Union + +@export( + TwinPointer="BoundBox3d", + Constructor=True, + Delete=True, +) +class BoundBox(PyObjectBase): + """ + Base.BoundBox class. + + This class represents a bounding box. + A bounding box is a rectangular cuboid which is a way to describe outer + boundaries and is obtained from a lot of 3D types. + It is often used to check if a 3D entity lies in the range of another object. + Checking for bounding interference first can save a lot of computing time! + An invalid BoundBox is represented by inconsistent values at each direction: + The maximum float value of the system at the minimum coordinates, and the + opposite value at the maximum coordinates. + + The following constructors are supported: + + BoundBox() + Empty constructor. Returns an invalid BoundBox. + + BoundBox(boundBox) + Copy constructor. + boundBox : Base.BoundBox + + BoundBox(xMin, yMin=0, zMin=0, xMax=0, yMax=0, zMax=0) + Define from the minimum and maximum values at each direction. + xMin : float + Minimum value at x-coordinate. + yMin : float + Minimum value at y-coordinate. + zMin : float + Minimum value at z-coordinate. + xMax : float + Maximum value at x-coordinate. + yMax : float + Maximum value at y-coordinate. + zMax : float + Maximum value at z-coordinate. + + App.BoundBox(min, max) + Define from two containers representing the minimum and maximum values of the + coordinates in each direction. + min : Base.Vector, tuple + Minimum values of the coordinates. + max : Base.Vector, tuple + Maximum values of the coordinates. + + Author: Juergen Riegel (FreeCAD@juergen-riegel.net) + Licence: LGPL + """ + + Center: Final[Any] = None + """Center point of the bounding box.""" + + XMax: float = 0.0 + """The maximum x boundary position.""" + + YMax: float = 0.0 + """The maximum y boundary position.""" + + ZMax: float = 0.0 + """The maximum z boundary position.""" + + XMin: float = 0.0 + """The minimum x boundary position.""" + + YMin: float = 0.0 + """The minimum y boundary position.""" + + ZMin: float = 0.0 + """The minimum z boundary position.""" + + XLength: Final[float] = 0.0 + """Length of the bounding box in x direction.""" + + YLength: Final[float] = 0.0 + """Length of the bounding box in y direction.""" + + ZLength: Final[float] = 0.0 + """Length of the bounding box in z direction.""" + + DiagonalLength: Final[float] = 0.0 + """Diagonal length of the bounding box.""" + + # fmt: off + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, boundBox: "BoundBox") -> None: ... + @overload + def __init__( + self, + xMin: float, + yMin: float = 0, + zMin: float = 0, + xMax: float = 0, + yMax: float = 0, + zMax: float = 0, + ) -> None: ... + @overload + def __init__( + self, + min: Union[Vector, Tuple[float, float, float]], + max: Union[Vector, Tuple[float, float, float]], + ) -> None: ... + # fmt: on + + def setVoid(self) -> None: + """ + setVoid() -> None + + Invalidate the bounding box. + """ + ... + + @constmethod + def isValid(self) -> bool: + """ + isValid() -> bool + + Checks if the bounding box is valid. + """ + ... + + @overload + def add(self, minMax: Vector) -> None: ... + @overload + def add(self, minMax: Tuple[float, float, float]) -> None: ... + @overload + def add(self, x: float, y: float, z: float) -> None: ... + def add(self, *args: Any, **kwargs: Any) -> None: + """ + add(minMax) -> None + add(x, y, z) -> None + + Increase the maximum values or decrease the minimum values of this BoundBox by + replacing the current values with the given values, so the bounding box can grow + but not shrink. + + minMax : Base.Vector, tuple + Values to enlarge at each direction. + x : float + Value to enlarge at x-direction. + y : float + Value to enlarge at y-direction. + z : float + Value to enlarge at z-direction. + """ + ... + + @constmethod + def getPoint(self, index: int) -> Vector: + """ + getPoint(index) -> Base.Vector + + Get the point of the given index. + The index must be in the range of [0, 7]. + + index : int + """ + ... + + @constmethod + def getEdge(self, index: int) -> Tuple[Vector, ...]: + """ + getEdge(index) -> tuple of Base.Vector + + Get the edge points of the given index. + The index must be in the range of [0, 11]. + + index : int + """ + ... + + @overload + def closestPoint(self, point: Vector) -> Vector: ... + @overload + def closestPoint(self, x: float, y: float, z: float) -> Vector: ... + @constmethod + def closestPoint(self, *args: Any, **kwargs: Any) -> Vector: + """ + closestPoint(point) -> Base.Vector + closestPoint(x, y, z) -> Base.Vector + + Get the closest point of the bounding box to the given point. + + point : Base.Vector, tuple + Coordinates of the given point. + x : float + X-coordinate of the given point. + y : float + Y-coordinate of the given point. + z : float + Z-coordinate of the given point. + """ + ... + + @overload + def intersect(self, boundBox2: "BoundBox") -> bool: ... + @overload + def intersect( + self, + base: Union[Vector, Tuple[float, float, float]], + dir: Union[Vector, Tuple[float, float, float]], + ) -> bool: ... + def intersect(self, *args: Any) -> bool: + """ + intersect(boundBox2) -> bool + intersect(base, dir) -> bool + + Checks if the given object intersects with this bounding box. That can be + another bounding box or a line specified by base and direction. + + boundBox2 : Base.BoundBox + base : Base.Vector, tuple + dir : Base.Vector, tuple + """ + ... + + def intersected(self, boundBox2: "BoundBox") -> "BoundBox": + """ + intersected(boundBox2) -> Base.BoundBox + + Returns the intersection of this and the given bounding box. + + boundBox2 : Base.BoundBox + """ + ... + + def united(self, boundBox2: "BoundBox") -> "BoundBox": + """ + united(boundBox2) -> Base.BoundBox + + Returns the union of this and the given bounding box. + + boundBox2 : Base.BoundBox + """ + ... + + def enlarge(self, variation: float) -> None: + """ + enlarge(variation) -> None + + Decrease the minimum values and increase the maximum values by the given value. + A negative value shrinks the bounding box. + + variation : float + """ + ... + + def getIntersectionPoint(self, base: Vector, dir: Vector, epsilon: float = 0.0001) -> Vector: + """ + getIntersectionPoint(base, dir, epsilon=0.0001) -> Base.Vector + + Calculate the intersection point of a line with the bounding box. + The base point must lie inside the bounding box, if not an exception is thrown. + + base : Base.Vector + Base point of the line. + dir : Base.Vector + Direction of the line. + epsilon : float + Bounding box size tolerance. + """ + ... + + @overload + def move(self, displacement: Vector) -> None: ... + @overload + def move(self, displacement: Tuple[float, float, float]) -> None: ... + @overload + def move(self, x: float, y: float, z: float) -> None: ... + def move(self, *args: Any, **kwargs: Any) -> None: + """ + move(displacement) -> None + move(x, y, z) -> None + + Move the bounding box by the given values. + + displacement : Base.Vector, tuple + Displacement at each direction. + x : float + Displacement at x-direction. + y : float + Displacement at y-direction. + z : float + Displacement at z-direction. + """ + ... + + @overload + def scale(self, factor: Vector) -> None: ... + @overload + def scale(self, factor: Tuple[float, float, float]) -> None: ... + @overload + def scale(self, x: float, y: float, z: float) -> None: ... + def scale(self, *args: Any, **kwargs: Any) -> None: + """ + scale(factor) -> None + scale(x, y, z) -> None + + Scale the bounding box by the given values. + + factor : Base.Vector, tuple + Factor scale at each direction. + x : float + Scale at x-direction. + y : float + Scale at y-direction. + z : float + Scale at z-direction. + """ + ... + + def transformed(self, matrix: Matrix) -> "BoundBox": + """ + transformed(matrix) -> Base.BoundBox + + Returns a new BoundBox containing the transformed rectangular cuboid + represented by this BoundBox. + + matrix : Base.Matrix + Transformation matrix. + """ + ... + + def isCutPlane(self, base: Vector, normal: Vector) -> bool: + """ + isCutPlane(base, normal) -> bool + + Check if the plane specified by base and normal intersects (cuts) this bounding + box. + + base : Base.Vector + normal : Base.Vector + """ + ... + + @overload + def isInside(self, object: Vector) -> bool: ... + @overload + def isInside(self, object: "BoundBox") -> bool: ... + @overload + def isInside(self, x: float, y: float, z: float) -> bool: ... + def isInside(self, *args: Any) -> bool: + """ + isInside(object) -> bool + isInside(x, y, z) -> bool + + Check if a point or a bounding box is inside this bounding box. + + object : Base.Vector, Base.BoundBox + Object to check if it is inside this bounding box. + x : float + X-coordinate of the point to check. + y : float + Y-coordinate of the point to check. + z : float + Z-coordinate of the point to check. + """ + ... diff --git a/src/Base/CMakeLists.txt b/src/Base/CMakeLists.txt index 2bf1e6433d..05bee80c9b 100644 --- a/src/Base/CMakeLists.txt +++ b/src/Base/CMakeLists.txt @@ -79,18 +79,31 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") endif() generate_from_xml(TypePy) +generate_from_py(Type) generate_from_xml(BaseClassPy) +generate_from_py(BaseClass) generate_from_xml(BoundBoxPy) +generate_from_py(BoundBox) generate_from_xml(CoordinateSystemPy) +generate_from_py(CoordinateSystem) generate_from_xml(PersistencePy) +generate_from_py(Persistence) generate_from_xml(VectorPy) +generate_from_py(Vector) generate_from_xml(MatrixPy) +generate_from_py(Matrix) generate_from_xml(RotationPy) +generate_from_py(Rotation) generate_from_xml(PlacementPy) +generate_from_py(Placement) generate_from_xml(AxisPy) +generate_from_py(Axis) generate_from_xml(UnitPy) +generate_from_py(Unit) generate_from_xml(QuantityPy) +generate_from_py(Quantity) generate_from_xml(PrecisionPy) +generate_from_py(Precision) generate_from_any(Parameter.xsd Parameter.inl xmlSchemeString) if(SWIG_FOUND) diff --git a/src/Base/CoordinateSystem.pyi b/src/Base/CoordinateSystem.pyi new file mode 100644 index 0000000000..041fa695ea --- /dev/null +++ b/src/Base/CoordinateSystem.pyi @@ -0,0 +1,93 @@ +from Metadata import export, constmethod +from PyObjectBase import PyObjectBase +from Axis import Axis as AxisPy +from Vector import Vector +from Placement import Placement +from Rotation import Rotation +from typing import Union + +@export( + Constructor=True, + Delete=True, +) +class CoordinateSystem(PyObjectBase): + """ + Base.CoordinateSystem class. + + An orthonormal right-handed coordinate system in 3D space. + + CoordinateSystem() + Empty constructor. + + Author: Juergen Riegel (FreeCAD@juergen-riegel.net) + Licence: LGPL + """ + + Axis: AxisPy = None + """Set or get axis.""" + + XDirection: Vector = None + """Set or get X-direction.""" + + YDirection: Vector = None + """Set or get Y-direction.""" + + ZDirection: Vector = None + """Set or get Z-direction.""" + + Position: Vector = None + """Set or get position.""" + + def setAxes(self, axis: Union[AxisPy, Vector], xDir: Vector) -> None: + """ + setAxes(axis, xDir) -> None + + Set axis or Z-direction, and X-direction. + The X-direction is determined from the orthonormal compononent of `xDir` + with respect to `axis` direction. + + axis : Base.Axis, Base.Vector + xDir : Base.Vector + """ + ... + + @constmethod + def displacement(self, coordSystem2: "CoordinateSystem") -> Placement: + """ + displacement(coordSystem2) -> Base.Placement + + Computes the placement from this to the passed coordinate system `coordSystem2`. + + coordSystem2 : Base.CoordinateSystem + """ + ... + + def transformTo(self, vector: Vector) -> Vector: + """ + transformTo(vector) -> Base.Vector + + Computes the coordinates of the point in coordinates of this coordinate system. + + vector : Base.Vector + """ + ... + + def transform(self, trans: Union[Rotation, Placement]) -> None: + """ + transform(trans) -> None + + Applies a transformation on this coordinate system. + + trans : Base.Rotation, Base.Placement + """ + ... + + def setPlacement(self, placement: Placement) -> None: + """ + setPlacment(placement) -> None + + Set placement to the coordinate system. + + placement : Base.Placement + """ + ... diff --git a/src/Base/Matrix.pyi b/src/Base/Matrix.pyi new file mode 100644 index 0000000000..8ba42df1b3 --- /dev/null +++ b/src/Base/Matrix.pyi @@ -0,0 +1,445 @@ +from Vector import Vector +from Metadata import export, constmethod, class_declarations, no_args +from PyObjectBase import PyObjectBase +from enum import IntEnum +from typing import overload, Union, Tuple, Sequence + +class ScaleType(IntEnum): + Other = -1 + NoScaling = 0 + NonUniformRight = 1 + NonUniformLeft = 2 + Uniform = 3 + +@export( + TwinPointer="Matrix4D", + Constructor=True, + Delete=True, + NumberProtocol=True, + RichCompare=True, +) +@class_declarations( + """public: + MatrixPy(const Matrix4D & mat, PyTypeObject *T = &Type) + :PyObjectBase(new Matrix4D(mat),T){} + Matrix4D value() const + { return *(getMatrixPtr()); } + """ +) +class Matrix(PyObjectBase): + """ + Base.Matrix class. + + A 4x4 Matrix. + In particular, this matrix can represent an affine transformation, that is, + given a 3D vector `x`, apply the transformation y = M*x + b, where the matrix + `M` is a linear map and the vector `b` is a translation. + `y` can be obtained using a linear transformation represented by the 4x4 matrix + `A` conformed by the augmented 3x4 matrix (M|b), augmented by row with + (0,0,0,1), therefore: (y, 1) = A*(x, 1). + + The following constructors are supported: + + Matrix() + Empty constructor. + + Matrix(matrix) + Copy constructor. + matrix : Base.Matrix. + + Matrix(*coef) + Define from 16 coefficients of the 4x4 matrix. + coef : sequence of float + The sequence can have up to 16 elements which complete the matrix by rows. + + Matrix(vector1, vector2, vector3, vector4) + Define from four 3D vectors which represent the columns of the 3x4 submatrix, + useful to represent an affine transformation. The fourth row is made up by + (0,0,0,1). + vector1 : Base.Vector + vector2 : Base.Vector + vector3 : Base.Vector + vector4 : Base.Vector + Default to (0,0,0). Optional. + + Author: Juergen Riegel (FreeCAD@juergen-riegel.net) + Licence: LGPL + """ + + A11: float = 0.0 + """The (1,1) matrix element.""" + A12: float = 0.0 + """The (1,2) matrix element.""" + A13: float = 0.0 + """The (1,3) matrix element.""" + A14: float = 0.0 + """The (1,4) matrix element.""" + A21: float = 0.0 + """The (2,1) matrix element.""" + A22: float = 0.0 + """The (2,2) matrix element.""" + A23: float = 0.0 + """The (2,3) matrix element.""" + A24: float = 0.0 + """The (2,4) matrix element.""" + A31: float = 0.0 + """The (3,1) matrix element.""" + A32: float = 0.0 + """The (3,2) matrix element.""" + A33: float = 0.0 + """The (3,3) matrix element.""" + A34: float = 0.0 + """The (3,4) matrix element.""" + A41: float = 0.0 + """The (4,1) matrix element.""" + A42: float = 0.0 + """The (4,2) matrix element.""" + A43: float = 0.0 + """The (4,3) matrix element.""" + A44: float = 0.0 + """The (4,4) matrix element.""" + + A: Sequence[float] = [] + """The matrix elements.""" + + @overload + def move(self, vector: Vector) -> None: ... + @overload + def move(self, x: float, y: float, z: float) -> None: ... + def move(self, *args) -> None: + """ + move(vector) -> None + move(x, y, z) -> None + + Move the matrix along a vector, equivalent to left multiply the matrix + by a pure translation transformation. + + vector : Base.Vector, tuple + x : float + `x` translation. + y : float + `y` translation. + z : float + `z` translation. + """ + ... + + @overload + def scale(self, vector: Vector) -> None: ... + @overload + def scale(self, x: float, y: float, z: float) -> None: ... + @overload + def scale(self, factor: float) -> None: ... + def scale(self, *args) -> None: + """ + scale(vector) -> None + scale(x, y, z) -> None + scale(factor) -> None + + Scale the first three rows of the matrix. + + vector : Base.Vector + x : float + First row factor scale. + y : float + Second row factor scale. + z : float + Third row factor scale. + factor : float + global factor scale. + """ + ... + + @constmethod + def hasScale(self, tol: float = 0) -> ScaleType: + """ + hasScale(tol=0) -> ScaleType + + Return an enum value of ScaleType. Possible values are: + Uniform, NonUniformLeft, NonUniformRight, NoScaling or Other + if it's not a scale matrix. + + tol : float + """ + ... + + @constmethod + def decompose(self) -> Tuple["Matrix", "Matrix", "Matrix", "Matrix"]: + """ + decompose() -> Base.Matrix, Base.Matrix, Base.Matrix, Base.Matrix + + Return a tuple of matrices representing shear, scale, rotation and move. + So that matrix = move * rotation * scale * shear. + """ + ... + + @no_args + def nullify(self) -> None: + """ + nullify() -> None + + Make this the null matrix. + """ + ... + + @no_args + @constmethod + def isNull(self) -> bool: + """ + isNull() -> bool + + Check if this is the null matrix. + """ + ... + + @no_args + def unity(self) -> None: + """ + unity() -> None + + Make this matrix to unity (4D identity matrix). + """ + ... + + @constmethod + def isUnity(self, tol: float = 0.0) -> bool: + """ + isUnity([tol=0.0]) -> bool + + Check if this is the unit matrix (4D identity matrix). + """ + ... + + def transform(self, vector: Vector, matrix2: "Matrix") -> None: + """ + transform(vector, matrix2) -> None + + Transform the matrix around a given point. + Equivalent to left multiply the matrix by T*M*T_inv, where M is `matrix2`, T the + translation generated by `vector` and T_inv the inverse translation. + For example, if `matrix2` is a rotation, the result is the transformation generated + by the current matrix followed by a rotation around the point represented by `vector`. + + vector : Base.Vector + matrix2 : Base.Matrix + """ + ... + + @constmethod + def col(self, index: int) -> Vector: + """ + col(index) -> Base.Vector + + Return the vector of a column, that is, the vector generated by the three + first elements of the specified column. + + index : int + Required column index. + """ + ... + + def setCol(self, index: int, vector: Vector) -> None: + """ + setCol(index, vector) -> None + + Set the vector of a column, that is, the three first elements of the specified + column by index. + + index : int + Required column index. + vector : Base.Vector + """ + ... + + @constmethod + def row(self, index: int) -> Vector: + """ + row(index) -> Base.Vector + + Return the vector of a row, that is, the vector generated by the three + first elements of the specified row. + + index : int + Required row index. + """ + ... + + def setRow(self, index: int, vector: Vector) -> None: + """ + setRow(index, vector) -> None + + Set the vector of a row, that is, the three first elements of the specified + row by index. + + index : int + Required row index. + vector : Base.Vector + """ + ... + + @no_args + @constmethod + def diagonal(self) -> Vector: + """ + diagonal() -> Base.Vector + + Return the diagonal of the 3x3 leading principal submatrix as vector. + """ + ... + + def setDiagonal(self, vector: Vector) -> None: + """ + setDiagonal(vector) -> None + + Set the diagonal of the 3x3 leading principal submatrix. + + vector : Base.Vector + """ + ... + + def rotateX(self, angle: float) -> None: + """ + rotateX(angle) -> None + + Rotate around X axis. + + angle : float + Angle in radians. + """ + ... + + def rotateY(self, angle: float) -> None: + """ + rotateY(angle) -> None + + Rotate around Y axis. + + angle : float + Angle in radians. + """ + ... + + def rotateZ(self, angle: float) -> None: + """ + rotateZ(angle) -> None + + Rotate around Z axis. + + angle : float + Angle in radians. + """ + ... + + @overload + def multiply(self, matrix: "Matrix") -> "Matrix": ... + @overload + def multiply(self, vector: Vector) -> Vector: ... + @constmethod + def multiply(self, obj: Union["Matrix", Vector]) -> Union["Matrix", Vector]: + """ + multiply(matrix) -> Base.Matrix + multiply(vector) -> Base.Vector + + Right multiply the matrix by the given object. + If the argument is a vector, this is augmented to the 4D vector (`vector`, 1). + + matrix : Base.Matrix + vector : Base.Vector + """ + ... + + @constmethod + def multVec(self, vector: Vector) -> Vector: + """ + multVec(vector) -> Base.Vector + + Compute the transformed vector using the matrix. + + vector : Base.Vector + """ + ... + + @no_args + def invert(self) -> None: + """ + invert() -> None + + Compute the inverse matrix in-place, if possible. + """ + ... + + @no_args + @constmethod + def inverse(self) -> "Matrix": + """ + inverse() -> Base.Matrix + + Compute the inverse matrix, if possible. + """ + ... + + @no_args + def transpose(self) -> None: + """ + transpose() -> None + + Transpose the matrix in-place. + """ + ... + + @no_args + @constmethod + def transposed(self) -> "Matrix": + """ + transposed() -> Base.Matrix + + Returns a transposed copy of this matrix. + """ + ... + + @no_args + @constmethod + def determinant(self) -> float: + """ + determinant() -> float + + Compute the determinant of the matrix. + """ + ... + + @constmethod + def isOrthogonal(self, tol: float = 1e-6) -> float: + """ + isOrthogonal(tol=1e-6) -> float + + Checks if the matrix is orthogonal, i.e. M * M^T = k*I and returns + the multiple of the identity matrix. If it's not orthogonal 0 is returned. + + tol : float + Tolerance used to check orthogonality. + """ + ... + + @constmethod + def submatrix(self, dim: int) -> "Matrix": + """ + submatrix(dim) -> Base.Matrix + + Get the leading principal submatrix of the given dimension. + The (4 - `dim`) remaining dimensions are completed with the + corresponding identity matrix. + + dim : int + Dimension parameter must be in the range [1,4]. + """ + ... + + @no_args + @constmethod + def analyze(self) -> str: + """ + analyze() -> str + + Analyzes the type of transformation. + """ + ... diff --git a/src/Base/Persistence.pyi b/src/Base/Persistence.pyi new file mode 100644 index 0000000000..06c8506653 --- /dev/null +++ b/src/Base/Persistence.pyi @@ -0,0 +1,45 @@ +from Metadata import constmethod +from BaseClass import BaseClass +from typing import Final + +class Persistence(BaseClass): + """ + Base.Persistence class. + + Class to dump and restore the content of an object. + + Author: Juergen Riegel (FreeCAD@juergen-riegel.net) + Licence: LGPL + """ + + Content: Final[str] = "" + """Content of the object in XML representation.""" + + MemSize: Final[int] = 0 + """Memory size of the object in bytes.""" + + @constmethod + def dumpContent(self, *, Compression: int = 3) -> bytearray: + """ + dumpContent(Compression=3) -> bytearray + + Dumps the content of the object, both the XML representation and the additional + data files required, into a byte representation. + + Compression : int + Set the data compression level in the range [0,9]. Set to 0 for no compression. + """ + ... + + def restoreContent(self, obj: object) -> None: + # TODO: Starting with Python 3.12, collections.abc.Buffer can be used for type hinting + """ + restoreContent(obj) -> None + + Restore the content of the object from a byte representation as stored by `dumpContent`. + It could be restored from any Python object implementing the buffer protocol. + + obj : buffer + Object with buffer protocol support. + """ + ... diff --git a/src/Base/Placement.pyi b/src/Base/Placement.pyi new file mode 100644 index 0000000000..4939e57ed9 --- /dev/null +++ b/src/Base/Placement.pyi @@ -0,0 +1,257 @@ +from Metadata import export, constmethod, class_declarations +from PyObjectBase import PyObjectBase +from Matrix import Matrix as MatrixPy +from Rotation import Rotation as RotationPy +from Vector import Vector +from typing import Sequence, overload + +@export( + Constructor=True, + Delete=True, + NumberProtocol=True, + RichCompare=True, +) +@class_declarations( + """public: + PlacementPy(const Placement & pla, PyTypeObject *T = &Type) + :PyObjectBase(new Placement(pla),T){} + Placement value() const + { return *(getPlacementPtr()); } + """ +) +class Placement(PyObjectBase): + """ + Base.Placement class. + + A Placement defines an orientation (rotation) and a position (base) in 3D space. + It is used when no scaling or other distortion is needed. + + The following constructors are supported: + + Placement() + Empty constructor. + + Placement(placement) + Copy constructor. + placement : Base.Placement + + Placement(matrix) + Define from a 4D matrix consisting of rotation and translation. + matrix : Base.Matrix + + Placement(base, rotation) + Define from position and rotation. + base : Base.Vector + rotation : Base.Rotation + + Placement(base, rotation, center) + Define from position and rotation with center. + base : Base.Vector + rotation : Base.Rotation + center : Base.Vector + + Placement(base, axis, angle) + define position and rotation. + base : Base.Vector + axis : Base.Vector + angle : float + """ + + Base: Vector = None + """Vector to the Base Position of the Placement.""" + + Rotation: Vector = None + """Orientation of the placement expressed as rotation.""" + + Matrix: MatrixPy = None + """Set/get matrix representation of the placement.""" + + # fmt: off + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, placement: "Placement") -> None: ... + @overload + def __init__(self, matrix: MatrixPy) -> None: ... + @overload + def __init__(self, base: Vector, rotation: RotationPy) -> None: ... + @overload + def __init__(self, base: Vector, rotation: RotationPy, center: Vector) -> None: ... + @overload + def __init__(self, base: Vector, axis: Vector, angle: float) -> None: ... + # fmt: on + + @constmethod + def copy(self) -> "Placement": + """ + copy() -> Base.Placement + + Returns a copy of this placement. + """ + ... + + def move(self, vector: Vector) -> None: + """ + move(vector) -> None + + Move the placement along a vector. + + vector : Base.Vector + Vector by which to move the placement. + """ + ... + + def translate(self, vector: Vector) -> None: + """ + translate(vector) -> None + + Alias to move(), to be compatible with TopoShape.translate(). + + vector : Base.Vector + Vector by which to move the placement. + """ + ... + + @overload + def rotate( + self, center: Sequence[float], axis: Sequence[float], angle: float, *, comp: bool = False + ) -> None: ... + def rotate(self, center: Vector, axis: Vector, angle: float, *, comp: bool = False) -> None: + """ + rotate(center, axis, angle, comp) -> None + + Rotate the current placement around center and axis with the given angle. + This method is compatible with TopoShape.rotate() if the (optional) keyword + argument comp is True (default=False). + + center : Base.Vector, sequence of float + Rotation center. + axis : Base.Vector, sequence of float + Rotation axis. + angle : float + Rotation angle in degrees. + comp : bool + optional keyword only argument, if True (default=False), + behave like TopoShape.rotate() (i.e. the resulting placements are interchangeable). + """ + ... + + @constmethod + def multiply(self, placement: "Placement") -> "Placement": + """ + multiply(placement) -> Base.Placement + + Right multiply this placement with another placement. + Also available as `*` operator. + + placement : Base.Placement + Placement by which to multiply this placement. + """ + ... + + @constmethod + def multVec(self, vector: Vector) -> Vector: + """ + multVec(vector) -> Base.Vector + + Compute the transformed vector using the placement. + + vector : Base.Vector + Vector to be transformed. + """ + ... + + @constmethod + def toMatrix(self) -> MatrixPy: + """ + toMatrix() -> Base.Matrix + + Compute the matrix representation of the placement. + """ + ... + + @constmethod + def inverse(self) -> "Placement": + """ + inverse() -> Base.Placement + + Compute the inverse placement. + """ + ... + + @constmethod + def pow(self, t: float, shorten: bool = True) -> "Placement": + """ + pow(t, shorten=True) -> Base.Placement + + Raise this placement to real power using ScLERP interpolation. + Also available as `**` operator. + + t : float + Real power. + shorten : bool + If True, ensures rotation quaternion is net positive to make + the path shorter. + """ + ... + + @constmethod + def sclerp(self, placement2: "Placement", t: float, shorten: bool = True) -> "Placement": + """ + sclerp(placement2, t, shorten=True) -> Base.Placement + + Screw Linear Interpolation (ScLERP) between this placement and `placement2`. + Interpolation is a continuous motion along a helical path parametrized by `t` + made of equal transforms if discretized. + If quaternions of rotations of the two placements differ in sign, the interpolation + will take a long path. + + placement2 : Base.Placement + t : float + Parameter of helical path. t=0 returns this placement, t=1 returns + `placement2`. t can also be outside of [0, 1] range for extrapolation. + shorten : bool + If True, the signs are harmonized before interpolation and the interpolation + takes the shorter path. + """ + ... + + @constmethod + def slerp(self, placement2: "Placement", t: float) -> "Placement": + """ + slerp(placement2, t) -> Base.Placement + + Spherical Linear Interpolation (SLERP) between this placement and `placement2`. + This function performs independent interpolation of rotation and movement. + Result of such interpolation might be not what application expects, thus this tool + might be considered for simple cases or for interpolating between small intervals. + For more complex cases you better use the advanced sclerp() function. + + placement2 : Base.Placement + t : float + Parameter of the path. t=0 returns this placement, t=1 returns `placement2`. + """ + ... + + @constmethod + def isIdentity(self, tol: float = 0.0) -> bool: + """ + isIdentity([tol=0.0]) -> bool + + Returns True if the placement has no displacement and no rotation. + Matrix representation is the 4D identity matrix. + tol : float + Tolerance used to check for identity. + If tol is negative or zero, no tolerance is used. + """ + ... + + @constmethod + def isSame(self, other: "Placement", tol: float = 0.0) -> bool: + """ + isSame(Base.Placement, [tol=0.0]) -> bool + + Checks whether this and the given placement are the same. + The default tolerance is set to 0.0 + """ + ... diff --git a/src/Base/Precision.pyi b/src/Base/Precision.pyi new file mode 100644 index 0000000000..ebc70d8988 --- /dev/null +++ b/src/Base/Precision.pyi @@ -0,0 +1,79 @@ +from PyObjectBase import PyObjectBase + +class Precision(PyObjectBase): + """ + This is the Precision class + + Author: Werner Mayer (wmayer@users.sourceforge.net) + Licence: LGPL + """ + + @staticmethod + def angular() -> float: + """ + Returns the recommended precision value when checking the equality of two angles (given in radians) + """ + ... + + @staticmethod + def confusion() -> float: + """ + Returns the recommended precision value when checking coincidence of two points in real space + """ + ... + + @staticmethod + def squareConfusion() -> float: + """ + Returns square of confusion + """ + ... + + @staticmethod + def intersection() -> float: + """ + Returns the precision value in real space, frequently used by intersection algorithms + """ + ... + + @staticmethod + def approximation() -> float: + """ + Returns the precision value in real space, frequently used by approximation algorithms + """ + ... + + @staticmethod + def parametric() -> float: + """ + Convert a real space precision to a parametric space precision + """ + ... + + @staticmethod + def isInfinite() -> bool: + """ + Returns True if R may be considered as an infinite number + """ + ... + + @staticmethod + def isPositiveInfinite() -> bool: + """ + Returns True if R may be considered as a positive infinite number + """ + ... + + @staticmethod + def isNegativeInfinite() -> bool: + """ + Returns True if R may be considered as a negative infinite number + """ + ... + + @staticmethod + def infinite() -> float: + """ + Returns a big number that can be considered as infinite + """ + ... diff --git a/src/Base/PyObjectBase.pyi b/src/Base/PyObjectBase.pyi new file mode 100644 index 0000000000..624434f693 --- /dev/null +++ b/src/Base/PyObjectBase.pyi @@ -0,0 +1,6 @@ +class PyObjectBase: + """ + The most base class for Python bindings. + """ + + ... diff --git a/src/Base/Quantity.pyi b/src/Base/Quantity.pyi new file mode 100644 index 0000000000..4e6f621fa7 --- /dev/null +++ b/src/Base/Quantity.pyi @@ -0,0 +1,107 @@ +from Metadata import export, constmethod +from PyObjectBase import PyObjectBase +from typing import overload, Final, Tuple, Union +from Unit import Unit as UnitPy + +@export( + NumberProtocol=True, + RichCompare=True, + Constructor=True, + Delete=True, +) +class Quantity(PyObjectBase): + """ + Quantity + defined by a value and a unit. + + The following constructors are supported: + Quantity() -- empty constructor + Quantity(Value) -- empty constructor + Quantity(Value,Unit) -- empty constructor + Quantity(Quantity) -- copy constructor + Quantity(string) -- arbitrary mixture of numbers and chars defining a Quantity + + Author: Juergen Riegel (FreeCAD@juergen-riegel.net) + Licence: LGPL + """ + + Value: float = ... + """Numeric Value of the Quantity (in internal system mm,kg,s)""" + + Unit: UnitPy = ... + """Unit of the Quantity""" + + UserString: Final[str] = ... + """Unit of the Quantity""" + + Format: dict = ... + """Format of the Quantity""" + + # fmt: off + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, value: float) -> None: ... + @overload + def __init__(self, value: float, unit: UnitPy) -> None: ... + @overload + def __init__(self, quantity: "Quantity") -> None: ... + @overload + def __init__(self, string: str) -> None: ... + # fmt: on + + @constmethod + def toStr(self, decimals: int = ...) -> str: + """ + toStr([decimals]) + + Returns a string representation rounded to number of decimals. If no decimals are specified then + the internal precision is used + """ + ... + + @overload + def toStr(self) -> str: ... + @overload + def toStr(self, decimals: int) -> str: ... + @constmethod + def getUserPreferred(self) -> Tuple["Quantity", str]: + """ + Returns a quantity with the translation factor and a string with the prevered unit + """ + ... + + @overload + def getValueAs(self, unit: str) -> float: ... + @overload + def getValueAs(self, translation: float, unit_signature: int) -> float: ... + @overload + def getValueAs(self, unit: UnitPy) -> float: ... + @overload + def getValueAs(self, quantity: "Quantity") -> float: ... + @constmethod + def getValueAs(self, *args) -> float: + """ + Returns a floating point value as the provided unit + + Following parameters are allowed: + getValueAs('m/s') # unit string to parse + getValueAs(2.45,1) # translation value and unit signature + getValueAs(FreeCAD.Units.Pascal) # predefined standard units + getValueAs(Qantity('N/m^2')) # a quantity + getValueAs(Unit(0,1,0,0,0,0,0,0)) # a unit + """ + ... + + @constmethod + def __round__(self, ndigits: int = ...) -> Union[int, float]: + """ + Returns the Integral closest to x, rounding half toward even. + When an argument is passed, work like built-in round(x, ndigits). + """ + ... + + @overload + def __round__(self) -> int: ... + @overload + def __round__(self, ndigits: int) -> float: ... diff --git a/src/Base/Rotation.pyi b/src/Base/Rotation.pyi new file mode 100644 index 0000000000..20b52cc9f8 --- /dev/null +++ b/src/Base/Rotation.pyi @@ -0,0 +1,275 @@ +from Metadata import export, constmethod, class_declarations +from PyObjectBase import PyObjectBase +from Vector import Vector +from Matrix import Matrix +from typing import overload, Tuple, List, Final + +@export( + Constructor=True, + Delete=True, + NumberProtocol=True, + RichCompare=True, +) +@class_declarations( + """public: + RotationPy(const Rotation & mat, PyTypeObject *T = &Type) + :PyObjectBase(new Rotation(mat),T){} + Rotation value() const + { return *(getRotationPtr()); } + """ +) +class Rotation(PyObjectBase): + """ + Base.Rotation class. + + A Rotation using a quaternion. + + The following constructors are supported: + + Rotation() + Empty constructor. + + Rotation(rotation) + Copy constructor. + + Rotation(Axis, Radian) + Rotation(Axis, Degree) + Define from an axis and an angle (in radians or degrees according to the keyword). + Axis : Base.Vector + Radian : float + Degree : float + + Rotation(vector_start, vector_end) + Define from two vectors (rotation from/to vector). + vector_start : Base.Vector + vector_end : Base.Vector + + Rotation(angle1, angle2, angle3) + Define from three floats (Euler angles) as yaw-pitch-roll in XY'Z'' convention. + angle1 : float + angle2 : float + angle3 : float + + Rotation(seq, angle1, angle2, angle3) + Define from one string and three floats (Euler angles) as Euler rotation + of a given type. Call toEulerAngles() for supported sequence types. + seq : str + angle1 : float + angle2 : float + angle3 : float + + Rotation(x, y, z, w) + Define from four floats (quaternion) where the quaternion is specified as: + q = xi+yj+zk+w, i.e. the last parameter is the real part. + x : float + y : float + z : float + w : float + + Rotation(dir1, dir2, dir3, seq) + Define from three vectors that define rotated axes directions plus an optional + 3-characher string of capital letters 'X', 'Y', 'Z' that sets the order of + importance of the axes (e.g., 'ZXY' means z direction is followed strictly, + x is used but corrected if necessary, y is ignored). + dir1 : Base.Vector + dir2 : Base.Vector + dir3 : Base.Vector + seq : str + + Rotation(matrix) + Define from a matrix rotation in the 4D representation. + matrix : Base.Matrix + + Rotation(*coef) + Define from 16 or 9 elements which represent the rotation in the 4D matrix + representation or in the 3D matrix representation, respectively. + coef : sequence of float + + Author: Juergen Riegel (FreeCAD@juergen-riegel.net) + Licence: LGPL + """ + + Q: Tuple[float, ...] = () + """The rotation elements (as quaternion).""" + + Axis: object = None + """The rotation axis of the quaternion.""" + + RawAxis: Final[object] = None + """The rotation axis without normalization.""" + + Angle: float = 0.0 + """The rotation angle of the quaternion.""" + + # TODO: Provide strongly-typed enum for `seq` + # fmt: off + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, rotation: "Rotation") -> None: ... + @overload + def __init__(self, axis: Vector, angle: float) -> None: ... + @overload + def __init__(self, vector_start: Vector, vector_end: Vector) -> None: ... + @overload + def __init__(self, angle1: float, angle2: float, angle3: float) -> None: ... + @overload + def __init__(self, seq: str, angle1: float, angle2: float, angle3: float) -> None: ... + @overload + def __init__(self, x: float, y: float, z: float, w: float) -> None: ... + @overload + def __init__(self, dir1: Vector, dir2: Vector, dir3: Vector, seq: str) -> None: ... + @overload + def __init__(self, matrix: Matrix) -> None: ... + @overload + def __init__(self, *coef: float) -> None: ... + # fmt: on + + def invert(self) -> None: + """ + invert() -> None + + Sets the rotation to its inverse. + """ + ... + + @constmethod + def inverted(self) -> "Rotation": + """ + inverted() -> Base.Rotation + + Returns the inverse of the rotation. + """ + ... + + def isSame(self, rotation: "Rotation", tol: float = 0) -> bool: + """ + isSame(rotation, tol=0) -> bool + + Checks if `rotation` perform the same transformation as this rotation. + + rotation : Base.Rotation + tol : float + Tolerance used to compare both rotations. + If tol is negative or zero, no tolerance is used. + """ + ... + + @constmethod + def multiply(self, rotation: "Rotation") -> "Rotation": + """ + multiply(rotation) -> Base.Rotation + + Right multiply this rotation with another rotation. + + rotation : Base.Rotation + Rotation by which to multiply this rotation. + """ + ... + + @constmethod + def multVec(self, vector: Vector) -> Vector: + """ + multVec(vector) -> Base.Vector + + Compute the transformed vector using the rotation. + + vector : Base.Vector + Vector to be transformed. + """ + ... + + @constmethod + def slerp(self, rotation2: "Rotation", t: float) -> "Rotation": + """ + slerp(rotation2, t) -> Base.Rotation + + Spherical Linear Interpolation (SLERP) of this rotation and `rotation2`. + + t : float + Parameter of the path. t=0 returns this rotation, t=1 returns `rotation2`. + """ + ... + + def setYawPitchRoll(self, angle1: float, angle2: float, angle3: float) -> None: + """ + setYawPitchRoll(angle1, angle2, angle3) -> None + + Set the Euler angles of this rotation as yaw-pitch-roll in XY'Z'' convention. + + angle1 : float + Angle around yaw axis in degrees. + angle2 : float + Angle around pitch axis in degrees. + angle3 : float + Angle around roll axis in degrees. + """ + ... + + @constmethod + def getYawPitchRoll(self) -> Tuple[float, float, float]: + """ + getYawPitchRoll() -> tuple + + Get the Euler angles of this rotation as yaw-pitch-roll in XY'Z'' convention. + The angles are given in degrees. + """ + ... + + def setEulerAngles(self, seq: str, angle1: float, angle2: float, angle3: float) -> None: + """ + setEulerAngles(seq, angle1, angle2, angle3) -> None + + Set the Euler angles in a given sequence for this rotation. + The angles must be given in degrees. + + seq : str + Euler sequence name. All possible values given by toEulerAngles(). + angle1 : float + angle2 : float + angle3 : float + """ + ... + + @constmethod + def toEulerAngles(self, seq: str = "") -> List[float]: + """ + toEulerAngles(seq) -> list + + Get the Euler angles in a given sequence for this rotation. + + seq : str + Euler sequence name. If not given, the function returns + all possible values of `seq`. Optional. + """ + ... + + @constmethod + def toMatrix(self) -> Matrix: + """ + toMatrix() -> Base.Matrix + + Convert the rotation to a 4D matrix representation. + """ + ... + + @constmethod + def isNull(self) -> bool: + """ + isNull() -> bool + + Returns True if all values in the quaternion representation are zero. + """ + ... + + @constmethod + def isIdentity(self, tol: float = 0) -> bool: + """ + isIdentity(tol=0) -> bool + + Returns True if the rotation equals the 4D identity matrix. + tol : float + Tolerance used to check for identity. + If tol is negative or zero, no tolerance is used. + """ + ... diff --git a/src/Base/Type.pyi b/src/Base/Type.pyi new file mode 100644 index 0000000000..e8a550e59a --- /dev/null +++ b/src/Base/Type.pyi @@ -0,0 +1,143 @@ +from Metadata import export, forward_declarations, constmethod +from PyObjectBase import PyObjectBase +from typing import List, Final + +@export( + Twin="BaseType", + TwinPointer="BaseType", + Delete=True, +) +@forward_declarations( + """ +namespace Base { + using BaseType = Type; +}""" +) +class Type(PyObjectBase): + """ + BaseTypePy class. + + This class provides functionality related to type management in the Base module. It's not intended for direct instantiation but for accessing type information and creating instances of various types. Instantiation is possible for classes that inherit from the Base::BaseClass class and are not abstract. + + Author: Juergen Riegel (FreeCAD@juergen-riegel.net) + Licence: LGPL + """ + + Name: Final[str] = "" + """The name of the type id.""" + + Key: Final[int] = 0 + """The key of the type id.""" + + Module: Final[str] = "" + """Module in which this class is defined.""" + + @staticmethod + def fromName(name: str) -> "Type": + """ + fromName(name) -> Base.BaseType + + Returns a type object by name. + + name : str + """ + ... + + @staticmethod + def fromKey(key: int) -> "Type": + """ + fromKey(key) -> Base.BaseType + + Returns a type id object by key. + + key : int + """ + ... + + @staticmethod + def getNumTypes() -> int: + """ + getNumTypes() -> int + + Returns the number of type ids created so far. + """ + ... + + @staticmethod + def getBadType() -> "Type": + """ + getBadType() -> Base.BaseType + + Returns an invalid type id. + """ + ... + + @staticmethod + def getAllDerivedFrom(type: str) -> List[str]: + """ + getAllDerivedFrom(type) -> list + + Returns all descendants from the given type id. + + type : str, Base.BaseType + """ + ... + + @constmethod + def getParent(self) -> "Type": + """ + getParent() -> Base.BaseType + + Returns the parent type id. + """ + ... + + @constmethod + def isBad(self) -> bool: + """ + isBad() -> bool + + Checks if the type id is invalid. + """ + ... + + @constmethod + def isDerivedFrom(self, type: str) -> bool: + """ + isDerivedFrom(type) -> bool + + Returns true if given type id is a father of this type id. + + type : str, Base.BaseType + """ + ... + + @constmethod + def getAllDerived(self) -> List[object]: + """ + getAllDerived() -> list + + Returns all descendants from this type id. + """ + ... + + def createInstance(self) -> object: + """ + createInstance() -> object + + Creates an instance of this type id. + """ + ... + + @staticmethod + def createInstanceByName(name: str, load: bool = False) -> object: + """ + createInstanceByName(name, load=False) -> object + + Creates an instance of the named type id. + + name : str + load : bool + Load named type id module. + """ + ... diff --git a/src/Base/Unit.pyi b/src/Base/Unit.pyi new file mode 100644 index 0000000000..7f5426394a --- /dev/null +++ b/src/Base/Unit.pyi @@ -0,0 +1,54 @@ +from Metadata import export +from PyObjectBase import PyObjectBase +from Quantity import Quantity +from Unit import Unit +from typing import Final, Tuple, overload + +@export( + NumberProtocol=True, + RichCompare=True, + Constructor=True, + Delete=True, +) +class Unit(PyObjectBase): + """ + Unit + defines a unit type, calculate and compare. + + The following constructors are supported: + Unit() -- empty constructor + Unit(i1,i2,i3,i4,i5,i6,i7,i8) -- unit signature + Unit(Quantity) -- copy unit from Quantity + Unit(Unit) -- copy constructor + Unit(string) -- parse the string for units + + Author: Juergen Riegel (FreeCAD@juergen-riegel.net) + Licence: LGPL + """ + + @overload + def __init__(self) -> None: ... + @overload + def __init__( + self, + i1: float, + i2: float, + i3: float, + i4: float, + i5: float, + i6: float, + i7: float, + i8: float, + ) -> None: ... + @overload + def __init__(self, quantity: Quantity) -> None: ... + @overload + def __init__(self, unit: Unit) -> None: ... + @overload + def __init__(self, string: str) -> None: ... + + Type: Final[str] = ... + """holds the unit type as a string, e.g. 'Area'.""" + + Signature: Final[Tuple] = ... + """Returns the signature.""" diff --git a/src/Base/Vector.pyi b/src/Base/Vector.pyi new file mode 100644 index 0000000000..37ebf0416a --- /dev/null +++ b/src/Base/Vector.pyi @@ -0,0 +1,320 @@ +from Metadata import export, constmethod, sequence_protocol, class_declarations +from PyObjectBase import PyObjectBase +from typing import overload, Sequence, TYPE_CHECKING + +@export( + TwinPointer="Vector3d", + Include="Base/Vector3D.h", + Constructor=True, + Delete=True, + NumberProtocol=True, + RichCompare=True, +) +@sequence_protocol( + sq_length=True, + sq_concat=False, + sq_repeat=False, + sq_item=True, + mp_subscript=True, + sq_ass_item=True, + mp_ass_subscript=False, + sq_contains=False, + sq_inplace_concat=False, + sq_inplace_repeat=False, +) +@class_declarations( + """public: + VectorPy(const Vector3d & vec, PyTypeObject *T = &Type) + :PyObjectBase(new Vector3d(vec),T){} + VectorPy(const Vector3f & vec, PyTypeObject *T = &Type) + :PyObjectBase(new Vector3d(vec.x,vec.y,vec.z),T){} + Vector3d value() const + { return *(getVectorPtr()); } +private: + Py::List sequence; + """ +) +class Vector(PyObjectBase): + """ + Base.Vector class. + + This class represents a 3D float vector. + Useful to represent points in the 3D space. + + The following constructors are supported: + + Vector(x=0, y=0, z=0) + x : float + y : float + z : float + + Vector(vector) + Copy constructor. + vector : Base.Vector + + Vector(seq) + Define from a sequence of float. + seq : sequence of float. + + Author: Juergen Riegel (FreeCAD@juergen-riegel.net) + Licence: LGPL + """ + + Length: float = 0.0 + """Gets or sets the length of this vector.""" + + x: float = 0.0 + """Gets or sets the X component of this vector.""" + + y: float = 0.0 + """Gets or sets the Y component of this vector.""" + + z: float = 0.0 + """Gets or sets the Z component of this vector.""" + + # fmt: off + @overload + def __init__(self, *, x: float = 0, y: float = 0, z: float = 0) -> None: ... + @overload + def __init__(self, vector: "Vector") -> None: ... + @overload + def __init__(self, seq: Sequence[float]) -> None: ... + # fmt: on + + @constmethod + def __reduce__(self) -> tuple: + """ + __reduce__() -> tuple + + Serialization of Vector objects. + """ + ... + + @constmethod + def add(self, vector2: "Vector") -> "Vector": + """ + add(vector2) -> Base.Vector + + Returns the sum of this vector and `vector2`. + + vector2 : Base.Vector + """ + ... + + @constmethod + def sub(self, vector2: "Vector") -> "Vector": + """ + sub(vector2) -> Base.Vector + + Returns the difference of this vector and `vector2`. + + vector2 : Base.Vector + """ + ... + + @constmethod + def negative(self) -> "Vector": + """ + negative() -> Base.Vector + + Returns the negative (opposite) of this vector. + """ + ... + + def scale(self, x: float, y: float, z: float) -> "Vector": + """ + scale(x, y, z) -> Base.Vector + + Scales in-place this vector by the given factor in each component. + + x : float + x-component factor scale. + y : float + y-component factor scale. + z : float + z-component factor scale. + """ + ... + + def multiply(self, factor: float) -> "Vector": + """ + multiply(factor) -> Base.Vector + + Multiplies in-place each component of this vector by a single factor. + Equivalent to scale(factor, factor, factor). + + factor : float + """ + ... + + @constmethod + def dot(self, vector2: "Vector") -> float: + """ + dot(vector2) -> float + + Returns the scalar product (dot product) between this vector and `vector2`. + + vector2 : Base.Vector + """ + ... + + @constmethod + def cross(self, vector2: "Vector") -> "Vector": + """ + cross(vector2) -> Base.Vector + + Returns the vector product (cross product) between this vector and `vector2`. + + vector2 : Base.Vector + """ + ... + + @constmethod + def isOnLineSegment(self, vector1: "Vector", vector2: "Vector") -> bool: + """ + isOnLineSegment(vector1, vector2) -> bool + + Checks if this vector is on the line segment generated by `vector1` and `vector2`. + + vector1 : Base.Vector + vector2 : Base.Vector + """ + ... + + @constmethod + def getAngle(self, vector2: "Vector") -> float: + """ + getAngle(vector2) -> float + + Returns the angle in radians between this vector and `vector2`. + + vector2 : Base.Vector + """ + ... + + def normalize(self) -> "Vector": + """ + normalize() -> Base.Vector + + Normalizes in-place this vector to the length of 1.0. + """ + ... + + @constmethod + def isEqual(self, vector2: "Vector", tol: float = 0) -> bool: + """ + isEqual(vector2, tol=0) -> bool + + Checks if the distance between the points represented by this vector + and `vector2` is less or equal to the given tolerance. + + vector2 : Base.Vector + tol : float + """ + ... + + @constmethod + def isParallel(self, vector2: "Vector", tol: float = 0) -> bool: + """ + isParallel(vector2, tol=0) -> bool + + Checks if this vector and `vector2` are + parallel less or equal to the given tolerance. + + vector2 : Base.Vector + tol : float + """ + ... + + @constmethod + def isNormal(self, vector2: "Vector", tol: float = 0) -> bool: + """ + isNormal(vector2, tol=0) -> bool + + Checks if this vector and `vector2` are + normal less or equal to the given tolerance. + + vector2 : Base.Vector + tol : float + """ + ... + + def projectToLine(self, point: "Vector", dir: "Vector") -> "Vector": + """ + projectToLine(point, dir) -> Base.Vector + + Projects `point` on a line that goes through the origin with the direction `dir`. + The result is the vector from `point` to the projected point. + The operation is equivalent to dir_n.cross(dir_n.cross(point)), where `dir_n` is + the vector `dir` normalized. + The method modifies this vector instance according to result and does not + depend on the vector itself. + + point : Base.Vector + dir : Base.Vector + """ + ... + + def projectToPlane(self, base: "Vector", normal: "Vector") -> "Vector": + """ + projectToPlane(base, normal) -> Base.Vector + + Projects in-place this vector on a plane defined by a base point + represented by `base` and a normal defined by `normal`. + + base : Base.Vector + normal : Base.Vector + """ + ... + + @constmethod + def distanceToPoint(self, point2: "Vector") -> float: + """ + distanceToPoint(point2) -> float + + Returns the distance to another point represented by `point2`. + . + point : Base.Vector + """ + ... + + @constmethod + def distanceToLine(self, base: "Vector", dir: "Vector") -> float: + """ + distanceToLine(base, dir) -> float + + Returns the distance between the point represented by this vector + and a line defined by a base point represented by `base` and a + direction `dir`. + + base : Base.Vector + dir : Base.Vector + """ + ... + + @constmethod + def distanceToLineSegment(self, point1: "Vector", point2: "Vector") -> "Vector": + """ + distanceToLineSegment(point1, point2) -> Base.Vector + + Returns the vector between the point represented by this vector and the point + on the line segment with the shortest distance. The line segment is defined by + `point1` and `point2`. + + point1 : Base.Vector + point2 : Base.Vector + """ + ... + + @constmethod + def distanceToPlane(self, base: "Vector", normal: "Vector") -> float: + """ + distanceToPlane(base, normal) -> float + + Returns the distance between this vector and a plane defined by a + base point represented by `base` and a normal defined by `normal`. + + base : Base.Vector + normal : Base.Vector + """ + ... diff --git a/src/Tools/bindings/generate.py b/src/Tools/bindings/generate.py index b399b17a0c..62c6270ed7 100644 --- a/src/Tools/bindings/generate.py +++ b/src/Tools/bindings/generate.py @@ -57,7 +57,7 @@ def generate(filename, outputPath): Export.outputDir = outputPath + "/" Export.inputDir = os.path.dirname(filename) + "/" Export.export = GenerateModelInst.PythonExport[0] - Export.is_python = filename.endswith(".py") + Export.is_python = filename.endswith(".pyi") Export.Generate() if Export.is_python: Export.Compare() diff --git a/src/Tools/bindings/model/generateModel_Python.py b/src/Tools/bindings/model/generateModel_Python.py index 5ebcb9d088..de97c1015a 100644 --- a/src/Tools/bindings/model/generateModel_Python.py +++ b/src/Tools/bindings/model/generateModel_Python.py @@ -210,10 +210,17 @@ def _parse_methods(class_node: ast.ClassDef) -> List[Methode]: """ methods = [] - for stmt in class_node.body: - if not isinstance(stmt, ast.FunctionDef): - continue + def collect_function_defs(nodes): + funcs = [] + for node in nodes: + if isinstance(node, ast.FunctionDef): + funcs.append(node) + elif isinstance(node, ast.If): + funcs.extend(collect_function_defs(node.body)) + funcs.extend(collect_function_defs(node.orelse)) + return funcs + for stmt in collect_function_defs(class_node.body): # Skip methods decorated with @overload skip_method = False for deco in stmt.decorator_list: @@ -542,28 +549,42 @@ def _parse_class(class_node, source_code: str, path: str, imports_mapping: dict) def parse_python_code(path: str) -> GenerateModel: """ Parse the given Python source code and build a GenerateModel containing - PythonExport entries for each class that inherits from a relevant binding class. + PythonExport entries. If any class is explicitly exported using @export, + only those classes are used. If no classes have the @export decorator, + then a single non-exported class is assumed to be the export. If there + are multiple non-exported classes, an exception is raised. """ - - source_code = None with open(path, "r") as file: source_code = file.read() tree = ast.parse(source_code) imports_mapping = _parse_imports(tree) - model = GenerateModel() + + explicit_exports = [] + non_explicit_exports = [] for node in tree.body: if isinstance(node, ast.ClassDef): py_export = _parse_class(node, source_code, path, imports_mapping) - model.PythonExport.append(py_export) + if py_export.IsExplicitlyExported: + explicit_exports.append(py_export) + else: + non_explicit_exports.append(py_export) - # Check for multiple non explicitly exported classes - non_exported_classes = [ - item for item in model.PythonExport if not getattr(item, "IsExplicitlyExported", False) - ] - if len(non_exported_classes) > 1: - raise Exception("Multiple non explicitly-exported classes were found, please use @export.") + model = GenerateModel() + if explicit_exports: + # Use only explicitly exported classes. + model.PythonExport.extend(explicit_exports) + else: + # No explicit exports; allow only one non-exported class. + if len(non_explicit_exports) == 1: + model.PythonExport.append(non_explicit_exports[0]) + elif len(non_explicit_exports) > 1: + raise Exception( + "Multiple non explicitly-exported classes were found, please use @export." + ) + else: + raise Exception("No classes found for export.") return model diff --git a/src/Tools/bindings/templates/templateClassPyExport.py b/src/Tools/bindings/templates/templateClassPyExport.py index 7fc9298fdf..1fa4582caa 100644 --- a/src/Tools/bindings/templates/templateClassPyExport.py +++ b/src/Tools/bindings/templates/templateClassPyExport.py @@ -15,7 +15,7 @@ def compareFiles(file1, file2): # Check if files exist for file in (file1, file2): if not os.path.exists(file): - raise FileNotFoundError(f"File not found: {file1} {file2}") + raise FileNotFoundError(f"File not found: {file}") # Read file contents with open(file1, "r", encoding="utf-8") as f1, open(file2, "r", encoding="utf-8") as f2: