Skip to content

Commit

Permalink
Merge pull request #2 from Dooders/v0.1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
csmangum authored Aug 22, 2024
2 parents 071e06a + db53463 commit 663a478
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 263 deletions.
371 changes: 151 additions & 220 deletions fizicks/collision.py

Large diffs are not rendered by default.

50 changes: 30 additions & 20 deletions fizicks/data.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
class Vector:
"""A vector is a quantity in three-dimensional space that has both magnitude and direction."""
"""
A vector is a quantity in three-dimensional space that has both magnitude and direction.
Methods
-------
magnitude():
Returns the magnitude of the vector.
normalize():
Returns the normalized vector.
dot(other: "Vector") -> float:
Returns the dot product of two vectors.
copy():
Returns a copy of the vector.
distance(other: "Vector") -> float:
Returns the distance between two vectors.
"""

def __init__(self, x: float = 0.0, y: float = 0.0, z: float = 0.0) -> None:
"""
Expand Down Expand Up @@ -52,23 +67,36 @@ def __mod__(self, other):
)

def __iter__(self):
"""Returns an iterator over the vector."""
return iter((self.x, self.y, self.z))

def __call__(self):
"""Returns the vector as a tuple."""
return (self.x, self.y, self.z)

def magnitude(self) -> float:
"""Returns the magnitude of the vector."""
return (self.x**2 + self.y**2 + self.z**2) ** 0.5

def normalize(self) -> "Vector":
return self / self.magnitude()
"""Returns the normalized vector."""
mag = self.magnitude()
if mag == 0:
return Vector(0, 0, 0) # Return a zero vector if magnitude is zero
return self / mag

def dot(self, other: "Vector") -> float:
"""Returns the dot product of two vectors."""
return self.x * other.x + self.y * other.y + self.z * other.z

def copy(self) -> "Vector":
"""Returns a copy of the vector."""
return self.__class__(self.x, self.y, self.z)

def distance(self, other: "Vector") -> float:
"""Returns the distance between two vectors."""
return (self - other).magnitude()


class Force(Vector):
"""A force is a vector that describes the change in momentum of an object over time."""
Expand All @@ -89,21 +117,3 @@ class Velocity(Vector):

def __init__(self, x: float, y: float, z: float):
super().__init__(x, y, z)


class Universe:
#! Stores the properties, constants, and restrictions of the universe.
#! Contain special handling for restrictions
def __init__(self, **kwargs) -> None:
self.dimensions = kwargs.get("dimensions", Vector(100, 100, 100))
self.toroidal = kwargs.get("toroidal", False)
self.gravity = kwargs.get("gravity", Vector(0, 0, 0))
self.c = kwargs.get("c", 1)
self.viscosity = kwargs.get("viscosity", 0)
self.restitution = kwargs.get("restitution", 1)
self.friction = kwargs.get("friction", 0)
self.air_resistance = kwargs.get("air_resistance", 0)
self.air_resistance_coefficient = kwargs.get("air_resistance_coefficient", 0)
self.air_resistance_area = kwargs.get("air_resistance_area", 0)
self.air_resistance_density = kwargs.get("air_resistance_density", 0)
self.objects = []
8 changes: 5 additions & 3 deletions fizicks/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from fizicks.motion import Motion

if TYPE_CHECKING:
from fizicks.data import Universe
from fizicks.matter import Matter
from fizicks.universe import Universe


class Fizicks:
Expand All @@ -20,7 +20,9 @@ class Fizicks:
"""

@classmethod
def update(cls, object: "Matter", universe: "Universe") -> None:
def update(
cls, object: "Matter", universe: "Universe", debug: bool = False
) -> None:
"""
Updates the object's state by applying rigid motion physics.
Expand All @@ -31,4 +33,4 @@ def update(cls, object: "Matter", universe: "Universe") -> None:
universe : Universe
The space to apply the physics to.
"""
Motion.update(object, universe)
Motion.update(object, universe, debug=debug)
49 changes: 45 additions & 4 deletions fizicks/matter.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import uuid
from typing import TYPE_CHECKING

from fizicks.data import Force, Position, Velocity
from fizicks.main import Fizicks

if TYPE_CHECKING:
from fizicks.data import Vector
from fizicks.main import Universe
from fizicks.universe import Universe


class Matter:
Expand All @@ -22,6 +23,8 @@ class Matter:
The mass of the object.
radius : float
The radius of the object.
color : tuple[int, int, int]
The color of the object.
debt : list[Force]
The list of forces to apply to the object.
Expand All @@ -31,6 +34,12 @@ class Matter:
Add a force to the object's debt. Will be applied in the next update.
update(universe)
Update the object's state based on accumulated forces and current state.
collides_with(other)
Check if the object collides with another object.
description(short=True)
A short description of the object.
description(short=False)
A long description of the object.
Properties
----------
Expand All @@ -41,12 +50,20 @@ class Matter:
"""

def __init__(
self, position: "Position", velocity: "Velocity", mass: float, radius: float
self,
position: "Position",
velocity: "Velocity",
mass: float,
radius: float,
color: tuple[int, int, int] = (255, 255, 255),
) -> None:
self.id = uuid.uuid4()
self.time = 0
self._position: Position = position
self._velocity: Velocity = velocity
self.mass: float = mass
self.radius: float = radius
self.color: tuple[int, int, int] = color
self.debt: list[Force] = []

def add_debt(self, force: "Force") -> None:
Expand All @@ -60,7 +77,7 @@ def add_debt(self, force: "Force") -> None:
"""
self.debt.append(force)

def update(self, universe: "Universe") -> None:
def update(self, universe: "Universe", debug: bool = False) -> None:
"""
Update the object's state based on accumulated forces and current state.
Expand All @@ -69,8 +86,32 @@ def update(self, universe: "Universe") -> None:
universe : Universe
The universe to update the object in.
"""
Fizicks.update(self, universe)
Fizicks.update(self, universe, debug=debug)
self.debt = [] # Clear forces after applying
self.time += 1

def collides_with(self, other: "Matter") -> bool:
"""
Check if the object collides with another object.
Parameters
----------
other : Matter
The object to check for collision with.
"""
return self.position.distance(other.position) <= self.radius + other.radius

def description(self, short: bool = True) -> str:
if short:
return f"Matter(id={self.id})"
else:
return f"Matter(id={self.id}, position={self.position}, velocity={self.velocity}, mass={self.mass}, radius={self.radius}, color={self.color})"

def __repr__(self) -> str:
return f"Matter(id={self.id}, position={self.position}, velocity={self.velocity}, mass={self.mass}, radius={self.radius}, color={self.color})"

def __str__(self) -> str:
return f"Matter(id={self.id}, position={self.position}, velocity={self.velocity}, mass={self.mass}, radius={self.radius}, color={self.color})"

@property
def position(self) -> "Position":
Expand Down
65 changes: 52 additions & 13 deletions fizicks/motion.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
from fizicks.collision import Collision

if TYPE_CHECKING:
from fizicks.data import Force, Universe
from fizicks.data import Force
from fizicks.matter import Matter
from fizicks.universe import Universe


class FirstLaw:
"""An object in motion will remain in motion unless acted on by an external force."""

@classmethod
def apply(cls, object: "Matter", force: "Force") -> None:
def apply(cls, object: "Matter", force: "Force", debug: bool = False) -> None:
"""
Updates the velocity of the object based on the force applied.
Expand All @@ -21,40 +22,72 @@ def apply(cls, object: "Matter", force: "Force") -> None:
The object to apply the force to.
force : Force
The force to apply to the object.
debug : bool
Whether to print debug information.
"""
if debug:
debug_log(
f"Step {object.time}: Applying force: {force} to {object.id}", object
)
object.velocity = object.velocity + force
if debug:
debug_log(f"Updated velocity: {object.velocity}", object)


class SecondLaw:
"""The acceleration of an object is directly proportional to the net force acting on it and inversely proportional to its mass."""

@classmethod
def apply(cls, object: "Matter") -> None:
def apply(cls, object: "Matter", universe: "Universe", debug: bool = False) -> None:
"""
Updates the position of the object based on the velocity.
Parameters
----------
object : Matter
The object to apply the force to.
universe : Universe
The universe to apply the force to.
debug : bool
Whether to print debug information.
"""
if debug:
debug_log(
f"Step {object.time}: Applying force: {object.velocity} to {object.id}",
object,
)

object.position = object.position + object.velocity
if debug:
debug_log(f"Updated position: {object.position}", object)

if Collision.detect(object, universe):
Collision.resolve(object, universe)


class ThirdLaw:
"""For every action, there is an equal and opposite reaction."""

@classmethod
def apply(cls, object: "Matter") -> None:
def apply(cls, object: "Matter", debug: bool = False) -> None:
"""
Updates the acceleration of the object based on the velocity and mass.
Parameters
----------
object : Matter
The object to apply the force to.
debug : bool
Whether to print debug information.
"""
if debug:
debug_log(
f"Step {object.time}: Applying force: {object.velocity / object.mass} to {object.id}",
object,
)
object.acceleration = object.velocity / object.mass
if debug:
debug_log(f"Updated acceleration: {object.acceleration}", object)


class Motion:
Expand All @@ -68,7 +101,7 @@ class Motion:
"""

@classmethod
def update(cls, object: Any, universe: "Universe") -> None:
def update(cls, object: Any, universe: "Universe", debug: bool = False) -> None:
"""
Updates the object based on the forces applied and its current state.
Expand All @@ -78,19 +111,25 @@ def update(cls, object: Any, universe: "Universe") -> None:
The object to update.
universe : Universe
The universe to update the object in.
debug : bool
Whether to print debug information.
"""
# Check for collisions with the universe
if Collision.detect(object, universe):
if debug:
debug_log(
f"Step {object.time}: Collision detected between {object.id} and {universe.id}",
object,
)
Collision.resolve(object, universe)

# Check for collisions with other objects
for other_object in universe.objects:
if other_object is not object and Collision.detect(object, other_object):
Collision.resolve(object, other_object)

# Apply the forces of motion
if object.debt:
if debug:
debug_log(
f"Step {object.time}: Applying forces for {object.id}", object
)
for debt in object.debt:
FirstLaw.apply(object, debt)
SecondLaw.apply(object)
ThirdLaw.apply(object)
FirstLaw.apply(object, debt, debug=debug)
SecondLaw.apply(object, universe, debug=debug)
ThirdLaw.apply(object, debug=debug)
3 changes: 2 additions & 1 deletion tests/test_collision.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import unittest

from fizicks.collision import Collision
from fizicks.data import Position, Universe, Vector
from fizicks.data import Position, Vector
from fizicks.matter import Matter
from fizicks.universe import Universe


class TestCollision(unittest.TestCase):
Expand Down
3 changes: 2 additions & 1 deletion tests/test_matter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import pytest

from fizicks.data import Force, Position, Universe, Velocity
from fizicks.data import Force, Position, Velocity
from fizicks.matter import Matter
from fizicks.universe import Universe


class TestMatter:
Expand Down
3 changes: 2 additions & 1 deletion tests/test_motion.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import pytest

from fizicks.data import Force, Position, Universe, Vector, Velocity
from fizicks.data import Force, Position, Vector, Velocity
from fizicks.matter import Matter
from fizicks.motion import FirstLaw, Motion, SecondLaw, ThirdLaw
from fizicks.universe import Universe


class TestMotion:
Expand Down

0 comments on commit 663a478

Please sign in to comment.