My Github Github Mark White
My Discord Discord Mark White
My Steam Steam Mark White
My Website Website Mark White
PyPhyEngine – Welcome

Welcome to this documentation! — In this documentation you will learn how to use PyPhyEngine, what my workflow looks like and what my goals for the engine are.

First, we will talk about how the project started and what it was originally meant to be. Then, we will go over to how it's going today. That will answer questions like:

  • What are you currently working on?
  • What are your projects for the future?
  • Then, we walk over to the "getting started" section. This is where the coding begins. You'll find code snippets and other helpful resources here along with the API Reference section.

    You will find out how you can create PhysicsObjects, enable collision between them, record videos in the program and more.

    PyPhyEngine – How it started

    Please finish this...

    PyPhyEngine – How it's going

    Currently I am working on an editor to assist you with creating PhysicsRooms, PhysicsObjects and StaticObjects all within a graphical editor.

    The editor would give you a script that you can then insert into any editor and if you run the script it'll work. It was originally meant to assist the user in creating StaticObjects without needing to write down each individual vector, but I then decided on just creating an entire editor for everything.

    In the future I'd like to create a 3D physics engine within the limits of MPL.

    PyPhyEngine – Getting Started

    A normal program utilizing PyPhyEngine usually consists of the following things:

  • PhysicsObjects and/or StaticObjects
  • An ObjectCollection
  • A PhysicsRoom
  • And optionally:

  • A videoRecorder
  • A Collider
  • A PhysicsObject (PO) is an object, which moves and is affected by things like gravity or collision with other objects.

    A PO consists of an x-position and y-position, which is just the position on the axis where the PO lays on. A PO also has a size which declares, well, the size of the PO. The parameter "grav" indicates gravity and stands for how much gravity is being applied to the PO. The fps or frames per second is the amount of frames that are rendered each second for that object. Bounce stands for how much of the kinetic energy, which the PO has when it hits the ground, will be reversed so the ball bounces on the floor. Bounce is an integer or float between 0 and 1. Friction manipulates the speed of the PO when it's on the floor. Friction is an integer or float between 0 and 1, which declares how much of the velocity on the x-axis will be lost. The general formula for the calculation which will be executed each frame, where the PO is on the floor, is

    x_velocity = x_velocity / (friction + 1)

    velocity_x and velocity_y indicate how much the PO will move on either the x- or y-axis. Both variables can be integers or floats. border_x and border_y are a list of integers which declare the barriers where the PO will be.

    For example, if border_x = [5, 25] the PO will only move between these two values. Any position outside of that range will be interpreted as a wall. The same applies to border_y. If border_y = [0, 50] any position on the axis when y <= 0 will act as the floor and any position on the axis when y >= 50 will act as the ceiling of a room.

    The color string will define the color of the PO, very simple. The color palette is based on the named colors of MPL. show_trajectory is a boolean which decides whether or not the trajectory of the PO will be shown. Finally, last but not least, max_trajectory_iterations declares how many points of the trajectory will be saved before it starts deleting the oldest point. Essentially, the higher the number, the longer the trajectory.

    If you want to create a PhysicsObject, you could do this:

    
    ball = PhysicsObject(
        x = 5,
        y = 35,
        size = 100,
        grav = 1,
        fps = 30,
        bounce = 0.8,
        friction = 0.2,
        velocity_x = 1,
        velocity_y = 0.5,
        border_x = [0, 20],
        border_y = [0, 50],
        color = "Blue",
        show_trajectory = True,
        max_trajectory_iterations = 50
    )
                    
                

    This initiates a PhysicsObject object which later can be used to be implemented into the simulation

    Next are StaticObjects (SO), which won't move at all. Not the gravity or the collision with other objects moves the SO. StaticObjects consist of a list of vectors, called matrix, color and fill_color. matrix is a list of vectors which define the points and lines of the StaticObject. The first list consists of x-positions and the second list of y-positions. The color defines the color, which is based on the named colors of MPL, of the lines and points of the StaticObject. Lastly, the fill_color is the color, which will be used as the filling.

    To initiate a StaticObject you can do one of the following

                    
    # this method uses a list of vectors
    
    box1 = StaticObject(
        [
            [0, 5, 5, 5, 5, 0, 0, 0], # list of x-positions
            [20, 20, 20, 25, 25, 25, 20, 20] # list of y-positions
        ],
        "black",
        "black"
    )
                    
                
                    
    # this method uses a function to generate a list of vectors for you
    
    rectangle = StaticObject(
        utils.generate_rectangle(2, 8, 6, 12), # x1, y1, x2, y2 (corner points)
        "red",
        "red"
    )
                    
                
    PyPhyEngine – PhysicsRoom

    A PhysicsRoom consists of an ObjectCollection and a few other settings which we'll get to later. First we have to define a ObjectCollection which can built using a list of PhysicsObjects and/or StaticObjects. The ObjectCollection is a class which is used to store all the PhysicsObjects and StaticObjects in one place as a dictionary. This makes it easier to work with every type of object when rendering the PhysicsRoom. This ObjectCollection will then be passed to the PhysicsRoom.

                    
    # here we declare the list of POs, build a ObjectCollection and then render the PhysicsRoom
    
    objects = [ball1, ball2, ball3]
    collection = ObjectCollection().build_collection(objects)
    
    PhysicsRoom(
        OC = collection,
    ).render()
                    
                
    PyPhyEngine – Collisions, Recording and more

    When initializing a PhysicsRoom you can pass multiple settings like collision, recording as more. The collision parameter is a boolean which decides whether or not the objects in the room will collide with each other. The KEGraphing parameter is also a boolean which decides whether or not there should be another subplot which displays the kinetic energy of each PhysicsObject. The recording parameter is a boolean too which decides whether or not the simulation should be recorded and saved in the current directory as a .avi file.

    Here is an example of how you can use these parameters:

                    
    # this script enables collision, recording and KEGraphing
    
    objects = [ball1, ball2, ball3]
    collection = ObjectCollection().build_collection(objects)
    
    PhysicsRoom(
        OC = collection,
        collision = True,
        KEGraphing = True,
        recording = True
    ).render()
                    
                

    Heres a video of a simulation created with PyPhyEngine:

    Here's the code for the video above:

                    
    import pyPhyEngine as phy
    import pyPhyEngine.models as models
    
    ball = models.PhysicsObject(
        x = 1,
        y = 20,
        size = 65,
        grav = 0.25,
        fps = 90,
        mass=1,
        bounce = 1,
        friction = 0.05,
        velocity_x = 0.15,
        color = "blue",
        show_trajectory = True
    )
    
    ball2 = models.PhysicsObject(
        x = 9,
        y = 20,
        size = 200,
        grav = 0.25,
        fps = 90,
        bounce = 1,
        mass=1,
        friction = 0.05,
        velocity_x = -0.15,
        color = "purple",
        show_trajectory = True
    )
    
    collection = phy.ObjectCollection().build_collection([ball, ball2])
    
    phy.PhysicsRoom(
        OC=collection,
        record_video=True,
        collision=True
    ).render()
    
                    
                
    PyPhyEngine – pyPhyEngine

    The pyPhyEngine module is the main module of the engine. It contains the PhysicsRoom class and ObjectCollection class. The PhysicsRoom class is used to render the simulation, using the ObjectCollection. The ObjectCollection class is used to store all the PhysicsObjects and StaticObjects in one place as a dictionary.

    Usage PhysicsRoom
    room = PhysicsRoom(OC: dict, collision: bool = True, KEGraphing: bool = False, tolerance: int | float = 0.25, loop: bool = False, record_video: bool = False, debug: bool = False)
    > Initializes the PhysicsRoom.
    > The OC parameter is the ObjectCollection which contains all the PhysicsObjects and StaticObjects.
    > The collision parameter decides whether or not the objects in the room will collide with each other.
    > The KEGraphing parameter decides whether or not there should be another subplot which displays the kinetic energy of each PhysicsObject.
    > The tolerance parameter is used to calculate the distance between two objects.
    > The loop parameter decides whether or not the generation should be looped.
    > The record_video parameter decides whether or not the simulation should be recorded and saved in the current directory as a .avi file.
    > The debug parameter decides whether or not the debug mode should be enabled.
    ObjectCollection
    collection = ObjectCollection().build_collection(*objects: PhysicsObject | StaticObject)
    # TODO: Explain each function of the ObjectCollection class KEGraphing
    keGraphing = KEGraphing(objects: list[PhysicsObject])
    > Initializes the KEGraphing class.
    > The objects parameter is a list of PhysicsObjects.
    > The KEGraphing class will then calculate the kinetic energy of each PhysicsObject and display it in a subplot as a bar graph.

    get_kinetic_energy

    keGraphing.get_kinetic_energy() -> list
    > Calculates the kinetic energy of each PhysicsObject.
    > Returns a list containing the kinetic energy of each PhysicsObject.

    get_limit

    keGraphing.get_limit() -> int
    > Calculates the limit of the y-axis based of the bar graph based on the potential energy of each PhysicsObject.
    > Returns the limit.

    get_axes

    keGraphing.get_axes() -> dict
    > Returns the value for the x- and y-axis of the KE graph.
    PyPhyEngine – pyPhyEngine.collider

    The pyPhyEngine.collider module contains the Collider class, which detects collisions between objects.

    Usage
    collision = pyPhyEngine.collider(tolerance: int | float = 0.25)

    get_objects

    collision.get_objects(collection: dict) -> None
    > Adds the objects of the collection to self.objects of Collider.

    extract_current_object

    collision.extract_current_object(iteration: int) -> PhysicsObject
    > Usually used by pyPhyEngine.renderer to extract each PhysicsObject. > Returns the current PhysicsObject.

    check_collides_physicsObject

    collision.check_collides_physicsObject(object1: PhysicsObject, physicsObject: PhysicsObject) -> tuple[bool] | PhysicsObject
    > Checks if the current PhysicsObject collides with any other PhysicsObject.
    > Returns a tuple with a boolean and the two colliding PhysicsObjects.

    lines_staticObject

    collision.lines_staticObject(staticObject: StaticObject) -> list
    > Returns a list containing each line (pair of points).

    check_collides_staticObject

    collision.check_collides_staticObject(object1: PhysicsObject, staticObject: StaticObject) -> bool
    > Checks if the current PhysicsObject collides with any StaticObject.
    > Returns a boolean depending on whether or not there are any collisions.

    check_collides

    collision.check_collides(object1: PhysicsObject) -> bool
    > Checks if the current PhysicsObject collides with any other object.
    > It checks this by iterating over each PhysicsObject and StaticObject and then
    > calling check_collides_physicsObject and check_collides_staticObject accordingly.
    > Returns a boolean depending on whether or not there are any collisions.

    calculate_velocities

    collision.calculate_velocities(object1: PhysicsObject | StaticObject, object2: PhysicsObject | StaticObject) -> dict[str, dict[str, int | float]]
    > Calculates the new velocities of the two colliding objects.
    > Returns a dictionary containing the new velocities of the two objects.

    collide

    collision.collide(object1: PhysicsObject, object2: PhysicsObject | None) -> None
    > This gets called to apply the new velocities on both objects.
    > You would pass None as the second object if the second object is a StaticObject.
    > Thats because StaticObjects don't move.
    > Returns None.
    PyPhyEngine – pyPhyEngine.generator

    The pyPhyEngine.generator module contains the PhysicsRoomGenerator class, which generates a completely random PhysicsRoom. Note that the PhysicsRoomGenerator is not a PhysicsRoom, but a class which generates a PhysicsRoom. Also, recording and KEGraphing will be disabled by default.

    Usage
    generator = pyPhyEngine.generator.PhysicsRoomGenerator(loop: bool = False, collision: bool = True)
    > Initializes the PhysicsRoomGenerator.
    > The loop parameter decides whether or not the generation should be looped.
    > The collision parameter decides whether or not the objects in the room will collide with each other.

    create_objects

    generator.create_objects() -> None
    > Creates a random amount of PhysicsObjects. Maximum is 10.
    > The objects get appended to the self.PhysicsObjects list.
    > Each PhysicsObject gets a random position, size, color, velocity and more.

    ObjectCollection

    generator.ObjectCollection() -> None
    > Creates a ObjectCollection with the PhysicsObjects.
    > The ObjectCollection dict will be saved in a variable within the class.

    create_room

    generator.create_room() -> None
    > Renders the PhysicsRoom with the generated ObjectCollection.
    > The PhysicsRoom will be rendered immediately.
    PyPhyEngine – pyPhyEngine.models

    The pyPhyEngine.models module contains the PhysicsObject and StaticObject classes.

    Usage PhysicsObject
    object = pyPhyEngine.models.PhysicsObject(x: int | float, y: int | float, size: int, grav: int | float, fps: int, bounce: int | float, friction: int | float, velocity_x: int | float, velocity_y: int | float, border_x: list[int], border_y: list[int], color: str, show_trajectory: bool, max_trajectory_iterations: int)
    > Initializes a PhysicsObject.
    > The x and y parameters are the position of the PhysicsObject.
    > The size parameter is the size of the PhysicsObject.
    > The grav parameter is the gravity which is applied to the PhysicsObject.
    > The fps parameter is the frames per second of the PhysicsObject.
    > The bounce parameter is the bounce of the PhysicsObject.
    > The friction parameter is the friction of the PhysicsObject.
    > The velocity_x and velocity_y parameters are the velocities of the PhysicsObject.
    > The border_x and border_y parameters are the borders of the PhysicsObject.
    > The color parameter is the color of the PhysicsObject.
    > The show_trajectory parameter decides whether or not the trajectory of the PhysicsObject should be shown.
    > The max_trajectory_iterations parameter is the maximum amount of trajectory points.

    tick

    PhysicsObject.tick() -> int | dict
    > Updates the PhysicsObject.
    > This will usually run each frame of the simulation.
    > It validates the new x-, y-position and trajectory points
    > Returns the new position of the PhysicsObject.

    validate_x_position

    PhysicsObject.validate_x_position(x: int) -> int
    > Validates the x-position of the PhysicsObject.
    > Returns the new x-position.

    validate_y_position

    PhysicsObject.validate_y_position(y: int) -> int
    > Validates the y-position of the PhysicsObject.
    > Returns the new y-position.

    validate_trajectory

    PhysicsObject.validate_trajectory() -> None
    > Validates the trajectory of the PhysicsObject.
    > If the trajectory is too long, it will delete the oldest point.
    > The trajectory will be appended to the self.trajectory variable of the class.

    get_data_string

    PhysicsObject.get_data_string() -> str
    > Returns a string with a lot of data of the PhysicsObject.
    > This is usually used for debugging purposes.
    StaticObject
    object = pyPhyEngine.models.StaticObject(matrix: list[list[int]], color: str = "blue", fill_color: str = "blue")
    > Initializes a StaticObject.
    > The matrix parameter is a list of vectors which define the points and lines of the StaticObject.
    > The color parameter is the color of the StaticObject.
    > The fill_color parameter is the color of the filling of the StaticObject.
    PyPhyEngine – pyPhyEngine.renderer

    The pyPhyEngine.renderer module contains the Renderer class, which renders the simulation.

    Usage
    renderer = pyPhyEngine.renderer.Renderer(fps: int = 60, loop: bool = False, collision: bool = True, recorder: bool = False, KEGraphing: bool = False, collection: dict = None)
    > Initializes the Renderer.
    > The fps parameter is the frames per second of the simulation.
    > The loop parameter decides whether or not the simulation should be looped.
    > The collision parameter decides whether or not the objects in the room will collide with each other.
    > The recorder parameter decides whether or not the simulation should be recorded and saved in the current directory as a .avi file.
    > The KEGraphing parameter decides whether or not there should be another subplot which displays the kinetic energy of each PhysicsObject.
    > The collection parameter is the ObjectCollection which contains all the PhysicsObjects and StaticObjects.

    setup

    renderer.setup() -> Figure | Axes
    > Sets up the figure and axes of the simulation.
    > If recorder is True, it will set up the recorder.
    > If KEGraphing is True, it will inititalize 2 subplots instead of 1 and the KEGraphing class
    > If collider is True, it will initialize the Collider class.
    > Returns the figure and axes.

    save_frame

    renderer.save_frame() -> None
    > Saves the current frame of the simulation.
    > This is only used when recording the simulation.

    render_object

    renderer.render_object(object: PhysicsObject | StaticObject, axis: Axes) -> None
    > Renders the PhysicsObject or StaticObject on the axis.
    > Returns None.

    render_all_objects

    renderer.render_all_objects(collection: dict, axis: Axes) -> None
    > Iterates through each object type in the ObjectCollection.
    > Returns None.

    set_axis_template

    renderer.set_axis_template(axis: Axes, room_limits: dict) -> None
    > Sets the limits of the axis.
    > Returns None.

    draw_kinetic_energy

    renderer.draw_kinetic_energy(axis: Axes) -> None
    > Draws the kinetic energy of each PhysicsObject on the axis of the second subplot.
    > Returns None.

    initialize_render

    renderer.initialize_render(room_limits: dict) -> None
    > Initializes the rendering of the simulation.
    > First it sets the limits of the axis, then renders all objects and finally draws the kinetic energy graph.
    > Then, it also checks for collisions and updates the positions of the PhysicsObjects if any collide.
    > Returns None.

    finish_video

    renderer.finish_video() -> None
    > Finishes the video rendering.
    > Returns None.
    PyPhyEngine – pyPhyEngine.utils

    The pyPhyEngine.utils module contains the generate_rectangle, generate_circle and extract_physicsObjects functions.

    Usage

    generate_rectangle

    rectangle = pyPhyEngine.utils.generate_rectangle(x1: int, y1: int, x2: int, y2: int) -> list[list[int]]
    > Generates a rectangle based on the corner points.
    > Returns a list of vectors which define the points and lines of the rectangle.

    generate_circle

    circle = pyPhyEngine.utils.generate_circle(x: int, y: int, radius: int, max_lines: int) -> list[list[int]]
    > Generates a circle based on the center point and radius.
    > The max_lines parameter is the amount of lines which will be generated.
    > Returns a list of vectors which define the points and lines of the circle.

    extract_physicsObjects

    objects = pyPhyEngine.utils.extract_physicsObjects(collection: dict) -> list[PhysicsObject]
    > Extracts all PhysicsObjects from the ObjectCollection.
    > Returns a list of PhysicsObjects.
    PyPhyEngine – pyPhyEngine.vectors

    The pyPhyEngine.vectors module contains functions which are used to calculate vectors.
    Typically they are used in the project whenever we need to find if two objects collide or not. We use them especially when PhysicsObjects and StaticObjects meet as we have to calculate whether or not the PhysicsObject is on a line of the StaticObject or not. As of today (20.12.2024) the entire system is very unstable and not very reliable. I am working on a new system which will be more reliable and actually calculate the velocities for colliding objects correctly.
    Therefore, the collisions system is not very reliable with StaticObjects.

    Usage

    get_vector_difference

    vector = pyPhyEngine.vectors.get_vector_difference(vector1: list[int], vector2: list[int]) -> list[int]
    > Calculates the difference between two vectors.
    > Returns the difference as a vector.

    extract_points

    points = pyPhyEngine.vectors.extract_points(line: list[list[int]]) -> list[list[int]]
    > Extracts the connected points of a line.
    > Returns a list of points.

    neutralize_points

    points = pyPhyEngine.vectors.neutralize_points(*points: list[list[int]]) -> list[list[int]]
    > Neutralizes the points of a line.
    > Returns a list of points with their absolute values.

    point_collides_line

    collision = pyPhyEngine.vectors.point_collides_line(point: list[int], line: list[list[int]]) -> bool
    > Checks if a point collides with a line.
    > Returns a boolean depending on whether or not there is a collision.
    PyPhyEngine – pyPhyEngine.video

    The pyPhyEngine.video module contains the VideoRecorder class, which records the simulation as a video.

    Usage
    recorder = pyPhyEngine.video.VideoRecorder(fps: int = 60, video_path: str = '.\\', delete_temp: bool = False)
    > Initializes the VideoRecorder.
    > The fps parameter is the frames per second of the video.
    > The video_path parameter is the path where the video will be saved.
    > The delete_temp parameter decides whether or not the temporary frames should be deleted.

    delete_tmp

    recorder.delete_tmp() -> None
    > Deletes the temporary frames.
    > Returns None.

    save_frame

    recorder.save_frame(plot: Figure) -> None
    > Saves the current frame of the simulation.
    > Returns None.

    render_video

    recorder.render_video() -> None
    > Renders the video and saves it in the video_path.
    > Returns None.