``pooltool.objects`` ==================== .. py:module:: pooltool.objects Simulation object classes. The three main simulation objects are :class:`pooltool.objects.Ball`, :class:`pooltool.objects.Cue`, and :class:`pooltool.objects.Table`, however there are many more objects that either help create the primary objects or comprise the primary objects. Those are all kept in this module. Classes ------- .. autoclass:: Ball .. py:property:: xyz The displacement (from origin) vector of the ball. A shortcut for ``self.state.rvw[0]``. .. py:property:: vel The velocity vector of the ball. A shortcut for ``self.state.rvw[1]``. .. py:property:: avel The angular velocity vector of the ball. A shortcut for ``self.state.rvw[2]``. .. rubric:: Methods: .. py:method:: set_ballset(ballset: pooltool.objects.ball.sets.BallSet) -> None Update the ballset :raises ValueError: If the ball ID doesn't match to a model name of the ballset. .. seealso:: - See :class:`pooltool.objects.BallSet` for details about ball sets. - See :meth:`pooltool.system.System.set_ballset` for setting the ballset for all the balls in a system. .. py:method:: copy(drop_history: bool = False) -> Ball Create a copy :param drop_history: If True, the returned copy :attr:`history` and :attr:`history_cts` attributes are both set to empty :class:`pooltool.objects.BallHistory` objects. .. py:method:: create(id: str, *, xy: collections.abc.Sequence[float] | None = None, ballset: pooltool.objects.ball.sets.BallSet | None = None, **kwargs) -> Ball :staticmethod: Create a ball using keyword arguments. This constructor flattens the tunable parameter space, allowing one to construct a ``Ball`` without directly instancing objects like like :class:`pooltool.objects.BallParams` and :class:`pooltool.objects.BallState`. :param xy: The x and y coordinates of the ball position. :param ballset: A ballset. :param \*\*kwargs: Arguments accepted by :class:`pooltool.objects.BallParams` .. py:method:: dummy(id: str = 'dummy') -> Ball :staticmethod: .. autoclass:: BallHistory .. py:property:: empty :type: bool Returns whether or not the ball history is empty :returns: True if :attr:`states` has no length else False :rtype: bool .. rubric:: Methods: .. py:method:: add(state: BallState) -> None Append a state to the history :raises AssertionError: If ``state.t < self.states[-1]`` .. admonition:: Notes - This appends ``state`` to :attr:`states` - ``state`` is not copied before appending to the history, so they share the same memory address. .. py:method:: copy() -> BallHistory Create a copy .. py:method:: vectorize() -> tuple[numpy.typing.NDArray[numpy.float64], numpy.typing.NDArray[numpy.float64], numpy.typing.NDArray[numpy.float64]] Compile the attribute from each ball state into arrays This method unzips each :class:`pooltool.objects.BallState` in :attr:`states`, resulting in an array of :attr:`pooltool.objects.BallState.rvw` values, an array of :attr:`pooltool.objects.BallState.s` values, and an array of :attr:`pooltool.objects.BallState.t` values. The vectors have the following properties: >>> import pooltool as pt >>> history = pt.simulate(pt.System.example(), continuous=True).balls["cue"].history_cts >>> rvws, ss, ts = history.vectorize() >>> # Their lengths are equal to the BallHistory >>> len(rvws) == len(ss) == len(ts) == len(history) True >>> # The indices of the arrays match the values of the history >>> pt.objects.BallState(rvws[26], ss[26], ts[26]) == history[26] True :returns: A length 3 tuple (``rvws``, ``ss`` and ``ts``). :raises ValueError: If the history is empty. .. admonition:: Example ``vectorize`` can be useful for plotting trajectories. .. code:: python import pooltool as pt import matplotlib.pyplot as plt system = pt.System.example() pt.simulate(system, continuous=True, inplace=True) for ball in system.balls.values(): rvw, ss, ts = ball.history_cts.vectorize() plt.plot(rvw[:, 0, 0], rvw[:, 0, 1], color=ss) plt.show() .. seealso:: - :meth:`from_vectorization` .. py:method:: from_vectorization(vectorization: tuple[numpy.typing.NDArray[numpy.float64], numpy.typing.NDArray[numpy.float64], numpy.typing.NDArray[numpy.float64]] | None) -> BallHistory :staticmethod: Zips a vectorization into a BallHistory An inverse method of :meth:`vectorize`. :returns: A BallHistory constructed from the input vectors. :rtype: BallHistory .. admonition:: Example This illustrates a round-trip with :meth:`vectorize` and :meth:`from_vectorization`. First create history >>> import pooltool as pt >>> history = pt.simulate(pt.System.example(), continuous=True).balls["cue"].history_cts Illustrate a lossless round trip: >>> pt.objects.BallHistory.from_vectorization(history.vectorize()) == history True .. seealso:: - :meth:`vectorize` .. py:method:: factory() -> BallHistory :staticmethod: .. autoclass:: BallOrientation .. rubric:: Methods: .. py:method:: random() -> BallOrientation :staticmethod: Generate a random BallOrientation This generates a ball orientation from a uniform sampling of possible orientations. :returns: A randomized ball orientation. :rtype: BallOrientation .. py:method:: copy() -> BallOrientation Create a copy .. note:: - Since the class is frozen and its attributes are immutable, this just returns ``self``. .. autoclass:: BallState .. rubric:: Methods: .. py:method:: copy() -> BallState Create a copy .. py:method:: default() -> BallState :staticmethod: Construct a default BallState :returns: A valid yet undercooked state. >>> import pooltool as pt >>> pt.objects.BallState.default() BallState(rvw=array([[nan, nan, nan], [ 0., 0., 0.], [ 0., 0., 0.]]), s=0, t=0.0) :rtype: BallState .. autoclass:: BallParams .. py:property:: u_sp :type: float Coefficient of spinning friction. This is equal to :attr:`u_sp_proportionality` * :attr:`R`. .. rubric:: Methods: .. py:method:: copy() -> BallParams Return a copy .. note:: - Since the class is frozen and its attributes are immutable, this just returns ``self``. .. py:method:: default(game_type: pooltool.game.datatypes.GameType = GameType.EIGHTBALL) -> BallParams :classmethod: Return prebuilt ball parameters based on game type :param game_type: What type of game is being played? :returns: The prebuilt ball parameters associated with the passed game type. :rtype: BallParams .. py:method:: prebuilt(name: PrebuiltBallParams) -> BallParams :classmethod: Return prebuilt ball parameters based on name :param name: A :class:`pooltool.objects.PrebuiltBallParams` member. All prebuilt ball parameters are are members of the :class:`pooltool.objects.PrebuiltBallParams` Enum. This constructor takes a prebuilt name and returns the corresponding ball parameters. .. seealso:: - :class:`pooltool.objects.PrebuiltBallParams` .. autoclass:: PrebuiltBallParams Bases: :py:obj:`pooltool.utils.strenum.StrEnum` .. autoclass:: BallSet .. py:property:: path :type: pathlib.Path The path of the ballset directory This directory holds the ball models. .. py:property:: ids :type: list[str] .. rubric:: Methods: .. py:method:: ball_path(id: str) -> pathlib.Path The model path used for a given ball ID :param id: The ball ID. :raises ValueError: If Ball ID doesn't match to the ballset. :returns: The model path. :rtype: Path .. autoclass:: Cue .. rubric:: Methods: .. py:method:: copy() -> Cue Create a copy .. note:: :attr:`specs` is shared between ``self`` and the copy, but that's ok because it's frozen and has no mutable attributes. .. py:method:: reset_state() -> None Resets :attr:`V0`, :attr:`phi`, :attr:`theta`, :attr:`a` and :attr:`b` to their defaults. .. py:method:: set_state(V0: float | None = None, phi: float | None = None, theta: float | None = None, a: float | None = None, b: float | None = None, cue_ball_id: str | None = None) -> None Set the cueing parameters :param V0: See :attr:`V0` :param phi: See :attr:`phi` :param theta: See :attr:`theta` :param a: See :attr:`a` :param b: See :attr:`b` :param cue_ball_id: See :attr:`cue_ball_id` If any arguments are ``None``, they will be left untouched--they will not be set to None. .. py:method:: from_game_type(game_type: pooltool.game.datatypes.GameType, id: str | None = None) -> Cue :classmethod: .. py:method:: default() -> Cue :classmethod: Construct a cue with defaults .. autoclass:: CueSpecs .. rubric:: Methods: .. py:method:: default(game_type: pooltool.game.datatypes.GameType = GameType.EIGHTBALL) -> CueSpecs :classmethod: Return prebuilt cue specs based on game type. :param game_type: What type of game is being played? :returns: The prebuilt cue specs associated with the passed game type. .. py:method:: prebuilt(name: PrebuiltCueSpecs) -> CueSpecs :classmethod: Return prebuilt cue specs based on name. :param name: A :class:`PrebuiltCueSpecs` member. .. autoclass:: PrebuiltCueSpecs Bases: :py:obj:`pooltool.utils.strenum.StrEnum` .. autoclass:: TableName Bases: :py:obj:`pooltool.utils.strenum.StrEnum` .. autoclass:: CircularCushionSegment .. py:property:: height :type: float The height of the cushion. .. py:property:: a :type: float The x-coordinate of the cushion's center. .. py:property:: b :type: float The y-coordinate of the cushion's center. .. rubric:: Methods: .. py:method:: get_normal_xy(xyz: numpy.typing.NDArray[numpy.float64]) -> numpy.typing.NDArray[numpy.float64] Calculates the normal vector for a ball contacting the cushion Assumes that the ball is in fact in contact with the cushion. :param xyz: The position of the ball. (See ``xyz`` property of class `pooltool.objects.BallState`). :returns: The normal vector, with the z-component zeroed prior to normalization. :rtype: NDArray[np.float64] .. py:method:: get_normal_3d(xyz: numpy.typing.NDArray[numpy.float64]) -> numpy.typing.NDArray[numpy.float64] Calculates the 3D normal vector for a point contacting the cushion. :param xyz: The 3D coordinate of the contacting point. :returns: The 3D normal vector pointing outward from the cushion surface. :rtype: NDArray[np.float64] .. py:method:: copy() -> CircularCushionSegment Create a copy .. py:method:: dummy() -> CircularCushionSegment :staticmethod: .. autoclass:: CushionDirection .. autoclass:: CushionSegments .. rubric:: Methods: .. py:method:: copy() -> CushionSegments Create a copy .. autoclass:: LinearCushionSegment .. py:property:: height :type: float The height of the cushion. .. py:property:: lx :type: float The x-coefficient (:math:`l_x`) of the cushion's 2D general form line equation. The cushion's general form line equation in the :math:`XY` plane (*i.e.* dismissing the z-component) is .. math:: l_x x + l_y y + l_0 = 0 where .. math:: \begin{align*} l_x &= -\frac{p_{2y} - p_{1y}}{p_{2x} - p_{1x}} \\ l_y &= 1 \\ l_0 &= \frac{p_{2y} - p_{1y}}{p_{2x} - p_{1x}} p_{1x} - p_{1y} \\ \end{align*} .. py:property:: ly :type: float The x-coefficient (:math:`l_y`) of the cushion's 2D general form line equation. See :meth:`lx` for definition. .. py:property:: l0 :type: float The constant term (:math:`l_0`) of the cushion's 2D general form line equation. See :meth:`lx` for definition. .. py:property:: unit_axis :type: numpy.typing.NDArray[numpy.float64] The unit vector :math:`\frac{p_2 - p_1}{\|p_2 - p_1\|}`. .. py:property:: normal :type: numpy.typing.NDArray[numpy.float64] The line's normal vector, with the z-component zeroed prior to normalization. .. warning:: The returned normal vector is arbitrarily directed, meaning it may point away from the table surface, rather than towards it. This nonideality is properly handled in downstream simulation logic, however if you're using this method for custom purposes, you may want to reverse the direction of this vector by negating it. .. rubric:: Methods: .. py:method:: get_normal_xy(xyz: numpy.typing.NDArray[numpy.float64]) -> numpy.typing.NDArray[numpy.float64] Calculates the normal vector for a ball contacting the cushion. .. warning:: The returned normal vector is arbitrarily directed, meaning it may point away from the table surface, rather than towards it. This nonideality is properly handled in downstream simulation logic, however if you're using this method for custom purposes, you may want to reverse the direction of this vector by negating it. :param xyz: The position of the ball. See ``xyz`` property of :class:`pooltool.objects.BallState`. :returns: The line's normal vector, with the z-component zeroed prior to normalization. :rtype: NDArray[np.float64] .. note:: - This method only exists for call signature parity with :meth:`pooltool.objects.CircularCushionSegment.get_normal_xy`. Consider using :meth:`normal` instead. .. py:method:: get_normal_3d(xyz: numpy.typing.NDArray[numpy.float64]) -> numpy.typing.NDArray[numpy.float64] Calculates the 3D normal vector for a point contacting the cushion. This method computes the normal by finding the component of the vector from :attr:`p1` to the contact point that is perpendicular to the cushion's :attr:`unit_axis`. Mathematically, this is achieved by subtracting the projection of the position vector onto the cushion's axis from the position vector itself, yielding the perpendicular component which defines the normal direction. .. warning:: The returned normal vector is arbitrarily directed, meaning it may point away from the table surface, rather than towards it. This nonideality is properly handled in downstream simulation logic, however if you're using this method for custom purposes, you may want to reverse the direction of this vector by negating it. :param xyz: The 3D coordinate of the contacting point. :returns: The 3D normal vector pointing outward from the cushion surface. :rtype: NDArray[np.float64] .. py:method:: copy() -> LinearCushionSegment Create a copy .. py:method:: dummy() -> LinearCushionSegment :staticmethod: .. autoclass:: Pocket .. py:property:: a :type: float The x-coordinate of the pocket's center. .. py:property:: b :type: float The y-coordinate of the pocket's center. .. rubric:: Methods: .. py:method:: add(ball_id: str) -> None Add a ball ID to :attr:`contains` .. py:method:: remove(ball_id: str) -> None Remove a ball ID from :attr:`contains` .. py:method:: copy() -> Pocket Create a copy .. py:method:: dummy() -> Pocket :staticmethod: .. autoclass:: Table .. py:property:: w :type: float The width of the table. .. warning:: This assumes the table follows the layout similar to `this diagram `_. Specifically, it must have the linear cushion segments with IDs ``"3"``` and ``"12"``. .. py:property:: l :type: float The length of the table. .. warning:: This assumes the table follows the layout similar to `this diagram `_. Specifically, it must have the linear cushion segments with IDs ``"9"``` and ``"18"``. .. py:property:: center :type: tuple[float, float] Return the 2D coordinates of the table's center .. warning:: This assumes :meth:`l` and :meth:`w` are defined. .. py:property:: has_linear_cushions :type: bool .. py:property:: has_circular_cushions :type: bool .. py:property:: has_pockets :type: bool .. rubric:: Methods: .. py:method:: set_cushion_height(height: float) -> None Set the height of all cushion segments. :param height: The new height to set for all cushion segments. .. py:method:: copy() -> Table Create a copy. .. py:method:: from_table_specs(specs: pooltool.objects.table.specs.TableSpecs) -> Table :staticmethod: Build a table from a table specifications object :param specs: A valid table specification. Accepted objects: - :class:`pooltool.objects.PocketTableSpecs` - :class:`pooltool.objects.BilliardTableSpecs` - :class:`pooltool.objects.SnookerTableSpecs` :returns: A table matching the specifications of the input. - :class:`pooltool.objects.PocketTableSpecs` has :attr:`table_type` set to `pooltool.objects.TableType.POCKET` - :class:`pooltool.objects.BilliardTableSpecs` has :attr:`table_type` set to `pooltool.objects.TableType.BILLIARD` - :class:`pooltool.objects.SnookerTableSpecs` has :attr:`table_type` set to `pooltool.objects.TableType.SNOOKER` :rtype: Table .. py:method:: prebuilt(name: pooltool.objects.table.collection.TableName) -> Table :classmethod: Create a default table based on name :param name: The name of the prebuilt table specs. :returns: A prebuilt table. :rtype: Table .. py:method:: default(table_type: pooltool.objects.table.specs.TableType = TableType.POCKET) -> Table :classmethod: Create a default table based on table type A default table is associated to each table type. :param table_type: The type of table. :returns: The default table for the given table type. :rtype: Table .. py:method:: from_game_type(game_type: pooltool.game.datatypes.GameType) -> Table :classmethod: Create a default table based on table type A default table is associated with each game type. :param game_type: The game type. :returns: The default table for the given game type. :rtype: Table .. autoclass:: BilliardTableSpecs .. autoclass:: PocketTableSpecs .. autoclass:: SnookerTableSpecs .. autoclass:: TableModelDescr .. rubric:: Methods: .. py:method:: get_path(use_pbr: bool = False) -> str Get the path of the model based on PBR preference The path is searched for in ``pooltool/models/table/{name}/{name}[_pbr].glb``. If the PBR variant is requested but doesn't exist, falls back to the non-PBR variant. :param use_pbr: Whether to use physical based rendering variant :raises ConfigError: If model path cannot be found from name. :returns: A filename specified with Panda3D filename syntax (see https://docs.panda3d.org/1.10/python/programming/advanced-loading/filename-syntax). :rtype: str .. py:method:: null() -> TableModelDescr :staticmethod: .. autoclass:: TableType Bases: :py:obj:`pooltool.utils.strenum.StrEnum` Functions --------- .. py:function:: get_ballset(name: str) -> BallSet Return the ballset with the given name. :param name: The name of the ballset. To list available ballset names, call :func:`pooltool.objects.get_ballset_names`. :raises ValueError: If Ball ID doesn't match to the ballset. :returns: A ballset. :rtype: BallSet .. py:function:: get_ballset_names() -> list[str] Returns a list of available ballset names