From 076c5a97de0cd6020f1b08faf07c11431bc13a7b Mon Sep 17 00:00:00 2001 From: Pete Haughie Date: Fri, 10 Oct 2025 03:27:17 +0200 Subject: [PATCH 1/2] Ported all of the code from the Nature of Code processing port https://github.com/nature-of-code/noc-2-processing-port/ --- .gitignore | 1 - .../Example_0_01_Random_Walk.py | 33 +++ .../Example_0_02_Random_Distribution.py | 33 +++ ...Example_0_03_Random_Walk_Tends_To_Right.py | 47 +++++ .../Example_0_04_Gaussian_Distribution.py | 25 +++ ...Example_0_05_Accept_Reject_Distribution.py | 49 +++++ .../Example_0_06_Perlin_Noise_Walker.py | 49 +++++ .../Example_1_01_Bouncing_Ball_No_Vectors.py | 40 ++++ .../Example_1_02_Bouncing_Ball_Vectors.py | 32 +++ .../Example_1_03_Vector_Subtraction.py | 34 +++ .../Example_1_04_Vector_Multiplication.py | 36 ++++ .../Example_1_05_Vector_Magnitude.py | 33 +++ .../Example_1_06_Vector_Normalize.py | 40 ++++ .../Example_1_07_Motion_101_Velocity.py | 23 +++ .../Example_1_07_Motion_101_Velocity/Mover.py | 29 +++ ..._101_Velocity_And_Constant_Acceleration.py | 26 +++ .../Mover.py | 33 +++ ...on_101_Velocity_And_Random_Acceleration.py | 26 +++ .../Mover.py | 37 ++++ ...Example_1_10_Accelerating_Towards_Mouse.py | 24 +++ .../Mover.py | 40 ++++ .../Example_2_01_Forces.py | 42 ++++ .../chapter02/Example_2_01_Forces/Mover.py | 37 ++++ ...ample_2_02_Forces_Acting_On_Two_Objects.py | 55 +++++ .../Mover.py | 37 ++++ .../Example_2_03_Gravity_Scaled_By_Mass.py | 55 +++++ .../Mover.py | 39 ++++ .../Example_2_04_Including_Friction.py | 51 +++++ .../Example_2_04_Including_Friction/Mover.py | 43 ++++ .../Example_2_05_Fluid_Resistance.py | 44 ++++ .../Example_2_05_Fluid_Resistance/Liquid.py | 34 +++ .../Example_2_05_Fluid_Resistance/Mover.py | 43 ++++ .../Example_2_06_Attraction/Attractor.py | 60 ++++++ .../Example_2_06_Attraction.py | 46 +++++ .../Example_2_06_Attraction/Mover.py | 28 +++ .../Attractor.py | 54 +++++ ...xample_2_07_Attraction_With_Many_Movers.py | 44 ++++ .../Mover.py | 27 +++ .../Example_2_08_Two_Body_Attraction/Body.py | 36 ++++ .../Example_2_08_Two_Body_Attraction.py | 34 +++ .../chapter02/Example_2_09_N_Bodies/Body.py | 40 ++++ .../Example_2_09_N_Bodies.py | 34 +++ ...xample_3_01_Angular_Motion_Using_Rotate.py | 46 +++++ .../Attractor.py | 30 +++ ...02_Forces_With_Arbitrary_Angular_Motion.py | 34 +++ .../Mover.py | 40 ++++ ..._03_Pointing_In_The_Direction_Of_Motion.py | 28 +++ .../Mover.py | 50 +++++ .../Example_3_04_Polar_To_Cartesian.py | 41 ++++ .../Example_3_05_Simple_Harmonic_Motion.py | 38 ++++ .../Example_3_06_Simple_Harmonic_Motion_II.py | 38 ++++ .../Example_3_07_Oscillator_Objects.py | 31 +++ .../Oscillator.py | 37 ++++ .../Example_3_08_Static_Wave.py | 40 ++++ .../Example_3_09_The_Wave.py | 38 ++++ .../Example_4_01_Single_Particle.py | 34 +++ .../Example_4_01_Single_Particle/Particle.py | 33 +++ .../Example_4_02_Array_Of_Particles.py | 32 +++ .../Particle.py | 39 ++++ .../Emitter.py | 27 +++ .../Example_4_03_A_Particle_Emitter.py | 28 +++ .../Particle.py | 29 +++ .../Emitter.py | 26 +++ .../Example_4_04_A_System_Of_Systems.py | 34 +++ .../Particle.py | 29 +++ .../Confetti.py | 22 ++ .../Emitter.py | 27 +++ ...ystem_With_Inheritance_And_Polymorphism.py | 25 +++ .../Particle.py | 36 ++++ .../Emitter.py | 26 +++ ...xample_4_06_Particle_System_With_Forces.py | 31 +++ .../Particle.py | 37 ++++ .../Emitter.py | 32 +++ ...mple_4_07_Particle_System_With_Repeller.py | 33 +++ .../Particle.py | 33 +++ .../Repeller.py | 24 +++ .../Emitter.py | 26 +++ ...Example_4_08_Image_Texture_System_Smoke.py | 48 +++++ .../Particle.py | 35 ++++ .../data/texture.png | Bin 0 -> 3554 bytes .../Example_4_09_Additive_Blending/Emitter.py | 27 +++ .../Example_4_09_Additive_Blending.py | 59 ++++++ .../Particle.py | 36 ++++ .../data/texture.png | Bin 0 -> 3554 bytes .../Example_5_01_Seek/Example_5_01_Seek.py | 37 ++++ .../chapter05/Example_5_01_Seek/Vehicle.py | 59 ++++++ .../Example_5_02_Arrive.py | 44 ++++ .../chapter05/Example_5_02_Arrive/Vehicle.py | 62 ++++++ .../Example_5_03_Stay_Within_Walls.py | 40 ++++ .../Example_5_03_Stay_Within_Walls/Vehicle.py | 68 ++++++ .../Example_5_04_Flow_Field.py | 53 +++++ .../Example_5_04_Flow_Field/FlowField.py | 56 +++++ .../Example_5_04_Flow_Field/Vehicle.py | 74 +++++++ .../Example_5_05_Create_Path_Object.py | 28 +++ .../Example_5_05_Create_Path_Object/Path.py | 23 +++ .../Example_5_06_Simple_Path_Following.py | 50 +++++ .../Path.py | 23 +++ .../Vehicle.py | 128 ++++++++++++ .../Example_5_07_Multiple_Segments.py | 31 +++ .../Example_5_07_Multiple_Segments/Path.py | 35 ++++ .../Example_5_08_Path_Following.py | 65 ++++++ .../Example_5_08_Path_Following/Path.py | 41 ++++ .../Example_5_08_Path_Following/Vehicle.py | 195 ++++++++++++++++++ .../Example_5_09_Separation.py | 38 ++++ .../Example_5_09_Separation/Vehicle.py | 76 +++++++ .../Example_5_10_Combining_Seek_Separate.py | 35 ++++ .../Vehicle.py | 111 ++++++++++ .../chapter05/Example_5_11_Flocking/Boid.py | 153 ++++++++++++++ .../Example_5_11_Flocking.py | 38 ++++ .../chapter05/Example_5_11_Flocking/Flock.py | 14 ++ .../Boid.py | 192 +++++++++++++++++ ...ple_5_12_Bin_Lattice_Spatial_Separation.py | 89 ++++++++ .../Flock.py | 16 ++ .../Example_5_13_QuadTree.py | 57 +++++ .../Example_5_13_QuadTree/QuadTree.py | 127 ++++++++++++ .../Example_5_14_COS_SIN_LUT.py | 58 ++++++ ...01_Wolfram_Elementary_Cellular_Automata.py | 66 ++++++ .../Example_7_02_Game_of_Life.py | 57 +++++ .../Cell.py | 28 +++ ...ample_7_03_Object_Oriented_Game_of_Life.py | 64 ++++++ .../Example_8_01_Recursive_Circles_Once.py | 33 +++ .../Example_8_2_Recursive_Circles_Twice.py | 33 +++ ...xample_8_3_Recursive_Circles_Four_Times.py | 34 +++ .../Example_8_4_Cantor_Set.py | 34 +++ .../Example_8_5_Koch_Curve.py | 63 ++++++ .../Example_8_5_Koch_Curve/KochLine.py | 41 ++++ .../Example_8_6_Recursive_Tree.py | 53 +++++ .../Example_8_7_Stochastic_Tree.py | 59 ++++++ .../Example_8_8_LSystem_String_Only.py | 55 +++++ .../Example_8_9_LSystem.py | 41 ++++ .../chapter08/Example_8_9_LSystem/LSystem.py | 19 ++ .../chapter08/Example_8_9_LSystem/Turtle.py | 24 +++ .../Example_11_1_Flappy_Bird/Bird.py | 37 ++++ .../Example_11_1_Flappy_Bird.py | 44 ++++ .../Example_11_1_Flappy_Bird/Pipe.py | 35 ++++ 135 files changed, 5867 insertions(+), 1 deletion(-) create mode 100644 examples/Nature of Code/chapter00/Example_0_01_Random_Walk/Example_0_01_Random_Walk.py create mode 100644 examples/Nature of Code/chapter00/Example_0_02_Random_Distribution/Example_0_02_Random_Distribution.py create mode 100644 examples/Nature of Code/chapter00/Example_0_03_Random_Walk_Tends_To_Right/Example_0_03_Random_Walk_Tends_To_Right.py create mode 100644 examples/Nature of Code/chapter00/Example_0_04_Gaussian_Distribution/Example_0_04_Gaussian_Distribution.py create mode 100644 examples/Nature of Code/chapter00/Example_0_05_Accept_Reject_Distribution/Example_0_05_Accept_Reject_Distribution.py create mode 100644 examples/Nature of Code/chapter00/Example_0_06_Perlin_Noise_Walker/Example_0_06_Perlin_Noise_Walker.py create mode 100644 examples/Nature of Code/chapter01/Example_1_01_Bouncing_Ball_No_Vectors/Example_1_01_Bouncing_Ball_No_Vectors.py create mode 100644 examples/Nature of Code/chapter01/Example_1_02_Bouncing_Ball_Vectors/Example_1_02_Bouncing_Ball_Vectors.py create mode 100644 examples/Nature of Code/chapter01/Example_1_03_Vector_Subtraction/Example_1_03_Vector_Subtraction.py create mode 100644 examples/Nature of Code/chapter01/Example_1_04_Vector_Multiplication/Example_1_04_Vector_Multiplication.py create mode 100644 examples/Nature of Code/chapter01/Example_1_05_Vector_Magnitude/Example_1_05_Vector_Magnitude.py create mode 100644 examples/Nature of Code/chapter01/Example_1_06_Vector_Normalize/Example_1_06_Vector_Normalize.py create mode 100644 examples/Nature of Code/chapter01/Example_1_07_Motion_101_Velocity/Example_1_07_Motion_101_Velocity.py create mode 100644 examples/Nature of Code/chapter01/Example_1_07_Motion_101_Velocity/Mover.py create mode 100644 examples/Nature of Code/chapter01/Example_1_08_Motion_101_Velocity_And_Constant_Acceleration/Example_1_08_Motion_101_Velocity_And_Constant_Acceleration.py create mode 100644 examples/Nature of Code/chapter01/Example_1_08_Motion_101_Velocity_And_Constant_Acceleration/Mover.py create mode 100644 examples/Nature of Code/chapter01/Example_1_09_Motion_101_Velocity_And_Random_Acceleration/Example_1_09_Motion_101_Velocity_And_Random_Acceleration.py create mode 100644 examples/Nature of Code/chapter01/Example_1_09_Motion_101_Velocity_And_Random_Acceleration/Mover.py create mode 100644 examples/Nature of Code/chapter01/Example_1_10_Accelerating_Towards_Mouse/Example_1_10_Accelerating_Towards_Mouse.py create mode 100644 examples/Nature of Code/chapter01/Example_1_10_Accelerating_Towards_Mouse/Mover.py create mode 100644 examples/Nature of Code/chapter02/Example_2_01_Forces/Example_2_01_Forces.py create mode 100644 examples/Nature of Code/chapter02/Example_2_01_Forces/Mover.py create mode 100644 examples/Nature of Code/chapter02/Example_2_02_Forces_Acting_On_Two_Objects/Example_2_02_Forces_Acting_On_Two_Objects.py create mode 100644 examples/Nature of Code/chapter02/Example_2_02_Forces_Acting_On_Two_Objects/Mover.py create mode 100644 examples/Nature of Code/chapter02/Example_2_03_Gravity_Scaled_By_Mass/Example_2_03_Gravity_Scaled_By_Mass.py create mode 100644 examples/Nature of Code/chapter02/Example_2_03_Gravity_Scaled_By_Mass/Mover.py create mode 100644 examples/Nature of Code/chapter02/Example_2_04_Including_Friction/Example_2_04_Including_Friction.py create mode 100644 examples/Nature of Code/chapter02/Example_2_04_Including_Friction/Mover.py create mode 100644 examples/Nature of Code/chapter02/Example_2_05_Fluid_Resistance/Example_2_05_Fluid_Resistance.py create mode 100644 examples/Nature of Code/chapter02/Example_2_05_Fluid_Resistance/Liquid.py create mode 100644 examples/Nature of Code/chapter02/Example_2_05_Fluid_Resistance/Mover.py create mode 100644 examples/Nature of Code/chapter02/Example_2_06_Attraction/Attractor.py create mode 100644 examples/Nature of Code/chapter02/Example_2_06_Attraction/Example_2_06_Attraction.py create mode 100644 examples/Nature of Code/chapter02/Example_2_06_Attraction/Mover.py create mode 100644 examples/Nature of Code/chapter02/Example_2_07_Attraction_With_Many_Movers/Attractor.py create mode 100644 examples/Nature of Code/chapter02/Example_2_07_Attraction_With_Many_Movers/Example_2_07_Attraction_With_Many_Movers.py create mode 100644 examples/Nature of Code/chapter02/Example_2_07_Attraction_With_Many_Movers/Mover.py create mode 100644 examples/Nature of Code/chapter02/Example_2_08_Two_Body_Attraction/Body.py create mode 100644 examples/Nature of Code/chapter02/Example_2_08_Two_Body_Attraction/Example_2_08_Two_Body_Attraction.py create mode 100644 examples/Nature of Code/chapter02/Example_2_09_N_Bodies/Body.py create mode 100644 examples/Nature of Code/chapter02/Example_2_09_N_Bodies/Example_2_09_N_Bodies.py create mode 100644 examples/Nature of Code/chapter03/Example_3_01_Angular_Motion_Using_Rotate/Example_3_01_Angular_Motion_Using_Rotate.py create mode 100644 examples/Nature of Code/chapter03/Example_3_02_Forces_With_Arbitrary_Angular_Motion/Attractor.py create mode 100644 examples/Nature of Code/chapter03/Example_3_02_Forces_With_Arbitrary_Angular_Motion/Example_3_02_Forces_With_Arbitrary_Angular_Motion.py create mode 100644 examples/Nature of Code/chapter03/Example_3_02_Forces_With_Arbitrary_Angular_Motion/Mover.py create mode 100644 examples/Nature of Code/chapter03/Example_3_03_Pointing_In_The_Direction_Of_Motion/Example_3_03_Pointing_In_The_Direction_Of_Motion.py create mode 100644 examples/Nature of Code/chapter03/Example_3_03_Pointing_In_The_Direction_Of_Motion/Mover.py create mode 100644 examples/Nature of Code/chapter03/Example_3_04_Polar_To_Cartesian/Example_3_04_Polar_To_Cartesian.py create mode 100644 examples/Nature of Code/chapter03/Example_3_05_Simple_Harmonic_Motion/Example_3_05_Simple_Harmonic_Motion.py create mode 100644 examples/Nature of Code/chapter03/Example_3_06_Simple_Harmonic_Motion_II/Example_3_06_Simple_Harmonic_Motion_II.py create mode 100644 examples/Nature of Code/chapter03/Example_3_07_Oscillator_Objects/Example_3_07_Oscillator_Objects.py create mode 100644 examples/Nature of Code/chapter03/Example_3_07_Oscillator_Objects/Oscillator.py create mode 100644 examples/Nature of Code/chapter03/Example_3_08_Static_Wave/Example_3_08_Static_Wave.py create mode 100644 examples/Nature of Code/chapter03/Example_3_09_The_Wave/Example_3_09_The_Wave.py create mode 100644 examples/Nature of Code/chapter04/Example_4_01_Single_Particle/Example_4_01_Single_Particle.py create mode 100644 examples/Nature of Code/chapter04/Example_4_01_Single_Particle/Particle.py create mode 100644 examples/Nature of Code/chapter04/Example_4_02_Array_Of_Particles/Example_4_02_Array_Of_Particles.py create mode 100644 examples/Nature of Code/chapter04/Example_4_02_Array_Of_Particles/Particle.py create mode 100644 examples/Nature of Code/chapter04/Example_4_03_A_Particle_Emitter/Emitter.py create mode 100644 examples/Nature of Code/chapter04/Example_4_03_A_Particle_Emitter/Example_4_03_A_Particle_Emitter.py create mode 100644 examples/Nature of Code/chapter04/Example_4_03_A_Particle_Emitter/Particle.py create mode 100644 examples/Nature of Code/chapter04/Example_4_04_A_System_Of_Systems/Emitter.py create mode 100644 examples/Nature of Code/chapter04/Example_4_04_A_System_Of_Systems/Example_4_04_A_System_Of_Systems.py create mode 100644 examples/Nature of Code/chapter04/Example_4_04_A_System_Of_Systems/Particle.py create mode 100644 examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Confetti.py create mode 100644 examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Emitter.py create mode 100644 examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism.py create mode 100644 examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Particle.py create mode 100644 examples/Nature of Code/chapter04/Example_4_06_Particle_System_With_Forces/Emitter.py create mode 100644 examples/Nature of Code/chapter04/Example_4_06_Particle_System_With_Forces/Example_4_06_Particle_System_With_Forces.py create mode 100644 examples/Nature of Code/chapter04/Example_4_06_Particle_System_With_Forces/Particle.py create mode 100644 examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Emitter.py create mode 100644 examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Example_4_07_Particle_System_With_Repeller.py create mode 100644 examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Particle.py create mode 100644 examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Repeller.py create mode 100644 examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/Emitter.py create mode 100644 examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/Example_4_08_Image_Texture_System_Smoke.py create mode 100644 examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/Particle.py create mode 100644 examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/data/texture.png create mode 100644 examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/Emitter.py create mode 100644 examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/Example_4_09_Additive_Blending.py create mode 100644 examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/Particle.py create mode 100644 examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/data/texture.png create mode 100644 examples/Nature of Code/chapter05/Example_5_01_Seek/Example_5_01_Seek.py create mode 100644 examples/Nature of Code/chapter05/Example_5_01_Seek/Vehicle.py create mode 100644 examples/Nature of Code/chapter05/Example_5_02_Arrive/Example_5_02_Arrive.py create mode 100644 examples/Nature of Code/chapter05/Example_5_02_Arrive/Vehicle.py create mode 100644 examples/Nature of Code/chapter05/Example_5_03_Stay_Within_Walls/Example_5_03_Stay_Within_Walls.py create mode 100644 examples/Nature of Code/chapter05/Example_5_03_Stay_Within_Walls/Vehicle.py create mode 100644 examples/Nature of Code/chapter05/Example_5_04_Flow_Field/Example_5_04_Flow_Field.py create mode 100644 examples/Nature of Code/chapter05/Example_5_04_Flow_Field/FlowField.py create mode 100644 examples/Nature of Code/chapter05/Example_5_04_Flow_Field/Vehicle.py create mode 100644 examples/Nature of Code/chapter05/Example_5_05_Create_Path_Object/Example_5_05_Create_Path_Object.py create mode 100644 examples/Nature of Code/chapter05/Example_5_05_Create_Path_Object/Path.py create mode 100644 examples/Nature of Code/chapter05/Example_5_06_Simple_Path_Following/Example_5_06_Simple_Path_Following.py create mode 100644 examples/Nature of Code/chapter05/Example_5_06_Simple_Path_Following/Path.py create mode 100644 examples/Nature of Code/chapter05/Example_5_06_Simple_Path_Following/Vehicle.py create mode 100644 examples/Nature of Code/chapter05/Example_5_07_Multiple_Segments/Example_5_07_Multiple_Segments.py create mode 100644 examples/Nature of Code/chapter05/Example_5_07_Multiple_Segments/Path.py create mode 100644 examples/Nature of Code/chapter05/Example_5_08_Path_Following/Example_5_08_Path_Following.py create mode 100644 examples/Nature of Code/chapter05/Example_5_08_Path_Following/Path.py create mode 100644 examples/Nature of Code/chapter05/Example_5_08_Path_Following/Vehicle.py create mode 100644 examples/Nature of Code/chapter05/Example_5_09_Separation/Example_5_09_Separation.py create mode 100644 examples/Nature of Code/chapter05/Example_5_09_Separation/Vehicle.py create mode 100644 examples/Nature of Code/chapter05/Example_5_10_Combining_Seek_Separate/Example_5_10_Combining_Seek_Separate.py create mode 100644 examples/Nature of Code/chapter05/Example_5_10_Combining_Seek_Separate/Vehicle.py create mode 100644 examples/Nature of Code/chapter05/Example_5_11_Flocking/Boid.py create mode 100644 examples/Nature of Code/chapter05/Example_5_11_Flocking/Example_5_11_Flocking.py create mode 100644 examples/Nature of Code/chapter05/Example_5_11_Flocking/Flock.py create mode 100644 examples/Nature of Code/chapter05/Example_5_12_Bin_Lattice_Spatial_Separation/Boid.py create mode 100644 examples/Nature of Code/chapter05/Example_5_12_Bin_Lattice_Spatial_Separation/Example_5_12_Bin_Lattice_Spatial_Separation.py create mode 100644 examples/Nature of Code/chapter05/Example_5_12_Bin_Lattice_Spatial_Separation/Flock.py create mode 100644 examples/Nature of Code/chapter05/Example_5_13_QuadTree/Example_5_13_QuadTree.py create mode 100644 examples/Nature of Code/chapter05/Example_5_13_QuadTree/QuadTree.py create mode 100644 examples/Nature of Code/chapter05/Example_5_14_COS_SIN_LUT/Example_5_14_COS_SIN_LUT.py create mode 100644 examples/Nature of Code/chapter07/Example_7_01_Wolfram_Elementary_Cellular_Automata/Example_7_01_Wolfram_Elementary_Cellular_Automata.py create mode 100644 examples/Nature of Code/chapter07/Example_7_02_Game_of_Life/Example_7_02_Game_of_Life.py create mode 100644 examples/Nature of Code/chapter07/Example_7_03_Object_Oriented_Game_of_Life/Cell.py create mode 100644 examples/Nature of Code/chapter07/Example_7_03_Object_Oriented_Game_of_Life/Example_7_03_Object_Oriented_Game_of_Life.py create mode 100644 examples/Nature of Code/chapter08/Example_8_01_Recursive_Circles_Once/Example_8_01_Recursive_Circles_Once.py create mode 100644 examples/Nature of Code/chapter08/Example_8_2_Recursive_Circles_Twice/Example_8_2_Recursive_Circles_Twice.py create mode 100644 examples/Nature of Code/chapter08/Example_8_3_Recursive_Circles_Four_Times/Example_8_3_Recursive_Circles_Four_Times.py create mode 100644 examples/Nature of Code/chapter08/Example_8_4_Cantor_Set/Example_8_4_Cantor_Set.py create mode 100644 examples/Nature of Code/chapter08/Example_8_5_Koch_Curve/Example_8_5_Koch_Curve.py create mode 100644 examples/Nature of Code/chapter08/Example_8_5_Koch_Curve/KochLine.py create mode 100644 examples/Nature of Code/chapter08/Example_8_6_Recursive_Tree/Example_8_6_Recursive_Tree.py create mode 100644 examples/Nature of Code/chapter08/Example_8_7_Stochastic_Tree/Example_8_7_Stochastic_Tree.py create mode 100644 examples/Nature of Code/chapter08/Example_8_8_LSystem_String_Only/Example_8_8_LSystem_String_Only.py create mode 100644 examples/Nature of Code/chapter08/Example_8_9_LSystem/Example_8_9_LSystem.py create mode 100644 examples/Nature of Code/chapter08/Example_8_9_LSystem/LSystem.py create mode 100644 examples/Nature of Code/chapter08/Example_8_9_LSystem/Turtle.py create mode 100644 examples/Nature of Code/chapter11/Example_11_1_Flappy_Bird/Bird.py create mode 100644 examples/Nature of Code/chapter11/Example_11_1_Flappy_Bird/Example_11_1_Flappy_Bird.py create mode 100644 examples/Nature of Code/chapter11/Example_11_1_Flappy_Bird/Pipe.py diff --git a/.gitignore b/.gitignore index 6b3055d..61438fb 100644 --- a/.gitignore +++ b/.gitignore @@ -31,5 +31,4 @@ debug/ # Work in progress examples/Generative Gestaltung -examples/Nature of Code sketches/ \ No newline at end of file diff --git a/examples/Nature of Code/chapter00/Example_0_01_Random_Walk/Example_0_01_Random_Walk.py b/examples/Nature of Code/chapter00/Example_0_01_Random_Walk/Example_0_01_Random_Walk.py new file mode 100644 index 0000000..86837d4 --- /dev/null +++ b/examples/Nature of Code/chapter00/Example_0_01_Random_Walk/Example_0_01_Random_Walk.py @@ -0,0 +1,33 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter0/Example_0_1_Random_Walk/Example_0_1_Random_Walk.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 0-1: Random Walk +""" + +from pycreative.app import Sketch + +class Example_0_1_Random_Walk(Sketch): + def setup(self): + self.size(640, 360) + self.walker_x = self.width // 2 + self.walker_y = self.height // 2 + self.background(255) # White background + + def draw(self): + self.stroke(0) # Black stroke + self.stroke_weight(1) + self.point(self.walker_x, self.walker_y) + choice = int(self.random(4)) + if choice == 0: + self.walker_x += 1 + elif choice == 1: + self.walker_x -= 1 + elif choice == 2: + self.walker_y += 1 + else: + self.walker_y -= 1 diff --git a/examples/Nature of Code/chapter00/Example_0_02_Random_Distribution/Example_0_02_Random_Distribution.py b/examples/Nature of Code/chapter00/Example_0_02_Random_Distribution/Example_0_02_Random_Distribution.py new file mode 100644 index 0000000..d131a03 --- /dev/null +++ b/examples/Nature of Code/chapter00/Example_0_02_Random_Distribution/Example_0_02_Random_Distribution.py @@ -0,0 +1,33 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter0/Example_0_2_Random_Distribution/Example_0_2_Random_Distribution.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 0-2: Random Distribution +""" + +from pycreative.app import Sketch + +class Example_0_2_Random_Distribution(Sketch): + def setup(self) -> None: + self.size(640, 360) + self.total = 20 + self.random_counts = [0] * self.total + + def draw(self) -> None: + self.background(255) + index = int(self.random(self.total)) + self.random_counts[index] += 1 + + # Draw a rectangle to graph results + self.stroke(0) + self.stroke_weight(2) + self.fill(127) + w = self.width / len(self.random_counts) + + for x in range(len(self.random_counts)): + rc = self.random_counts[x] + self.rect(x * w, self.height - rc, w - 1, rc) diff --git a/examples/Nature of Code/chapter00/Example_0_03_Random_Walk_Tends_To_Right/Example_0_03_Random_Walk_Tends_To_Right.py b/examples/Nature of Code/chapter00/Example_0_03_Random_Walk_Tends_To_Right/Example_0_03_Random_Walk_Tends_To_Right.py new file mode 100644 index 0000000..bf564bf --- /dev/null +++ b/examples/Nature of Code/chapter00/Example_0_03_Random_Walk_Tends_To_Right/Example_0_03_Random_Walk_Tends_To_Right.py @@ -0,0 +1,47 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter0/Example_0_3_Random_Walk_Tends_To_Right/Example_0_3_Random_Walk_Tends_To_Right.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 0-3: Random Walk Tends to Right +""" + +from pycreative.app import Sketch + + +class Example_0_3_Random_Walk_Tends_To_Right(Sketch): + def setup(self): + self.size(640, 360) + self.background(255) + self.walker = Walker(self, int(self.width / 2), int(self.height / 2)) + + def draw(self): + self.walker.update() + self.walker.draw() + +class Walker(): + def __init__(self, sketch, x=0, y=0): + self.sketch = sketch + self.x = x + self.y = y + + def update(self): + r = self.sketch.random() + # A 40% of moving to the right! + if r < 0.4: + self.x += 1 + elif r < 0.6: + self.x -= 1 + elif r < 0.8: + self.y += 1 + else: + self.y -= 1 + self.x = self.sketch.constrain(self.x, 0, self.sketch.width - 1) + self.y = self.sketch.constrain(self.y, 0, self.sketch.height - 1) + + def draw(self): + self.sketch.stroke(0) + self.sketch.point(self.x, self.y) diff --git a/examples/Nature of Code/chapter00/Example_0_04_Gaussian_Distribution/Example_0_04_Gaussian_Distribution.py b/examples/Nature of Code/chapter00/Example_0_04_Gaussian_Distribution/Example_0_04_Gaussian_Distribution.py new file mode 100644 index 0000000..1ab5801 --- /dev/null +++ b/examples/Nature of Code/chapter00/Example_0_04_Gaussian_Distribution/Example_0_04_Gaussian_Distribution.py @@ -0,0 +1,25 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter0/Example_0_4_Gaussian_Distribution/Example_0_4_Gaussian_Distribution.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 0-4: Gaussian Distribution +""" + +from pycreative.app import Sketch + + +class Example_0_4_Gaussian_Distribution(Sketch): + def setup(self): + self.size(640, 360) + self.background(255) + + def draw(self): + # A normal distribution with mean 320 and standard deviation 60 + x = self.random_gaussian() * 60 + 320 + self.no_stroke() + self.fill(1, 10) + self.circle(x, 120, 16) diff --git a/examples/Nature of Code/chapter00/Example_0_05_Accept_Reject_Distribution/Example_0_05_Accept_Reject_Distribution.py b/examples/Nature of Code/chapter00/Example_0_05_Accept_Reject_Distribution/Example_0_05_Accept_Reject_Distribution.py new file mode 100644 index 0000000..f837e70 --- /dev/null +++ b/examples/Nature of Code/chapter00/Example_0_05_Accept_Reject_Distribution/Example_0_05_Accept_Reject_Distribution.py @@ -0,0 +1,49 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter0/Example_0_5_Accept_Reject_Distribution/Example_0_5_Accept_Reject_Distribution.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 0-5: Accept-Reject Distribution +""" + +from pycreative.app import Sketch + + +class Example_0_5_Accept_Reject_Distribution(Sketch): + def setup(self) -> None: + self.size(640, 360) + self.total = 20 + self.random_counts = [0] * self.total + + def draw(self) -> None: + self.background(255) + index = int(self.accept_reject() * len(self.random_counts)) + self.random_counts[index] += 1 + + # Draw a rectangle to graph results + self.stroke(0) + self.stroke_weight(2) + self.fill(127) + w = self.width / len(self.random_counts) + + for x in range(len(self.random_counts)): + rc = self.random_counts[x] + self.rect(x * w, self.height - rc, w - 1, rc) + + def accept_reject(self) -> float: + # An algorithm for picking a random number based on monte carlo method + # Here probability is determined by formula y = x + while True: + # Pick a random value. + r1 = self.random(1) + # Assign a probability. + probability = r1 + # Pick a second random value. + r2 = self.random(1) + + # Does it qualify? If so, we’re done! + if r2 < probability: + return r1 diff --git a/examples/Nature of Code/chapter00/Example_0_06_Perlin_Noise_Walker/Example_0_06_Perlin_Noise_Walker.py b/examples/Nature of Code/chapter00/Example_0_06_Perlin_Noise_Walker/Example_0_06_Perlin_Noise_Walker.py new file mode 100644 index 0000000..9a24e52 --- /dev/null +++ b/examples/Nature of Code/chapter00/Example_0_06_Perlin_Noise_Walker/Example_0_06_Perlin_Noise_Walker.py @@ -0,0 +1,49 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter0/Example_0_6_Perlin_Noise_Walker/Example_0_6_Perlin_Noise_Walker.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 0-6: Perlin Noise Walker +""" + +from pycreative.app import Sketch + + +class Example_0_6_Perlin_Noise_Walker(Sketch): + def setup(self): + self.size(640, 360) + self.background(255) + self.walker = Walker(self) + + def draw(self): + self.walker.update() + self.walker.draw() + +class Walker: + def __init__(self, sketch): + self.sketch = sketch + # Keep original Processing offsets but add a tiny fractional offset + # to avoid starting at an exact lattice boundary which can produce + # symmetric/biased derivatives in some Perlin implementations. + self.tx = 0.0 + self.ty = 10000.1 + self.x = 0 + self.y = 0 + + def update(self): + # x- and y-position mapped from noise + self.x = self.sketch.map(self.sketch.noise(self.tx), 0, 1, 0, self.sketch.width) + self.y = self.sketch.map(self.sketch.noise(self.ty), 0, 1, 0, self.sketch.height) + + # Move forward through time. + self.tx += 0.01 + self.ty += 0.01 + + def draw(self): + self.sketch.stroke_weight(2) + self.sketch.fill(127) + self.sketch.stroke(0) + self.sketch.ellipse(self.x, self.y, 48) \ No newline at end of file diff --git a/examples/Nature of Code/chapter01/Example_1_01_Bouncing_Ball_No_Vectors/Example_1_01_Bouncing_Ball_No_Vectors.py b/examples/Nature of Code/chapter01/Example_1_01_Bouncing_Ball_No_Vectors/Example_1_01_Bouncing_Ball_No_Vectors.py new file mode 100644 index 0000000..7a6e95c --- /dev/null +++ b/examples/Nature of Code/chapter01/Example_1_01_Bouncing_Ball_No_Vectors/Example_1_01_Bouncing_Ball_No_Vectors.py @@ -0,0 +1,40 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter1/Example_1_1_Bouncing_Ball_No_Vectors/Example_1_1_Bouncing_Ball_No_Vectors.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 1-1: Bouncing Ball, no PVector! +""" + +from pycreative.app import Sketch + + +class Example_1_1_Bouncing_Ball_No_Vectors(Sketch): + def setup(self): + self.size(640, 360) + self.x = 100 + self.y = 100 + self.xspeed = 2.5 + self.yspeed = 2 + + def draw(self): + self.background(255) + + # Move the ball according to its speed. + self.x = self.x + self.xspeed + self.y = self.y + self.yspeed + + # Check for bouncing. + if self.x > self.width or self.x < 0: + self.xspeed = self.xspeed * -1 + if self.y > self.height or self.y < 0: + self.yspeed = self.yspeed * -1 + + self.stroke(0) + self.fill(127) + self.stroke_weight(2) + # Draw the ball at the position (x,y). + self.circle(self.x, self.y, 48) diff --git a/examples/Nature of Code/chapter01/Example_1_02_Bouncing_Ball_Vectors/Example_1_02_Bouncing_Ball_Vectors.py b/examples/Nature of Code/chapter01/Example_1_02_Bouncing_Ball_Vectors/Example_1_02_Bouncing_Ball_Vectors.py new file mode 100644 index 0000000..b36d293 --- /dev/null +++ b/examples/Nature of Code/chapter01/Example_1_02_Bouncing_Ball_Vectors/Example_1_02_Bouncing_Ball_Vectors.py @@ -0,0 +1,32 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter1/Example_1_2_Bouncing_Ball_Vectors/Example_1_2_Bouncing_Ball_Vectors.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 1-2: Bouncing Ball, with PVector! +""" + +from pycreative.app import Sketch + +class Example_1_2_Bouncing_Ball_Vectors(Sketch): + def setup(self): + self.size(640, 360) + self.position = self.pvector(100, 100) + self.velocity = self.pvector(2.5, 2) + + def draw(self): + self.background(255) + self.position.add(self.velocity) + + if self.position.x > self.width or self.position.x < 0: + self.velocity.x = self.velocity.x * -1 + if self.position.y > self.height or self.position.y < 0: + self.velocity.y = self.velocity.y * -1 + + self.stroke(0) + self.fill(127) + self.stroke_weight(2) + self.circle(self.position.x, self.position.y, 48) diff --git a/examples/Nature of Code/chapter01/Example_1_03_Vector_Subtraction/Example_1_03_Vector_Subtraction.py b/examples/Nature of Code/chapter01/Example_1_03_Vector_Subtraction/Example_1_03_Vector_Subtraction.py new file mode 100644 index 0000000..fe65f81 --- /dev/null +++ b/examples/Nature of Code/chapter01/Example_1_03_Vector_Subtraction/Example_1_03_Vector_Subtraction.py @@ -0,0 +1,34 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter1/Example_1_3_Vector_Subtraction/Example_1_3_Vector_Subtraction.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 1-3: Vector subtraction +""" + +from pycreative.app import Sketch + + +class Example_1_3_Vector_Subtraction(Sketch): + def setup(self): + self.size(640, 360) + + def draw(self): + self.background(255) + + mouse = self.pvector(self.mouse_x, self.mouse_y) + center = self.pvector(self.width / 2, self.height / 2) + + self.stroke_weight(4) + self.stroke(200) + self.line(0, 0, mouse.x, mouse.y) + self.line(0, 0, center.x, center.y) + + mouse.sub(center) + + self.stroke(0) + self.translate(self.width / 2, self.height / 2) + self.line(0, 0, mouse.x, mouse.y) diff --git a/examples/Nature of Code/chapter01/Example_1_04_Vector_Multiplication/Example_1_04_Vector_Multiplication.py b/examples/Nature of Code/chapter01/Example_1_04_Vector_Multiplication/Example_1_04_Vector_Multiplication.py new file mode 100644 index 0000000..aa8c5cb --- /dev/null +++ b/examples/Nature of Code/chapter01/Example_1_04_Vector_Multiplication/Example_1_04_Vector_Multiplication.py @@ -0,0 +1,36 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter1/Example_1_4_Vector_Multiplication/Example_1_4_Vector_Multiplication.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 1-4: Vector Multiplication +""" + +from pycreative.app import Sketch + + +class Example_1_4_Vector_Multiplication(Sketch): + def setup(self): + self.size(640, 360) + + def draw(self): + self.background(255) + + mouse = self.pvector(self.mouse_x, self.mouse_y) + center = self.pvector(self.width / 2, self.height / 2) + mouse.sub(center) + + self.translate(self.width / 2, self.height / 2) + self.stroke_weight(2) + self.stroke(200) + self.line(0, 0, mouse.x, mouse.y) + + # Multiplying a vector! The vector is now half its original size (multiplied by 0.5). + mouse.mult(0.5) + + self.stroke(0) + self.stroke_weight(4) + self.line(0, 0, mouse.x, mouse.y) diff --git a/examples/Nature of Code/chapter01/Example_1_05_Vector_Magnitude/Example_1_05_Vector_Magnitude.py b/examples/Nature of Code/chapter01/Example_1_05_Vector_Magnitude/Example_1_05_Vector_Magnitude.py new file mode 100644 index 0000000..0f49ab9 --- /dev/null +++ b/examples/Nature of Code/chapter01/Example_1_05_Vector_Magnitude/Example_1_05_Vector_Magnitude.py @@ -0,0 +1,33 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter1/Example_1_5_Vector_Magnitude/Example_1_5_Vector_Magnitude.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 1-5: Vector magnitude +""" + +from pycreative.app import Sketch + + +class Example_1_5_Vector_Magnitude(Sketch): + def setup(self): + self.size(640, 360) + + def draw(self): + self.background(255) + + mouse = self.pvector(self.mouse_x, self.mouse_y) + center = self.pvector(self.width / 2, self.height / 2) + mouse.sub(center) + + # The magnitude (i.e. length) of a vector can be accessed via the mag() function. + # Here it is used as the width of a rectangle drawn at the top of the window. + m = mouse.mag() + self.fill(0) + self.rect(10, 10, m, 10) + + self.translate(self.width / 2, self.height / 2) + self.line(0, 0, mouse.x, mouse.y) diff --git a/examples/Nature of Code/chapter01/Example_1_06_Vector_Normalize/Example_1_06_Vector_Normalize.py b/examples/Nature of Code/chapter01/Example_1_06_Vector_Normalize/Example_1_06_Vector_Normalize.py new file mode 100644 index 0000000..a4a5122 --- /dev/null +++ b/examples/Nature of Code/chapter01/Example_1_06_Vector_Normalize/Example_1_06_Vector_Normalize.py @@ -0,0 +1,40 @@ +"""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter1/Example_1_6_Vector_Normalize/Example_1_6_Vector_Normalize.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 1-6: Vector normalize +""" + +from pycreative.app import Sketch + + +class Example_1_6_Vector_Normalize(Sketch): + def setup(self): + self.size(640, 360) + + def draw(self): + self.background(255) + + mouse = self.pvector(self.mouse_x, self.mouse_y) + center = self.pvector(self.width / 2, self.height / 2) + mouse.sub(center) + + self.translate(self.width / 2, self.height / 2) + self.stroke(200) + self.stroke_weight(2) + self.line(0, 0, mouse.x, mouse.y) + + # Normalize the vector + mouse.normalize() + + # Multiply its length by 50 + mouse.mult(50) + + # Draw the resulting vector + self.stroke(0) + self.stroke_weight(8) + self.line(0, 0, mouse.x, mouse.y) diff --git a/examples/Nature of Code/chapter01/Example_1_07_Motion_101_Velocity/Example_1_07_Motion_101_Velocity.py b/examples/Nature of Code/chapter01/Example_1_07_Motion_101_Velocity/Example_1_07_Motion_101_Velocity.py new file mode 100644 index 0000000..11e8f12 --- /dev/null +++ b/examples/Nature of Code/chapter01/Example_1_07_Motion_101_Velocity/Example_1_07_Motion_101_Velocity.py @@ -0,0 +1,23 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter1/Example_1_7_Motion_101_Velocity/Example_1_7_Motion_101_Velocity.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com +""" + +from pycreative.app import Sketch +from Mover import Mover + +class Example_1_7_Motion_101_Velocity(Sketch): + def setup(self): + self.size(640, 360) + self.mover = Mover(self) + + def draw(self): + self.background(255) + + self.mover.update() + self.mover.checkEdges() + self.mover.show() diff --git a/examples/Nature of Code/chapter01/Example_1_07_Motion_101_Velocity/Mover.py b/examples/Nature of Code/chapter01/Example_1_07_Motion_101_Velocity/Mover.py new file mode 100644 index 0000000..1ac8717 --- /dev/null +++ b/examples/Nature of Code/chapter01/Example_1_07_Motion_101_Velocity/Mover.py @@ -0,0 +1,29 @@ +""" +Mover class for Example 1.7: Motion 101: Velocity +""" + +class Mover: + def __init__(self, sketch): + self.sketch = sketch + self.position = self.sketch.pvector(self.sketch.random(self.sketch.width), self.sketch.random(self.sketch.height)) + self.velocity = self.sketch.pvector(self.sketch.random(-2, 2), self.sketch.random(-2, 2)) + + def update(self): + self.position.add(self.velocity) + + def show(self): + self.sketch.stroke((0, 0, 0)) + self.sketch.stroke_weight(2) + self.sketch.fill(127) + self.sketch.circle(self.position.x, self.position.y, 48) + + def checkEdges(self): + if self.position.x > self.sketch.width: + self.position.x = 0 + elif self.position.x < 0: + self.position.x = self.sketch.width + + if self.position.y > self.sketch.height: + self.position.y = 0 + elif self.position.y < 0: + self.position.y = self.sketch.height \ No newline at end of file diff --git a/examples/Nature of Code/chapter01/Example_1_08_Motion_101_Velocity_And_Constant_Acceleration/Example_1_08_Motion_101_Velocity_And_Constant_Acceleration.py b/examples/Nature of Code/chapter01/Example_1_08_Motion_101_Velocity_And_Constant_Acceleration/Example_1_08_Motion_101_Velocity_And_Constant_Acceleration.py new file mode 100644 index 0000000..57e9a4c --- /dev/null +++ b/examples/Nature of Code/chapter01/Example_1_08_Motion_101_Velocity_And_Constant_Acceleration/Example_1_08_Motion_101_Velocity_And_Constant_Acceleration.py @@ -0,0 +1,26 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter1/Example_1_8_Motion_101_Velocity_And_Constant_Acceleration/Example_1_8_Motion_101_Velocity_And_Constant_Acceleration.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 1-8: Motion 101: Velocity and Constant Acceleration +""" + +from pycreative.app import Sketch +from Mover import Mover + + +class Example_1_8_Motion_101_Velocity_And_Constant_Acceleration(Sketch): + def setup(self): + self.size(640, 360) + self.mover = Mover(self) + + def draw(self): + self.background(255) + + self.mover.update() + self.mover.checkEdges() + self.mover.show() diff --git a/examples/Nature of Code/chapter01/Example_1_08_Motion_101_Velocity_And_Constant_Acceleration/Mover.py b/examples/Nature of Code/chapter01/Example_1_08_Motion_101_Velocity_And_Constant_Acceleration/Mover.py new file mode 100644 index 0000000..da4b3e8 --- /dev/null +++ b/examples/Nature of Code/chapter01/Example_1_08_Motion_101_Velocity_And_Constant_Acceleration/Mover.py @@ -0,0 +1,33 @@ +""" +Mover class for Example 1.8: Motion 101: Velocity and Constant Acceleration +""" + +class Mover: + def __init__(self, sketch): + self.sketch = sketch + self.position = self.sketch.pvector(self.sketch.width / 2, self.sketch.height / 2) + self.velocity = self.sketch.pvector(0, 0) + self.acceleration = self.sketch.pvector(-0.001, 0.01) + self.top_speed = 10 + + def update(self): + self.velocity.add(self.acceleration) + self.velocity.limit(self.top_speed) + self.position.add(self.velocity) + + def show(self): + self.sketch.stroke((0, 0, 0)) + self.sketch.stroke_weight(2) + self.sketch.fill(127) + self.sketch.circle(self.position.x, self.position.y, 48) + + def checkEdges(self): + if self.position.x > self.sketch.width: + self.position.x = 0 + elif self.position.x < 0: + self.position.x = self.sketch.width + + if self.position.y > self.sketch.height: + self.position.y = 0 + elif self.position.y < 0: + self.position.y = self.sketch.height diff --git a/examples/Nature of Code/chapter01/Example_1_09_Motion_101_Velocity_And_Random_Acceleration/Example_1_09_Motion_101_Velocity_And_Random_Acceleration.py b/examples/Nature of Code/chapter01/Example_1_09_Motion_101_Velocity_And_Random_Acceleration/Example_1_09_Motion_101_Velocity_And_Random_Acceleration.py new file mode 100644 index 0000000..53ad48d --- /dev/null +++ b/examples/Nature of Code/chapter01/Example_1_09_Motion_101_Velocity_And_Random_Acceleration/Example_1_09_Motion_101_Velocity_And_Random_Acceleration.py @@ -0,0 +1,26 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter1/Example_1_9_Motion_101_Velocity_And_Random_Acceleration/Example_1_9_Motion_101_Velocity_And_Random_Acceleration.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 1-9: Motion 101: Velocity and Random Acceleration +""" + +from pycreative.app import Sketch +from Mover import Mover + + +class Example_1_9_Motion_101_Velocity_And_Random_Acceleration(Sketch): + def setup(self): + self.size(640, 360) + self.mover = Mover(self) + + def draw(self): + self.background(255) + + self.mover.update() + self.mover.checkEdges() + self.mover.draw() diff --git a/examples/Nature of Code/chapter01/Example_1_09_Motion_101_Velocity_And_Random_Acceleration/Mover.py b/examples/Nature of Code/chapter01/Example_1_09_Motion_101_Velocity_And_Random_Acceleration/Mover.py new file mode 100644 index 0000000..e2fc1e9 --- /dev/null +++ b/examples/Nature of Code/chapter01/Example_1_09_Motion_101_Velocity_And_Random_Acceleration/Mover.py @@ -0,0 +1,37 @@ +""" +Mover class for Example 1-9: Motion 101: Velocity and Random Acceleration +""" + +class Mover: + def __init__(self, sketch): + self.sketch = sketch + self.position = self.sketch.pvector(self.sketch.width / 2, self.sketch.height / 2) + self.velocity = self.sketch.pvector(0, 0) + self.acceleration = self.sketch.pvector(0, 0) + self.topspeed = 5 + + def update(self): + # The random2D() function returns a unit vector pointing in a random direction. + self.acceleration = self.sketch.pvector.random2D() + self.acceleration.mult(self.sketch.random(2)) + + self.velocity.add(self.acceleration) + self.velocity.limit(self.topspeed) + self.position.add(self.velocity) + + def draw(self): + self.sketch.stroke(0) + self.sketch.stroke_weight(2) + self.sketch.fill(127) + self.sketch.circle(self.position.x, self.position.y, 48) + + def checkEdges(self): + if self.position.x > self.sketch.width: + self.position.x = 0 + elif self.position.x < 0: + self.position.x = self.sketch.width + + if self.position.y > self.sketch.height: + self.position.y = 0 + elif self.position.y < 0: + self.position.y = self.sketch.height \ No newline at end of file diff --git a/examples/Nature of Code/chapter01/Example_1_10_Accelerating_Towards_Mouse/Example_1_10_Accelerating_Towards_Mouse.py b/examples/Nature of Code/chapter01/Example_1_10_Accelerating_Towards_Mouse/Example_1_10_Accelerating_Towards_Mouse.py new file mode 100644 index 0000000..4c3d282 --- /dev/null +++ b/examples/Nature of Code/chapter01/Example_1_10_Accelerating_Towards_Mouse/Example_1_10_Accelerating_Towards_Mouse.py @@ -0,0 +1,24 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter1/Example_1_10_Accelerating_Towards_Mouse/Example_1_10_Accelerating_Towards_Mouse.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 1-10: Accelerating towards the mouse +""" + +from pycreative.app import Sketch +from Mover import Mover + + +class Example_1_10_Accelerating_Towards_Mouse(Sketch): + def setup(self): + self.size(640, 360) + self.mover = Mover(self) + + def draw(self): + self.background(255) + self.mover.update() + self.mover.draw() diff --git a/examples/Nature of Code/chapter01/Example_1_10_Accelerating_Towards_Mouse/Mover.py b/examples/Nature of Code/chapter01/Example_1_10_Accelerating_Towards_Mouse/Mover.py new file mode 100644 index 0000000..cc244f7 --- /dev/null +++ b/examples/Nature of Code/chapter01/Example_1_10_Accelerating_Towards_Mouse/Mover.py @@ -0,0 +1,40 @@ +""" +Mover class for Example 1.10: Accelerating Towards Mouse +""" + +class Mover: + def __init__(self, sketch): + self.sketch = sketch + self.position = self.sketch.pvector(self.sketch.width / 2, self.sketch.height / 2) + self.velocity = self.sketch.pvector(0, 0) + self.acceleration = self.sketch.pvector(0, 0) + self.topspeed = 5 + + def update(self): + mouse = self.sketch.pvector(self.sketch.mouse_x, self.sketch.mouse_y) + # Step 1: Compute direction + dir = self.sketch.pvector.sub(mouse, self.position) + + # Step 2: Normalize + dir.normalize() + + # Step 3: Scale + dir.mult(0.2) + + # Steps 2 and 3 could be combined into: + # dir.setMag(0.2); + + # Step 4: Accelerate + self.acceleration = dir + + self.velocity.add(self.acceleration) + self.velocity.limit(self.topspeed) + self.position.add(self.velocity) + self.position.x = self.sketch.constrain(self.position.x, 0, self.sketch.width) + self.position.y = self.sketch.constrain(self.position.y, 0, self.sketch.height) + + def draw(self): + self.sketch.stroke((0, 0, 0)) + self.sketch.stroke_weight(2) + self.sketch.fill(127) + self.sketch.circle(self.position.x, self.position.y, 48) diff --git a/examples/Nature of Code/chapter02/Example_2_01_Forces/Example_2_01_Forces.py b/examples/Nature of Code/chapter02/Example_2_01_Forces/Example_2_01_Forces.py new file mode 100644 index 0000000..8fa73b1 --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_01_Forces/Example_2_01_Forces.py @@ -0,0 +1,42 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter2/Example_2_1_Forces/Example_2_1_Forces.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 2-1: Forces +""" + +from pycreative.app import Sketch +from Mover import Mover + + +class Example_2_01_Forces(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 2.1: Forces") + self.mover = Mover(self) + self.mouse_is_pressed = False + print("Click mouse to apply wind force.") + + def draw(self): + self.background(255) + + gravity = self.pvector(0, 0.1) + self.mover.apply_force(gravity) + + if self.mouse_is_pressed: + wind = self.pvector(0.1, 0) + self.mover.apply_force(wind) + + self.mover.update() + self.mover.display() + self.mover.check_edges() + + def mouse_pressed(self): + self.mouse_is_pressed = True + + def mouse_released(self): + self.mouse_is_pressed = False diff --git a/examples/Nature of Code/chapter02/Example_2_01_Forces/Mover.py b/examples/Nature of Code/chapter02/Example_2_01_Forces/Mover.py new file mode 100644 index 0000000..fee1318 --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_01_Forces/Mover.py @@ -0,0 +1,37 @@ +""" +Mover class for Example 2.1: Forces +""" + +class Mover: + def __init__(self, sketch): + self.sketch = sketch + self.mass = 1 + self.position = self.sketch.pvector(self.sketch.width / 2, 30) + self.velocity = self.sketch.pvector(0, 0) + self.acceleration = self.sketch.pvector(0, 0) + + def apply_force(self, force): + f = self.sketch.pvector.div(force, self.mass) + self.acceleration.add(f) + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.acceleration.mult(0) + + def display(self): + self.sketch.stroke((0, 0, 0)) + self.sketch.stroke_weight(2) + self.sketch.fill((127, 127)) + self.sketch.ellipse(self.position.x, self.position.y, 48, 48) + + def check_edges(self): + if self.position.x > self.sketch.width: + self.position.x = self.sketch.width + self.velocity.x *= -1 + elif self.position.x < 0: + self.velocity.x *= -1 + self.position.x = 0 + if self.position.y > self.sketch.height: + self.velocity.y *= -1 + self.position.y = self.sketch.height diff --git a/examples/Nature of Code/chapter02/Example_2_02_Forces_Acting_On_Two_Objects/Example_2_02_Forces_Acting_On_Two_Objects.py b/examples/Nature of Code/chapter02/Example_2_02_Forces_Acting_On_Two_Objects/Example_2_02_Forces_Acting_On_Two_Objects.py new file mode 100644 index 0000000..4c218b4 --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_02_Forces_Acting_On_Two_Objects/Example_2_02_Forces_Acting_On_Two_Objects.py @@ -0,0 +1,55 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter2/Example_2_2_Forces_Acting_On_Two_Objects/Example_2_2_Forces_Acting_On_Two_Objects.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 2-2: Forces Acting on Two Objects +""" + +from pycreative.app import Sketch +from Mover import Mover + + +class Example_2_02_Forces_Acting_On_Two_Objects(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 2.2: Forces Acting on Two Objects") + # A large Mover on the left side of the window + self.moverA = Mover(self) + self.moverA.position = self.pvector(200, 30) + self.moverA.mass = 10 + # A smaller Mover on the right side of the window + self.moverB = Mover(self) + self.moverB.position = self.pvector(440, 30) + self.moverB.mass = 2 + self.mouse_is_pressed = False + print("Click mouse to apply wind force.") + + def draw(self): + self.background(255) + + gravity = self.pvector(0, 0.1) + self.moverA.apply_force(gravity) + self.moverB.apply_force(gravity) + + if self.mouse_is_pressed: + wind = self.pvector(0.1, 0) + self.moverA.apply_force(wind) + self.moverB.apply_force(wind) + + self.moverA.update() + self.moverA.draw() + self.moverA.check_edges() + + self.moverB.update() + self.moverB.draw() + self.moverB.check_edges() + + def mouse_pressed(self): + self.mouse_is_pressed = True + + def mouse_released(self): + self.mouse_is_pressed = False diff --git a/examples/Nature of Code/chapter02/Example_2_02_Forces_Acting_On_Two_Objects/Mover.py b/examples/Nature of Code/chapter02/Example_2_02_Forces_Acting_On_Two_Objects/Mover.py new file mode 100644 index 0000000..8fb87f2 --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_02_Forces_Acting_On_Two_Objects/Mover.py @@ -0,0 +1,37 @@ +""" +Mover class for Example 2.1: Forces +""" + +class Mover: + def __init__(self, sketch): + self.sketch = sketch + self.mass = 1 + self.position = self.sketch.pvector(self.sketch.width / 2, 30) + self.velocity = self.sketch.pvector(0, 0) + self.acceleration = self.sketch.pvector(0, 0) + + def apply_force(self, force): + f = self.sketch.pvector.div(force, self.mass) + self.acceleration.add(f) + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.acceleration.mult(0) + + def draw(self): + self.sketch.stroke((0, 0, 0)) + self.sketch.stroke_weight(2) + self.sketch.fill((127, 127, 127)) + self.sketch.ellipse(self.position.x, self.position.y, self.mass * 16) + + def check_edges(self): + if self.position.x > self.sketch.width: + self.position.x = self.sketch.width + self.velocity.x *= -1 + elif self.position.x < 0: + self.velocity.x *= -1 + self.position.x = 0 + if self.position.y > self.sketch.height: + self.velocity.y *= -1 + self.position.y = self.sketch.height diff --git a/examples/Nature of Code/chapter02/Example_2_03_Gravity_Scaled_By_Mass/Example_2_03_Gravity_Scaled_By_Mass.py b/examples/Nature of Code/chapter02/Example_2_03_Gravity_Scaled_By_Mass/Example_2_03_Gravity_Scaled_By_Mass.py new file mode 100644 index 0000000..1c30c9a --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_03_Gravity_Scaled_By_Mass/Example_2_03_Gravity_Scaled_By_Mass.py @@ -0,0 +1,55 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter2/Example_2_3_Gravity_Scaled_By_Mass/Example_2_3_Gravity_Scaled_By_Mass.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 2-3: Gravity Scaled by Mass +""" + +from pycreative.app import Sketch +from Mover import Mover + + +class Example_2_03_Gravity_Scaled_By_Mass(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 2.3: Gravity Scaled by Mass") + # A large Mover on the left side of the window + self.moverA = Mover(self, 200, 30, 10) + # A smaller Mover on the right side of the window + self.moverB = Mover(self, 440, 30, 2) + self.mouse_is_pressed = False + + def draw(self): + self.background(255) + + gravity = self.pvector(0, 0.1) + + gravityA = gravity.copy().mult(self.moverA.mass) + self.moverA.apply_force(gravityA) + + gravityB = gravity.copy().mult(self.moverB.mass) + self.moverB.apply_force(gravityB) + + if self.mouse_is_pressed: + wind = self.pvector(0.1, 0) + self.moverA.apply_force(wind) + self.moverB.apply_force(wind) + + self.moverA.update() + self.moverA.draw() + self.moverA.check_edges() + + self.moverB.update() + self.moverB.draw() + self.moverB.check_edges() + + def mouse_pressed(self): + self.mouse_is_pressed = True + + def mouse_released(self): + self.mouse_is_pressed = False + \ No newline at end of file diff --git a/examples/Nature of Code/chapter02/Example_2_03_Gravity_Scaled_By_Mass/Mover.py b/examples/Nature of Code/chapter02/Example_2_03_Gravity_Scaled_By_Mass/Mover.py new file mode 100644 index 0000000..07b67b2 --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_03_Gravity_Scaled_By_Mass/Mover.py @@ -0,0 +1,39 @@ +""" +Mover class for Example 2.3: Gravity Scaled by Mass +""" + + +class Mover(): + def __init__(self, sketch, x=0, y=0, m=1): + self.sketch = sketch + self.mass = m + self.radius = self.mass * 8 + self.position = self.sketch.pvector(x, y) + self.velocity = self.sketch.pvector(0, 0) + self.acceleration = self.sketch.pvector(0, 0) + + def apply_force(self, force): + f = self.sketch.pvector.div(force, self.mass) + self.acceleration.add(f) + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.acceleration.mult(0) + + def draw(self): + self.sketch.stroke((0, 0, 0)) + self.sketch.stroke_weight(2) + self.sketch.fill((127, 127, 127)) + self.sketch.ellipse(self.position.x, self.position.y, self.radius * 2) + + def check_edges(self): + if self.position.x > self.sketch.width - self.radius: + self.position.x = self.sketch.width - self.radius + self.velocity.x *= -1 + elif self.position.x < self.radius: + self.position.x = self.radius + self.velocity.x *= -1 + if self.position.y > self.sketch.height - self.radius: + self.position.y = self.sketch.height - self.radius + self.velocity.y *= -1 diff --git a/examples/Nature of Code/chapter02/Example_2_04_Including_Friction/Example_2_04_Including_Friction.py b/examples/Nature of Code/chapter02/Example_2_04_Including_Friction/Example_2_04_Including_Friction.py new file mode 100644 index 0000000..94443f9 --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_04_Including_Friction/Example_2_04_Including_Friction.py @@ -0,0 +1,51 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter2/Example_2_4_Including_Friction/Example_2_4_Including_Friction.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 2-4: Including Friction +""" + +from pycreative.app import Sketch +from Mover import Mover + + +class Example_2_04_Including_Friction(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 2.4: Including Friction") + self.mover = Mover(self, self.width / 2, 30, 5) + self.mouse_is_pressed = False + + def draw(self): + self.background(255) + + gravity = self.pvector(0, 1) + # I should scale by mass to be more accurate, but this example only has one circle + self.mover.apply_force(gravity) + + if self.mouse_is_pressed: + wind = self.pvector(0.5, 0) + self.mover.apply_force(wind) + + if self.mover.contact_edge(): + c = 0.1 + friction = self.mover.velocity.copy() + friction.mult(-1) + friction.set_mag(c) + + # Apply the friction force vector to the object. + self.mover.apply_force(friction) + + self.mover.bounce_edges() + self.mover.update() + self.mover.draw() + + def mouse_pressed(self): + self.mouse_is_pressed = True + + def mouse_released(self): + self.mouse_is_pressed = False diff --git a/examples/Nature of Code/chapter02/Example_2_04_Including_Friction/Mover.py b/examples/Nature of Code/chapter02/Example_2_04_Including_Friction/Mover.py new file mode 100644 index 0000000..6e8df88 --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_04_Including_Friction/Mover.py @@ -0,0 +1,43 @@ +""" +Mover class for Example 2.4: Including Friction +""" + + +class Mover: + def __init__(self, sketch, x, y, m): + self.sketch = sketch + self.mass = m + self.radius = m * 8 + self.position = self.sketch.pvector(x, y) + self.velocity = self.sketch.pvector(0, 0) + self.acceleration = self.sketch.pvector(0, 0) + + def apply_force(self, force): + f = self.sketch.pvector.div(force, self.mass) + self.acceleration.add(f) + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.acceleration.mult(0) + + def draw(self): + self.sketch.stroke(0) + self.sketch.stroke_weight(2) + self.sketch.fill((127, 127, 127)) + self.sketch.ellipse(self.position.x, self.position.y, self.radius * 2) + + def contact_edge(self): + return self.position.y > self.sketch.height - self.radius - 1 + + def bounce_edges(self): + bounce = -0.9 + if self.position.x > self.sketch.width - self.radius: + self.position.x = self.sketch.width - self.radius + self.velocity.x *= bounce + elif self.position.x < self.radius: + self.position.x = self.radius + self.velocity.x *= bounce + if self.position.y > self.sketch.height - self.radius: + self.position.y = self.sketch.height - self.radius + self.velocity.y *= bounce diff --git a/examples/Nature of Code/chapter02/Example_2_05_Fluid_Resistance/Example_2_05_Fluid_Resistance.py b/examples/Nature of Code/chapter02/Example_2_05_Fluid_Resistance/Example_2_05_Fluid_Resistance.py new file mode 100644 index 0000000..ff59fcd --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_05_Fluid_Resistance/Example_2_05_Fluid_Resistance.py @@ -0,0 +1,44 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter2/Example_2_5_Fluid_Resistance/Example_2_5_Fluid_Resistance.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Forces (Gravity and Fluid Resistence) with Vectors +""" + +from pycreative.app import Sketch +from Mover import Mover +from Liquid import Liquid + + +class Example_2_05_Fluid_Resistance(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 2.5: Fluid Resistance") + self.reset() # Create movers + self.liquid = Liquid(self, 0, self.height / 2, self.width, self.height / 2, 0.1) + + def draw(self): + self.background(255) + self.liquid.draw() + + for mover in self.movers: + if self.liquid.contains(mover): + drag_force = self.liquid.calculate_drag(mover) + mover.apply_force(drag_force) + + gravity = self.pvector(0, 0.1 * mover.mass) + mover.apply_force(gravity) + + mover.update() + mover.draw() + mover.check_edges() + + def mouse_pressed(self): + self.reset() + + def reset(self): + self.movers = [Mover(self, 40 + i * 70, 0, self.random(0.5, 3)) for i in range(9)] diff --git a/examples/Nature of Code/chapter02/Example_2_05_Fluid_Resistance/Liquid.py b/examples/Nature of Code/chapter02/Example_2_05_Fluid_Resistance/Liquid.py new file mode 100644 index 0000000..b415c45 --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_05_Fluid_Resistance/Liquid.py @@ -0,0 +1,34 @@ +""" +Liquid class for simulating fluid resistance in a 2D environment. +""" + + +class Liquid: + def __init__(self, sketch, x=0.0, y=0.0, w=100.0, h=100.0, c=0.1): + self.sketch = sketch + self.x = x + self.y = y + self.w = w + self.h = h + self.c = c + + def contains(self, mover): + pos = mover.position + return (pos.x > self.x and + pos.x < self.x + self.w and + pos.y > self.y and + pos.y < self.y + self.h) + + def calculate_drag(self, mover): + speed = mover.velocity.mag() + drag_magnitude = self.c * speed * speed + + drag_force = mover.velocity.copy() + drag_force.mult(-1) + drag_force.set_mag(drag_magnitude) + return drag_force + + def draw(self): + self.sketch.no_stroke() + self.sketch.fill((220, 220, 220)) + self.sketch.rect(self.x, self.y, self.w, self.h) diff --git a/examples/Nature of Code/chapter02/Example_2_05_Fluid_Resistance/Mover.py b/examples/Nature of Code/chapter02/Example_2_05_Fluid_Resistance/Mover.py new file mode 100644 index 0000000..566507a --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_05_Fluid_Resistance/Mover.py @@ -0,0 +1,43 @@ +""" +Mover class for simulating an object with mass, position, velocity, and acceleration. +""" + + +class Mover: + def __init__(self, sketch, x=0, y=0, m=1.0): + self.sketch = sketch + self.mass = m + self.radius = m * 8 + self.position = self.sketch.pvector(x, y) + self.velocity = self.sketch.pvector(0, 0) + self.acceleration = self.sketch.pvector(0, 0) + + def on_mass(self, new_value): + self.mass = new_value + self.radius = new_value * 8 + print("mass set to", self.mass, "radius set to", self.radius) + + def set_radius(self, new_value): + self.mass = new_value / 8 + self.radius = new_value + print("radius set to", self.radius, "mass set to", self.mass) + + def apply_force(self, force): + f = self.sketch.pvector.div(force, self.mass) + self.acceleration.add(f) + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.acceleration.mult(0) + + def draw(self): + self.sketch.stroke((0, 0, 0)) + self.sketch.stroke_weight(2) + self.sketch.fill((127, 127)) + self.sketch.circle(self.position.x, self.position.y, self.radius * 2) + + def check_edges(self): + if self.position.y > self.sketch.height - self.radius: + self.velocity.y *= -0.9 # A little dampening when hitting the bottom + self.position.y = self.sketch.height - self.radius diff --git a/examples/Nature of Code/chapter02/Example_2_06_Attraction/Attractor.py b/examples/Nature of Code/chapter02/Example_2_06_Attraction/Attractor.py new file mode 100644 index 0000000..37108dd --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_06_Attraction/Attractor.py @@ -0,0 +1,60 @@ +""" +Attractor class for Example 2.6: Attraction +""" + + +class Attractor: + def __init__(self, sketch, x, y, m=20): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + self.mass = m + self.drag_offset = self.sketch.pvector(0, 0) + self.dragging = False + self.rollover = False + + def attract(self, mover): + # Calculate direction of force + force = self.sketch.pvector.sub(self.position, mover.position) + # Distance between objects + distance = force.mag() + # Limiting the distance to eliminate "extreme" results for very close or very far objects + distance = self.sketch.constrain(distance, 5, 25) + + # Calculate gravitational force magnitude + strength = (self.sketch.G * self.mass * mover.mass) / (distance * distance) + # Get force vector --> magnitude * direction + force.set_mag(strength) + return force + + def draw(self): + self.sketch.stroke_weight(4) + self.sketch.stroke(0) + if self.dragging: + self.sketch.fill(50) + elif self.rollover: + self.sketch.fill(100) + else: + self.sketch.fill(175, 200, 175) + self.sketch.ellipse(self.position.x, self.position.y, self.mass * 2) + + def handle_press(self, mx: float, my: float): + d = self.sketch.dist(mx, my, self.position.x, self.position.y) + if d < self.mass: + self.dragging = True + self.drag_offset.x = self.position.x - mx + self.drag_offset.y = self.position.y - my + + def handle_hover(self, mx: int, my: int): + d = self.sketch.dist(mx, my, self.position.x, self.position.y) + if d < self.mass: + self.rollover = True + else: + self.rollover = False + + def stop_dragging(self): + self.dragging = False + + def handle_drag(self, mx: float, my: float): + if self.dragging: + self.position.x = mx + self.drag_offset.x + self.position.y = my + self.drag_offset.y diff --git a/examples/Nature of Code/chapter02/Example_2_06_Attraction/Example_2_06_Attraction.py b/examples/Nature of Code/chapter02/Example_2_06_Attraction/Example_2_06_Attraction.py new file mode 100644 index 0000000..7c57051 --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_06_Attraction/Example_2_06_Attraction.py @@ -0,0 +1,46 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter2/Example_2_6_Attraction/Example_2_6_Attraction.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// A Mover and an Attractor +""" + +from pycreative.app import Sketch +from Attractor import Attractor +from Mover import Mover + + +class Example_2_06_Attraction(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 2.6: Attraction") + self.mover = Mover(self, 300, 50, 2) + self.attractor = Attractor(self, self.width / 2, self.height / 2) + self.G = 1 # gravitational constant (for global scaling) + + def draw(self): + self.background(255) + + force = self.attractor.attract(self.mover) + self.mover.apply_force(force) + self.mover.update() + + self.attractor.draw() + self.mover.draw() + + def mouse_moved(self): + self.attractor.handle_hover(self.mouse_x or 0, self.mouse_y or 0) + + def mouse_pressed(self): + self.attractor.handle_press(self.mouse_x or 0, self.mouse_y or 0) + + def mouse_dragged(self): + self.attractor.handle_hover(self.mouse_x or 0, self.mouse_y or 0) + self.attractor.handle_drag(self.mouse_x or 0, self.mouse_y or 0) + + def mouse_released(self): + self.attractor.stop_dragging() diff --git a/examples/Nature of Code/chapter02/Example_2_06_Attraction/Mover.py b/examples/Nature of Code/chapter02/Example_2_06_Attraction/Mover.py new file mode 100644 index 0000000..78d147c --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_06_Attraction/Mover.py @@ -0,0 +1,28 @@ +""" +Mover class for Example 2-6: Attraction +""" + + +class Mover: + def __init__(self, sketch, x, y, m): + self.sketch = sketch + self.mass = m + self.radius = m * 8 + self.position = self.sketch.pvector(x, y) + self.velocity = self.sketch.pvector(0, 0) + self.acceleration = self.sketch.pvector(0, 0) + + def apply_force(self, force): + f = self.sketch.pvector.div(force, self.mass) + self.acceleration.add(f) + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.acceleration.mult(0) + + def draw(self): + self.sketch.stroke(0) + self.sketch.stroke_weight(2) + self.sketch.fill(127) + self.sketch.ellipse(self.position.x, self.position.y, self.radius * 2) diff --git a/examples/Nature of Code/chapter02/Example_2_07_Attraction_With_Many_Movers/Attractor.py b/examples/Nature of Code/chapter02/Example_2_07_Attraction_With_Many_Movers/Attractor.py new file mode 100644 index 0000000..252abaa --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_07_Attraction_With_Many_Movers/Attractor.py @@ -0,0 +1,54 @@ +""" +Attractor class for Example 2.7: Attraction with Many Movers +""" + +class Attractor: + def __init__(self, sketch, x, y, m=20): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + self.mass = m + self.G = 1 + self.drag_offset = self.sketch.pvector(0, 0) + self.dragging = False + self.rollover = False + + def attract(self, mover): + force = self.position - mover.position + distance = force.mag() + distance = self.sketch.constrain(distance, 5, 25) + strength = (self.G * self.mass * mover.mass) / (distance * distance) + force.set_mag(strength) + return force + + def draw(self): + self.sketch.stroke_weight(4) + self.sketch.stroke((0, 0, 0)) + if self.dragging: + self.sketch.fill((50, 50, 50)) + elif self.rollover: + self.sketch.fill((100, 100, 100)) + else: + self.sketch.fill((175, 200, 200)) + self.sketch.circle(self.position.x, self.position.y, self.mass * 2) + + def handle_press(self, mx, my): + d = ((mx - self.position.x) ** 2 + (my - self.position.y) ** 2) ** 0.5 + if d < self.mass: + self.dragging = True + self.drag_offset.x = self.position.x - mx + self.drag_offset.y = self.position.y - my + + def handle_hover(self, mx, my): + d = ((mx - self.position.x) ** 2 + (my - self.position.y) ** 2) ** 0.5 + if d < self.mass: + self.rollover = True + else: + self.rollover = False + + def stop_dragging(self): + self.dragging = False + + def handle_drag(self, mx, my): + if self.dragging: + self.position.x = mx + self.drag_offset.x + self.position.y = my + self.drag_offset.y \ No newline at end of file diff --git a/examples/Nature of Code/chapter02/Example_2_07_Attraction_With_Many_Movers/Example_2_07_Attraction_With_Many_Movers.py b/examples/Nature of Code/chapter02/Example_2_07_Attraction_With_Many_Movers/Example_2_07_Attraction_With_Many_Movers.py new file mode 100644 index 0000000..41857d3 --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_07_Attraction_With_Many_Movers/Example_2_07_Attraction_With_Many_Movers.py @@ -0,0 +1,44 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter2/Example_2_7_Attraction_With_Many_Movers/Example_2_7_Attraction_With_Many_Movers.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 2.7: Attraction with Many Movers +""" + +from pycreative.app import Sketch +from Attractor import Attractor +from Mover import Mover + + +class Example_2_07_Attraction_With_Many_Movers(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 2.7: Attraction with Many Movers") + self.movers = [Mover(self, self.random(self.width), self.random(self.height), self.random(0.5, 3)) for _ in range(10)] + self.attractor = Attractor(self, self.width / 2, self.height / 2) + + def draw(self): + self.background((255, 255, 255)) + + self.attractor.draw() + + for mover in self.movers: + force = self.attractor.attract(mover) + mover.apply_force(force) + + mover.update() + mover.draw() + + def mouse_moved(self): + self.attractor.handle_hover(self.mouse_x or 0, self.mouse_y or 0) + + def mouse_pressed(self): + self.attractor.handle_press(self.mouse_x or 0, self.mouse_y or 0) + + def mouse_dragged(self): + self.attractor.handle_hover(self.mouse_x or 0, self.mouse_y or 0) + self.attractor.handle_drag(self.mouse_x or 0, self.mouse_y or 0) \ No newline at end of file diff --git a/examples/Nature of Code/chapter02/Example_2_07_Attraction_With_Many_Movers/Mover.py b/examples/Nature of Code/chapter02/Example_2_07_Attraction_With_Many_Movers/Mover.py new file mode 100644 index 0000000..5fef421 --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_07_Attraction_With_Many_Movers/Mover.py @@ -0,0 +1,27 @@ +""" +Mover class for Example 2.7: Attraction with Many Movers +""" + +class Mover: + def __init__(self, sketch, x=0.0, y=0.0, m=1.0): + self.sketch = sketch + self.mass = m + self.radius = self.mass * 8 + self.position = self.sketch.pvector(x, y) + self.velocity = self.sketch.pvector(1, 0) + self.acceleration = self.sketch.pvector(0, 0) + + def apply_force(self, force): + f = force / self.mass + self.acceleration += f + + def update(self): + self.velocity += self.acceleration + self.position += self.velocity + self.acceleration *= 0 + + def draw(self): + self.sketch.stroke(0) + self.sketch.stroke_weight(2) + self.sketch.fill((127, 127, 127)) + self.sketch.ellipse(self.position.x, self.position.y, self.radius * 2) diff --git a/examples/Nature of Code/chapter02/Example_2_08_Two_Body_Attraction/Body.py b/examples/Nature of Code/chapter02/Example_2_08_Two_Body_Attraction/Body.py new file mode 100644 index 0000000..b822e77 --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_08_Two_Body_Attraction/Body.py @@ -0,0 +1,36 @@ +""" +Body class for Example 2-8: Two Body Attraction +""" + + +class Body: + def __init__(self, sketch, x=0, y=0): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + self.velocity = self.sketch.pvector(0, 0) + self.acceleration = self.sketch.pvector(0, 0) + self.mass = 8 + self.r = (self.mass ** 0.5) * 2 + + def attract(self, body): + force = self.sketch.pvector.sub(self.position, body.position) + d = self.sketch.constrain(force.mag(), 5, 25) + G = 1 + strength = (G * (self.mass * body.mass)) / (d * d) + force.set_mag(strength) + body.apply_force(force) + + def apply_force(self, force): + f = self.sketch.pvector.div(force, self.mass) + self.acceleration.add(f) + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.acceleration.set(0, 0) + + def draw(self): + self.sketch.stroke(0) + self.sketch.stroke_weight(2) + self.sketch.fill(127, 100, 100) + self.sketch.ellipse(self.position.x, self.position.y, self.r * 4) diff --git a/examples/Nature of Code/chapter02/Example_2_08_Two_Body_Attraction/Example_2_08_Two_Body_Attraction.py b/examples/Nature of Code/chapter02/Example_2_08_Two_Body_Attraction/Example_2_08_Two_Body_Attraction.py new file mode 100644 index 0000000..5d8f07c --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_08_Two_Body_Attraction/Example_2_08_Two_Body_Attraction.py @@ -0,0 +1,34 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter2/Example_2_8_Two_Body_Attraction/Example_2_8_Two_Body_Attraction.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 2.8: Two Body Attraction +""" + +from pycreative.app import Sketch +from Body import Body + + +class Example_2_8_Two_Body_Attraction(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 2.8: Two Body Attraction") + self.bodyA = Body(self, 320, 60) + self.bodyB = Body(self, 320, 300) + self.bodyA.velocity = self.pvector(1, 0) + self.bodyB.velocity = self.pvector(-1, 0) + + def draw(self): + self.background(255) + + self.bodyA.attract(self.bodyB) + self.bodyB.attract(self.bodyA) + + self.bodyA.update() + self.bodyA.draw() + self.bodyB.update() + self.bodyB.draw() diff --git a/examples/Nature of Code/chapter02/Example_2_09_N_Bodies/Body.py b/examples/Nature of Code/chapter02/Example_2_09_N_Bodies/Body.py new file mode 100644 index 0000000..bd2791e --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_09_N_Bodies/Body.py @@ -0,0 +1,40 @@ +""" +Body class for Example 2-9: N Bodies +""" + + +class Body: + def __init__(self, sketch, x: float, y: float, m: float): + self.mass = m + self.position = sketch.pvector(x, y) + self.velocity = sketch.pvector(0, 0) + self.acceleration = sketch.pvector(0, 0) + + def apply_force(self, sketch, force): + f = sketch.pvector.div(force, self.mass) + self.acceleration.add(f) + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.acceleration.mult(0) + + def draw(self, sketch): + sketch.stroke(0) + sketch.stroke_weight(2) + sketch.fill(127) + sketch.circle(self.position.x, self.position.y, self.mass * 16) + + def attract(self, sketch, other: "Body", G: float): + # Calculate direction of force + force = sketch.pvector.sub(self.position, other.position) + # Distance between objects + distance = force.mag() + # Limiting the distance to eliminate "extreme" results for very close or very far objects + distance = max(5.0, min(distance, 25.0)) + + # Calculate gravitional force magnitude + strength = (G * self.mass * other.mass) / (distance * distance) + # Get force vector --> magnitude * direction + force.set_mag(strength) + return force diff --git a/examples/Nature of Code/chapter02/Example_2_09_N_Bodies/Example_2_09_N_Bodies.py b/examples/Nature of Code/chapter02/Example_2_09_N_Bodies/Example_2_09_N_Bodies.py new file mode 100644 index 0000000..dd105c8 --- /dev/null +++ b/examples/Nature of Code/chapter02/Example_2_09_N_Bodies/Example_2_09_N_Bodies.py @@ -0,0 +1,34 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter2/Example_2_9_N_Bodies/Example_2_9_N_Bodies.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 2.9: N Bodies +""" + +from pycreative.app import Sketch +from Body import Body + + +class Example_2_09_N_Bodies(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("N Bodies Example") + self.bodies = [Body(self, self.random(self.width), self.random(self.height), self.random(0.1, 2)) for _ in range(10)] + self.G = 1 + + def update(self, dt): + pass + + def draw(self): + self.clear((255, 255, 255)) + for i, body_i in enumerate(self.bodies): + for j, body_j in enumerate(self.bodies): + if i != j: + force = body_j.attract(self, body_i, self.G) + body_i.apply_force(self, force) + body_i.update() + body_i.draw(self) \ No newline at end of file diff --git a/examples/Nature of Code/chapter03/Example_3_01_Angular_Motion_Using_Rotate/Example_3_01_Angular_Motion_Using_Rotate.py b/examples/Nature of Code/chapter03/Example_3_01_Angular_Motion_Using_Rotate/Example_3_01_Angular_Motion_Using_Rotate.py new file mode 100644 index 0000000..489b475 --- /dev/null +++ b/examples/Nature of Code/chapter03/Example_3_01_Angular_Motion_Using_Rotate/Example_3_01_Angular_Motion_Using_Rotate.py @@ -0,0 +1,46 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter3/Example_3_1_Angular_Motion_Using_Rotate/Example_3_1_Angular_Motion_Using_Rotate.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 3.1: Angular Motion Using Rotate +""" + +from pycreative.app import Sketch + + +class Example_3_01_Angular_Motion_Using_Rotate(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 3.1: Angular Motion Using Rotate") + # Position + self.angle = 0.0 + # Velocity + self.angle_velocity = 0.0 + # Acceleration + self.angle_acceleration = 0.0001 + + def update(self, dt): + pass + + def draw(self): + self.clear(255) + + self.translate(self.width / 2, self.height / 2) + self.rotate(self.angle) + + self.stroke(0) + self.stroke_weight(2) + self.fill(127) + + self.line(-60, 0, 60, 0) + self.circle(60, 0, 16) + self.circle(-60, 0, 16) + + # Angular equivalent of velocity.add(acceleration); + self.angle_velocity += self.angle_acceleration + # Angular equivalent of position.add(velocity); + self.angle += self.angle_velocity diff --git a/examples/Nature of Code/chapter03/Example_3_02_Forces_With_Arbitrary_Angular_Motion/Attractor.py b/examples/Nature of Code/chapter03/Example_3_02_Forces_With_Arbitrary_Angular_Motion/Attractor.py new file mode 100644 index 0000000..ce39e3b --- /dev/null +++ b/examples/Nature of Code/chapter03/Example_3_02_Forces_With_Arbitrary_Angular_Motion/Attractor.py @@ -0,0 +1,30 @@ +""" +Attractor class for Example 3-2: Forces with Arbitrary Angular Motion +""" + +class Attractor: + def __init__(self, sketch, x: float = 0.0, y: float = 0.0, m: float = 20.0): + self.sketch = sketch + self.position = sketch.pvector(x, y) + self.mass = m + self.G = 1.0 + + def attract(self, mover): + # Calculate direction of force + force = self.position.copy().sub(mover.position) + # Distance between objects + distance = force.mag() + # Limiting the distance to eliminate "extreme" results for very close or very far objects + distance = self.sketch.constrain(distance, 5.0, 25.0) + + # Calculate gravitational force magnitude + strength = (self.G * self.mass * mover.mass) / (distance * distance) + # Get force vector --> magnitude * direction + force.set_mag(strength) + return force + + def display(self): + self.sketch.ellipse_mode('center') + self.sketch.stroke(0) + self.sketch.fill(175, 200, 200) + self.sketch.circle(self.position.x, self.position.y, self.mass * 2) diff --git a/examples/Nature of Code/chapter03/Example_3_02_Forces_With_Arbitrary_Angular_Motion/Example_3_02_Forces_With_Arbitrary_Angular_Motion.py b/examples/Nature of Code/chapter03/Example_3_02_Forces_With_Arbitrary_Angular_Motion/Example_3_02_Forces_With_Arbitrary_Angular_Motion.py new file mode 100644 index 0000000..241951d --- /dev/null +++ b/examples/Nature of Code/chapter03/Example_3_02_Forces_With_Arbitrary_Angular_Motion/Example_3_02_Forces_With_Arbitrary_Angular_Motion.py @@ -0,0 +1,34 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter3/Example_3_2_Forces_With_Arbitrary_Angular_Motion/Example_3_2_Forces_With_Arbitrary_Angular_Motion.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 3-2: Forces with Arbitrary Angular Motion +""" + +from pycreative.app import Sketch +from Attractor import Attractor +from Mover import Mover + + +class Example_3_02_Forces_With_Arbitrary_Angular_Motion(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 3.2: Forces with Arbitrary Angular Motion") + self.movers = [Mover(self, self.random(self.width), self.random(self.height), self.random(0.1, 2.0)) for _ in range(20)] + self.attractor = Attractor(self, self.width / 2, self.height / 2, 20.0) + + def update(self, dt): + pass + + def draw(self): + self.clear(255) + self.attractor.display() + for mover in self.movers: + force = self.attractor.attract(mover) + mover.apply_force(force) + mover.update() + mover.draw(self) diff --git a/examples/Nature of Code/chapter03/Example_3_02_Forces_With_Arbitrary_Angular_Motion/Mover.py b/examples/Nature of Code/chapter03/Example_3_02_Forces_With_Arbitrary_Angular_Motion/Mover.py new file mode 100644 index 0000000..294d7ee --- /dev/null +++ b/examples/Nature of Code/chapter03/Example_3_02_Forces_With_Arbitrary_Angular_Motion/Mover.py @@ -0,0 +1,40 @@ +""" +Mover class for Example 3-2: Forces with Arbitrary Angular Motion +""" + + +class Mover: + def __init__(self, sketch, x = 0.0, y = 0.0, mass = 1.0): + self.mass = mass + self.radius = self.mass * 8 + self.position = sketch.pvector(x, y) + self.angle = 0.0 + self.angle_velocity = 0.0 + self.angle_acceleration = 0.0 + self.velocity = sketch.pvector(sketch.random(-1, 1), sketch.random(-1, 1)) + self.acceleration = sketch.pvector(0.0, 0.0) + + def apply_force(self, force): + f = force / self.mass + self.acceleration += f + + def update(self): + self.velocity += self.acceleration + self.position += self.velocity + self.angle_acceleration = self.acceleration.x / 10.0 + self.angle_velocity += self.angle_acceleration + self.angle_velocity = max(-0.1, min(0.1, self.angle_velocity)) + self.angle += self.angle_velocity + self.acceleration *= 0.0 + + def draw(self, sketch): + sketch.stroke_weight(2) + sketch.stroke(0) + sketch.fill(127) + sketch.rect_mode(sketch.CENTER) + sketch.push_matrix() + sketch.translate(self.position.x, self.position.y) + sketch.rotate(self.angle) + sketch.circle(0, 0, self.radius * 2) + sketch.line(0, 0, self.radius, 0) + sketch.pop_matrix() \ No newline at end of file diff --git a/examples/Nature of Code/chapter03/Example_3_03_Pointing_In_The_Direction_Of_Motion/Example_3_03_Pointing_In_The_Direction_Of_Motion.py b/examples/Nature of Code/chapter03/Example_3_03_Pointing_In_The_Direction_Of_Motion/Example_3_03_Pointing_In_The_Direction_Of_Motion.py new file mode 100644 index 0000000..062fec0 --- /dev/null +++ b/examples/Nature of Code/chapter03/Example_3_03_Pointing_In_The_Direction_Of_Motion/Example_3_03_Pointing_In_The_Direction_Of_Motion.py @@ -0,0 +1,28 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter3/Example_3_3_Pointing_In_The_Direction_Of_Motion/Example_3_3_Pointing_In_The_Direction_Of_Motion.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 3-3: Pointing in the Direction of Motion +""" + +from pycreative.app import Sketch +from Mover import Mover + + +class Example_3_03_Pointing_In_The_Direction_Of_Motion(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 3.3: Pointing in the Direction of Motion") + self.mover = Mover(self) + + def update(self, dt): + self.mover.update() + + def draw(self): + self.clear(255) + self.mover.check_edges() + self.mover.draw() diff --git a/examples/Nature of Code/chapter03/Example_3_03_Pointing_In_The_Direction_Of_Motion/Mover.py b/examples/Nature of Code/chapter03/Example_3_03_Pointing_In_The_Direction_Of_Motion/Mover.py new file mode 100644 index 0000000..5a84eb2 --- /dev/null +++ b/examples/Nature of Code/chapter03/Example_3_03_Pointing_In_The_Direction_Of_Motion/Mover.py @@ -0,0 +1,50 @@ +""" +Mover class for Example 3-3: Pointing in the Direction of Motion +""" + + +class Mover: + def __init__(self, sketch): + self.sketch = sketch + self.position = self.sketch.pvector(self.sketch.width / 2, self.sketch.height / 2) + self.velocity = self.sketch.pvector(0, 0) + self.acceleration = self.sketch.pvector(0, 0) + self.topspeed = 4 + self.r = 16 + + def update(self): + mouse = self.sketch.pvector(self.sketch.mouse_x, self.sketch.mouse_y) + dir = self.sketch.pvector.sub(mouse, self.position) + dir.normalize() + dir.mult(0.5) + self.acceleration = dir + + self.velocity.add(self.acceleration) + self.velocity.limit(self.topspeed) + self.position.add(self.velocity) + + def draw(self): + angle = self.velocity.heading() + + self.sketch.stroke(0) + self.sketch.stroke_weight(2) + self.sketch.fill(127) + self.sketch.push() + self.sketch.rect_mode(self.sketch.CENTER) + + self.sketch.translate(self.position.x, self.position.y) + self.sketch.rotate(angle) + self.sketch.rect(0, 0, 30, 10) + + self.sketch.pop() + + def check_edges(self): + if self.position.x > self.sketch.width: + self.position.x = 0 + elif self.position.x < 0: + self.position.x = self.sketch.width + + if self.position.y > self.sketch.height: + self.position.y = 0 + elif self.position.y < 0: + self.position.y = self.sketch.height diff --git a/examples/Nature of Code/chapter03/Example_3_04_Polar_To_Cartesian/Example_3_04_Polar_To_Cartesian.py b/examples/Nature of Code/chapter03/Example_3_04_Polar_To_Cartesian/Example_3_04_Polar_To_Cartesian.py new file mode 100644 index 0000000..3e4fca4 --- /dev/null +++ b/examples/Nature of Code/chapter03/Example_3_04_Polar_To_Cartesian/Example_3_04_Polar_To_Cartesian.py @@ -0,0 +1,41 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter3/Example_3_4_Polar_To_Cartesian/Example_3_4_Polar_To_Cartesian.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 3.4: Polar to Cartesian +""" + +from pycreative.app import Sketch + + +class Example_3_04_Polar_To_Cartesian(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 3.4: Polar to Cartesian") + # Initialize all values + self.r = self.height * 0.35 + self.theta = 0.0 + + def draw(self): + self.background(255) + + # Translate the origin point to the center of the screen + self.translate(self.width / 2, self.height / 2) + + # Convert polar to cartesian + x = self.r * self.cos(self.theta) + y = self.r * self.sin(self.theta) + + # Draw the ellipse at the cartesian coordinate + self.fill(127) + self.stroke(0) + self.stroke_weight(2) + self.line(0, 0, x, y) + self.circle(x, y, 48) + + # Increase the angle over time + self.theta += 0.02 diff --git a/examples/Nature of Code/chapter03/Example_3_05_Simple_Harmonic_Motion/Example_3_05_Simple_Harmonic_Motion.py b/examples/Nature of Code/chapter03/Example_3_05_Simple_Harmonic_Motion/Example_3_05_Simple_Harmonic_Motion.py new file mode 100644 index 0000000..3fc94bf --- /dev/null +++ b/examples/Nature of Code/chapter03/Example_3_05_Simple_Harmonic_Motion/Example_3_05_Simple_Harmonic_Motion.py @@ -0,0 +1,38 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter3/Example_3_5_Simple_Harmonic_Motion/Example_3_5_Simple_Harmonic_Motion.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 3.5: Simple Harmonic Motion +""" + +from pycreative.app import Sketch + + +class Example_3_05_Simple_Harmonic_Motion(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 3.5: Simple Harmonic Motion") + + def draw(self): + self.background(255) + + period = 120 + amplitude = 200 + + # Calculating horizontal position according to formula for simple harmonic motion + x = amplitude * self.sin((self.TWO_PI * (self.frame_count or 0)) / period) + + self.stroke(0) + self.stroke_weight(2) + self.fill(127) + self.push() + try: + self.translate(self.width / 2, self.height / 2) + self.line(0, 0, x, 0) + self.circle(x, 0, 48) + finally: + self.pop() diff --git a/examples/Nature of Code/chapter03/Example_3_06_Simple_Harmonic_Motion_II/Example_3_06_Simple_Harmonic_Motion_II.py b/examples/Nature of Code/chapter03/Example_3_06_Simple_Harmonic_Motion_II/Example_3_06_Simple_Harmonic_Motion_II.py new file mode 100644 index 0000000..21983be --- /dev/null +++ b/examples/Nature of Code/chapter03/Example_3_06_Simple_Harmonic_Motion_II/Example_3_06_Simple_Harmonic_Motion_II.py @@ -0,0 +1,38 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter3/Example_3_6_Simple_Harmonic_Motion_II/Example_3_6_Simple_Harmonic_Motion_II.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 3.6: Simple Harmonic Motion II +""" + +from pycreative.app import Sketch + + +class Example_3_06_Simple_Harmonic_Motion_II(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 3.6: Simple Harmonic Motion II") + self.angle = 0.0 + self.angle_velocity = 0.05 + + def draw(self): + self.background(255) + + amplitude = 200 + x = amplitude * self.sin(self.angle) + self.angle += self.angle_velocity + + self.push() + try: + self.translate(self.width / 2, self.height / 2) + self.stroke((0, 0, 0)) + self.stroke_weight(2) + self.fill(127) + self.line(0, 0, x, 0) + self.circle(x, 0, 48) + finally: + self.pop() diff --git a/examples/Nature of Code/chapter03/Example_3_07_Oscillator_Objects/Example_3_07_Oscillator_Objects.py b/examples/Nature of Code/chapter03/Example_3_07_Oscillator_Objects/Example_3_07_Oscillator_Objects.py new file mode 100644 index 0000000..d8c1faf --- /dev/null +++ b/examples/Nature of Code/chapter03/Example_3_07_Oscillator_Objects/Example_3_07_Oscillator_Objects.py @@ -0,0 +1,31 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter3/Example_3_7_Oscillator_Objects/Example_3_7_Oscillator_Objects.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 3.7: Oscillator Objects +""" + +from pycreative.app import Sketch +from Oscillator import Oscillator + + +class Example_3_07_Oscillator_Objects(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 3.7: Oscillator Objects") + # Initialize all objects + self.oscillators = [Oscillator(self) for _ in range(10)] + + def update(self, dt): + for osc in self.oscillators: + osc.update() + + def draw(self): + self.clear(255) + # Run all objects + for osc in self.oscillators: + osc.draw() diff --git a/examples/Nature of Code/chapter03/Example_3_07_Oscillator_Objects/Oscillator.py b/examples/Nature of Code/chapter03/Example_3_07_Oscillator_Objects/Oscillator.py new file mode 100644 index 0000000..76238ad --- /dev/null +++ b/examples/Nature of Code/chapter03/Example_3_07_Oscillator_Objects/Oscillator.py @@ -0,0 +1,37 @@ +""" +Oscillator object for Example 3-7: Oscillator Objects +""" + + +class Oscillator: + def __init__(self, sketch): + import random + + self.sketch = sketch + self.angle = sketch.pvector(0, 0) + self.angle_velocity = sketch.pvector( + random.uniform(-0.05, 0.05), random.uniform(-0.05, 0.05) + ) + self.amplitude = sketch.pvector( + random.uniform(20, sketch.width / 2), + random.uniform(20, sketch.height / 2), + ) + + def update(self): + self.angle.add(self.angle_velocity) + + def draw(self): + + x = self.sketch.sin(self.angle.x) * self.amplitude.x + y = self.sketch.sin(self.angle.y) * self.amplitude.y + + self.sketch.push() + try: + self.sketch.translate(self.sketch.width / 2, self.sketch.height / 2) + self.sketch.stroke(0) + self.sketch.stroke_weight(2) + self.sketch.fill(127) + self.sketch.line(0, 0, x, y) + self.sketch.circle(x, y, 32) + finally: + self.sketch.pop() diff --git a/examples/Nature of Code/chapter03/Example_3_08_Static_Wave/Example_3_08_Static_Wave.py b/examples/Nature of Code/chapter03/Example_3_08_Static_Wave/Example_3_08_Static_Wave.py new file mode 100644 index 0000000..6dc2aa7 --- /dev/null +++ b/examples/Nature of Code/chapter03/Example_3_08_Static_Wave/Example_3_08_Static_Wave.py @@ -0,0 +1,40 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter3/Example_3_8_Static_Wave/Example_3_8_Static_Wave.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 3.8: Static Wave +""" + +from pycreative.app import Sketch + + +class Example_3_08_Static_Wave(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 3.8: Static Wave") + + def update(self, dt): + pass + + def draw(self): + self.background(255) + + angle = 0 + angle_velocity = 0.2 + amplitude = 100 + + self.stroke((0, 0, 0)) + self.stroke_weight(2) + self.fill(127) + + for x in range(0, self.width + 1, 24): + # 1) Calculate the y position according to amplitude and sine of the angle. + y = amplitude * self.sin(angle) + # 2) Draw a circle at the (x,y) position. + self.circle(x, y + self.height / 2, 48) + # 3) Increment the angle according to angular velocity. + angle += angle_velocity diff --git a/examples/Nature of Code/chapter03/Example_3_09_The_Wave/Example_3_09_The_Wave.py b/examples/Nature of Code/chapter03/Example_3_09_The_Wave/Example_3_09_The_Wave.py new file mode 100644 index 0000000..e2e9651 --- /dev/null +++ b/examples/Nature of Code/chapter03/Example_3_09_The_Wave/Example_3_09_The_Wave.py @@ -0,0 +1,38 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter3/Example_3_9_The_Wave/Example_3_9_The_Wave.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 3.9: The Wave +""" + +from pycreative.app import Sketch + + +class Example_3_09_The_Wave(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 3.9: The Wave") + self.start_angle = 0 + self.angle_velocity = 0.2 + + def update(self, dt): + pass + + def draw(self): + self.clear(255) + + angle = self.start_angle + self.start_angle += 0.02 + + self.stroke(0) + self.stroke_weight(2) + self.fill(127, 127) + + for x in range(0, self.width + 1, 24): + y = self.map(self.sin(angle), -1, 1, 0, self.height) + self.circle(x, y, 48) + angle += self.angle_velocity diff --git a/examples/Nature of Code/chapter04/Example_4_01_Single_Particle/Example_4_01_Single_Particle.py b/examples/Nature of Code/chapter04/Example_4_01_Single_Particle/Example_4_01_Single_Particle.py new file mode 100644 index 0000000..a7caf85 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_01_Single_Particle/Example_4_01_Single_Particle.py @@ -0,0 +1,34 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter4/Example_4_1_Single_Particle/Example_4_1_Single_Particle.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 4-1: Single Particle +""" + +from pycreative.app import Sketch +from Particle import Particle + + +class Example_4_01_Single_Particle(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example_4_01_Single_Particle") + self.particle = Particle(self, self.width / 2, 10) + + def draw(self): + self.clear(255) + # Operating the single Particle + self.particle.update() + self.particle.draw() + + # Applying a gravity force + gravity = self.pvector(0, 0.1) + self.particle.apply_force(gravity) + + # Checking the particle's state and making a new particle + if self.particle.is_dead(): + self.particle = Particle(self, self.width / 2, 20) diff --git a/examples/Nature of Code/chapter04/Example_4_01_Single_Particle/Particle.py b/examples/Nature of Code/chapter04/Example_4_01_Single_Particle/Particle.py new file mode 100644 index 0000000..4309bba --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_01_Single_Particle/Particle.py @@ -0,0 +1,33 @@ +""" +Particle class for Example 4-1: A Single Particle +""" + + +class Particle: + def __init__(self, sketch, x = 0.0, y = 0.0): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + # For demonstration purposes the Particle has a random velocity. + self.velocity = self.sketch.pvector(self.sketch.random(-1, 1), self.sketch.random(-2, 0)) + self.acceleration = self.sketch.pvector(0, 0) + self.lifespan = 255.0 + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.lifespan -= 2.0 + self.acceleration.mult(0) + + def draw(self): + self.sketch.fill((self.lifespan, self.lifespan, self.lifespan)) + self.sketch.stroke((self.lifespan, self.lifespan, self.lifespan)) + self.sketch.stroke_weight(1) + self.sketch.circle(self.position.x, self.position.y, 8) + + # Keeping the same physics model as with previous chapters + def apply_force(self, force): + self.acceleration.add(force) + + # Is the Particle alive or dead? + def is_dead(self) -> bool: + return self.lifespan < 0 diff --git a/examples/Nature of Code/chapter04/Example_4_02_Array_Of_Particles/Example_4_02_Array_Of_Particles.py b/examples/Nature of Code/chapter04/Example_4_02_Array_Of_Particles/Example_4_02_Array_Of_Particles.py new file mode 100644 index 0000000..971a2f5 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_02_Array_Of_Particles/Example_4_02_Array_Of_Particles.py @@ -0,0 +1,32 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter4/Example_4_2_Array_Of_Particles/Example_4_2_Array_Of_Particles.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 4-2: Array of Particles +""" + +from pycreative.app import Sketch +from Particle import Particle + + +class Example_4_02_Array_Of_Particles(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example_4_02_Array_Of_Particles") + self.particles = [] + + def draw(self): + self.background(255) + self.particles.append(Particle(self, self.width / 2, 20)) + + # Looping through backwards to delete + for i in range(len(self.particles) - 1, -1, -1): + particle = self.particles[i] + particle.run() + if particle.is_dead(): + # remove the particle + self.particles.pop(i) diff --git a/examples/Nature of Code/chapter04/Example_4_02_Array_Of_Particles/Particle.py b/examples/Nature of Code/chapter04/Example_4_02_Array_Of_Particles/Particle.py new file mode 100644 index 0000000..5fa1fcc --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_02_Array_Of_Particles/Particle.py @@ -0,0 +1,39 @@ +""" +Particle class for Example 4-2: Array of Particles +""" + +class Particle: + def __init__(self, sketch, x=0.0, y=0.0): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + # For demonstration purposes the Particle has a random velocity. + self.velocity = self.sketch.pvector(self.sketch.random(-1, 1), self.sketch.random(-2, 0)) + self.acceleration = self.sketch.pvector(0, 0) + self.lifespan = 255.0 + + def run(self): + gravity = self.sketch.pvector(0, 0.05) + self.apply_force(gravity) + self.update() + self.draw() + + def apply_force(self, force): + self.acceleration.add(force) + + # Method to update position + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.lifespan -= 2.0 + self.acceleration.mult(0) + + # Method to display + def draw(self): + self.sketch.fill((self.lifespan, self.lifespan, self.lifespan)) + self.sketch.stroke((self.lifespan, self.lifespan, self.lifespan)) + self.sketch.stroke_weight(2) + self.sketch.circle(self.position.x, self.position.y, 8) + + # Is the particle still useful? + def is_dead(self) -> bool: + return self.lifespan < 0.0 diff --git a/examples/Nature of Code/chapter04/Example_4_03_A_Particle_Emitter/Emitter.py b/examples/Nature of Code/chapter04/Example_4_03_A_Particle_Emitter/Emitter.py new file mode 100644 index 0000000..778e587 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_03_A_Particle_Emitter/Emitter.py @@ -0,0 +1,27 @@ +""" +Particle Emitter class for Example 4-3: A Particle Emitter +""" + +from Particle import Particle + + +class Emitter: + def __init__(self, sketch, x=0.0, y=0.0): + self.sketch = sketch + self.origin = self.sketch.pvector(x, y) + self.particles = [] + + def update(self): + for particle in self.particles: + particle.update() + + def add_particle(self): + self.particles.append(Particle(self.sketch, self.origin.x, self.origin.y)) + + def draw(self): + # Looping through backwards to delete + for i in range(len(self.particles) - 1, -1, -1): + self.particles[i].draw() + if self.particles[i].is_dead(): + # Remove the particle + self.particles.pop(i) diff --git a/examples/Nature of Code/chapter04/Example_4_03_A_Particle_Emitter/Example_4_03_A_Particle_Emitter.py b/examples/Nature of Code/chapter04/Example_4_03_A_Particle_Emitter/Example_4_03_A_Particle_Emitter.py new file mode 100644 index 0000000..87334b7 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_03_A_Particle_Emitter/Example_4_03_A_Particle_Emitter.py @@ -0,0 +1,28 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter4/Example_4_3_A_Particle_Emitter/Example_4_3_A_Particle_Emitter.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 4-3: A Particle Emitter +""" + +from pycreative.app import Sketch +from Emitter import Emitter + + +class Example_4_03_A_Particle_Emitter(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example_4_03_A_Particle_Emitter") + self.emitter = Emitter(self, self.width / 2, 50) + + def update(self, dt): + self.emitter.update() + self.emitter.add_particle() + + def draw(self): + self.clear(255) + self.emitter.draw() \ No newline at end of file diff --git a/examples/Nature of Code/chapter04/Example_4_03_A_Particle_Emitter/Particle.py b/examples/Nature of Code/chapter04/Example_4_03_A_Particle_Emitter/Particle.py new file mode 100644 index 0000000..a7f516a --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_03_A_Particle_Emitter/Particle.py @@ -0,0 +1,29 @@ +""" +Particle class for Example 4-3: A Particle Emitter +""" + +class Particle: + def __init__(self, sketch, x=0.0, y=0.0): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + self.velocity = self.sketch.pvector(self.sketch.random(-1, 1), self.sketch.random(-2, 0)) + self.acceleration = self.sketch.pvector(0, 0) + self.lifespan = 255.0 + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.lifespan -= 2.0 + self.acceleration.mult(0) + + def draw(self): + self.sketch.fill(self.lifespan) + self.sketch.stroke(self.lifespan) + self.sketch.stroke_weight(2) + self.sketch.circle(self.position.x, self.position.y, 8) + + def apply_force(self, force): + self.acceleration.add(force) + + def is_dead(self): + return self.lifespan < 0.0 diff --git a/examples/Nature of Code/chapter04/Example_4_04_A_System_Of_Systems/Emitter.py b/examples/Nature of Code/chapter04/Example_4_04_A_System_Of_Systems/Emitter.py new file mode 100644 index 0000000..4498b6b --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_04_A_System_Of_Systems/Emitter.py @@ -0,0 +1,26 @@ +""" +Emitter class for a particle system. +""" +from Particle import Particle + + +class Emitter: + def __init__(self, sketch, x=0.0, y=0.0): + self.sketch = sketch + self.origin = self.sketch.pvector(x, y) + self.particles = [] + + def add_particle(self): + self.particles.append(Particle(self.sketch, self.origin.x, self.origin.y)) + + def update(self): + for particle in self.particles: + particle.update() + + def draw(self): + # Looping through backwards to delete + for i in range(len(self.particles) - 1, -1, -1): + self.particles[i].draw() + if self.particles[i].is_dead(): + # Remove the particle + self.particles.pop(i) diff --git a/examples/Nature of Code/chapter04/Example_4_04_A_System_Of_Systems/Example_4_04_A_System_Of_Systems.py b/examples/Nature of Code/chapter04/Example_4_04_A_System_Of_Systems/Example_4_04_A_System_Of_Systems.py new file mode 100644 index 0000000..73a3077 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_04_A_System_Of_Systems/Example_4_04_A_System_Of_Systems.py @@ -0,0 +1,34 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter4/Example_4_4_A_System_Of_Systems/Example_4_4_A_System_Of_Systems.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 4-4: A System of Systems +""" + +from pycreative.app import Sketch +from Emitter import Emitter + + +class Example_4_04_A_System_Of_Systems(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example_4_04_A_System_Of_Systems") + self.emitters = [] + + def update(self, dt): + for emitter in self.emitters: + emitter.update() + + def draw(self): + self.clear(255) + for emitter in self.emitters: + emitter.draw() + emitter.add_particle() + + def mouse_pressed(self): + if self.mouse_x is not None and self.mouse_y is not None: # Optional safety check and suppress type warning + self.emitters.append(Emitter(self, self.mouse_x, self.mouse_y)) diff --git a/examples/Nature of Code/chapter04/Example_4_04_A_System_Of_Systems/Particle.py b/examples/Nature of Code/chapter04/Example_4_04_A_System_Of_Systems/Particle.py new file mode 100644 index 0000000..b1d6114 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_04_A_System_Of_Systems/Particle.py @@ -0,0 +1,29 @@ +""" +Particle class for Example 4.04: A System of Systems +""" + +class Particle: + def __init__(self, sketch, x=0.0, y=0.0): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + self.velocity = self.sketch.pvector(self.sketch.random(-1, 1), self.sketch.random(-2, 0)) + self.acceleration = self.sketch.pvector(0, 0) + self.lifespan = 255.0 + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.lifespan -= 2.0 + self.acceleration.mult(0) + + def draw(self): + self.sketch.fill(self.lifespan) + self.sketch.stroke(self.lifespan) + self.sketch.stroke_weight(2) + self.sketch.circle(self.position.x, self.position.y, 8) + + def apply_force(self, force): + self.acceleration.add(force) + + def is_dead(self): + return self.lifespan < 0.0 diff --git a/examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Confetti.py b/examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Confetti.py new file mode 100644 index 0000000..424d70a --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Confetti.py @@ -0,0 +1,22 @@ +""" +Confetti class for Example 4.5: Particle System with Inheritance and Polymorphism +""" + +from Particle import Particle + + +class Confetti(Particle): + def __init__(self, sketch, x=0.0, y=0.0): + super().__init__(sketch, x, y) + + def show(self): + angle = self.sketch.map(self.position.x, 0, self.sketch.width, 0, self.sketch.TWO_PI) + self.sketch.rect_mode(self.sketch.CENTER) + self.sketch.fill(self.lifespan) + self.sketch.stroke(self.lifespan) + self.sketch.stroke_weight(2) + self.sketch.push() + self.sketch.translate(self.position.x, self.position.y) + self.sketch.rotate(angle) + self.sketch.rect(0, 0, 12, 12) + self.sketch.pop() diff --git a/examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Emitter.py b/examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Emitter.py new file mode 100644 index 0000000..0608bc6 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Emitter.py @@ -0,0 +1,27 @@ +""" +Emitter class for Example 4-5: Particle System with Inheritance and Polymorphism +""" + +from Particle import Particle +from Confetti import Confetti + + +class Emitter: + def __init__(self, sketch, x=0.0, y=0.0): + self.sketch = sketch + self.origin = self.sketch.pvector(x, y) + self.particles = [] + + def add_particle(self): + r = self.sketch.random(1) + if r < 0.5: + self.particles.append(Particle(self.sketch, self.origin.x, self.origin.y)) + else: + self.particles.append(Confetti(self.sketch, self.origin.x, self.origin.y)) + + def draw(self): + for i in range(len(self.particles) - 1, -1, -1): + p = self.particles[i] + p.draw() + if p.is_dead(): + self.particles.pop(i) diff --git a/examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism.py b/examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism.py new file mode 100644 index 0000000..2b25308 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism.py @@ -0,0 +1,25 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter4/Example_4_5_Particle_System_With_Inheritance_And_Polymorphism/Example_4_5_Particle_System_With_Inheritance_And_Polymorphism.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 4-5: Particle System with Inheritance and Polymorphism +""" + +from pycreative.app import Sketch +from Emitter import Emitter + + +class Example_4_05_Particle_System_With_Inheritance_And_Polymorphism(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example_4_05_Particle_System_With_Inheritance_And_Polymorphism") + self.emitter = Emitter(self, self.width / 2, 50) + + def draw(self): + self.background(255) + self.emitter.add_particle() + self.emitter.draw() diff --git a/examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Particle.py b/examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Particle.py new file mode 100644 index 0000000..302beb8 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_05_Particle_System_With_Inheritance_And_Polymorphism/Particle.py @@ -0,0 +1,36 @@ +""" +Particle class for Example 4-5: Particle System with Inheritance and Polymorphism +""" + + +class Particle: + def __init__(self, sketch, x=0.0, y=0.0): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + self.velocity = self.sketch.pvector(self.sketch.random(-1, 1), self.sketch.random(-2, 0)) + self.acceleration = self.sketch.pvector(0, 0) + self.lifespan = 255.0 + + def draw(self): + gravity = self.sketch.pvector(0, 0.05) + self.apply_force(gravity) + self.update() + self.show() + + def apply_force(self, force): + self.acceleration.add(force) + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.lifespan -= 2.0 + self.acceleration.mult(0) + + def show(self): + self.sketch.stroke(self.lifespan) + self.sketch.stroke_weight(2) + self.sketch.fill(self.lifespan) + self.sketch.circle(self.position.x, self.position.y, 8) + + def is_dead(self) -> bool: + return self.lifespan < 0.0 diff --git a/examples/Nature of Code/chapter04/Example_4_06_Particle_System_With_Forces/Emitter.py b/examples/Nature of Code/chapter04/Example_4_06_Particle_System_With_Forces/Emitter.py new file mode 100644 index 0000000..421e168 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_06_Particle_System_With_Forces/Emitter.py @@ -0,0 +1,26 @@ +""" +Emitter class for Example 4.6: Particle System with Forces +""" + +from Particle import Particle + + +class Emitter: + def __init__(self, sketch, x, y): + self.sketch = sketch + self.origin = self.sketch.pvector(x, y) + self.particles = [] + + def add_particle(self): + self.particles.append(Particle(self.sketch, self.origin.x, self.origin.y)) + + def apply_force(self, force): + for particle in self.particles: + particle.apply_force(force) + + def run(self): + for i in range(len(self.particles) - 1, -1, -1): + particle = self.particles[i] + particle.draw() + if particle.is_dead(): + self.particles.pop(i) \ No newline at end of file diff --git a/examples/Nature of Code/chapter04/Example_4_06_Particle_System_With_Forces/Example_4_06_Particle_System_With_Forces.py b/examples/Nature of Code/chapter04/Example_4_06_Particle_System_With_Forces/Example_4_06_Particle_System_With_Forces.py new file mode 100644 index 0000000..3b95391 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_06_Particle_System_With_Forces/Example_4_06_Particle_System_With_Forces.py @@ -0,0 +1,31 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter4/Example_4_6_Particle_System_With_Forces/Example_4_6_Particle_System_With_Forces.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 4-6: Particle System with Forces +""" + +from pycreative.app import Sketch +from Emitter import Emitter + + +class Example_4_06_Particle_System_With_Forces(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example_4_06_Particle_System_With_Forces") + self.emitter = Emitter(self, self.width / 2, 50) + + def draw(self): + self.no_stroke() + self.fill(255, 30) + self.rect(0, 0, self.width, self.height) + + gravity = self.pvector(0, 0.1) + self.emitter.apply_force(gravity) + + self.emitter.add_particle() + self.emitter.run() diff --git a/examples/Nature of Code/chapter04/Example_4_06_Particle_System_With_Forces/Particle.py b/examples/Nature of Code/chapter04/Example_4_06_Particle_System_With_Forces/Particle.py new file mode 100644 index 0000000..144a654 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_06_Particle_System_With_Forces/Particle.py @@ -0,0 +1,37 @@ +""" +Particle class for Example 4.6: Particle System with Forces +""" + + +class Particle: + def __init__(self, sketch, x=0.0, y=0.0): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + self.velocity = self.sketch.pvector(self.sketch.random(-1, 1), self.sketch.random(-2, 0)) + self.acceleration = self.sketch.pvector(0, 0) + self.lifespan = 255.0 + self.mass = 1.0 # Let's do something better here! + + def draw(self): + self.update() + self.show() + + def apply_force(self, force): + f = force.copy() + f.div(self.mass) + self.acceleration.add(f) + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.acceleration.mult(0) + self.lifespan -= 2.0 + + def show(self): + self.sketch.stroke((self.lifespan, self.lifespan, self.lifespan)) + self.sketch.stroke_weight(2) + self.sketch.fill((self.lifespan, self.lifespan, self.lifespan)) + self.sketch.ellipse(self.position.x, self.position.y, 8, 8) + + def is_dead(self): + return self.lifespan < 0.0 \ No newline at end of file diff --git a/examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Emitter.py b/examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Emitter.py new file mode 100644 index 0000000..3eaf03b --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Emitter.py @@ -0,0 +1,32 @@ +""" +Emitter class for Example 4.7: Particle System with Repeller +""" + +from Particle import Particle + + +class Emitter: + def __init__(self, sketch, x, y): + self.sketch = sketch + self.origin = self.sketch.pvector(x, y) + self.particles = [] + + def add_particle(self): + self.particles.append(Particle(self.sketch, self.origin.x, self.origin.y)) + + def apply_force(self, force): + for particle in self.particles: + particle.apply_force(force) + + def apply_repeller(self, repeller): + for particle in self.particles: + force = repeller.repel(particle) + particle.apply_force(force) + + def run(self): + for i in range(len(self.particles) - 1, -1, -1): + particle = self.particles[i] + particle.update() + particle.run() + if particle.is_dead(): + self.particles.pop(i) \ No newline at end of file diff --git a/examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Example_4_07_Particle_System_With_Repeller.py b/examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Example_4_07_Particle_System_With_Repeller.py new file mode 100644 index 0000000..038411a --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Example_4_07_Particle_System_With_Repeller.py @@ -0,0 +1,33 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter4/Example_4_7_Particle_System_With_Repeller/Example_4_7_Particle_System_With_Repeller.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// One ParticleSystem + +// Example 4-7: Particle System with Repeller +""" + +from pycreative.app import Sketch +from Emitter import Emitter +from Repeller import Repeller + + +class Example_4_07_Particle_System_With_Repeller(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example_4_07_Particle_System_With_Repeller") + self.emitter = Emitter(self, self.width / 2, 60) + self.repeller = Repeller(self, self.width / 2, 250) + + def draw(self): + self.background((255, 255, 255)) + self.emitter.add_particle() + gravity = self.pvector(0, 0.1) + self.emitter.apply_force(gravity) + self.emitter.apply_repeller(self.repeller) + self.emitter.run() + self.repeller.show() \ No newline at end of file diff --git a/examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Particle.py b/examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Particle.py new file mode 100644 index 0000000..83ae99f --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Particle.py @@ -0,0 +1,33 @@ +""" +Particle class for Example 4.7: Particle System with Repeller +""" + + +class Particle: + def __init__(self, sketch, x=0.0, y=0.0): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + # For demonstration purposes the Particle has a random velocity. + self.velocity = self.sketch.pvector(self.sketch.random(-1, 1), self.sketch.random(-2, 0)) + self.acceleration = self.sketch.pvector(0, 0) + self.lifespan = 255.0 + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.lifespan -= 2.0 + self.acceleration.mult(0) + + def run(self): + self.sketch.fill((127, 127, 127, self.lifespan)) + self.sketch.stroke((0, 0, 0, self.lifespan)) + self.sketch.stroke_weight(2) + self.sketch.circle(self.position.x, self.position.y, 8) + + # Keeping the same physics model as with previous chapters + def apply_force(self, force): + self.acceleration.add(force) + + # Is the Particle alive or dead? + def is_dead(self) -> bool: + return self.lifespan < 0.0 \ No newline at end of file diff --git a/examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Repeller.py b/examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Repeller.py new file mode 100644 index 0000000..76d168a --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_07_Particle_System_With_Repeller/Repeller.py @@ -0,0 +1,24 @@ +""" +Repeller class for Example 4.7: Particle System with Repeller +""" + + +class Repeller: + def __init__(self, sketch, x, y): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + self.power = 150 # How strong is the repeller? + + def show(self): + self.sketch.stroke(0) + self.sketch.stroke_weight(2) + self.sketch.fill((127, 127, 127)) + self.sketch.circle(self.position.x, self.position.y, 32) + + def repel(self, particle): + force = self.sketch.pvector.sub(self.position, particle.position) + distance = force.mag() + distance = self.sketch.constrain(distance, 5, 50) + strength = (-1 * self.power) / (distance * distance) + force.set_mag(strength) + return force \ No newline at end of file diff --git a/examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/Emitter.py b/examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/Emitter.py new file mode 100644 index 0000000..c207753 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/Emitter.py @@ -0,0 +1,26 @@ +""" +Emitter class for Example 4.8: Image Texture System - Smoke +""" + +from Particle import Particle + + +class Emitter: + def __init__(self, sketch, x=0, y=0): + self.sketch = sketch + self.origin = self.sketch.pvector(x, y) + self.particles = [] + + def run(self): + for i in range(len(self.particles) - 1, -1, -1): + particle = self.particles[i] + particle.run() + if particle.is_dead(): + self.particles.pop(i) + + def apply_force(self, force): + for particle in self.particles: + particle.apply_force(force) + + def add_particle(self): + self.particles.append(Particle(self.sketch, self.origin.x, self.origin.y)) diff --git a/examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/Example_4_08_Image_Texture_System_Smoke.py b/examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/Example_4_08_Image_Texture_System_Smoke.py new file mode 100644 index 0000000..0617aa6 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/Example_4_08_Image_Texture_System_Smoke.py @@ -0,0 +1,48 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter4/Example_4_8_Image_Texture_System_Smoke/Example_4_8_Image_Texture_System_Smoke.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Smoke Particle System + +// A basic smoke effect using a particle system +// Each particle is rendered as an alpha masked image +""" + +from pycreative.app import Sketch +from Emitter import Emitter + + +class Example_4_08_Image_Texture_System_Smoke(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example_4_08_Image_Texture_System_Smoke") + self.img = self.load_image("texture.png") + self.emitter = Emitter(self, int(self.width / 2), self.height - 75) + self.frame_rate(30) + + def draw(self): + self.background(0) + dx = self.map(self.mouse_x or 0, 0, self.width, -0.2, 0.2) + wind = self.pvector(dx, 0) + self.emitter.apply_force(wind) + self.emitter.run() + self.emitter.add_particle() + self.draw_vector(wind, self.pvector(self.width / 2, 50), 500) + + def draw_vector(self, v, pos, scayl): + self.push() + try: + arrowsize = 4 + self.translate(pos.x, pos.y) + self.stroke(255) + self.rotate(v.heading()) + length = v.mag() * scayl + self.line(0, 0, length, 0) + self.line(length, 0, length - arrowsize, +arrowsize / 2) + self.line(length, 0, length - arrowsize, -arrowsize / 2) + finally: + self.pop() \ No newline at end of file diff --git a/examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/Particle.py b/examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/Particle.py new file mode 100644 index 0000000..9946420 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/Particle.py @@ -0,0 +1,35 @@ +""" +Particle class for Example 4.8: Image Texture System (Smoke) +""" + + +class Particle: + def __init__(self, sketch, x=0.0, y=0.0): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + vx = self.sketch.random_gaussian() * 0.3 + vy = (self.sketch.random_gaussian() * 0.3) - 1 + self.velocity = self.sketch.pvector(vx, vy) + self.acceleration = self.sketch.pvector(0, 0) + self.lifespan = 100.0 + + def run(self): + self.update() + self.show() + + def apply_force(self, force): + self.acceleration.add(force) + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.lifespan -= 2 + self.acceleration.mult(0) + + def show(self): + self.sketch.tint(255, self.lifespan) + self.sketch.image_mode('CENTER') + self.sketch.image(self.sketch.img, self.position.x, self.position.y) + + def is_dead(self): + return self.lifespan < 0.0 \ No newline at end of file diff --git a/examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/data/texture.png b/examples/Nature of Code/chapter04/Example_4_08_Image_Texture_System_Smoke/data/texture.png new file mode 100644 index 0000000000000000000000000000000000000000..0852c88e1d473f948dc8bc34ff0f50dd52a7bc9c GIT binary patch literal 3554 zcmV<84IT1{P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0009GNkl~vNy5qdS8V6F6=L$fbF8*%Z$9wUO5kLdybl8d& zFcU`S3mnoKj0Nxq02Ak771rX~N~p-dVrF--aBlc!4FD$8(s7qq6($&4I-T9_W_I(d zk$y8tBzn%n01p_Nqe+AGA5K8|OfUuf4KbhH^Ur+wL2MY}V#L5R>h$iZ)ra91ofujRB zJAktrLA1#50C4d+vJh!`mMW`{_#Xk_H(no%5Ny+gY=P-EVe23^Is$TfJVS)iq|B2ho}8R-Puh1Z-cQwOiaWj6P#>5%K)q* z7TVb+SbQ5fFrnOTC^gRa4MEAOnB|iv+P%i5n@OP)-*(+fZtq2+?3>p8{e`n7AtdHVDe*0P$c` z3C*tv!GsWfnAsl(F+dHKg{3A9V@8s-vTs}++yH?wXnu6VFSk$se*p_mVW>!mI|OKw z`rhE29gq8g0Dgtj2tzQy5Ig`Za1_a;!m?s)gtjv@x*cQHjFN~?05$>9B!`X_wcca( zMG|vHD6Xi0Yf_G@3el2CCbeNk%VlHP{;@00#A&7$)Z|c+X*L0{rRu>XI~UBd6MB+t zRzbn(W!pLJwBo${@MN?I^=Sxf2ZTB=0$`I|**hCPIo4jl36$0bAGb*h-Vzqpq-(B8 cSGB(b0IJ&yc|~D`y8r+H07*qoM6N<$f_qt!Pyhe` literal 0 HcmV?d00001 diff --git a/examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/Emitter.py b/examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/Emitter.py new file mode 100644 index 0000000..72c1b35 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/Emitter.py @@ -0,0 +1,27 @@ +""" +Emitter class for Example 4.9: Additive Blending +""" + +from Particle import Particle + + +class Emitter: + def __init__(self, sketch, x=0, y=0, img=None): + self.sketch = sketch + self.origin = self.sketch.pvector(x, y) + self.particles = [] + self.img = img + + def run(self): + for i in range(len(self.particles) - 1, -1, -1): + particle = self.particles[i] + particle.run() + if particle.is_dead(): + self.particles.pop(i) + + def apply_force(self, force): + for particle in self.particles: + particle.apply_force(force) + + def add_particle(self): + self.particles.append(Particle(self.sketch, self.origin.x, self.origin.y, self.img)) diff --git a/examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/Example_4_09_Additive_Blending.py b/examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/Example_4_09_Additive_Blending.py new file mode 100644 index 0000000..0b67d4a --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/Example_4_09_Additive_Blending.py @@ -0,0 +1,59 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter4/Example_4_9_Additive_Blending/Example_4_9_Additive_Blending.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Smoke Particle System + +// A basic smoke effect using a particle system +// Each particle is rendered as an alpha masked image +""" + +from pycreative.app import Sketch +from Emitter import Emitter + + +class Example_4_09_Additive_Blending(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example_4_09_Additive_Blending") + self.img = self.load_image("texture.png") + self.emitter = Emitter(self, int(self.width / 2), self.height - 75, self.img) + self.frame_rate(30) + + def draw(self): + self.background(0) + # Additive blending! + self.blend_mode('ADD') + + # Calculate a "wind" force based on mouse horizontal position + dx = self.map(self.mouse_x or 0, 0, self.width, -0.2, 0.2) + wind = self.pvector(dx, 0) + self.emitter.apply_force(wind) + self.emitter.run() + for _ in range(3): + self.emitter.add_particle() + + # Draw an arrow representing the wind force + self.draw_vector(wind, self.pvector(self.width / 2, 50), 500) + + def draw_vector(self, v, pos, scayl): + self.push() + try: + arrowsize = 4 + # Translate to position to render vector + self.translate(pos.x, pos.y) + self.stroke((255, 255, 255)) + # Call vector heading function to get direction (note that pointing up is a heading of 0) and rotate + self.rotate(v.heading()) + # Calculate length of vector & scale it to be bigger or smaller if necessary + length = v.mag() * scayl + # Draw three lines to make an arrow (draw pointing up since we've rotate to the proper direction) + self.line(0, 0, length, 0) + self.line(length, 0, length - arrowsize, +arrowsize / 2) + self.line(length, 0, length - arrowsize, -arrowsize / 2) + finally: + self.pop() diff --git a/examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/Particle.py b/examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/Particle.py new file mode 100644 index 0000000..08dd9f8 --- /dev/null +++ b/examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/Particle.py @@ -0,0 +1,36 @@ +""" +Particle class for Example 4.9: Additive Blending +""" + +class Particle: + def __init__(self, sketch, x=0.0, y=0.0, img=None): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + vx = self.sketch.random_gaussian() * 0.3 + vy = (self.sketch.random_gaussian() * 0.3) - 1 + self.velocity = self.sketch.pvector(vx, vy) + self.acceleration = self.sketch.pvector(0, 0) + self.lifespan = 100.0 + self.img = img + + def run(self): + self.update() + self.show() + + def apply_force(self, force): + self.acceleration.add(force) + + def update(self): + self.velocity.add(self.acceleration) + self.position.add(self.velocity) + self.lifespan -= 2.5 + self.acceleration.mult(0) # clear Acceleration + + def show(self): + self.sketch.tint(255, 100, 255, self.lifespan) + self.sketch.image_mode(self.sketch.CENTER) + if self.img: + self.sketch.image(self.img, self.position.x, self.position.y) + + def is_dead(self): + return self.lifespan < 0.0 \ No newline at end of file diff --git a/examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/data/texture.png b/examples/Nature of Code/chapter04/Example_4_09_Additive_Blending/data/texture.png new file mode 100644 index 0000000000000000000000000000000000000000..0852c88e1d473f948dc8bc34ff0f50dd52a7bc9c GIT binary patch literal 3554 zcmV<84IT1{P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0009GNkl~vNy5qdS8V6F6=L$fbF8*%Z$9wUO5kLdybl8d& zFcU`S3mnoKj0Nxq02Ak771rX~N~p-dVrF--aBlc!4FD$8(s7qq6($&4I-T9_W_I(d zk$y8tBzn%n01p_Nqe+AGA5K8|OfUuf4KbhH^Ur+wL2MY}V#L5R>h$iZ)ra91ofujRB zJAktrLA1#50C4d+vJh!`mMW`{_#Xk_H(no%5Ny+gY=P-EVe23^Is$TfJVS)iq|B2ho}8R-Puh1Z-cQwOiaWj6P#>5%K)q* z7TVb+SbQ5fFrnOTC^gRa4MEAOnB|iv+P%i5n@OP)-*(+fZtq2+?3>p8{e`n7AtdHVDe*0P$c` z3C*tv!GsWfnAsl(F+dHKg{3A9V@8s-vTs}++yH?wXnu6VFSk$se*p_mVW>!mI|OKw z`rhE29gq8g0Dgtj2tzQy5Ig`Za1_a;!m?s)gtjv@x*cQHjFN~?05$>9B!`X_wcca( zMG|vHD6Xi0Yf_G@3el2CCbeNk%VlHP{;@00#A&7$)Z|c+X*L0{rRu>XI~UBd6MB+t zRzbn(W!pLJwBo${@MN?I^=Sxf2ZTB=0$`I|**hCPIo4jl36$0bAGb*h-Vzqpq-(B8 cSGB(b0IJ&yc|~D`y8r+H07*qoM6N<$f_qt!Pyhe` literal 0 HcmV?d00001 diff --git a/examples/Nature of Code/chapter05/Example_5_01_Seek/Example_5_01_Seek.py b/examples/Nature of Code/chapter05/Example_5_01_Seek/Example_5_01_Seek.py new file mode 100644 index 0000000..d673752 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_01_Seek/Example_5_01_Seek.py @@ -0,0 +1,37 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter5/Example_5_1_Seek/Example_5_1_Seek.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Seeking "vehicle" follows the mouse position + +// Example 5.1: Seek +""" + +from pycreative.app import Sketch +from Vehicle import Vehicle + + +class Example_5_01_Seek(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 5.1: Seek") + self.vehicle = Vehicle(self, self.width / 2, self.height / 2) + + def draw(self): + self.clear(255) + mouse = (self.mouse_x or 0, self.mouse_y or 0) + + # Draw an ellipse at the mouse position + self.fill(127) + self.stroke(0) + self.stroke_weight(2) + self.circle(mouse[0], mouse[1], 48) + + # Call the appropriate steering behaviors for our agents + self.vehicle.seek(mouse) + self.vehicle.update() + self.vehicle.show() diff --git a/examples/Nature of Code/chapter05/Example_5_01_Seek/Vehicle.py b/examples/Nature of Code/chapter05/Example_5_01_Seek/Vehicle.py new file mode 100644 index 0000000..067a003 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_01_Seek/Vehicle.py @@ -0,0 +1,59 @@ +""" +Vehicle class for Example 5.1: Seek +""" + + +class Vehicle: + def __init__(self, sketch, x: float, y: float): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + self.velocity = self.sketch.pvector(0, 0) + self.acceleration = self.sketch.pvector(0, 0) + self.r = 6 + self.maxspeed = 8 + self.maxforce = 0.2 + + # Method to update location + def update(self): + # Update velocity + self.velocity += self.acceleration + # Limit speed + self.velocity.limit(self.maxspeed) + self.position += self.velocity + # Reset acceleration to 0 each cycle + self.acceleration *= 0 + + def apply_force(self, force): + # We could add mass here if we want A = F / M + self.acceleration += force + + # A method that calculates a steering force towards a target + # STEER = DESIRED MINUS VELOCITY + def seek(self, target): + desired = target - self.position # A vector pointing from the location to the target + + # Scale to maximum speed + desired.set_mag(self.maxspeed) + + # Steering = Desired minus velocity + steer = desired - self.velocity + steer.limit(self.maxforce) # Limit to maximum steering force + + self.apply_force(steer) + + def show(self): + # Vehicle is a triangle pointing in the direction of velocity + angle = self.velocity.heading() + self.sketch.fill(127) + self.sketch.stroke(0) + self.sketch.push_matrix() + try: + self.sketch.translate(self.position.x, self.position.y) + self.sketch.rotate(angle) + self.sketch.begin_shape('POLYGON') + self.sketch.vertex(self.r * 2, 0) + self.sketch.vertex(-self.r * 2, -self.r) + self.sketch.vertex(-self.r * 2, self.r) + self.sketch.end_shape() + finally: + self.sketch.pop_matrix() \ No newline at end of file diff --git a/examples/Nature of Code/chapter05/Example_5_02_Arrive/Example_5_02_Arrive.py b/examples/Nature of Code/chapter05/Example_5_02_Arrive/Example_5_02_Arrive.py new file mode 100644 index 0000000..c96a744 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_02_Arrive/Example_5_02_Arrive.py @@ -0,0 +1,44 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter5/Example_5_2_Arrive/Example_5_2_Arrive.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Arriving "vehicle" follows the mouse position + +// Implements Craig Reynold's autonomous steering behaviors +// One vehicle "arrive" +// See: http://www.red3d.com/cwr/ +""" + +from pycreative.app import Sketch +from Vehicle import Vehicle + + +class Example_5_02_Arrive(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 5.2: Arrive") + self.vehicle = Vehicle(self, self.width / 2, self.height / 2) + + def update(self, dt): + self.vehicle.update() + + def draw(self): + self.background(255) + mouse = self.pvector(self.mouse_x or 0, self.mouse_y or 0) + + # Draw an ellipse at the mouse position + self.fill(127) + self.stroke(0) + self.stroke_weight(2) + self.circle(mouse.x, mouse.y, 48) + + # Call the appropriate steering behaviors for our agents + self.vehicle.arrive(mouse) + self.vehicle.show() + + def mouse_pressed(self): + self.save_frame("screenshot.png") diff --git a/examples/Nature of Code/chapter05/Example_5_02_Arrive/Vehicle.py b/examples/Nature of Code/chapter05/Example_5_02_Arrive/Vehicle.py new file mode 100644 index 0000000..b5e694d --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_02_Arrive/Vehicle.py @@ -0,0 +1,62 @@ +""" +Vehicle class for Example 5.2: Arrive +""" + + +class Vehicle: + def __init__(self, sketch, x: float, y: float): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + self.velocity = self.sketch.pvector(0, 0) + self.acceleration = self.sketch.pvector(0, 0) + self.r = 6 + self.maxspeed = 8 + self.maxforce = 0.2 + + # Method to update location + def update(self): + # Update velocity + self.velocity += self.acceleration + # Limit speed + self.velocity.limit(self.maxspeed) + self.position += self.velocity + # Reset acceleration to 0 each cycle + self.acceleration *= 0 + + def apply_force(self, force): + # We could add mass here if we want A = F / M + self.acceleration += force + + # A method that calculates a steering force towards a target + # STEER = DESIRED MINUS VELOCITY + def arrive(self, target): + desired = target - self.position # A vector pointing from the location to the target + d = desired.mag() + # Scale with arbitrary damping within 100 pixels + if d < 100: + m = self.sketch.map(d, 0, 100, 0, self.maxspeed) + desired.set_mag(m) + else: + desired.set_mag(self.maxspeed) + + # Steering = Desired minus velocity + steer = desired - self.velocity + steer.limit(self.maxforce) # Limit to maximum steering force + self.apply_force(steer) + + def show(self): + # Vehicle is a triangle pointing in the direction of velocity + angle = self.velocity.heading() + self.sketch.fill(127) + self.sketch.stroke(0) + self.sketch.push_matrix() + try: + self.sketch.translate(self.position.x, self.position.y) + self.sketch.rotate(angle) + self.sketch.begin_shape('POLYGON') + self.sketch.vertex(self.r * 2, 0) + self.sketch.vertex(-self.r * 2, -self.r) + self.sketch.vertex(-self.r * 2, self.r) + self.sketch.end_shape() + finally: + self.sketch.pop_matrix() diff --git a/examples/Nature of Code/chapter05/Example_5_03_Stay_Within_Walls/Example_5_03_Stay_Within_Walls.py b/examples/Nature of Code/chapter05/Example_5_03_Stay_Within_Walls/Example_5_03_Stay_Within_Walls.py new file mode 100644 index 0000000..fb1d146 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_03_Stay_Within_Walls/Example_5_03_Stay_Within_Walls.py @@ -0,0 +1,40 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter5/Example_5_3_Stay_Within_Walls/Example_5_3_Stay_Within_Walls.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Stay Within Walls +// "Made-up" Steering behavior to stay within walls +""" + +from pycreative.app import Sketch +from Vehicle import Vehicle + + +class Example_5_03_Stay_Within_Walls(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 5.3: Stay Within Walls") + self.vehicle = Vehicle(self, self.width / 2, self.height / 2) + self.debug = True + self.offset = 25 + + def draw(self): + self.clear(255) + + if self.debug: + self.stroke(0) + self.no_fill() + self.rect_mode('CENTER') + self.rect(self.width / 2, self.height / 2, self.width - self.offset * 2, self.height - self.offset * 2) + + # Call the appropriate steering behaviors for our agents + self.vehicle.boundaries(self.offset) + self.vehicle.update() + self.vehicle.show() + + def mouse_pressed(self): + self.debug = not self.debug diff --git a/examples/Nature of Code/chapter05/Example_5_03_Stay_Within_Walls/Vehicle.py b/examples/Nature of Code/chapter05/Example_5_03_Stay_Within_Walls/Vehicle.py new file mode 100644 index 0000000..168ba96 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_03_Stay_Within_Walls/Vehicle.py @@ -0,0 +1,68 @@ +""" +Vehicle class for Example 5-3: Stay Within Walls +""" + + +class Vehicle: + def __init__(self, sketch, x: float, y: float): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + self.velocity = self.sketch.pvector(3, 4) + self.acceleration = self.sketch.pvector(0, 0) + self.r = 6 + self.maxspeed = 3 + self.maxforce = 0.15 + + # Method to update location + def update(self): + # Update velocity + self.velocity += self.acceleration + # Limit speed + self.velocity.limit(self.maxspeed) + self.position += self.velocity + # Reset acceleration to 0 each cycle + self.acceleration *= 0 + + def apply_force(self, force): + # We could add mass here if we want A = F / M + self.acceleration += force + + def boundaries(self, offset): + desired = None + + if self.position.x < offset: + desired = self.sketch.pvector(self.maxspeed, self.velocity.y) + elif self.position.x > self.sketch.width - offset: + desired = self.sketch.pvector(-self.maxspeed, self.velocity.y) + + if self.position.y < offset: + desired = self.sketch.pvector(self.velocity.x, self.maxspeed) + elif self.position.y > self.sketch.height - offset: + desired = self.sketch.pvector(self.velocity.x, -self.maxspeed) + + if desired is not None: + desired.normalize() + desired.mult(self.maxspeed) + steer = desired - self.velocity + steer.limit(self.maxforce) + self.apply_force(steer) + + # A method that calculates a steering force towards a target + # STEER = DESIRED MINUS VELOCITY + def show(self): + # Vehicle is a triangle pointing in the direction of velocity + angle = self.velocity.heading() + self.sketch.fill(127) + self.sketch.stroke(0) + self.sketch.stroke_weight(3) + self.sketch.push_matrix() + try: + self.sketch.translate(self.position.x, self.position.y) + self.sketch.rotate(angle) + self.sketch.begin_shape('POLYGON') + self.sketch.vertex(self.r * 2, 0) + self.sketch.vertex(-self.r * 2, -self.r) + self.sketch.vertex(-self.r * 2, self.r) + self.sketch.end_shape(close=True) + finally: + self.sketch.pop_matrix() diff --git a/examples/Nature of Code/chapter05/Example_5_04_Flow_Field/Example_5_04_Flow_Field.py b/examples/Nature of Code/chapter05/Example_5_04_Flow_Field/Example_5_04_Flow_Field.py new file mode 100644 index 0000000..d05bea0 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_04_Flow_Field/Example_5_04_Flow_Field.py @@ -0,0 +1,53 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter5/Example_5_4_Flow_Field/Example_5_4_Flow_Field.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Flow Field Following +// Via Reynolds: http://www.red3d.com/cwr/steer/FlowFollow.html +""" + +from pycreative.app import Sketch +from FlowField import FlowField +from Vehicle import Vehicle + + +class Example_5_04_Flow_Field(Sketch): + def setup(self): + self.size(640, 360) + self.set_title('Example 5.4: Flow Field Following') + print("Hit space bar to toggle debugging lines.\nClick the mouse to generate a new flow field.") + # Make a new flow field with "resolution" of 16 + self.flowfield = FlowField(self, 20) + self.vehicles = [] + # Make a whole bunch of vehicles with random maxspeed and maxforce values + import random + for _ in range(120): + ms = random.uniform(2, 5) + mf = random.uniform(0.1, 0.5) + v = Vehicle(self, random.uniform(0, self.width), random.uniform(0, self.height), ms, mf) + self.vehicles.append(v) + self.debug = True + + def update(self, dt): + pass + + def draw(self): + self.clear((255, 255, 255)) + # Display the flowfield in "debug" mode + self.flowfield.show(self.debug) + # Tell all the vehicles to follow the flow field + for v in self.vehicles: + v.follow(self.flowfield) + v.run() + + def key_pressed(self): + if self.key == 'space': + self.debug = not self.debug + + def mouse_pressed(self): + # Make a new flowfield + self.flowfield.init() diff --git a/examples/Nature of Code/chapter05/Example_5_04_Flow_Field/FlowField.py b/examples/Nature of Code/chapter05/Example_5_04_Flow_Field/FlowField.py new file mode 100644 index 0000000..0b97dba --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_04_Flow_Field/FlowField.py @@ -0,0 +1,56 @@ +""" +Flow Field class for Example 5-4: Flow Field +""" + + +class FlowField: + def __init__(self, sketch, resolution: int): + self.sketch = sketch + self.resolution = resolution + self.cols = 0 + self.rows = 0 + self.field: list[list[self.sketch.pvector]] = [] + self.init_dimensions() + + def init(self): + self.init_dimensions() + + def init_dimensions(self): + self.cols = self.sketch.width // self.resolution + self.rows = self.sketch.height // self.resolution + self.field = [[self.sketch.pvector(0, 0) for _ in range(self.rows)] for _ in range(self.cols)] + self.init_field() + + def init_field(self): + for i in range(self.cols): + for j in range(self.rows): + self.field[i][j] = self.sketch.pvector(0, 0) + noise_seed = self.sketch.random(0, 10000) + self.sketch.noise_seed(noise_seed) + xoff = 0.0 + for i in range(self.cols): + yoff = 0.0 + for j in range(self.rows): + angle = self.sketch.map(self.sketch.noise(xoff, yoff), 0, 1, 0, self.sketch.TWO_PI) + self.field[i][j] = self.sketch.pvector.from_angle(angle) + yoff += 0.1 + xoff += 0.1 + + def show(self, debug): + if not debug: + return + for i in range(self.cols): + for j in range(self.rows): + w = self.sketch.width / self.cols + h = self.sketch.height / self.rows + v = self.field[i][j].copy() + v.set_mag(w * 0.5) + x = i * w + w / 2 + y = j * h + h / 2 + self.sketch.stroke_weight(1) + self.sketch.line(x, y, x + v.x, y + v.y) + + def lookup(self, position): + column = self.sketch.constrain(int(position.x // self.resolution), 0, self.cols - 1) + row = self.sketch.constrain(int(position.y // self.resolution), 0, self.rows - 1) + return self.field[column][row].copy() diff --git a/examples/Nature of Code/chapter05/Example_5_04_Flow_Field/Vehicle.py b/examples/Nature of Code/chapter05/Example_5_04_Flow_Field/Vehicle.py new file mode 100644 index 0000000..12faa14 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_04_Flow_Field/Vehicle.py @@ -0,0 +1,74 @@ +""" +Flow Field class for Example 5-4: Flow Field +""" + + +class Vehicle: + def __init__(self, sketch, x: float, y: float, ms: float, mf: float): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + self.acceleration = self.sketch.pvector(0, 0) + self.velocity = self.sketch.pvector(0, 0) + self.r = 4 + self.maxspeed = ms + self.maxforce = mf + + def run(self): + self.update() + self.borders() + self.show() + + # Implementing Reynolds' flow field following algorithm + # http://www.red3d.com/cwr/steer/FlowFollow.html + def follow(self, flow): + # What is the vector at that spot in the flow field? + desired = flow.lookup(self.position) + # Scale it up by maxspeed + desired.mult(self.maxspeed) + # Steering is desired minus velocity + steer = desired - self.velocity + steer.limit(self.maxforce) # Limit to maximum steering force + self.apply_force(steer) + + def apply_force(self, force): + # We could add mass here if we want A = F / M + self.acceleration += force + + # Method to update location + def update(self): + # Update velocity + self.velocity += self.acceleration + # Limit speed + self.velocity.limit(self.maxspeed) + self.position += self.velocity + # Reset acceleration to 0 each cycle + self.acceleration *= 0 + + # Wraparound + def borders(self): + if self.position.x < -self.r: + self.position.x = self.sketch.width + self.r + if self.position.y < -self.r: + self.position.y = self.sketch.height + self.r + if self.position.x > self.sketch.width + self.r: + self.position.x = -self.r + if self.position.y > self.sketch.height + self.r: + self.position.y = -self.r + + def show(self): + # Draw a triangle rotated in the direction of velocity + angle = self.velocity.heading() + self.sketch.fill(127) + self.sketch.stroke(0) + self.sketch.stroke_weight(2) + self.sketch.push_matrix() + try: + self.sketch.translate(self.position.x, self.position.y) + self.sketch.rotate(angle) + self.sketch.begin_shape('POLYGON') + self.sketch.vertex(self.r * 2, 0) + self.sketch.vertex(-self.r * 2, -self.r) + self.sketch.vertex(-self.r * 2, self.r) + self.sketch.end_shape(close=True) + finally: + self.sketch.pop_matrix() diff --git a/examples/Nature of Code/chapter05/Example_5_05_Create_Path_Object/Example_5_05_Create_Path_Object.py b/examples/Nature of Code/chapter05/Example_5_05_Create_Path_Object/Example_5_05_Create_Path_Object.py new file mode 100644 index 0000000..8ba7a07 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_05_Create_Path_Object/Example_5_05_Create_Path_Object.py @@ -0,0 +1,28 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter5/Example_5_5_Create_Path_Object/Example_5_5_Create_Path_Object.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Path Following +// Path is a just a straight line in this example +// Via Reynolds: // http://www.red3d.com/cwr/steer/PathFollow.html + +// A path object (series of connected points) +""" + +from pycreative.app import Sketch +from Path import Path + + +class Example_5_05_Create_Path_Object(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 5.5: Create Path Object") + self.path = Path(self) + + def draw(self): + self.background(255) + self.path.show() diff --git a/examples/Nature of Code/chapter05/Example_5_05_Create_Path_Object/Path.py b/examples/Nature of Code/chapter05/Example_5_05_Create_Path_Object/Path.py new file mode 100644 index 0000000..ba8a905 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_05_Create_Path_Object/Path.py @@ -0,0 +1,23 @@ +""" +Path object for Example 5-5: Create Path Object +""" + + +class Path: + def __init__(self, sketch): + self.sketch = sketch + # A path has a radius, how wide is it. + # {!3} Picking some arbitrary values to initialize the path + self.radius = 20 + self.start = sketch.pvector(0, sketch.height / 3) + # {!2} A path is only two points, start and end. + self.end = sketch.pvector(sketch.width, (2 * sketch.height) / 3) + + # {!7} Display the path. + def show(self): + self.sketch.stroke(0, 100) + self.sketch.stroke_weight(self.radius * 2) + self.sketch.line(self.start.x, self.start.y, self.end.x, self.end.y) + self.sketch.stroke(0) + self.sketch.stroke_weight(1) + self.sketch.line(self.start.x, self.start.y, self.end.x, self.end.y) diff --git a/examples/Nature of Code/chapter05/Example_5_06_Simple_Path_Following/Example_5_06_Simple_Path_Following.py b/examples/Nature of Code/chapter05/Example_5_06_Simple_Path_Following/Example_5_06_Simple_Path_Following.py new file mode 100644 index 0000000..b26b032 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_06_Simple_Path_Following/Example_5_06_Simple_Path_Following.py @@ -0,0 +1,50 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter5/Example_5_6_Simple_Path_Following/Example_5_6_Simple_Path_Following.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Path Following +// Path is a just a straight line in this example +// Via Reynolds: // http://www.red3d.com/cwr/steer/PathFollow.html +""" + +from pycreative.app import Sketch +from Vehicle import Vehicle +from Path import Path + + +class Example_5_6_Simple_Path_Following(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 5.6: Simple Path Following") + self.debug = True # Using this variable to decide whether to draw all the stuff + self.path = Path(self) # A path object (series of connected points) + # Two vehicles + # Each vehicle has different maxspeed and maxforce for demo purposes + self.vehicle1 = Vehicle(self, 0, self.height / 2, 2, 0.02) + self.vehicle2 = Vehicle(self, 0, self.height / 2, 3, 0.05) + + def update(self, dt): + pass + + def draw(self): + self.background((255, 255, 255)) + # Display the path + self.path.show() + # The boids follow the path + self.vehicle1.follow(self.path) + self.vehicle2.follow(self.path) + # Call the generic run method (update, borders, display, etc.) + self.vehicle1.run() + self.vehicle2.run() + + # Check if it gets to the end of the path since it's not a loop + self.vehicle1.borders(self.path) + self.vehicle2.borders(self.path) + + def key_pressed(self): + if self.key == ' ': + self.debug = not self.debug \ No newline at end of file diff --git a/examples/Nature of Code/chapter05/Example_5_06_Simple_Path_Following/Path.py b/examples/Nature of Code/chapter05/Example_5_06_Simple_Path_Following/Path.py new file mode 100644 index 0000000..ba8a905 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_06_Simple_Path_Following/Path.py @@ -0,0 +1,23 @@ +""" +Path object for Example 5-5: Create Path Object +""" + + +class Path: + def __init__(self, sketch): + self.sketch = sketch + # A path has a radius, how wide is it. + # {!3} Picking some arbitrary values to initialize the path + self.radius = 20 + self.start = sketch.pvector(0, sketch.height / 3) + # {!2} A path is only two points, start and end. + self.end = sketch.pvector(sketch.width, (2 * sketch.height) / 3) + + # {!7} Display the path. + def show(self): + self.sketch.stroke(0, 100) + self.sketch.stroke_weight(self.radius * 2) + self.sketch.line(self.start.x, self.start.y, self.end.x, self.end.y) + self.sketch.stroke(0) + self.sketch.stroke_weight(1) + self.sketch.line(self.start.x, self.start.y, self.end.x, self.end.y) diff --git a/examples/Nature of Code/chapter05/Example_5_06_Simple_Path_Following/Vehicle.py b/examples/Nature of Code/chapter05/Example_5_06_Simple_Path_Following/Vehicle.py new file mode 100644 index 0000000..68ef3eb --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_06_Simple_Path_Following/Vehicle.py @@ -0,0 +1,128 @@ +""" +Vehicle class for Example 5-6: Simple Path Following +""" + +class Vehicle: + def __init__(self, sketch, x, y, maxspeed, maxforce): + self.sketch = sketch + self.position = sketch.pvector(x, y) + self.acceleration = sketch.pvector(0, 0) + self.velocity = sketch.pvector(2, 0) + self.r = 4 + self.maxspeed = maxspeed + self.maxforce = maxforce + self.debug = False + + def run(self): + self.update() + self.show() + + # This function implements Craig Reynolds' path following algorithm + # http://www.red3d.com/cwr/steer/PathFollow.html + def follow(self, path): + # {!3} Step 1: Predict the vehicles future position. + future = self.velocity.copy() + future.set_mag(25) + future += self.position + + # {!1} Step 2: Find the normal point along the path. + normal_point = get_normal_point(self.sketch, future, path.start, path.end) + + # {!3} Step 3: Move a little further along the path and set a target. + b = path.end - path.start + b.set_mag(25) + target = normal_point + b + + # {!5} Step 4: If we are off the path, + # seek that target in order to stay on the path. + distance = normal_point.dist(future) + if distance > path.radius: + self.seek(target) + + # Draw the debugging stuff + if self.debug: + self.sketch.fill(127) + self.sketch.stroke(0) + self.sketch.line(self.position.x, self.position.y, future.x, future.y) + self.sketch.ellipse(future.x, future.y, 4, 4) + + # Draw normal location + self.sketch.fill(127) + self.sketch.stroke(0) + self.sketch.line(future.x, future.y, normal_point.x, normal_point.y) + self.sketch.ellipse(normal_point.x, normal_point.y, 4, 4) + self.sketch.stroke(0) + if distance > path.radius: + self.sketch.fill(255, 0, 0) + else: + self.sketch.no_fill() + self.sketch.circle(target.x + b.x, target.y + b.y, 8) + self.sketch.no_stroke() + + def apply_force(self, force): + # We could add mass here if we want A = F / M + self.acceleration += force + + # A method that calculates and applies a steering force towards a target + # STEER = DESIRED MINUS VELOCITY + def seek(self, target): + desired = target - self.position # A vector pointing from the position to the target + + # If the magnitude of desired equals 0, skip out of here + # (We could optimize this to check if x and y are 0 to avoid mag() square root + if desired.mag() == 0: + return + + # Normalize desired and scale to maximum speed + desired.normalize() + desired *= self.maxspeed + # Steering = Desired minus Velocity + steer = desired - self.velocity + steer.limit(self.maxforce) # Limit to maximum steering force + + self.apply_force(steer) + + def show(self): + # Draw a triangle rotated in the direction of velocity + theta = self.velocity.heading() + self.sketch.fill(127) + self.sketch.stroke(0) + self.sketch.stroke_weight(2) + self.sketch.push_matrix() + try: + self.sketch.translate(self.position.x, self.position.y) + self.sketch.rotate(theta) + self.sketch.begin_shape() + self.sketch.vertex(self.r * 2, 0) + self.sketch.vertex(-self.r * 2, -self.r) + self.sketch.vertex(-self.r * 2, self.r) + self.sketch.end_shape(close=True) + finally: + self.sketch.pop_matrix() + + def borders(self, path): + if self.position.x > path.end.x + self.r: + self.position.x = path.start.x - self.r + self.position.y = path.start.y + (self.position.y - path.end.y) + + def update(self): + # Update velocity + self.velocity += self.acceleration + # Limit speed + self.velocity.limit(self.maxspeed) + self.position += self.velocity + # Reset acceleration to 0 each cycle + self.acceleration *= 0 + +def get_normal_point(self, position, a, b): + # Vector that points from a to position + vector_a = position - a + # Vector that points from a to b + vector_b = b - a + + # Using the dot product for scalar projection + vector_b.normalize() + vector_b *= vector_a.dot(vector_b) + # {!1} Finding the normal point along the line segment + normal_point = a + vector_b + return normal_point diff --git a/examples/Nature of Code/chapter05/Example_5_07_Multiple_Segments/Example_5_07_Multiple_Segments.py b/examples/Nature of Code/chapter05/Example_5_07_Multiple_Segments/Example_5_07_Multiple_Segments.py new file mode 100644 index 0000000..03913da --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_07_Multiple_Segments/Example_5_07_Multiple_Segments.py @@ -0,0 +1,31 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter5/Example_5_7_Multiple_Segments/Example_5_7_Multiple_Segments.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Path Following +// Path is a just a straight line in this example +// Via Reynolds: // http://www.red3d.com/cwr/steer/PathFollow.html +""" + +from pycreative.app import Sketch +from Path import Path + + +class Example_5_7_Multiple_Segments(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 5.7: Multiple Segments") + self.path = Path(self) + self.path.add_point(-20, self.height / 2) + self.path.add_point(100, 50) + self.path.add_point(400, 200) + self.path.add_point(self.width + 20, self.height / 2) + + def draw(self): + self.background(255) + # Display the path + self.path.show() diff --git a/examples/Nature of Code/chapter05/Example_5_07_Multiple_Segments/Path.py b/examples/Nature of Code/chapter05/Example_5_07_Multiple_Segments/Path.py new file mode 100644 index 0000000..841933c --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_07_Multiple_Segments/Path.py @@ -0,0 +1,35 @@ +""" +Path class for Example 5-7: Multiple Segments +""" + + +class Path: + def __init__(self, sketch): + self.sketch = sketch + self.radius = 20 + # create empty list that will store PVector objects + self.points = [] + + # Add a point to the path + def add_point(self, x, y): + point = self.sketch.pvector(x, y) + self.points.append(point) + + # Draw the path + def show(self): + # Draw thick line for radius + self.sketch.stroke(200) + self.sketch.stroke_weight(self.radius * 2) + self.sketch.no_fill() + self.sketch.begin_shape() + for path_point in self.points: + self.sketch.vertex(path_point.x, path_point.y) + self.sketch.end_shape() + # Draw thin line for center of path + self.sketch.stroke(0) + self.sketch.stroke_weight(1) + self.sketch.no_fill() + self.sketch.begin_shape() + for path_point in self.points: + self.sketch.vertex(path_point.x, path_point.y) + self.sketch.end_shape() diff --git a/examples/Nature of Code/chapter05/Example_5_08_Path_Following/Example_5_08_Path_Following.py b/examples/Nature of Code/chapter05/Example_5_08_Path_Following/Example_5_08_Path_Following.py new file mode 100644 index 0000000..b71e885 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_08_Path_Following/Example_5_08_Path_Following.py @@ -0,0 +1,65 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter5/Example_5_8_Path_Following/Example_5_8_Path_Following.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Path Following +// Path is a just a straight line in this example +// Via Reynolds: // http://www.red3d.com/cwr/steer/PathFollow.html + +// A path is a series of connected points +""" + +from pycreative.app import Sketch +from Path import Path +from Vehicle import Vehicle + + +class Example_5_8_Path_Following(Sketch): + def setup(self): + self.size(640, 360) + print("Hit space bar to toggle debugging lines.\nClick the mouse to generate a new path.") + + self.new_path() + + # Each vehicle has different maxspeed and maxforce for demo purposes + self.car1 = Vehicle(self, 0, self.height / 2, 2, 0.04) + self.car2 = Vehicle(self, 0, self.height / 2, 3, 0.1) + + self.debug = True + + def draw(self): + self.background((255, 255, 255)) + # Display the path + self.path.show() + # The boids follow the path + self.car1.follow(self.path) + self.car2.follow(self.path) + # Call the generic run method (update, borders, display, etc.) + self.car1.run() + self.car2.run() + + # Check if it gets to the end of the path since it's not a loop + self.car1.borders(self.path) + self.car2.borders(self.path) + + def new_path(self): + # A path is a series of connected points + # A more sophisticated path might be a curve + self.path = Path(self) + self.path.add_point(-20, self.height / 2) + from random import uniform + + self.path.add_point(uniform(0, self.width / 2), uniform(0, self.height)) + self.path.add_point(uniform(self.width / 2, self.width), uniform(0, self.height)) + self.path.add_point(self.width + 20, self.height / 2) + + def key_pressed(self): + if self.key == ' ': + self.debug = not self.debug + + def mouse_pressed(self): + self.new_path() diff --git a/examples/Nature of Code/chapter05/Example_5_08_Path_Following/Path.py b/examples/Nature of Code/chapter05/Example_5_08_Path_Following/Path.py new file mode 100644 index 0000000..defb2b8 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_08_Path_Following/Path.py @@ -0,0 +1,41 @@ +""" +Path class for Example 5-8: Path Following +""" + +class Path: + def __init__(self, sketch): + self.sketch = sketch + # A path has a radius, i how far is it ok for the vehicle to wander off + self.radius = 20 + # A Path is an array of points (PVector objects) + self.points = [] + + # Add a point to the path + def add_point(self, x, y): + point = self.sketch.pvector(x, y) + self.points.append(point) + + def get_start(self): + return self.points[0] + + def get_end(self): + return self.points[-1] + + # Draw the path + def show(self): + # Draw thick line for radius + self.sketch.stroke(200) + self.sketch.stroke_weight(self.radius * 2) + self.sketch.no_fill() + self.sketch.begin_shape() + for path_point in self.points: + self.sketch.vertex(path_point.x, path_point.y) + self.sketch.end_shape() + # Draw thin line for center of path + self.sketch.stroke(0) + self.sketch.stroke_weight(1) + self.sketch.no_fill() + self.sketch.begin_shape() + for path_point in self.points: + self.sketch.vertex(path_point.x, path_point.y) + self.sketch.end_shape() diff --git a/examples/Nature of Code/chapter05/Example_5_08_Path_Following/Vehicle.py b/examples/Nature of Code/chapter05/Example_5_08_Path_Following/Vehicle.py new file mode 100644 index 0000000..63c1c52 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_08_Path_Following/Vehicle.py @@ -0,0 +1,195 @@ +""" +Vehicle class for Example 5-8: Path Following +""" + +class Vehicle: + def __init__(self, sketch, x, y, maxspeed=4, maxforce=0.1): + self.sketch = sketch + self.position = sketch.pvector(x, y) + self.acceleration = sketch.pvector(0, 0) + self.velocity = sketch.pvector(2, 0) + self.r = 4 + self.maxspeed = maxspeed # || 4; + self.maxforce = maxforce # || 0.1; + self.debug = False + + def run(self): + self.update() + self.show() + + # This function implements Craig Reynolds' path following algorithm + # http://www.red3d.com/cwr/steer/PathFollow.html + def follow(self, path): + """ + Predicts the vehicle's future position and steers it to follow a given path. + + The method works by projecting the vehicle's current velocity forward to estimate a future position. + It then finds the closest point (normal) on the path to this predicted position by checking each segment + of the path. If the predicted position is farther from the path than the path's radius, the vehicle will + steer towards a target point slightly ahead of the closest normal point. + + Args: + path: An object representing the path to follow. It must have: + - points: a list of vector-like objects with x and y attributes. + - radius: a float representing the path's radius. + + Attributes Used: + self.position: Current position vector of the vehicle. + self.velocity: Current velocity vector of the vehicle. + self.debug: Boolean flag to enable debug drawing. + self.sketch: Drawing context for debug visualization. + + Notes: + - The target variable is a vector-like object with x and y attributes (e.g., a custom Vector class). + - The method assumes path points are ordered and the path is traversed left to right. + - For linters, you may use type hints or comments to indicate that 'target' is a vector-like object: + target: Vector # where Vector has x and y attributes + """ + # Predict location 50 (arbitrary choice) frames ahead + # This could be based on speed + future = self.velocity.copy() + future.set_mag(50) + future += self.position + + # Now we must find the normal to the path from the predicted location + # We look at the normal for each line segment and pick out the closest one + # how do we tell the linter that target is an xy vector? + target = None + normal = None + dir = None # Ensure dir is always defined + world_record = float('inf') # Start with a very high record distance that can easily be beaten + + # Loop through all points of the path + for i in range(len(path.points) - 1): + # Look at a line segment + a = path.points[i] + b = path.points[i + 1] + + # Get the normal point to that line + normal_point = self.get_normal_point(future, a, b) + # This only works because we know our path goes from left to right + # We could have a more sophisticated test to tell if the point is in the line segment or not + if normal_point.x < a.x or normal_point.x > b.x: + # This is something of a hacky solution, but if it's not within the line segment + # consider the normal to just be the end of the line segment (point b) + normal_point = b.copy() + + # How far away are we from the path? + distance = future.dist(normal_point) + # Did we beat the record and find the closest line segment? + if distance < world_record: + world_record = distance + # If so the target we want to steer towards is the normal + normal = normal_point + target = normal_point.copy() + # Look at the direction of the line segment so we can seek a little bit ahead of the normal + dir = b - a + # This is an oversimplification + # Should be based on distance to path & velocity + dir.set_mag(10) + target += dir + # Only if the distance is greater than the path's radius do we bother to steer + if world_record > path.radius and target is not None: + self.seek(target) # seek(target) + # Draw the debugging stuff + if self.debug: + # Draw predicted future location + self.sketch.stroke(0) + self.sketch.fill(127) + self.sketch.line(self.position.x, self.position.y, future.x, future.y) + self.sketch.ellipse(future.x, future.y, 4, 4) + + # Draw normal location + if normal is not None: + self.sketch.stroke(0) + self.sketch.fill(127) + self.sketch.circle(normal.x, normal.y, 4) + # Draw actual target (red if steering towards it) + self.sketch.line(future.x, future.y, normal.x, normal.y) + if world_record > path.radius: + self.sketch.fill(255, 0, 0) + self.sketch.no_stroke() + if target is not None: + self.sketch.circle(target.x, target.y, 8) + self.sketch.no_stroke() + self.sketch.no_fill() + if target is not None and dir is not None: + self.sketch.circle(target.x + dir.x, target.y + dir.y, 8) + self.sketch.circle(target.x + dir.x, target.y + dir.y, 8) + + def apply_force(self, force): + # We could add mass here if we want A = F / M + self.acceleration += force + + # A method that calculates and applies a steering force towards a target + # STEER = DESIRED MINUS VELOCITY + def steer_towards(self, target): + desired = target - self.position + desired.set_mag(self.maxspeed) + steer = desired - self.velocity + steer.limit(self.maxforce) + self.apply_force(steer) + + def seek(self, target): + desired = target - self.position # A vector pointing from the position to the target + + # If the magnitude of desired equals 0, skip out of here + # (We could optimize this to check if x and y are 0 to avoid mag() square root + if desired.mag() == 0: + return + + # Normalize desired and scale to maximum speed + desired.normalize() + desired *= self.maxspeed + # Steering = Desired minus Velocity + steer = desired - self.velocity + steer.limit(self.maxforce) # Limit to maximum steering force + + self.apply_force(steer) + + # Method to update position + def update(self): + # Update velocity + self.velocity += self.acceleration + # Limit speed + self.velocity.limit(self.maxspeed) + self.position += self.velocity + # Reset accelerationelertion to 0 each cycle + self.acceleration *= 0 + + # Wraparound + def borders(self, path): + if self.position.x > path.get_end().x + self.r: + self.position.x = path.get_start().x - self.r + self.position.y = path.get_start().y + (self.position.y - path.get_end().y) + + def show(self): + # Draw a triangle rotated in the direction of velocity + theta = self.velocity.heading() + self.sketch.fill(127) + self.sketch.stroke(0) + self.sketch.stroke_weight(2) + self.sketch.push_matrix() + try: + self.sketch.translate(self.position.x, self.position.y) + self.sketch.rotate(theta) + self.sketch.begin_shape() + self.sketch.vertex(self.r * 2, 0) + self.sketch.vertex(-self.r * 2, -self.r) + self.sketch.vertex(-self.r * 2, self.r) + self.sketch.end_shape(close=True) + finally: + self.sketch.pop_matrix() + + # A function to get the normal point from a point (p) to a line segment (a-b) + # This function could be optimized to make fewer new Vector objects + def get_normal_point(self, position, a, b): + # Vector from a to p + vector_a = position - a + # Vector from a to b + vector_b = b - a + vector_b.normalize() # Normalize the line + # Project vector "diff" onto line by using the dot product + vector_b *= vector_a.dot(vector_b) + normal_point = a + vector_b + return normal_point diff --git a/examples/Nature of Code/chapter05/Example_5_09_Separation/Example_5_09_Separation.py b/examples/Nature of Code/chapter05/Example_5_09_Separation/Example_5_09_Separation.py new file mode 100644 index 0000000..64006c0 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_09_Separation/Example_5_09_Separation.py @@ -0,0 +1,38 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter5/Example_5_9_Separation/Example_5_9_Separation.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Separation +// Via Reynolds: http://www.red3d.com/cwr/steer/ + +// A simple model of flocking behavior where boids try to keep a certain distance from one another +""" + +from pycreative.app import Sketch +from Vehicle import Vehicle + + +class Example_5_9_Separation(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 5.9: Separation") + self.vehicles = [Vehicle(self, self.random(self.width), self.random(self.height)) for _ in range(25)] + + def update(self, dt): + pass + + def draw(self): + self.clear((255, 255, 255)) + for v in self.vehicles: + v.separate(self.vehicles) + v.update() + v.borders() + v.show() + + def mouse_dragged(self): + x, y = self.mouse_x, self.mouse_y + self.vehicles.append(Vehicle(self, x, y)) diff --git a/examples/Nature of Code/chapter05/Example_5_09_Separation/Vehicle.py b/examples/Nature of Code/chapter05/Example_5_09_Separation/Vehicle.py new file mode 100644 index 0000000..eda5b87 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_09_Separation/Vehicle.py @@ -0,0 +1,76 @@ +""" +Vehicle class for Example 5-9: Separation +""" + +class Vehicle: + def __init__(self, sketch, x, y): + self.sketch = sketch + # All the usual stuff + self.position = sketch.pvector(x, y) + self.r = 12 + self.maxspeed = 3 # Maximum speed + self.maxforce = 0.2 # Maximum steering force + self.acceleration = sketch.pvector(0, 0) + self.velocity = sketch.pvector(0, 0) + + def apply_force(self, force): + # We could add mass here if we want A = F / M + self.acceleration += force + + # Separation + # Method checks for nearby vehicles and steers away + def separate(self, vehicles): + # {!1 .bold} Note how the desired separation is based + # on the Vehicles size. + desired_separation = self.r * 2 + sum_vector = self.sketch.pvector(0, 0) + count = 0 + for other in vehicles: + d = self.position.dist(other.position) + if self != other and d < desired_separation: + diff = self.position - other.position + # {!1 .bold} What is the magnitude of the p5.Vector + # pointing away from the other vehicle? + # The closer it is, the more the vehicle should flee. + # The farther, the less. So the magnitude is set + # to be inversely proportional to the distance. + diff.set_mag(1 / d) + sum_vector += diff + count += 1 + if count > 0: + sum_vector.set_mag(self.maxspeed) + steer = sum_vector - self.velocity + steer.limit(self.maxforce) + self.apply_force(steer) + + # Method to update location + def update(self): + # Update velocity + self.velocity += self.acceleration + # Limit speed + self.velocity.limit(self.maxspeed) + self.position += self.velocity + # Reset acceleration to 0 each cycle + self.acceleration *= 0 + + def show(self): + self.sketch.fill(127) + self.sketch.stroke(0) + self.sketch.stroke_weight(2) + self.sketch.push_matrix() + try: + self.sketch.translate(self.position.x, self.position.y) + self.sketch.circle(0, 0, self.r) + finally: + self.sketch.pop_matrix() + + # Wraparound + def borders(self): + if self.position.x < -self.r: + self.position.x = self.sketch.width + self.r + if self.position.y < -self.r: + self.position.y = self.sketch.height + self.r + if self.position.x > self.sketch.width + self.r: + self.position.x = -self.r + if self.position.y > self.sketch.height + self.r: + self.position.y = -self.r diff --git a/examples/Nature of Code/chapter05/Example_5_10_Combining_Seek_Separate/Example_5_10_Combining_Seek_Separate.py b/examples/Nature of Code/chapter05/Example_5_10_Combining_Seek_Separate/Example_5_10_Combining_Seek_Separate.py new file mode 100644 index 0000000..3960a2a --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_10_Combining_Seek_Separate/Example_5_10_Combining_Seek_Separate.py @@ -0,0 +1,35 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter5/Example_5_10_Combining_Seek_Separate/Example_5_10_Combining_Seek_Separate.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Separation +// Via Reynolds: http://www.red3d.com/cwr/steer/ +""" + +from pycreative.app import Sketch +from Vehicle import Vehicle + + +class Example_5_10_Combining_Seek_Separate(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 5.10: Combining Seek and Separate") + self.vehicles = [Vehicle(self, (int)(self.random(self.width)), (int)(self.random(self.height))) for _ in range(50)] + + def update(self, dt: float = 0) -> None: + for v in self.vehicles: + v.apply_behaviors(self.vehicles) + v.update() + v.borders() + + def draw(self): + self.background((255, 255, 255)) + for v in self.vehicles: + v.apply_behaviors(self.vehicles) + v.borders() + v.update() + v.show() diff --git a/examples/Nature of Code/chapter05/Example_5_10_Combining_Seek_Separate/Vehicle.py b/examples/Nature of Code/chapter05/Example_5_10_Combining_Seek_Separate/Vehicle.py new file mode 100644 index 0000000..479eacc --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_10_Combining_Seek_Separate/Vehicle.py @@ -0,0 +1,111 @@ +""" +Vehicle class for Example 5-10: Combining Seek and Separate +""" + +from pycreative.vector import PVector + + +class Vehicle: + def __init__(self, sketch, x=0, y=0): + self.sketch = sketch + self.position = self.sketch.pvector(x, y) + self.velocity = self.sketch.pvector.random2d() + self.acceleration = self.sketch.pvector(0, 0) + self.r = 6.0 + self.maxspeed = 3.0 # Maximum speed + self.maxforce = 0.2 # Maximum steering force + + def apply_behaviors(self, vehicles): + separate_force = self.separate(vehicles) + seek_force = self.seek(self.sketch.pvector(self.sketch.mouse_x or 0, self.sketch.mouse_y or 0)) + + separate_force.mult(1.5) + seek_force.mult(0.5) + + self.apply_force(separate_force) + self.apply_force(seek_force) + + def apply_force(self, force): + # We could add mass here if we want A = F / M + self.acceleration.add(force) + + def separate(self, vehicles: list["Vehicle"]) -> "PVector": + # Separation behavior: steer to avoid crowding local flockmates. + desired_separation = self.r * 2 + sum = self.sketch.pvector(0, 0) + count = 0 + # For every vehicle in the system, check if it's too close + for other in vehicles: + d = self.position.dist(other.position) + # If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself) + if self != other and d < desired_separation: + # Calculate vector pointing away from neighbor + diff = self.position.copy().sub(other.position) + diff.set_mag(1 / d) # Weight by distance + sum.add(diff) + count += 1 # Keep track of how many + # Average -- divide by how many + if count > 0: + sum.div(count) + # Our desired vector is the average scaled to maximum speed + sum.set_mag(self.maxspeed) + # Implement Reynolds: Steering = Desired - Velocity + sum.sub(self.velocity) + sum.limit(self.maxforce) + return sum + + def seek(self, target): + # A method that calculates a steering force towards a target + desired = target.copy().sub(self.position) # A vector pointing from the location to the target + + # Normalize desired and scale to maximum speed + desired.normalize() + desired.mult(self.maxspeed) + # Steering = Desired minus velocity + steer = desired.sub(self.velocity) + steer.limit(self.maxforce) # Limit to maximum steering force + return steer + + def update(self): + # Method to update location + # Update velocity + self.velocity.add(self.acceleration) + # Limit speed + self.velocity.limit(self.maxspeed) + self.position.add(self.velocity) + # Reset acceleration to 0 each cycle + self.acceleration.mult(0) + + def borders(self): + # Wraparound + if self.position.x < -self.r: + self.position.x = self.sketch.width + self.r + if self.position.y < -self.r: + self.position.y = self.sketch.height + self.r + if self.position.x > self.sketch.width + self.r: + self.position.x = -self.r + if self.position.y > self.sketch.height + self.r: + self.position.y = -self.r + + def show(self): + self.sketch.stroke((0, 0, 0)) + self.sketch.stroke_weight(2) + self.sketch.fill((127, 127, 127)) + self.sketch.push_matrix() + try: + self.sketch.translate(self.position.x, self.position.y) + self.sketch.rotate(self.velocity.heading()) + self.sketch.begin_shape() + self.sketch.vertex(self.r * 2, 0) + self.sketch.vertex(-self.r * 2, -self.r) + self.sketch.vertex(-self.r * 2, self.r) + self.sketch.end_shape(close=True) + # --- IGNORE --- + # self.sketch.begin_shape() + # self.sketch.vertex(self.r * 2, 0) + # self.sketch.vertex(-self.r * 2, -self.r) + # self.sketch.vertex(-self.r * 2, self.r) + # self.sketch.end_shape() + # --- IGNORE --- + finally: + self.sketch.pop_matrix() \ No newline at end of file diff --git a/examples/Nature of Code/chapter05/Example_5_11_Flocking/Boid.py b/examples/Nature of Code/chapter05/Example_5_11_Flocking/Boid.py new file mode 100644 index 0000000..d3aaf21 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_11_Flocking/Boid.py @@ -0,0 +1,153 @@ +""" +Boid class for Example 5-11: Flocking +""" + +class Boid: + def __init__(self, sketch, x: float, y: float): + self.sketch = sketch + self.position = sketch.pvector(float(x), float(y)) + self.velocity = sketch.pvector(sketch.random(-1, 1), sketch.random(-1, 1)) + self.acceleration = sketch.pvector(0.0, 0.0) + self.r = 3.0 + self.maxspeed = 3.0 # Maximum speed + self.maxforce = 0.05 # Maximum steering force + + def run(self, boids: list["Boid"]): + self.flock(boids) + self.update() + self.borders() + self.show() + + def apply_force(self, force): + # We could add mass here if we want A = F / M + self.acceleration.add(force) + + # We accumulate a new acceleration each time based on three rules + def flock(self, boids: list["Boid"]): + sep = self.separate(boids) # Separation + ali = self.align(boids) # Alignment + coh = self.cohere(boids) # Cohesion + # Arbitrarily weight these forces + sep.mult(1.5) + ali.mult(1.0) + coh.mult(1.0) + # Add the force vectors to acceleration + self.apply_force(sep) + self.apply_force(ali) + self.apply_force(coh) + + # Method to update location + def update(self): + # Update velocity + self.velocity.add(self.acceleration) + # Limit speed + self.velocity.limit(self.maxspeed) + self.position.add(self.velocity) + # Reset accelertion to 0 each cycle + self.acceleration.mult(0) + + # A method that calculates and applies a steering force towards a target + def seek(self, target): + desired = target.copy().sub(self.position) # A vector pointing from the location to the target + # Normalize desired and scale to maximum speed + desired.normalize() + desired.mult(self.maxspeed) + # Steering = Desired minus Velocity + steer = desired.sub(self.velocity) + steer.limit(self.maxforce) # Limit to maximum steering force + return steer + + def show(self): + # Draw a triangle rotated in the direction of velocity + angle = self.velocity.heading() + self.sketch.fill((127, 127, 127)) + self.sketch.stroke((0, 0, 0)) + self.sketch.push() + try: + self.sketch.translate(self.position.x, self.position.y) + self.sketch.rotate(angle) + self.sketch.begin_shape() + self.sketch.vertex(self.r * 2, 0) + self.sketch.vertex(-self.r * 2, -self.r) + self.sketch.vertex(-self.r * 2, self.r) + self.sketch.end_shape(close=True) + finally: + self.sketch.pop() + + # Wraparound + def borders(self): + if self.position.x < -self.r: + self.position.x = self.sketch.width + self.r + if self.position.y < -self.r: + self.position.y = self.sketch.height + self.r + if self.position.x > self.sketch.width + self.r: + self.position.x = -self.r + if self.position.y > self.sketch.height + self.r: + self.position.y = -self.r + + # Separation + # Method checks for nearby boids and steers away + def separate(self, boids: list["Boid"]): + desired_separation = 25.0 + steer = self.sketch.pvector(0.0, 0.0) + count = 0 + # For every boid in the system, check if it's too close + for other in boids: + d = self.position.dist(other.position) + # If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself) + if 0 < d < desired_separation: + # Calculate vector pointing away from neighbor + diff = self.position.copy().sub(other.position) + diff.normalize() + diff.div(d) # Weight by distance + steer.add(diff) + count += 1 # Keep track of how many + # Average -- divide by how many + if count > 0: + steer.div(float(count)) + # As long as the vector is greater than 0 + if steer.mag() > 0: + # Implement Reynolds: Steering = Desired - Velocity + steer.normalize() + steer.mult(self.maxspeed) + steer.sub(self.velocity) + steer.limit(self.maxforce) + return steer + + # Alignment + # For every nearby boid in the system, calculate the average velocity + def align(self, boids: list["Boid"]): + neighbor_distance = 50.0 + sum = self.sketch.pvector(0.0, 0.0) + count = 0 + for other in boids: + d = self.position.dist(other.position) + if 0 < d < neighbor_distance: + sum.add(other.velocity) + count += 1 + if count > 0: + sum.div(float(count)) + sum.normalize() + sum.mult(self.maxspeed) + steer = sum.sub(self.velocity) + steer.limit(self.maxforce) + return steer + else: + return self.sketch.pvector(0.0, 0.0) + + # Cohesion + # For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location + def cohere(self, boids: list["Boid"]): + neighbor_distance = 50.0 + sum = self.sketch.pvector(0.0, 0.0) # Start with empty vector to accumulate all locations + count = 0 + for other in boids: + d = self.position.dist(other.position) + if 0 < d < neighbor_distance: + sum.add(other.position) # Add location + count += 1 + if count > 0: + sum.div(float(count)) + return self.seek(sum) # Steer towards the location + else: + return self.sketch.pvector(0.0, 0.0) diff --git a/examples/Nature of Code/chapter05/Example_5_11_Flocking/Example_5_11_Flocking.py b/examples/Nature of Code/chapter05/Example_5_11_Flocking/Example_5_11_Flocking.py new file mode 100644 index 0000000..5f238d4 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_11_Flocking/Example_5_11_Flocking.py @@ -0,0 +1,38 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter5/Example_5_11_Flocking/Example_5_11_Flocking.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Demonstration of Craig Reynolds' "Flocking" behavior +// See: http://www.red3d.com/cwr/ +// Rules: Cohesion, Separation, Alignment + +// Click mouse to add boids into the system +""" + +from pycreative.app import Sketch +from Flock import Flock +from Boid import Boid + + +class Example_5_11_Flocking(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 5.11: Flocking") + self.flock = Flock() + for _ in range(120): + boid = Boid(self, (int)(self.width / 2), (int)(self.height / 2)) + self.flock.add_boid(boid) + + def update(self, dt: float = 0) -> None: + self.flock.run() + + def draw(self): + self.background((255, 255, 255)) + self.flock.run() + + def mouse_dragged(self): + self.flock.add_boid(Boid(self, (int)(self.mouse_x or 0), (int)(self.mouse_y or 0))) diff --git a/examples/Nature of Code/chapter05/Example_5_11_Flocking/Flock.py b/examples/Nature of Code/chapter05/Example_5_11_Flocking/Flock.py new file mode 100644 index 0000000..e06010e --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_11_Flocking/Flock.py @@ -0,0 +1,14 @@ +""" +Flock class for Example 5-11: Flocking +""" + +class Flock: + def __init__(self): + self.boids = [] + + def run(self): + for boid in self.boids: + boid.run(self.boids) + + def add_boid(self, b): + self.boids.append(b) diff --git a/examples/Nature of Code/chapter05/Example_5_12_Bin_Lattice_Spatial_Separation/Boid.py b/examples/Nature of Code/chapter05/Example_5_12_Bin_Lattice_Spatial_Separation/Boid.py new file mode 100644 index 0000000..8cecf08 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_12_Bin_Lattice_Spatial_Separation/Boid.py @@ -0,0 +1,192 @@ +""" +Boid class for Example 5-12: Bin Lattice Spatial Separation +""" + +class Boid: + def __init__(self, sketch, x = 0, y = 0): + self.sketch = sketch + # Use sketch-provided pvector factory and don't create per-boid grid/resolution/cols/rows + self.acceleration = self.sketch.pvector(0, 0) + self.velocity = self.sketch.pvector(self.sketch.random(-1, 1), self.sketch.random(-1, 1)) + self.position = self.sketch.pvector(x, y) + self.r = 3.0 + self.maxspeed = 3.0 # Maximum speed + self.maxforce = 0.05 # Maximum steering force + + # No argument to run() anymore, use the sketch grid + def run(self): + # Defensive: ensure sketch has the grid and resolution + try: + resolution = self.sketch.resolution + cols = self.sketch.cols + rows = self.sketch.rows + grid = self.sketch.grid + except Exception: + return + + # Compute cell indices based on this boid's position + col = int(self.position.x // resolution) + row = int(self.position.y // resolution) + # Clamp to valid ranges + col = max(0, min(col, cols - 1)) + row = max(0, min(row, rows - 1)) + + neighbors: list[Boid] = [] + + # Check cells in a 3x3 block around the current boid + for i in range(-1, 2): + for j in range(-1, 2): + new_col = col + i + new_row = row + j + # Make sure this is a valid cell + if 0 <= new_col < cols and 0 <= new_row < rows: + try: + cell = grid[new_row][new_col] + for b in cell: + # don't add self as neighbor + if b is not self: + neighbors.append(b) + except Exception: + # ignore malformed cells + pass + + self.flock(neighbors) + self.update() + self.borders() + self.show() + + def apply_force(self, force): + # We could add mass here if we want A = F / M + self.acceleration.add(force) + + # We accumulate a new acceleration each time based on three rules + def flock(self, boids: list): + sep = self.separate(boids) # Separation + ali = self.align(boids) # Alignment + coh = self.cohesion(boids) # Cohesion + # Arbitrarily weight these forces + sep.mult(1.5) + ali.mult(1.0) + coh.mult(1.0) + # Add the force vectors to acceleration + self.apply_force(sep) + self.apply_force(ali) + self.apply_force(coh) + + # Method to update location + def update(self): + # Update velocity + self.velocity.add(self.acceleration) + # Limit speed + self.velocity.limit(self.maxspeed) + self.position.add(self.velocity) + # Reset accelertion to 0 each cycle + self.acceleration.mult(0) + # A method that calculates and applies a steering force towards a target + # STEER = DESIRED MINUS VELOCITY + def seek(self, target): + desired = self.sketch.pvector.sub(target, self.position) # A vector pointing from the location to the target + # Normalize desired and scale to maximum speed + desired.normalize() + desired.mult(self.maxspeed) + # Steering = Desired minus Velocity + steer = self.sketch.pvector.sub(desired, self.velocity) + steer.limit(self.maxforce) # Limit to maximum steering force + return steer + + def show(self): + # Draw a triangle rotated in the direction of velocity + angle = self.velocity.heading() + self.sketch.fill((127, 127, 127)) + self.sketch.stroke((0, 0, 0)) + self.sketch.push() + try: + self.sketch.translate(self.position.x, self.position.y) + self.sketch.rotate(angle) + self.sketch.begin_shape() + self.sketch.vertex(self.r * 2, 0) + self.sketch.vertex(-self.r * 2, -self.r) + self.sketch.vertex(-self.r * 2, self.r) + self.sketch.end_shape(close=True) + finally: + self.sketch.pop() + + # Wraparound + def borders(self): + if self.position.x < -self.r: + self.position.x = self.sketch.width + self.r + if self.position.y < -self.r: + self.position.y = self.sketch.height + self.r + if self.position.x > self.sketch.width + self.r: + self.position.x = -self.r + if self.position.y > self.sketch.height + self.r: + self.position.y = -self.r + + # Separation + # Method checks for nearby boids and steers away + def separate(self, boids: list): + desiredseparation = 25.0 + steer = self.sketch.pvector(0, 0) + count = 0 + # For every boid in the system, check if it's too close + for other in boids: + d = self.sketch.pvector.dist(self.position, other.position) + # If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself) + if 0 < d < desiredseparation: + # Calculate vector pointing away from neighbor + diff = self.sketch.pvector.sub(self.position, other.position) + diff.normalize() + diff.div(d) # Weight by distance + steer.add(diff) + count += 1 # Keep track of how many + # Average -- divide by how many + if count > 0: + steer.div(count) + + # As long as the vector is greater than 0 + if steer.mag() > 0: + # Implement Reynolds: Steering = Desired - Velocity + steer.normalize() + steer.mult(self.maxspeed) + steer.sub(self.velocity) + steer.limit(self.maxforce) + return steer + + # Alignment + # For every nearby boid in the system, calculate the average velocity + def align(self, boids: list): + neighbordist = 50 + sum = self.sketch.pvector(0, 0) + count = 0 + for other in boids: + d = self.sketch.pvector.dist(self.position, other.position) + if 0 < d < neighbordist: + sum.add(other.velocity) + count += 1 + if count > 0: + sum.div(count) + sum.normalize() + sum.mult(self.maxspeed) + steer = self.sketch.pvector.sub(sum, self.velocity) + steer.limit(self.maxforce) + return steer + else: + return self.sketch.pvector(0, 0) + + # Cohesion + # For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location + def cohesion(self, boids: list): + neighbordist = 50 + + sum = self.sketch.pvector(0, 0) # Start with empty vector to accumulate all locations + count = 0 + for other in boids: + d = self.sketch.pvector.dist(self.position, other.position) + if 0 < d < neighbordist: + sum.add(other.position) # Add location + count += 1 + if count > 0: + sum.div(count) + return self.seek(sum) # Steer towards the location + else: + return self.sketch.pvector(0, 0) diff --git a/examples/Nature of Code/chapter05/Example_5_12_Bin_Lattice_Spatial_Separation/Example_5_12_Bin_Lattice_Spatial_Separation.py b/examples/Nature of Code/chapter05/Example_5_12_Bin_Lattice_Spatial_Separation/Example_5_12_Bin_Lattice_Spatial_Separation.py new file mode 100644 index 0000000..73ce332 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_12_Bin_Lattice_Spatial_Separation/Example_5_12_Bin_Lattice_Spatial_Separation.py @@ -0,0 +1,89 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter5/Example_5_12_Bin_Lattice_Spatial_Separation/Example_5_12_Bin_Lattice_Spatial_Separation.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Demonstration of Craig Reynolds' "Flocking" behavior +// See: http://www.red3d.com/cwr/ +// Rules: Cohesion, Separation, Alignment + +// Click mouse to add boids into the system +""" + +from pycreative.app import Sketch +from Flock import Flock +from Boid import Boid + + +class Example_5_12_Bin_Lattice_Spatial_Separation(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 5.12: Bin Lattice Spatial Separation") + self.flock = Flock() + + self.resolution = 40 # adjust as necessary + self.cols = int(self.width // self.resolution) + self.rows = int(self.height // self.resolution) + self.make_3d_array(self.cols, self.rows) + + # Add an initial set of boids into the system + for _ in range(100): + boid = Boid(self, int(self.random(self.width)), int(self.random(self.height))) + self.flock.add_boid(boid) + + def make_3d_array(self, cols: int, rows: int): + # Create exact rows x cols grid + self.grid: list[list[list[Boid]]] = [[[] for _ in range(cols)] for _ in range(rows)] + + def update(self, dt: float): + self.set_title(f"Example 5.12: Bin Lattice Spatial Separation (Boids: {len(self.flock.boids)})") + + def draw(self): + self.clear((255, 255, 255)) + + # Reset grid at the beginning of each frame + for i in range(self.rows): + for j in range(self.cols): + self.grid[i][j].clear() + + # Place each boid into the appropriate cell in the grid + for boid in self.flock.boids: + col = int(boid.position.x // self.resolution) + row = int(boid.position.y // self.resolution) + col = max(0, min(col, self.cols - 1)) + row = max(0, min(row, self.rows - 1)) + self.grid[row][col].append(boid) + + # Draw the grid + self.stroke(200) + self.stroke_weight(1) + + # Draw vertical lines + for i in range(self.cols + 1): + x = i * self.resolution + self.line(x, 0, x, self.height) + + # Draw horizontal lines + for j in range(self.rows + 1): + y = j * self.resolution + self.line(0, y, self.width, y) + + # Highlight the 3x3 neighborhood the mouse is over + mouse_col = int((self.mouse_x or 0) // self.resolution) + mouse_row = int((self.mouse_y or 0) // self.resolution) + self.no_stroke() + self.fill(255, 50, 50, 100) # Semi-transparent red + for i in range(-1, 2): + for j in range(-1, 2): + col = mouse_col + i + row = mouse_row + j + # Check if the cell is within the grid + if 0 <= col < self.cols and 0 <= row < self.rows: + self.rect(col * self.resolution, row * self.resolution, self.resolution, self.resolution) + self.flock.run() + + def mouse_dragged(self): + self.flock.add_boid(Boid(self, self.mouse_x or 0, self.mouse_y or 0)) diff --git a/examples/Nature of Code/chapter05/Example_5_12_Bin_Lattice_Spatial_Separation/Flock.py b/examples/Nature of Code/chapter05/Example_5_12_Bin_Lattice_Spatial_Separation/Flock.py new file mode 100644 index 0000000..12da9d3 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_12_Bin_Lattice_Spatial_Separation/Flock.py @@ -0,0 +1,16 @@ +""" +Flock class for Example 5-12: Bin Lattice Spatial Separation +""" + +from Boid import Boid + +class Flock: + def __init__(self): + self.boids: list[Boid] = [] + + def run(self): + for boid in self.boids: + boid.run() + + def add_boid(self, boid): + self.boids.append(boid) \ No newline at end of file diff --git a/examples/Nature of Code/chapter05/Example_5_13_QuadTree/Example_5_13_QuadTree.py b/examples/Nature of Code/chapter05/Example_5_13_QuadTree/Example_5_13_QuadTree.py new file mode 100644 index 0000000..9af0035 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_13_QuadTree/Example_5_13_QuadTree.py @@ -0,0 +1,57 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter5/Example_5_13_QuadTree/Example_5_13_QuadTree.pde + +// Daniel Shiffman +// http://codingtra.in +// http://patreon.com/codingtrain + +// QuadTree +// 1: https://www.youtube.com/watch?v=OJxEcs0w_kE +// 2: https://www.youtube.com/watch?v=QQx_NmCIuCY + +// For more: +// https://github.com/CodingTrain/QuadTree +""" + +from pycreative.app import Sketch +from QuadTree import Point, Rectangle, QuadTree + + +class Example_5_13_QuadTree(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 5.13 QuadTree") + boundary = Rectangle(self.width / 2, self.height / 2, self.width, self.height) + self.qtree = QuadTree(self, boundary, 8) + import random + for _ in range(2000): + mean = self.width / 2.0 + stdDev = self.width / 8.0 + x = random.gauss(mean, stdDev) + y = random.gauss(mean, stdDev) + p = Point(x, y) + self.qtree.insert(p) + + def draw(self): + self.clear((255, 255, 255)) + self.qtree.show() + + rect_mode = 'CENTER' + range = Rectangle(self.mouse_x or 0, self.mouse_y or 0, 50, 50) + + # This check has been introduced due to a bug discussed in + # https://github.com/CodingTrain/website/pull/556 + if (self.mouse_x or 0) < self.width and (self.mouse_y or 0) < self.height: + self.stroke(255, 50, 50) + self.stroke_weight(2) + self.fill(255, 50, 50, 50) + self.rect_mode(rect_mode) + self.rect(range.x, range.y, range.w * 2, range.h * 2) + points = [] + points = self.qtree.query(range, points) + print(points) + for p in points: + self.stroke(50, 50, 50) + self.stroke_weight(3) + self.point(p.x, p.y) diff --git a/examples/Nature of Code/chapter05/Example_5_13_QuadTree/QuadTree.py b/examples/Nature of Code/chapter05/Example_5_13_QuadTree/QuadTree.py new file mode 100644 index 0000000..8d1eec1 --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_13_QuadTree/QuadTree.py @@ -0,0 +1,127 @@ +""" +Point, Rectangle, and QuadTree classes for Example 5-13: QuadTree +""" + +class Point: + def __init__(self, x, y): + self.x = x + self.y = y + + +class Rectangle: + def __init__(self, x, y, w, h): + self.x = x + self.y = y + self.w = w + self.h = h + + def contains(self, point): + return ( + point.x >= self.x - self.w + and point.x < self.x + self.w + and point.y >= self.y - self.h + and point.y < self.y + self.h + ) + + def intersects(self, range): + return not ( + range.x - range.w > self.x + self.w + or range.x + range.w < self.x - self.w + or range.y - range.h > self.y + self.h + or range.y + range.h < self.y - self.h + ) + + +class QuadTree: + def __init__(self, sketch, boundary, capacity): + self.sketch = sketch + self.boundary = boundary # Rectangle + self.capacity = capacity # int + self.points = [] # list of Points + self.divided = False # bool + self.northeast = None # QuadTree + self.northwest = None # QuadTree + self.southeast = None # QuadTree + self.southwest = None # QuadTree + + def subdivide(self): + x = self.boundary.x + y = self.boundary.y + w = self.boundary.w + h = self.boundary.h + ne = Rectangle(x + w / 2, y - h / 2, w / 2, h / 2) + self.northeast = QuadTree(self.sketch, ne, self.capacity) + nw = Rectangle(x - w / 2, y - h / 2, w / 2, h / 2) + self.northwest = QuadTree(self.sketch, nw, self.capacity) + se = Rectangle(x + w / 2, y + h / 2, w / 2, h / 2) + self.southeast = QuadTree(self.sketch, se, self.capacity) + sw = Rectangle(x - w / 2, y + h / 2, w / 2, h / 2) + self.southwest = QuadTree(self.sketch, sw, self.capacity) + self.divided = True + + def insert(self, point): + if not self.boundary.contains(point): + return False + + if len(self.points) < self.capacity: + self.points.append(point) + return True + else: + if not self.divided: + self.subdivide() + + # children may be None from static analysis perspective; guard calls + if self.northeast and self.northeast.insert(point): + return True + if self.northwest and self.northwest.insert(point): + return True + if self.southeast and self.southeast.insert(point): + return True + if self.southwest and self.southwest.insert(point): + return True + + return False + + def query(self, range, found): + if not self.boundary.intersects(range): + return found + + for p in self.points: + if range.contains(p): + found.append(p) + + if self.divided: + if self.northwest: + self.northwest.query(range, found) + if self.northeast: + self.northeast.query(range, found) + if self.southwest: + self.southwest.query(range, found) + if self.southeast: + self.southeast.query(range, found) + + return found + + def show(self): + self.sketch.stroke(0) + self.sketch.no_fill() + self.sketch.stroke_weight(1) + self.sketch.rect_mode(self.sketch.CENTER) + self.sketch.rect( + self.boundary.x, self.boundary.y, self.boundary.w * 2, self.boundary.h * 2 + ) + + for p in self.points: + self.sketch.stroke_weight(1) + self.sketch.stroke(0) + self.sketch.point(p.x, p.y) + + if self.divided: + if self.northeast: + self.northeast.show() + if self.northwest: + self.northwest.show() + if self.southeast: + self.southeast.show() + if self.southwest: + self.southwest.show() diff --git a/examples/Nature of Code/chapter05/Example_5_14_COS_SIN_LUT/Example_5_14_COS_SIN_LUT.py b/examples/Nature of Code/chapter05/Example_5_14_COS_SIN_LUT/Example_5_14_COS_SIN_LUT.py new file mode 100644 index 0000000..347e7cb --- /dev/null +++ b/examples/Nature of Code/chapter05/Example_5_14_COS_SIN_LUT/Example_5_14_COS_SIN_LUT.py @@ -0,0 +1,58 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter5/Example_5_14_COS_SIN_LUT/Example_5_14_COS_SIN_LUT.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Using a Lookup Table for Sine and Cosine +""" + +from pycreative.app import Sketch + + +class Example_5_14_COS_SIN_LUT(Sketch): + def settings(self): + self.size(640, 360) + + def setup(self): + self.initSinCos() # important call to initialize lookup tables + + def draw(self): + self.background(255) + # modulate the current radius + radius = 50 + 50 * self.sinLUT[self.frame_count % self.SC_PERIOD] + + # draw a circle made of points (every 5 degrees) + for i in range(0, 360, 5): + # convert degrees into array index: + # the modulo operator (%) ensures periodicity + theta = int((i * self.SC_INV_PREC) % self.SC_PERIOD) + self.stroke(0) + self.stroke_weight(4) + # draw the circle around mouse pos + self.point( + self.width / 2 + radius * self.cosLUT[theta], + self.height / 2 + radius * self.sinLUT[theta], + ) + + # declare arrays and params for storing sin/cos values + sinLUT = [] + cosLUT = [] + # set table precision to 0.5 degrees + SC_PRECISION = 0.5 + # caculate reciprocal for conversions + SC_INV_PREC = 1 / SC_PRECISION + # compute required table length + SC_PERIOD = int(360 * SC_INV_PREC) + + # init sin/cos tables with values + # should be called from setup() + def initSinCos(self): + # initialize as floats so type checkers infer List[float] + self.sinLUT = [0.0] * self.SC_PERIOD + self.cosLUT = [0.0] * self.SC_PERIOD + for i in range(self.SC_PERIOD): + self.sinLUT[i] = self.sin(i * (self.PI / 180) * self.SC_PRECISION) + self.cosLUT[i] = self.cos(i * (self.PI / 180) * self.SC_PRECISION) diff --git a/examples/Nature of Code/chapter07/Example_7_01_Wolfram_Elementary_Cellular_Automata/Example_7_01_Wolfram_Elementary_Cellular_Automata.py b/examples/Nature of Code/chapter07/Example_7_01_Wolfram_Elementary_Cellular_Automata/Example_7_01_Wolfram_Elementary_Cellular_Automata.py new file mode 100644 index 0000000..801a361 --- /dev/null +++ b/examples/Nature of Code/chapter07/Example_7_01_Wolfram_Elementary_Cellular_Automata/Example_7_01_Wolfram_Elementary_Cellular_Automata.py @@ -0,0 +1,66 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter7/Example_7_1_Wolfram_Elementary_Cellular_Automata/Example_7_1_Wolfram_Elementary_Cellular_Automata.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 7-1: Wolfram Elementary Cellular Automata +""" + +from pycreative.app import Sketch + + +class Example_7_01_Wolfram_Elementary_Cellular_Automata(Sketch): + def setup(self): + self.size(640, 320) + self.set_title("Example 7-1: Wolfram Elementary Cellular Automata") + self.background(255) + # An array of 0s and 1s + self.cells = [0] * (self.width // self.w) + self.cells[len(self.cells) // 2] = 1 + + def draw(self): + for i in range(1, len(self.cells) - 1): + # Only drawing the cell's with a state of 1 + if self.cells[i] == 1: + self.fill(0) + # Set the y-position according to the generation. + self.square(i * self.w, self.generation * self.w, self.w) + + # Compute the next generation. + nextgen = self.cells[:] + for i in range(1, len(self.cells) - 1): + left = self.cells[i - 1] + me = self.cells[i] + right = self.cells[i + 1] + nextgen[i] = self.rules(left, me, right) + self.cells = nextgen + + # The next generation + self.generation += 1 + + # Stopping when it gets to the bottom of the canvas + if self.generation * self.w > self.height: + self.no_loop() + + # Look up a new state from the ruleset. + def rules(self, a, b, c): + s = f"{a}{b}{c}" + index = int(s, 2) + return self.ruleset[7 - index] + + def key_pressed(self): + if self.key == 's': + self.save_frame("Example_7_01_Wolfram_Elementary_Cellular_Automata-##.png") + + # Array of cells + cells = [] + # Starting at generation 0 + generation = 0 + # Cell size + w = 10 + + # Rule 90 + ruleset = [0, 1, 0, 1, 1, 0, 1, 0] diff --git a/examples/Nature of Code/chapter07/Example_7_02_Game_of_Life/Example_7_02_Game_of_Life.py b/examples/Nature of Code/chapter07/Example_7_02_Game_of_Life/Example_7_02_Game_of_Life.py new file mode 100644 index 0000000..1814fd7 --- /dev/null +++ b/examples/Nature of Code/chapter07/Example_7_02_Game_of_Life/Example_7_02_Game_of_Life.py @@ -0,0 +1,57 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter7/Example_7_2_Game_of_Life/Example_7_2_Game_of_Life.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 7-2: Game of Life +""" + +from pycreative.app import Sketch + + +class Example_7_02_Game_of_Life(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 7-2: Game of Life") + self.w = 8 + self.columns = self.width // self.w + self.rows = self.height // self.w + self.board = self.create2DArray(self.columns, self.rows) + for i in range(1, self.columns - 1): + for j in range(1, self.rows - 1): + self.board[i][j] = int(self.random(2)) + + def draw(self): + next_board = self.create2DArray(self.columns, self.rows) + + for i in range(1, self.columns - 1): + for j in range(1, self.rows - 1): + neighbor_sum = 0 + for k in range(-1, 2): + for l in range(-1, 2): + neighbor_sum += self.board[i + k][j + l] + neighbor_sum -= self.board[i][j] + + if self.board[i][j] == 1 and neighbor_sum < 2: + next_board[i][j] = 0 + elif self.board[i][j] == 1 and neighbor_sum > 3: + next_board[i][j] = 0 + elif self.board[i][j] == 0 and neighbor_sum == 3: + next_board[i][j] = 1 + else: + next_board[i][j] = self.board[i][j] + + for i in range(self.columns): + for j in range(self.rows): + fill_value = 255 - self.board[i][j] * 255 + self.fill(fill_value) + self.stroke(0) + self.square(i * self.w, j * self.w, self.w) + + self.board = next_board + + def create2DArray(self, columns: int, rows: int) -> list[list[int]]: + return [[0 for _ in range(rows)] for _ in range(columns)] diff --git a/examples/Nature of Code/chapter07/Example_7_03_Object_Oriented_Game_of_Life/Cell.py b/examples/Nature of Code/chapter07/Example_7_03_Object_Oriented_Game_of_Life/Cell.py new file mode 100644 index 0000000..3ca9002 --- /dev/null +++ b/examples/Nature of Code/chapter07/Example_7_03_Object_Oriented_Game_of_Life/Cell.py @@ -0,0 +1,28 @@ +""" +Cell class for Example 7.3: Object Oriented Game of Life +""" + +class Cell: + def __init__(self, state: int, x: int, y: int, w: int): + # Cell State -> 1 = Alive, 0 = Not Alive + self.state = state + self.previous = self.state + + # position and size + self.x = x + self.y = y + self.w = w + + def show(self, sketch): + sketch.stroke(0) + # If the cell is born, color it blue! + if self.previous == 0 and self.state == 1: + sketch.fill(0, 0, 255) + elif self.state == 1: + sketch.fill(0) + # If the cell dies, color it red! + elif self.previous == 1 and self.state == 0: + sketch.fill(255, 0, 0) + else: + sketch.fill(255) + sketch.square(self.x, self.y, self.w) diff --git a/examples/Nature of Code/chapter07/Example_7_03_Object_Oriented_Game_of_Life/Example_7_03_Object_Oriented_Game_of_Life.py b/examples/Nature of Code/chapter07/Example_7_03_Object_Oriented_Game_of_Life/Example_7_03_Object_Oriented_Game_of_Life.py new file mode 100644 index 0000000..f284d8b --- /dev/null +++ b/examples/Nature of Code/chapter07/Example_7_03_Object_Oriented_Game_of_Life/Example_7_03_Object_Oriented_Game_of_Life.py @@ -0,0 +1,64 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter7/Example_7_3_Object_Oriented_Game_of_Life/Example_7_3_Object_Oriented_Game_of_Life.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 7-3: Object-Oriented Game of Life +""" + +from pycreative.app import Sketch +from Cell import Cell + + +class Example_7_03_Object_Oriented_Game_of_Life(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 7-3: Object-Oriented Game of Life") + self.w = 8 + self.columns = self.width // self.w + self.rows = self.height // self.w + self.board = self.create2DArray(self.columns, self.rows) + for i in range(1, self.columns - 1): + for j in range(1, self.rows - 1): + from random import randint + + self.board[i][j] = Cell(randint(0, 1), i * self.w, j * self.w, self.w) + + def draw(self): + # Looping but skipping the edge cells + for x in range(1, self.columns - 1): + for y in range(1, self.rows - 1): + neighborSum = 0 + for i in range(-1, 2): + for j in range(-1, 2): + # Use the previous state when counting neighbors + neighborSum += self.board[x + i][y + j].previous + neighborSum -= self.board[x][y].previous + + # Set the cell's new state based on the neighbor count + if self.board[x][y].state == 1 and neighborSum < 2: + self.board[x][y].state = 0 + elif self.board[x][y].state == 1 and neighborSum > 3: + self.board[x][y].state = 0 + elif self.board[x][y].state == 0 and neighborSum == 3: + self.board[x][y].state = 1 + # else do nothing! + + for i in range(self.columns): + for j in range(self.rows): + # evaluates to 255 when state is 0 and 0 when state is 1 + self.board[i][j].show(self) + + # save the previous state before the next generation! + self.board[i][j].previous = self.board[i][j].state + + def create2DArray(self, columns: int, rows: int): + arr = [[Cell(0, i * self.w, j * self.w, self.w) for j in range(rows)] for i in range(columns)] + return arr + + def key_pressed(self): + if self.key == 's': + self.save_frame("Example_7_03_Object_Oriented_Game_of_Life-##.png") diff --git a/examples/Nature of Code/chapter08/Example_8_01_Recursive_Circles_Once/Example_8_01_Recursive_Circles_Once.py b/examples/Nature of Code/chapter08/Example_8_01_Recursive_Circles_Once/Example_8_01_Recursive_Circles_Once.py new file mode 100644 index 0000000..9952d3a --- /dev/null +++ b/examples/Nature of Code/chapter08/Example_8_01_Recursive_Circles_Once/Example_8_01_Recursive_Circles_Once.py @@ -0,0 +1,33 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter8/Example_8_1_Recursive_Circles_Once/Example_8_1_Recursive_Circles_Once.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Simple Recursion +""" + +from pycreative.app import Sketch + + +class Example_8_01_Recursive_Circles_Once(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 8-1: Simple Recursion") + self.no_loop() + + def draw(self): + self.background(255) + self.draw_circles(int(self.width / 2), int(self.height / 2), int(self.width / 2)) + + def draw_circles(self, x: int, y: int, r: float): + self.stroke(0) + self.stroke_weight(2) + self.circle(x, y, r * 2) + # Exit condition, stop when radius is too small + if r > 4: + r *= 0.75 + # Call the function inside the function! (recursion!) + self.draw_circles(x, y, r) diff --git a/examples/Nature of Code/chapter08/Example_8_2_Recursive_Circles_Twice/Example_8_2_Recursive_Circles_Twice.py b/examples/Nature of Code/chapter08/Example_8_2_Recursive_Circles_Twice/Example_8_2_Recursive_Circles_Twice.py new file mode 100644 index 0000000..514e10d --- /dev/null +++ b/examples/Nature of Code/chapter08/Example_8_2_Recursive_Circles_Twice/Example_8_2_Recursive_Circles_Twice.py @@ -0,0 +1,33 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter8/Example_8_2_Recursive_Circles_Twice/Example_8_2_Recursive_Circles_Twice.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Simple Recursion +""" + +from pycreative.app import Sketch + + +class Example_8_2_Recursive_Circles_Twice(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 8-2: Recursion Twice") + self.no_loop() + + def draw(self): + self.background(255) + self.draw_circles(int(self.width / 2), int(self.height / 2), int(self.width / 2)) + + def draw_circles(self, x: float, y: int, radius: float): + self.stroke(0) + self.stroke_weight(2) + self.no_fill() + self.circle(x, y, radius * 2) + if radius > 4: + # draw_circles() calls itself twice. For every circle, a smaller circle is drawn to the left and the right. + self.draw_circles(x + radius / 2, y, radius / 2) + self.draw_circles(x - radius / 2, y, radius / 2) diff --git a/examples/Nature of Code/chapter08/Example_8_3_Recursive_Circles_Four_Times/Example_8_3_Recursive_Circles_Four_Times.py b/examples/Nature of Code/chapter08/Example_8_3_Recursive_Circles_Four_Times/Example_8_3_Recursive_Circles_Four_Times.py new file mode 100644 index 0000000..4690852 --- /dev/null +++ b/examples/Nature of Code/chapter08/Example_8_3_Recursive_Circles_Four_Times/Example_8_3_Recursive_Circles_Four_Times.py @@ -0,0 +1,34 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter8/Example_8_3_Recursive_Circles_Four_Times/Example_8_3_Recursive_Circles_Four_Times.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Simple Recursion +""" + +from pycreative.app import Sketch + + +class Example_8_3_Recursive_Circles_Four_Times(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 8-3: Recursion Four Times") + self.no_loop() + + def draw(self): + self.background(255) + self.draw_circles(int(self.width / 2), int(self.height / 2), int(self.width / 2)) + + def draw_circles(self, x: float, y: float, radius: float): + self.stroke(0) + self.no_fill() + self.circle(x, y, radius * 2) + if radius > 16: + # draw_circles() calls itself four times. + self.draw_circles(x + radius / 2, y, radius / 2) + self.draw_circles(x - radius / 2, y, radius / 2) + self.draw_circles(x, y + radius / 2, radius / 2) + self.draw_circles(x, y - radius / 2, radius / 2) diff --git a/examples/Nature of Code/chapter08/Example_8_4_Cantor_Set/Example_8_4_Cantor_Set.py b/examples/Nature of Code/chapter08/Example_8_4_Cantor_Set/Example_8_4_Cantor_Set.py new file mode 100644 index 0000000..cd8a7d1 --- /dev/null +++ b/examples/Nature of Code/chapter08/Example_8_4_Cantor_Set/Example_8_4_Cantor_Set.py @@ -0,0 +1,34 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter8/Example_8_4_Cantor_Set/Example_8_4_Cantor_Set.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Cantor Set +// Renders a simple fractal, the Cantor Set +""" + +from pycreative.app import Sketch + + +class Example_8_4_Cantor_Set(Sketch): + def setup(self): + self.size(640, 120) + self.set_title("Example 8-4: Cantor Set") + + def draw(self): + self.background(255) + self.stroke(0) + self.stroke_weight(2) + # Call the recursive function + self.cantor(10, 10, 620) + self.no_loop() + + def cantor(self, x: float, y: int, length: float): + # Stop at 1 pixel! + if length > 1: + self.line(x, y, x + length, y) + self.cantor(x, y + 20, length / 3) + self.cantor(x + (2 * length) / 3, y + 20, length / 3) diff --git a/examples/Nature of Code/chapter08/Example_8_5_Koch_Curve/Example_8_5_Koch_Curve.py b/examples/Nature of Code/chapter08/Example_8_5_Koch_Curve/Example_8_5_Koch_Curve.py new file mode 100644 index 0000000..2d80a07 --- /dev/null +++ b/examples/Nature of Code/chapter08/Example_8_5_Koch_Curve/Example_8_5_Koch_Curve.py @@ -0,0 +1,63 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter8/Example_8_5_Koch_Curve/Example_8_5_Koch_Curve.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Koch Curve +// Renders a simple fractal, the Koch snowflake +// Each recursive level drawn in sequence +""" + +from pycreative.app import Sketch +from KochLine import KochLine + + +class Example_8_5_Koch_Curve(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 8-5: Koch Curve") + # An array for all the line segments + self.segments = [] + + # Left side of canvas + start = self.pvector(0, 300) + # Right side of canvas + end = self.pvector(self.width, 300) + + # The first KochLine object + self.segments.append(KochLine(self, start, end)) + + # Apply the Koch rules five times. + for _ in range(5): + self.generate() + + self.no_loop() + + def draw(self): + self.background(255) + for segment in self.segments: + segment.show() + self.no_loop() + + def generate(self): + # Create the next array + next_segments = [] + # For every segment + for segment in self.segments: + # Calculate 5 koch PVectors (done for us by the line object) + koch_points = segment.koch_points() + a = koch_points[0] + b = koch_points[1] + c = koch_points[2] + d = koch_points[3] + e = koch_points[4] + # Make line segments between all the vectors and add them + next_segments.append(KochLine(self, a, b)) + next_segments.append(KochLine(self, b, c)) + next_segments.append(KochLine(self, c, d)) + next_segments.append(KochLine(self, d, e)) + # The next segments! + self.segments = next_segments diff --git a/examples/Nature of Code/chapter08/Example_8_5_Koch_Curve/KochLine.py b/examples/Nature of Code/chapter08/Example_8_5_Koch_Curve/KochLine.py new file mode 100644 index 0000000..55e67c6 --- /dev/null +++ b/examples/Nature of Code/chapter08/Example_8_5_Koch_Curve/KochLine.py @@ -0,0 +1,41 @@ +""" +KochLine class for Example 8-5: Koch Curve +""" + +from pycreative.vector import PVector + + +class KochLine: + def __init__(self, sketch, a: PVector, b: PVector): + self.sketch = sketch + self.start = a.copy() + self.end = b.copy() + + def show(self): + self.sketch.stroke(0) + self.sketch.stroke_weight(2) + self.sketch.line(self.start.x, self.start.y, self.end.x, self.end.y) + + def koch_points(self): + # Just the first point! + a = PVector(self.start.x, self.start.y) + # Just the last point! + e = PVector(self.end.x, self.end.y) + + # A vector pointing in the direction, 1/3rd the length + v = self.end - self.start + v *= 1 / 3.0 + + # b is just 1/3 of the way + b = a + v + # d is just another 1/3 of the way + d = b + v + + # Rotate by -PI/3 radians (negative angle so it rotates "up"). + v = v.rotate(-self.sketch.PI / 3) + # Move along + c = b + v + + # Return all five points in an array + points = [a, b, c, d, e] + return points \ No newline at end of file diff --git a/examples/Nature of Code/chapter08/Example_8_6_Recursive_Tree/Example_8_6_Recursive_Tree.py b/examples/Nature of Code/chapter08/Example_8_6_Recursive_Tree/Example_8_6_Recursive_Tree.py new file mode 100644 index 0000000..bda6855 --- /dev/null +++ b/examples/Nature of Code/chapter08/Example_8_6_Recursive_Tree/Example_8_6_Recursive_Tree.py @@ -0,0 +1,53 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter8/Example_8_6_Recursive_Tree/Example_8_6_Recursive_Tree.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Recursive Tree +// Renders a simple tree-like structure via recursion +// Branching angle calculated as a function of horizontal mouse position +""" + +from pycreative.app import Sketch + + +class Example_8_6_Recursive_Tree(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 8-6: Recursive Tree") + + def draw(self): + self.background(255) + # Mapping the angle between 0 to 90° (HALF_PI) according to mouseX + self.angle = self.map(self.mouse_x or 0, 0, self.width, 0, self.HALF_PI) + + # Start the tree from the bottom of the canvas + self.translate(self.width / 2, self.height) + self.stroke(0) + self.stroke_weight(2) + self.branch(100) + + # {!1} Each branch now receives its length as an argument. + def branch(self, length: float): + self.line(0, 0, 0, -length) + self.translate(0, -length) + + # {!1} Each branch's length shrinks by two-thirds. + length *= 0.67 + + if length > 2: + self.push() + try: + self.rotate(self.angle) + # {!1} Subsequent calls to branch() include the length argument. + self.branch(length) + self.pop() + + self.push() + self.rotate(-self.angle) + self.branch(length) + finally: + self.pop() diff --git a/examples/Nature of Code/chapter08/Example_8_7_Stochastic_Tree/Example_8_7_Stochastic_Tree.py b/examples/Nature of Code/chapter08/Example_8_7_Stochastic_Tree/Example_8_7_Stochastic_Tree.py new file mode 100644 index 0000000..c5c3bba --- /dev/null +++ b/examples/Nature of Code/chapter08/Example_8_7_Stochastic_Tree/Example_8_7_Stochastic_Tree.py @@ -0,0 +1,59 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter8/Example_8_7_Stochastic_Tree/Example_8_7_Stochastic_Tree.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Stochastic Tree +// Renders a simple tree-like structure via recursion +// Angles and number of branches are random +""" + +from pycreative.app import Sketch + + +class Example_8_7_Stochastic_Tree(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 8-7: Stochastic Tree") + self.frame_rate(1) + + def draw(self): + self.background(255) + + self.stroke(0) + self.push() + try: + # Start the tree from the bottom of the screen + self.translate(int(self.width / 2), self.height) + self.stroke_weight(2) + # Start the recursive branching! + self.branch(100) + finally: + self.pop() + + def branch(self, length: float): + # Draw the actual branch + self.line(0, 0, 0, -length) + # Move along to end + self.translate(0, -length) + + # Each branch will be 2/3rds the size of the previous one + length *= 0.67 + + # All recursive functions must have an exit condition!!!! + # Here, ours is when the length of the branch is 2 pixels or less + if length > 2: + # A random number of branches + n = int(self.floor(self.random(1, 4))) + for i in range(n): + # Picking a random angle + angle = self.random(-self.PI / 2, self.PI / 2) + self.push() # Save the current state of transformation (i.e. where are we now) + try: + self.rotate(angle) # Rotate by theta + self.branch(length) # Ok, now call myself to branch again + finally: + self.pop() # Whenever we get back here, we "pop" in order to restore the previous matrix state diff --git a/examples/Nature of Code/chapter08/Example_8_8_LSystem_String_Only/Example_8_8_LSystem_String_Only.py b/examples/Nature of Code/chapter08/Example_8_8_LSystem_String_Only/Example_8_8_LSystem_String_Only.py new file mode 100644 index 0000000..250f2ca --- /dev/null +++ b/examples/Nature of Code/chapter08/Example_8_8_LSystem_String_Only/Example_8_8_LSystem_String_Only.py @@ -0,0 +1,55 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter8/Example_8_8_LSystem_String_Only/Example_8_8_LSystem_String_Only.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Simple L-system Sentence Generation +""" + +from pycreative.app import Sketch + + +class Example_8_8_LSystem_String_Only(Sketch): + def setup(self): + self.size(640, 160) + + # (optional) list_fonts() is available to explore bundled/system fonts + + # Try to set a monospace font succinctly: prefer TTF/OTF system fonts + # (use_font will prefer non-.ttc matches like 'Courier New') and fall + # back to the default if none are available. + for candidate in ('courier new', 'courier', 'monospace'): + if self.use_font(candidate, size=16): + break + + self.set_title("Example 8-8: L-System String Only") + self.text_size(14) + self.fill(0) + self.current = "A" + self.no_loop() + + def draw(self): + self.background(255) + + self.push_matrix() + try: + self.translate(0, -16) + for i in range(9): + self.generate() + # Draw using the active font (the Sketch convenience API + # ensures a reasonable default when requested in setup()). + self.text(f"{i}: {self.current}", 4, 20 + i * 16) + finally: + self.pop_matrix() + + def generate(self): + next_s = "" + for c in self.current: + if c == 'A': + next_s += "AB" + elif c == 'B': + next_s += "A" + self.current = next_s \ No newline at end of file diff --git a/examples/Nature of Code/chapter08/Example_8_9_LSystem/Example_8_9_LSystem.py b/examples/Nature of Code/chapter08/Example_8_9_LSystem/Example_8_9_LSystem.py new file mode 100644 index 0000000..ef220d2 --- /dev/null +++ b/examples/Nature of Code/chapter08/Example_8_9_LSystem/Example_8_9_LSystem.py @@ -0,0 +1,41 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter8/Example_8_9_LSystem/Example_8_9_LSystem.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// L-Systems +""" + +from pycreative.app import Sketch +from Turtle import Turtle +from LSystem import LSystem + + +class Example_8_9_LSystem(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 8-9: L-System") + rules = {'F': "FF+[+F-F-F]-[-F+F+F]"} + self.lsystem = LSystem("F", rules) + self.turtle = Turtle(self, 6, self.radians(25)) + + for _ in range(4): + self.lsystem.generate() + + # Some other rules + # ruleset = {'F': "F[F]-F+F[--F]+F-F"} + # self.lsystem = LSystem("F-F-F-F", ruleset) + # self.turtle = Turtle(self, 4, self.PI / 2) + + # ruleset = {'F': "F--F--F--G", 'G': "GG"} + # self.lsystem = LSystem("F--F--F", ruleset) + # self.turtle = Turtle(self, 16, self.PI / 3) + + def draw(self): + self.background(255) + self.translate(self.width / 2, self.height) + self.turtle.render(self.lsystem.sentence) + self.no_loop() diff --git a/examples/Nature of Code/chapter08/Example_8_9_LSystem/LSystem.py b/examples/Nature of Code/chapter08/Example_8_9_LSystem/LSystem.py new file mode 100644 index 0000000..6113b55 --- /dev/null +++ b/examples/Nature of Code/chapter08/Example_8_9_LSystem/LSystem.py @@ -0,0 +1,19 @@ +""" +L-system class for Example 8-9: L-System +""" + +class LSystem: + def __init__(self, axiom: str, rules: dict[str, str]): + self.sentence = axiom # The sentence (a String) + self.ruleset = rules # The ruleset (a HashMap of Rule) + + # Generate the next generation + def generate(self): + # An empty string that we will fill + nextgen = "" + # For every character in the sentence + for c in self.sentence: + # Replace it with itself unless it matches one of our rules + nextgen += self.ruleset.get(c, c) + # Replace sentence + self.sentence = nextgen diff --git a/examples/Nature of Code/chapter08/Example_8_9_LSystem/Turtle.py b/examples/Nature of Code/chapter08/Example_8_9_LSystem/Turtle.py new file mode 100644 index 0000000..28b5593 --- /dev/null +++ b/examples/Nature of Code/chapter08/Example_8_9_LSystem/Turtle.py @@ -0,0 +1,24 @@ +""" +Turtle class for Example 8-9: L-System +""" + +class Turtle: + def __init__(self, sketch, length: int, angle: float): + self.sketch = sketch + self.length = length + self.angle = angle + + def render(self, sentence: str): + self.sketch.stroke(0) + for c in sentence: + if c == 'F' or c == 'G': + self.sketch.line(0, 0, 0, -self.length) + self.sketch.translate(0, -self.length) + elif c == '+': + self.sketch.rotate(self.angle) + elif c == '-': + self.sketch.rotate(-self.angle) + elif c == '[': + self.sketch.push() + elif c == ']': + self.sketch.pop() diff --git a/examples/Nature of Code/chapter11/Example_11_1_Flappy_Bird/Bird.py b/examples/Nature of Code/chapter11/Example_11_1_Flappy_Bird/Bird.py new file mode 100644 index 0000000..3e5acca --- /dev/null +++ b/examples/Nature of Code/chapter11/Example_11_1_Flappy_Bird/Bird.py @@ -0,0 +1,37 @@ +""" +Bird class for Example 11.1: Flappy Bird +""" + +class Bird: + def __init__(self, sketch): + self.sketch = sketch + # The bird's position (x will be constant) + self.x = 50 + self.y = 120 + + # Velocity and forces are scalar since the bird only moves along the y-axis + self.velocity = 0.0 + self.gravity = 0.5 + self.flap_force = -10.0 + + # The bird flaps its wings + def flap(self): + self.velocity += self.flap_force + + def update(self): + # Add gravity + self.velocity += self.gravity + self.y += self.velocity + # Dampen velocity + self.velocity *= 0.95 + + # Handle the "floor" + if self.y > self.sketch.height: + self.y = self.sketch.height + self.velocity = 0.0 + + def show(self): + self.sketch.stroke_weight(2) + self.sketch.stroke(0) + self.sketch.fill(127) + self.sketch.circle(self.x, self.y, 16) diff --git a/examples/Nature of Code/chapter11/Example_11_1_Flappy_Bird/Example_11_1_Flappy_Bird.py b/examples/Nature of Code/chapter11/Example_11_1_Flappy_Bird/Example_11_1_Flappy_Bird.py new file mode 100644 index 0000000..a208795 --- /dev/null +++ b/examples/Nature of Code/chapter11/Example_11_1_Flappy_Bird/Example_11_1_Flappy_Bird.py @@ -0,0 +1,44 @@ +""" +This is a recreation of the Nature of Code example found at: +https://github.com/nature-of-code/noc-2-processing-port/blob/main/chapter11/Example_11_1_Flappy_Bird/Example_11_1_Flappy_Bird.pde + +// The Nature of Code +// Daniel Shiffman +// http://natureofcode.com + +// Example 11-1: Flappy Bird +""" + +from pycreative.app import Sketch +from Bird import Bird +from Pipe import Pipe + + +class Example_11_1_Flappy_Bird(Sketch): + def setup(self): + self.size(640, 360) + self.set_title("Example 11-1: Flappy Bird") + self.bird = Bird(self) + self.pipes = [Pipe(self)] + + def draw(self): + self.background(255) + + for i in range(len(self.pipes) - 1, -1, -1): + self.pipes[i].show() + self.pipes[i].update() + + if self.pipes[i].collides(self.bird): + self.text("OOPS!", self.pipes[i].x, int(self.pipes[i].top + 20)) + + if self.pipes[i].offscreen(): + self.pipes.pop(i) + + self.bird.update() + self.bird.show() + + if self.frame_count % 100 == 0: + self.pipes.append(Pipe(self)) + + def mouse_pressed(self): + self.bird.flap() diff --git a/examples/Nature of Code/chapter11/Example_11_1_Flappy_Bird/Pipe.py b/examples/Nature of Code/chapter11/Example_11_1_Flappy_Bird/Pipe.py new file mode 100644 index 0000000..7be636c --- /dev/null +++ b/examples/Nature of Code/chapter11/Example_11_1_Flappy_Bird/Pipe.py @@ -0,0 +1,35 @@ +""" +Pip class for Example 11.1: Flappy Bird +""" + +from random import uniform + +class Pipe: + def __init__(self, sketch): + self.sketch = sketch + self.spacing = 100 + self.top = uniform(0, self.sketch.height - self.spacing) + self.bottom = self.top + self.spacing + self.x = self.sketch.width + self.w = 20 + self.velocity = 2 + + def collides(self, bird): + # Is the bird within the vertical range of the top or bottom pipe? + vertical_collision = bird.y < self.top or bird.y > self.bottom + # Is the bird within the horizontal range of the pipes? + horizontal_collision = bird.x > self.x and bird.x < self.x + self.w + # If it's both a vertical and horizontal hit, it's a hit! + return vertical_collision and horizontal_collision + + def show(self): + self.sketch.fill(0) + self.sketch.no_stroke() + self.sketch.rect(self.x, 0, self.w, self.top) + self.sketch.rect(self.x, self.bottom, self.w, self.sketch.height - self.bottom) + + def update(self): + self.x -= self.velocity + + def offscreen(self): + return self.x < -self.w From 922d1c367405fbe5c5411de91fee7a89f9bf0327 Mon Sep 17 00:00:00 2001 From: Pete Haughie Date: Fri, 10 Oct 2025 03:32:49 +0200 Subject: [PATCH 2/2] ruff was complaining about an l named for loop being ambiguous --- .../Example_7_02_Game_of_Life/Example_7_02_Game_of_Life.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/Nature of Code/chapter07/Example_7_02_Game_of_Life/Example_7_02_Game_of_Life.py b/examples/Nature of Code/chapter07/Example_7_02_Game_of_Life/Example_7_02_Game_of_Life.py index 1814fd7..516054f 100644 --- a/examples/Nature of Code/chapter07/Example_7_02_Game_of_Life/Example_7_02_Game_of_Life.py +++ b/examples/Nature of Code/chapter07/Example_7_02_Game_of_Life/Example_7_02_Game_of_Life.py @@ -30,9 +30,9 @@ def draw(self): for i in range(1, self.columns - 1): for j in range(1, self.rows - 1): neighbor_sum = 0 - for k in range(-1, 2): - for l in range(-1, 2): - neighbor_sum += self.board[i + k][j + l] + for dx in range(-1, 2): + for dy in range(-1, 2): + neighbor_sum += self.board[i + dx][j + dy] neighbor_sum -= self.board[i][j] if self.board[i][j] == 1 and neighbor_sum < 2: