File size: 4,825 Bytes
4fa8bcb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import math
import numpy as np

class PhysicsSimulator:
    def __init__(self, gravity=9.8, dt=0.1):
        """
        Initialize the physics simulator environment.

        Args:
            gravity (float): Gravitational acceleration (m/s^2)
            dt (float): Time step size in seconds
        """
        self.gravity = gravity
        self.dt = dt

    def simulate_ball_motion(self, mass, angle, friction, time_steps=50, height=64, width=64):
        """
        Simulate ball rolling down a ramp.

        Args:
            mass (float): Mass of ball (currently not affecting result)
            angle (float): Angle of ramp in degrees
            friction (float): Coefficient of friction
            time_steps (int): Number of timesteps

        Returns:
            List of positions over time.
        """
        acc = self.gravity * math.sin(math.radians(angle)) - friction * self.gravity * math.cos(math.radians(angle))
        velocity = 0
        s = 0
        coords = []

        # Start from top-left of the frame
        ramp_start = (0, 0)

        # Compute ramp end point based on angle and frame size
        rad = math.radians(angle)
        dx = math.cos(rad)     # → right
        dy = math.sin(rad)  

        # Normalize the ramp to fill the diagonal range of the frame
        length_scale = min(width / abs(dx) if dx != 0 else float('inf'),
                          height / abs(dy) if dy != 0 else float('inf'))
        
        ramp_end = (
            ramp_start[0] + dx * length_scale,
            ramp_start[1] + dy * length_scale
        )
        
        ramp_length = math.hypot(ramp_end[0] - ramp_start[0], ramp_end[1] - ramp_start[1])

        for _ in range(time_steps):
            velocity += acc * self.dt
            s += velocity * self.dt
            s = max(0, min(s, ramp_length))

            normalized = s / ramp_length

            # Compute current position along the ramp
            x = ramp_start[0] + normalized * (ramp_end[0] - ramp_start[0])
            y = ramp_start[1] + normalized * (ramp_end[1] - ramp_start[1])

            # Draw position in a blank image
            coord = np.zeros((height, width), dtype=np.float32)
            
            # Convert to integer coordinates and ensure they're within bounds
            xi = int(round(min(max(x, 0), width - 1)))
            yi = int(round(min(max(y, 0), height - 1)))
            coord[yi, xi] = 1.0
            coords.append(coord)

        return coords

    def simulate_projectile_motion(self, initial_velocity, angle, gravity, time_steps):
        """
        Simulate projectile motion of a particle launched with an initial velocity and angle.

        Args:
            initial_velocity (float): initial speed in m/s
            angle (float): launch angle in degrees
            gravity (float): gravitational constant (usually 9.8)
            time_steps (int): number of simulation steps

        Returns:
            List of coordinates where each coordinate is a 2D array with a single point marked as 1.0
        """
        angle_rad = np.deg2rad(angle)
        v_x = initial_velocity * np.cos(angle_rad)
        v_y = initial_velocity * np.sin(angle_rad)

        coords = []
        x, y = 0.0, 0.0
        for t in range(time_steps):
            # Debug
            # print(f"t={t}: BEFORE x={x:.2f}, y={y:.2f}")
            
            # Normalize to fit in 64x64 frame (arbitrary scaling)
            x_norm = min(max(x / 100.0, 0.0), 1.0)  # scale x max to ~100 meters
            y_norm = min(max(y / 100.0, 0.0), 1.0)  # scale y max to ~100 meters
            
            # stop when object hits the an edge of the frame
            if y_norm < 0 or x_norm > 1.0 or x_norm < 0.0 or y_norm > 1.0:
                break
              
            # Create a coordinate frame with the point marked
            coord = np.zeros((64, 64), dtype=np.float32)
            xi = int(round(x_norm * 63))
            yi = int(round((1.0 - y_norm) * 63))  # invert y for image coordinates
            if 0 <= xi < 64 and 0 <= yi < 64:
                coord[yi, xi] = 1.0
            coords.append(coord)
            
            # Debug
            # print(f"t={t}: x={x:.2f}, y={y:.2f}, v_x={v_x:.2f}, v_y={v_y:.2f}")
            
            # Update position
            x += v_x * self.dt
            y += v_y * self.dt
            v_y -= gravity * self.dt  # gravity reduces vertical velocity

        # Pad to maintain consistent sequence length
        while len(coords) < time_steps:
            coords.append(np.zeros((64, 64), dtype=np.float32))

        return coords

if __name__ == "__main__":
    simulator = PhysicsSimulator(gravity=9.8, dt=0.1)
    positions = simulator.simulate_ball_motion(mass=0.15, angle=30, friction=0.05, time_steps=50)
    print(positions)