diff --git a/content/call-center.csv b/content/call-center.csv
new file mode 100644
index 0000000..bc7689b
--- /dev/null
+++ b/content/call-center.csv
@@ -0,0 +1,401 @@
+Employee ID,Avg Tix / Day,Customer rating,Tardies,Graveyard Shifts Taken,Weekend Shifts Taken,Sick Days Taken,% Sick Days Taken on Friday,Employee Dev. Hours,Shift Swaps Requested,Shift Swaps Offered
+144624,151.8,3.32,1,0,2,3,0,0,2,1
+142619,155.2,3.16,1,3,1,1,0,12,1,2
+142285,164.2,4,3,3,1,0,0,23,2,0
+142158,159,2.77,0,3,1,2,50,13,1,0
+141008,155.5,3.52,4,1,0,3,67,16,1,0
+145082,153.8,3.9,3,2,1,3,100,5,1,0
+139410,162.1,3.45,3,3,1,3,0,13,2,1
+135014,154,3.67,0,3,1,1,0,18,1,2
+139356,157.5,3.4,0,1,1,4,25,14,0,3
+137368,160.8,3.3,1,3,1,0,0,33,2,4
+141982,157.3,3.85,2,3,1,2,0,8,1,2
+144753,164.1,2.75,1,2,0,0,0,5,0,2
+132229,152.9,3.77,1,1,1,3,67,19,2,2
+132744,158,2.74,1,2,0,0,0,8,0,0
+131177,154.8,3.21,1,1,2,0,0,14,2,3
+140074,153.3,3.13,1,3,1,0,0,18,1,3
+135633,159.7,3.45,3,2,2,4,0,10,0,0
+139582,155.7,3.19,2,2,1,5,0,9,1,0
+135197,160.7,4.43,2,4,1,2,0,6,1,3
+131975,143.1,4.37,0,3,1,3,33,0,2,3
+142782,150.2,4.33,1,1,1,0,0,0,3,0
+139071,151.5,3.43,1,1,2,0,0,4,2,3
+144270,154.2,3.82,1,2,1,0,0,0,1,0
+133358,157.8,2.8,2,3,2,7,43,0,3,3
+134858,157.1,3.18,1,2,0,6,17,2,2,2
+137434,152.5,4.42,1,2,2,2,50,9,0,0
+135673,152.5,3.65,2,2,1,0,0,6,2,4
+133639,156.7,4.22,1,1,2,0,0,4,1,4
+139089,151.2,3.56,1,1,1,2,50,20,2,1
+131303,161.7,3.46,2,2,1,3,67,9,1,3
+135743,152.2,3.96,3,1,1,2,0,16,2,2
+144571,157,3.59,2,3,1,1,100,12,1,2
+145158,158.5,3.69,1,2,0,0,0,18,1,0
+136077,156,3.71,3,1,1,2,50,34,0,2
+136372,156.7,3.39,0,2,1,2,50,22,1,0
+137095,161,3.14,3,3,0,1,0,1,3,6
+131832,155.1,2.28,2,1,1,0,0,6,2,3
+138338,149.4,3.41,2,1,1,2,50,13,0,0
+136542,161.5,3.05,1,2,0,5,20,17,2,1
+137903,154.2,3.37,0,4,2,4,100,6,3,3
+143210,158.8,4.24,2,3,1,3,0,26,2,0
+131617,157.5,3.35,2,3,1,4,25,3,3,2
+137184,161.5,2.95,2,2,1,2,50,18,1,0
+137322,158.4,3.67,2,1,1,3,67,4,1,0
+141947,153,3.5,1,2,1,0,0,16,0,3
+142425,155.8,3.75,1,3,0,1,0,7,0,0
+138392,163.8,3.87,0,0,1,2,50,11,1,1
+138787,152.5,3.48,2,1,1,1,100,0,1,3
+136397,151.8,3.13,1,3,1,0,0,0,3,4
+141411,152.9,3.24,1,2,1,5,0,16,0,0
+140317,159.1,3.52,1,1,1,1,0,19,2,2
+132864,150.5,3.89,2,0,1,0,0,20,1,1
+142597,156.8,3.58,1,2,1,2,50,8,1,5
+130880,153.2,3.84,1,3,1,1,0,24,2,0
+138054,161.6,3.75,1,2,1,3,0,0,1,3
+136255,150.4,3.86,3,2,1,3,100,12,2,2
+139885,156.4,3.44,1,2,2,0,0,8,1,2
+140542,153.8,3.73,4,2,0,0,0,9,2,0
+142028,156.1,3.19,2,2,2,4,50,9,2,8
+133101,156.9,3.61,2,2,1,0,0,7,2,0
+136811,156.1,3.36,1,3,1,2,100,1,1,4
+137347,161,3.56,4,2,0,1,100,1,1,4
+137988,153.7,3.42,0,2,0,3,100,23,2,1
+132269,158.3,4.23,0,1,1,3,67,0,3,1
+132991,153.9,3.61,2,3,1,0,0,4,2,2
+144185,148.2,3.3,2,2,1,0,0,10,0,3
+131108,155.3,3.59,1,2,1,3,100,17,0,0
+140803,155.2,3.32,1,2,2,1,0,4,0,0
+131321,152,2.77,3,2,1,5,80,3,1,0
+135100,159.1,2.91,2,2,1,3,33,20,1,2
+135864,165,3.19,1,2,1,2,100,15,0,0
+141882,156.8,3.49,0,2,2,2,100,8,2,3
+142556,158.7,3.86,0,2,1,3,33,16,1,6
+139686,157.6,3.38,0,2,0,1,0,6,1,1
+144464,150.1,3.81,2,2,1,0,0,6,0,1
+134943,153.7,4.39,4,2,1,3,33,0,1,0
+132494,156.1,3.09,0,1,0,0,0,18,2,2
+133559,155.3,3.09,1,2,1,0,0,7,2,3
+138557,162,3.49,4,3,0,0,0,13,1,2
+144328,162.1,3.41,4,2,1,0,0,20,1,1
+137581,150.1,3.24,1,1,1,2,50,0,1,1
+138488,156.8,3.31,2,2,1,0,0,0,2,1
+140043,154.4,3.36,0,3,1,0,0,8,1,1
+138557,165,4.11,2,2,1,2,50,30,0,2
+133993,148.1,3.42,3,2,2,0,0,17,0,1
+140464,157.6,3.97,2,3,1,2,100,10,2,1
+138875,161,3.48,1,1,1,2,50,11,1,0
+131667,155.9,3.64,1,0,1,1,0,15,3,7
+132658,156.7,3.42,1,2,1,4,75,11,0,1
+138993,150.9,3.67,2,1,1,2,0,6,2,2
+136133,162.5,3.81,1,3,0,1,0,14,2,4
+142541,157.4,3.74,0,3,1,4,50,8,3,6
+142310,150.3,3.99,1,2,1,0,0,11,1,3
+132833,149.8,4.51,3,2,1,6,100,16,0,0
+131243,152.9,4.8,2,3,0,1,0,15,2,0
+134833,154.4,3.62,3,3,0,0,0,14,0,1
+140712,150.5,3.44,2,2,1,5,20,0,1,0
+141905,161.1,2.07,1,2,0,4,50,8,2,0
+136214,163.2,3.85,2,2,1,0,0,15,1,1
+131200,161.3,3.67,3,2,1,2,50,12,0,3
+142114,161.9,3.54,2,2,1,2,0,19,0,0
+144696,154.6,4.21,4,1,1,0,0,22,0,0
+135862,161.4,3.59,1,1,0,1,0,6,1,2
+135258,150.5,3.33,1,2,1,4,50,14,3,1
+131652,151.4,3.12,1,3,1,2,50,13,2,1
+139267,157.6,3.56,1,1,1,0,0,9,1,0
+139148,157.1,3.77,1,2,1,3,67,8,1,4
+138378,157,3.95,1,2,1,1,0,13,1,6
+135400,154.5,3.65,2,3,1,1,100,19,2,2
+131072,158.5,3.99,0,2,0,1,0,17,3,0
+131478,155.2,3.42,2,1,0,0,0,19,0,0
+140643,159.3,3.51,1,3,1,1,0,22,1,1
+143040,162.4,3.59,3,3,1,1,0,29,0,0
+137232,159.3,3.55,1,2,1,3,33,21,1,0
+140864,156.9,3.18,1,3,2,4,75,5,1,3
+143159,156.6,2.87,2,3,1,3,33,15,2,0
+139294,153.8,4.33,0,2,1,0,0,14,1,0
+132524,154.1,3.76,1,2,1,3,67,21,2,3
+135149,151,2.82,2,2,1,3,33,16,3,3
+131443,150.7,3.94,1,2,1,1,100,12,0,0
+133489,157.1,2.63,1,3,0,0,0,24,2,0
+131417,153.5,3.91,2,1,1,1,0,12,2,6
+131969,153.9,3.44,0,2,1,2,50,0,2,3
+143213,154.9,3.39,2,1,1,3,33,6,1,2
+131010,151.2,2.9,3,1,1,0,0,10,1,2
+138731,160.6,3.21,0,2,1,1,0,1,0,1
+133851,155.5,4.66,1,2,1,3,33,7,1,2
+136956,157.9,4.04,1,3,1,0,0,9,0,2
+132990,159.4,3.69,0,2,0,2,0,21,3,8
+135794,154.7,3.88,1,3,0,1,100,13,2,3
+138109,158.4,3.53,1,2,1,0,0,6,1,0
+143999,161.4,2.88,2,3,1,2,50,0,1,0
+137501,156.7,3.95,0,1,1,5,40,18,1,4
+133236,157.7,3.86,0,1,2,2,50,13,3,5
+133193,152.4,2.47,1,4,1,2,0,13,1,0
+139915,156.7,4.07,1,2,1,1,100,7,3,1
+143242,145.5,3.16,0,2,1,2,50,11,3,3
+133765,154.1,3.31,3,2,1,2,50,10,2,3
+134271,154.8,2.95,2,1,1,1,100,15,3,0
+142545,153.8,2.72,0,3,1,1,100,2,1,0
+135044,148.5,2.62,1,2,1,1,100,10,3,2
+140438,150.7,3.15,1,2,1,1,100,17,2,2
+132529,161.9,3.61,3,3,0,4,50,5,0,3
+141524,155.1,3.28,3,1,1,3,33,27,2,3
+134654,152.7,3.09,2,2,1,3,0,4,1,0
+143197,152.7,2.29,3,2,1,3,0,18,1,5
+134462,153.1,3.46,2,2,1,1,0,20,2,1
+144853,156.3,3.5,1,1,1,5,40,2,3,2
+143544,148.3,2.76,2,2,2,3,33,23,2,5
+140842,160.8,3.31,3,2,0,0,0,17,1,0
+144695,159.9,3.46,0,3,1,0,0,18,1,3
+130564,148.8,4.48,2,3,1,1,100,5,3,2
+144430,156.7,3.09,0,2,1,3,33,16,2,4
+131385,159,2.96,1,2,1,1,100,0,4,7
+132619,150.6,3.22,3,0,1,1,0,14,2,4
+134078,152.1,3.29,2,2,1,5,100,14,1,0
+145025,155.4,4,1,1,1,5,80,10,1,1
+144857,154.6,3.22,0,1,2,0,0,15,2,1
+143672,160.7,3.67,2,2,1,1,100,9,1,0
+140188,154.5,3.55,2,1,1,1,0,0,2,4
+144105,163.1,3.16,1,3,2,5,80,8,1,1
+139958,154.2,3.65,4,3,1,1,100,17,2,1
+142792,160.4,3.66,1,2,1,3,33,11,2,3
+136609,153.7,3.98,2,0,0,4,0,16,2,3
+142853,161.4,3.92,1,3,0,5,80,24,2,0
+139060,162.2,3.45,0,3,2,0,0,11,1,2
+132431,149.8,3.43,0,2,1,3,33,7,2,5
+135028,150.7,3.33,1,1,1,1,100,12,1,0
+131801,150.3,3.21,2,2,1,2,100,4,0,0
+134577,158.6,3.64,2,2,1,2,0,20,2,1
+143722,148.5,3.82,0,1,1,2,100,17,1,0
+137532,161.1,3.44,1,1,0,3,33,4,1,0
+135477,149.1,3.27,2,2,2,2,100,12,0,0
+136354,161.7,3.59,1,1,1,3,67,12,1,0
+143293,158,3.45,2,2,0,4,25,19,0,0
+142354,163.9,3.4,1,2,2,2,100,10,0,2
+133699,162.4,3.63,3,2,1,2,50,8,0,2
+134179,156,3.3,2,2,0,0,0,10,3,3
+142910,158.2,3.92,2,2,1,3,67,14,3,6
+134839,161.1,3.2,0,3,1,0,0,13,1,0
+134454,158.6,3.54,2,2,1,3,0,18,1,3
+144128,156.6,3.94,2,1,1,0,0,21,1,0
+135743,151.5,3.14,0,2,1,0,0,12,2,0
+132373,161.7,3.29,1,0,1,0,0,14,3,0
+136649,154.5,4.65,1,2,1,3,0,0,2,4
+137482,152.1,3.53,2,2,1,4,75,7,3,2
+138234,147.1,3.71,1,2,1,1,100,13,0,0
+135345,149.6,3.89,0,2,1,0,0,14,1,2
+143601,153.9,3.41,3,2,1,0,0,19,1,0
+141828,159.5,2.97,2,1,2,1,0,12,1,0
+132686,160.1,2.47,2,2,1,0,0,12,2,0
+140087,155.7,3.99,2,2,1,1,100,24,4,4
+142662,146.7,3.37,2,2,1,1,0,16,0,0
+137279,154.7,3,1,2,0,1,100,15,0,0
+137582,156.9,3.69,1,2,1,3,33,4,2,2
+136474,163.8,2.42,1,1,1,0,0,14,2,2
+140749,147.1,3.76,1,2,1,0,0,20,0,0
+135048,156.8,3.87,2,2,1,6,17,21,2,2
+140767,155.1,3.31,1,3,2,4,50,16,2,1
+143009,158.6,3.25,0,1,1,3,67,34,2,1
+133306,155.5,3.55,2,2,0,4,25,1,3,5
+141875,149.1,3.3,1,1,2,3,100,0,3,6
+132100,155.5,2.79,1,3,0,2,0,8,2,3
+138618,158.9,2.96,3,2,1,1,100,19,2,3
+136454,149,2.87,2,2,1,3,0,9,2,4
+142228,157.6,3.71,1,1,1,2,50,21,0,3
+135920,162.5,3.1,2,0,2,1,100,10,2,3
+134694,159,2.77,1,2,1,1,100,15,3,5
+134941,157,2.9,2,2,1,1,0,10,2,1
+130946,159.9,4.23,1,3,2,1,0,0,1,0
+134657,160.1,3.49,2,1,0,0,0,15,0,2
+139767,159.7,3.84,2,2,1,0,0,19,0,0
+142461,158.5,3.68,0,3,0,1,0,12,0,2
+131385,161.5,4.81,1,2,0,2,0,22,0,0
+134880,159.6,3.51,2,1,0,1,100,16,1,4
+140016,152,3.8,2,2,1,2,0,23,1,0
+134977,147.9,3.05,2,2,1,0,0,9,4,4
+137079,156.8,4.04,2,3,1,4,25,7,1,0
+137614,160.8,3.64,2,1,1,3,33,4,2,2
+143038,158.2,3.65,2,2,0,2,50,9,1,3
+144815,155.2,3.43,1,3,1,6,50,6,3,1
+137930,154.7,4.17,0,1,1,1,0,10,2,5
+142830,156.9,3.8,0,2,1,1,0,17,4,3
+144038,148.2,3.71,1,1,1,3,67,6,3,9
+131679,159.1,4.71,2,1,1,3,67,13,1,0
+133041,161.7,3.26,2,2,1,0,0,13,1,0
+133218,157,4.36,0,2,1,3,33,9,3,2
+138921,152.6,3.94,2,3,1,0,0,5,1,3
+144836,158.1,3.78,1,1,1,2,50,11,1,3
+138265,156.5,3.83,2,2,1,0,0,12,1,0
+143234,152.1,3.76,2,3,1,5,100,0,1,5
+140484,152.6,3.45,2,2,1,2,100,13,3,6
+137683,152.1,2.33,0,2,0,3,33,5,1,3
+139709,155.5,2.98,3,2,1,1,0,2,2,0
+140466,149.1,2.81,0,3,0,3,67,26,3,3
+133829,160.4,3.82,2,1,1,0,0,26,3,2
+143707,150.4,4.03,0,3,0,0,0,3,2,4
+132162,152.4,3.33,0,2,1,3,67,14,3,1
+133347,151.1,4.1,1,4,1,3,0,22,0,3
+144967,167.4,3.35,3,1,1,1,100,5,1,0
+133555,156.4,3.77,0,2,0,3,100,0,1,2
+135447,155.7,3.91,1,1,1,0,0,12,1,0
+140948,151.4,3.41,2,2,2,0,0,0,1,0
+137014,166,3.02,2,0,1,2,50,12,0,0
+140238,153.1,3.23,2,2,1,5,40,5,1,1
+141665,162,3.79,1,2,1,0,0,17,1,2
+130632,161.9,3.53,1,2,1,2,100,0,1,0
+144360,150.6,2.73,1,2,1,0,0,12,3,3
+130876,157.4,3.68,0,4,2,3,67,8,2,5
+139472,152.6,3.32,1,2,0,2,50,16,2,4
+132353,154.6,3.27,1,2,1,2,0,12,1,0
+138328,155.2,3.34,1,1,0,0,0,4,1,0
+137095,155.3,3.59,1,2,1,3,33,16,2,2
+131897,163.4,3.54,2,2,1,0,0,0,2,1
+140456,164,3.55,0,2,0,0,0,22,1,0
+134754,152.6,2.78,2,2,1,4,50,20,2,0
+130759,156.5,3.11,3,2,2,1,100,8,1,1
+137486,162.9,3.39,0,2,1,2,50,12,0,0
+138912,166.7,3.48,3,2,0,0,0,13,1,0
+140880,162.1,3.31,0,2,1,1,100,11,0,0
+141298,156.8,2.76,1,2,1,1,100,4,3,2
+136874,160.2,4.33,1,2,1,5,100,3,2,1
+141770,157.3,3.65,3,3,0,1,100,11,4,4
+143623,158.4,4.23,3,2,0,3,100,27,0,1
+132931,156.1,3.73,1,1,1,0,0,16,1,5
+130885,151.9,3.49,1,3,2,0,0,23,0,1
+135541,161.2,3.75,3,1,1,0,0,11,1,4
+131642,150.3,3.4,1,3,1,1,0,19,2,2
+141082,158.9,3.85,2,2,2,1,100,13,2,1
+138265,162,3.25,1,2,1,4,25,15,3,3
+134517,161.5,4.5,1,2,1,0,0,9,2,1
+137799,154.2,3.51,1,1,1,0,0,29,1,1
+137977,157,2.57,1,2,0,0,0,20,3,5
+141042,154.6,3.93,2,1,1,2,0,5,2,1
+133418,159.2,3.71,1,3,1,0,0,16,2,0
+131688,153.2,4.02,1,1,1,6,33,0,0,0
+137586,154.5,4.26,2,2,1,4,25,20,1,0
+138249,160,3.23,2,2,0,0,0,12,2,2
+130664,145.9,2.81,2,1,1,1,0,3,2,0
+142732,153.4,3.02,2,1,1,5,40,29,1,0
+144664,157.7,3.01,3,2,1,1,0,5,3,3
+145152,154.1,2.73,1,1,0,4,50,13,2,3
+141775,153.7,3.22,0,2,1,3,100,8,5,7
+144501,146,3.53,1,2,2,2,50,9,0,1
+141593,152.6,3.77,1,2,1,5,20,0,3,3
+139624,148.6,3.84,2,1,0,1,0,19,1,3
+136761,150.2,3.34,2,3,1,0,0,23,1,0
+143882,150.3,3.1,3,2,1,1,100,19,1,2
+137737,158,3.96,3,2,1,0,0,0,2,3
+131851,163.1,3.27,2,3,1,1,100,19,2,0
+138645,156.2,3.57,0,3,1,4,100,17,2,1
+142399,156,3.64,4,3,1,1,0,21,1,0
+135314,150.5,3.71,1,3,0,0,0,16,1,0
+133155,161.7,2.41,1,2,1,0,0,18,2,0
+144899,155.2,3.91,1,3,0,1,100,9,1,0
+134458,147.7,3.88,1,0,1,2,0,16,1,1
+141796,149.4,3.73,2,3,1,5,0,5,1,2
+138686,157.8,4.11,1,2,2,3,67,1,1,2
+137155,165.3,4.49,1,3,2,1,0,30,1,7
+142940,155.7,3.06,1,1,1,6,0,12,2,1
+141231,158.2,3.46,2,3,1,1,100,23,0,0
+134409,154.1,3.94,0,2,1,2,50,0,0,0
+132548,157.5,3.46,3,1,2,2,100,7,1,0
+139962,155.7,3.84,2,3,0,0,0,7,1,0
+142861,153.4,3.17,2,2,1,0,0,21,0,2
+133776,160,2.91,2,1,1,1,100,11,1,0
+137095,159.1,3.01,0,2,2,2,100,9,1,4
+136316,161.2,3.71,1,3,1,2,0,9,2,4
+141160,154,3.67,0,2,1,0,0,14,1,2
+140184,164.6,3.64,1,1,1,6,100,15,1,1
+130698,154.3,3.7,3,3,1,0,0,22,2,4
+133790,158.7,3.15,1,3,0,3,0,0,1,1
+136276,162.2,2.45,3,2,1,0,0,11,1,1
+134057,157.3,2.88,2,2,1,3,100,21,0,2
+136667,157.1,3.17,3,2,2,0,0,15,2,2
+134738,153.4,3.53,2,2,1,2,50,0,1,0
+144863,157.8,3.72,1,3,1,4,25,10,2,0
+142920,155.2,3.48,1,2,2,6,17,18,2,6
+133578,149.4,3.11,2,1,1,5,60,13,0,0
+137866,153.9,3.35,1,3,1,3,0,4,2,1
+134319,152.6,3.4,1,1,1,0,0,6,1,3
+141090,151.5,3.43,3,2,1,2,100,5,3,4
+133974,154.8,2.65,1,3,1,0,0,23,3,5
+137044,154.1,3.75,2,1,1,2,50,15,2,3
+134649,152.7,2.76,0,2,1,5,80,16,1,0
+132781,152.8,2.92,1,2,1,3,67,20,3,4
+135598,155.8,3.31,1,3,1,0,0,1,1,0
+143787,153.4,2.96,1,3,1,1,100,21,0,0
+140380,159.8,3.25,0,1,1,0,0,24,1,0
+140658,148.3,3.87,2,3,0,0,0,8,0,0
+138288,158.5,2.74,2,2,1,0,0,15,1,0
+132328,155.1,3.97,0,2,2,4,75,14,1,1
+139452,156.5,3.11,2,1,1,1,100,9,2,4
+143774,158.8,3.09,1,1,1,2,50,2,2,4
+138710,152.6,4.28,2,4,2,1,0,14,2,0
+142961,158.2,2.79,2,2,1,2,50,12,0,0
+142065,151.8,3.81,1,3,1,4,25,17,0,0
+144832,151.6,2.54,1,3,1,3,33,2,3,3
+133920,156.2,3.61,2,2,2,3,0,18,3,3
+144232,154.4,3.1,1,2,1,0,0,6,2,2
+131316,156.4,3.92,1,3,1,2,50,0,2,0
+139530,166.1,4.01,3,1,2,4,25,18,1,0
+134249,154.1,3.96,1,2,1,0,0,15,2,2
+132259,162.1,3.32,1,2,2,0,0,24,2,0
+134129,155.4,2.8,1,1,2,3,67,16,0,0
+135988,157.1,3.86,0,3,2,6,67,0,1,4
+142337,157.1,3.62,3,2,2,2,100,6,1,0
+144249,157,2.86,2,2,1,3,67,18,2,2
+133228,152.5,3.43,3,1,1,2,50,2,3,0
+132046,165.2,4.07,1,2,1,1,100,13,3,3
+136092,153.6,3.81,2,2,1,1,0,7,2,0
+140238,155.1,3.67,1,1,0,1,0,15,3,6
+130808,157.2,3.64,1,2,1,2,0,9,0,0
+132089,159.6,4.03,2,2,1,0,0,10,1,0
+132641,153.2,3.52,3,2,1,2,50,24,1,2
+134766,153.7,3.62,2,1,0,3,67,5,2,3
+140158,152.8,4.03,1,1,1,6,17,18,2,0
+133974,158.4,3.75,4,3,0,3,67,2,3,2
+134437,145.2,3.77,1,2,1,1,0,11,1,0
+143335,162.3,4.03,1,2,1,3,100,0,1,1
+141728,159.9,3.26,0,3,1,2,100,17,0,2
+134379,159.6,3.86,0,1,2,2,100,17,1,1
+143472,149.8,3.76,1,2,0,4,75,10,2,1
+144076,155.7,3.02,2,2,0,0,0,14,2,4
+143819,163.4,3.42,1,2,1,3,67,0,1,0
+136718,151.9,3.6,0,3,1,1,0,9,1,2
+142480,153.1,3.03,1,2,2,3,33,26,3,2
+138166,154.4,3.63,1,1,1,0,0,26,0,0
+136042,160,3.36,2,3,1,2,50,12,2,2
+134999,155.4,3.27,2,3,1,1,0,13,0,0
+137910,159.5,2.71,2,4,1,3,33,13,3,2
+136944,161.3,3.92,1,1,0,2,0,11,0,0
+136145,154.9,3.14,2,1,1,7,86,1,0,2
+143406,145,2.33,3,1,0,6,83,30,4,0
+145176,151.7,3.23,2,2,1,2,100,15,1,1
+143091,159.3,2.92,1,3,2,0,0,21,2,4
+138759,153.4,3.96,1,2,0,0,0,6,3,3
+144013,150.2,3.79,1,2,2,5,20,15,0,0
+138843,151.3,3.55,0,2,2,0,0,15,2,5
+133915,162.1,3.08,1,2,0,6,83,8,1,2
+131731,157.9,3.03,1,2,0,2,0,18,0,0
+144570,155.3,3.23,2,1,1,2,0,2,2,3
+139219,153.9,3.97,1,2,1,0,0,16,0,0
+140601,156.4,3.01,1,3,1,2,0,3,2,4
+140308,156.7,3.85,1,3,1,1,0,10,3,2
+145100,154.7,3.28,0,2,1,3,67,9,2,3
+136259,151.5,3.3,2,1,1,4,100,6,1,0
+143941,163.4,3.04,2,1,1,0,0,9,1,0
+140376,158.8,3.88,1,2,1,1,100,9,2,1
+136365,155.6,3.79,2,2,1,0,0,26,3,0
+137581,168.7,3.37,2,2,1,4,75,14,1,0
+141467,158.7,4.34,2,1,0,2,50,7,2,3
+132149,160,3.92,1,2,1,2,50,10,0,2
+141343,159.1,3.6,4,1,0,0,0,17,0,2
+143981,160.3,3.7,1,2,1,0,0,1,2,4
+139820,162.6,3.37,2,3,1,1,100,6,1,3
+144780,159.6,3.5,1,2,1,2,0,15,1,1
+138420,155.4,4.29,3,3,1,2,50,18,1,3
+131547,150.7,3.99,1,2,1,4,25,30,2,3
+137942,160.6,3.87,1,1,1,2,100,16,1,0
\ No newline at end of file
diff --git a/content/call_center_anomaly.ipynb b/content/call_center_anomaly.ipynb
new file mode 100644
index 0000000..1a394c2
--- /dev/null
+++ b/content/call_center_anomaly.ipynb
@@ -0,0 +1,329 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "66907701-1675-4ef5-ac61-a88a436e5b16",
+ "metadata": {},
+ "source": [
+ "# Case 1 - Call Center Staffing Analytics - Detecting Favoritism using Machine Learning\n",
+ "## Situation: \n",
+ "A call center operation was under close scrutiny for uneven performance,\n",
+ "shoddy operations and low employee morale. There was a rumor floating around that\n",
+ "the call center manager was engaging in favoritism, that certain employees were given\n",
+ "unfairly easy working conditions, but no one was able to present a convincing case\n",
+ "against the manager.\n",
+ "## Complication: \n",
+ "Some even went so far as to accuse the manager of nepotism --\n",
+ "implying that the workers being given extra sweet deals were those that were related to\n",
+ "the manager. The case was before a judge who, given the seriousness of the case,\n",
+ "asked for hard evidence.\n",
+ "## Key question:\n",
+ "Can we use machine learning to ‘objectively’ identify whether there is\n",
+ "any hard evidence to prove that certain employees were being treated in a\n",
+ "systematically different way than others. Can we do this using not one, but multiple\n",
+ "dimensions, together?"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a4d3b2e4-8015-455f-8b10-9dad7beae687",
+ "metadata": {},
+ "source": [
+ "# start your analysis "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "f1efa156-e449-4e40-a2b2-a580df2bbe05",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pandas as pd\n",
+ "from sklearn.preprocessing import StandardScaler\n",
+ "from sklearn.neighbors import LocalOutlierFactor\n",
+ "import matplotlib.pyplot as plt\n",
+ "import seaborn as sns\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "dea8b335-e502-4b51-a825-f914c29d752e",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Employee ID | \n",
+ " Avg Tix / Day | \n",
+ " Customer rating | \n",
+ " Tardies | \n",
+ " Graveyard Shifts Taken | \n",
+ " Weekend Shifts Taken | \n",
+ " Sick Days Taken | \n",
+ " % Sick Days Taken on Friday | \n",
+ " Employee Dev. Hours | \n",
+ " Shift Swaps Requested | \n",
+ " Shift Swaps Offered | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " count | \n",
+ " 400.000000 | \n",
+ " 400.000000 | \n",
+ " 400.000000 | \n",
+ " 400.000000 | \n",
+ " 400.000000 | \n",
+ " 400.000000 | \n",
+ " 400.000000 | \n",
+ " 400.000000 | \n",
+ " 400.000000 | \n",
+ " 400.000000 | \n",
+ " 400.000000 | \n",
+ "
\n",
+ " \n",
+ " mean | \n",
+ " 137946.037500 | \n",
+ " 156.085750 | \n",
+ " 3.495150 | \n",
+ " 1.465000 | \n",
+ " 1.985000 | \n",
+ " 0.952500 | \n",
+ " 1.875000 | \n",
+ " 35.220000 | \n",
+ " 11.970000 | \n",
+ " 1.447500 | \n",
+ " 1.760000 | \n",
+ "
\n",
+ " \n",
+ " std | \n",
+ " 4240.877417 | \n",
+ " 4.416638 | \n",
+ " 0.461497 | \n",
+ " 0.972697 | \n",
+ " 0.794577 | \n",
+ " 0.548631 | \n",
+ " 1.673732 | \n",
+ " 39.295061 | \n",
+ " 7.470852 | \n",
+ " 0.999872 | \n",
+ " 1.812626 | \n",
+ "
\n",
+ " \n",
+ " min | \n",
+ " 130564.000000 | \n",
+ " 143.100000 | \n",
+ " 2.070000 | \n",
+ " 0.000000 | \n",
+ " 0.000000 | \n",
+ " 0.000000 | \n",
+ " 0.000000 | \n",
+ " 0.000000 | \n",
+ " 0.000000 | \n",
+ " 0.000000 | \n",
+ " 0.000000 | \n",
+ "
\n",
+ " \n",
+ " 25% | \n",
+ " 134401.500000 | \n",
+ " 153.075000 | \n",
+ " 3.210000 | \n",
+ " 1.000000 | \n",
+ " 1.000000 | \n",
+ " 1.000000 | \n",
+ " 0.000000 | \n",
+ " 0.000000 | \n",
+ " 6.000000 | \n",
+ " 1.000000 | \n",
+ " 0.000000 | \n",
+ "
\n",
+ " \n",
+ " 50% | \n",
+ " 137906.500000 | \n",
+ " 156.050000 | \n",
+ " 3.505000 | \n",
+ " 1.000000 | \n",
+ " 2.000000 | \n",
+ " 1.000000 | \n",
+ " 2.000000 | \n",
+ " 25.000000 | \n",
+ " 12.000000 | \n",
+ " 1.000000 | \n",
+ " 1.000000 | \n",
+ "
\n",
+ " \n",
+ " 75% | \n",
+ " 141771.250000 | \n",
+ " 159.100000 | \n",
+ " 3.810000 | \n",
+ " 2.000000 | \n",
+ " 2.000000 | \n",
+ " 1.000000 | \n",
+ " 3.000000 | \n",
+ " 67.000000 | \n",
+ " 17.000000 | \n",
+ " 2.000000 | \n",
+ " 3.000000 | \n",
+ "
\n",
+ " \n",
+ " max | \n",
+ " 145176.000000 | \n",
+ " 168.700000 | \n",
+ " 4.810000 | \n",
+ " 4.000000 | \n",
+ " 4.000000 | \n",
+ " 2.000000 | \n",
+ " 7.000000 | \n",
+ " 100.000000 | \n",
+ " 34.000000 | \n",
+ " 5.000000 | \n",
+ " 9.000000 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " Employee ID Avg Tix / Day Customer rating Tardies \\\n",
+ "count 400.000000 400.000000 400.000000 400.000000 \n",
+ "mean 137946.037500 156.085750 3.495150 1.465000 \n",
+ "std 4240.877417 4.416638 0.461497 0.972697 \n",
+ "min 130564.000000 143.100000 2.070000 0.000000 \n",
+ "25% 134401.500000 153.075000 3.210000 1.000000 \n",
+ "50% 137906.500000 156.050000 3.505000 1.000000 \n",
+ "75% 141771.250000 159.100000 3.810000 2.000000 \n",
+ "max 145176.000000 168.700000 4.810000 4.000000 \n",
+ "\n",
+ " Graveyard Shifts Taken Weekend Shifts Taken Sick Days Taken \\\n",
+ "count 400.000000 400.000000 400.000000 \n",
+ "mean 1.985000 0.952500 1.875000 \n",
+ "std 0.794577 0.548631 1.673732 \n",
+ "min 0.000000 0.000000 0.000000 \n",
+ "25% 1.000000 1.000000 0.000000 \n",
+ "50% 2.000000 1.000000 2.000000 \n",
+ "75% 2.000000 1.000000 3.000000 \n",
+ "max 4.000000 2.000000 7.000000 \n",
+ "\n",
+ " % Sick Days Taken on Friday Employee Dev. Hours \\\n",
+ "count 400.000000 400.000000 \n",
+ "mean 35.220000 11.970000 \n",
+ "std 39.295061 7.470852 \n",
+ "min 0.000000 0.000000 \n",
+ "25% 0.000000 6.000000 \n",
+ "50% 25.000000 12.000000 \n",
+ "75% 67.000000 17.000000 \n",
+ "max 100.000000 34.000000 \n",
+ "\n",
+ " Shift Swaps Requested Shift Swaps Offered \n",
+ "count 400.000000 400.000000 \n",
+ "mean 1.447500 1.760000 \n",
+ "std 0.999872 1.812626 \n",
+ "min 0.000000 0.000000 \n",
+ "25% 1.000000 0.000000 \n",
+ "50% 1.000000 1.000000 \n",
+ "75% 2.000000 3.000000 \n",
+ "max 5.000000 9.000000 "
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df = pd.read_csv(\"call-center.csv\")\n",
+ "df.describe()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7c792038-fc69-4471-bbd8-e4cd5f033faf",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Normalize the data\n",
+ "scaler = StandardScaler()\n",
+ "call_center_scaled = pd.DataFrame(scaler.fit_transform(call_center.iloc[:, 1:11]), columns=call_center.columns[1:11])\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "48dbc8e2-d38d-41fd-b8bb-3ffce9978336",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Calculate the outlier scores using LocalOutlierFactor (similar to LOF in R)\n",
+ "lof = LocalOutlierFactor(n_neighbors=5)\n",
+ "outlier_scores = -lof.fit_predict(call_center_scaled)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f94b9b53-ccfc-4368-9963-059bbad3b623",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Plot score density\n",
+ "sns.kdeplot(outlier_scores)\n",
+ "plt.show()\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f36ca55e-3624-4a4f-ad27-e271c9ce796f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Filter rows with high outlier scores\n",
+ "outliers = call_center[outlier_scores > 1.5]\n",
+ "print(outliers)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.10"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/content/javascript.ipynb b/content/javascript.ipynb
deleted file mode 100644
index a5e7487..0000000
--- a/content/javascript.ipynb
+++ /dev/null
@@ -1,86 +0,0 @@
-{
- "metadata": {
- "language_info": {
- "codemirror_mode": {
- "name": "javascript"
- },
- "file_extension": ".js",
- "mimetype": "text/javascript",
- "name": "javascript",
- "nbconvert_exporter": "javascript",
- "pygments_lexer": "javascript",
- "version": "es2017"
- },
- "kernelspec": {
- "name": "javascript",
- "display_name": "JavaScript",
- "language": "javascript"
- },
- "toc-showcode": true
- },
- "nbformat_minor": 4,
- "nbformat": 4,
- "cells": [
- {
- "cell_type": "markdown",
- "source": "# JavaScript in `JupyterLite`\n\n![](https://jupyterlite.readthedocs.io/en/latest/_static/kernelspecs/javascript.svg)",
- "metadata": {}
- },
- {
- "cell_type": "markdown",
- "source": "## Standard streams",
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": "console.log('hello world')",
- "metadata": {
- "trusted": true
- },
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "code",
- "source": "console.error('error')",
- "metadata": {
- "trusted": true
- },
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "markdown",
- "source": "## JavaScript specific constructs",
- "metadata": {}
- },
- {
- "cell_type": "code",
- "source": "const delay = 2000;\n\nsetTimeout(() => {\n console.log('done');\n}, delay);",
- "metadata": {
- "trusted": true
- },
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "code",
- "source": "var str = \"hello world\"\nstr.split('').forEach(c => {\n console.log(c)\n})",
- "metadata": {
- "trusted": true
- },
- "execution_count": null,
- "outputs": []
- },
- {
- "cell_type": "markdown",
- "source": "## Markdown cells",
- "metadata": {}
- },
- {
- "cell_type": "markdown",
- "source": "Lorenz system of differential equations\n\n$$\n\\begin{aligned}\n\\dot{x} & = \\sigma(y-x) \\\\\n\\dot{y} & = \\rho x - y - xz \\\\\n\\dot{z} & = -\\beta z + xy\n\\end{aligned}\n$$\n",
- "metadata": {}
- }
- ]
-}
\ No newline at end of file
diff --git a/content/p5.ipynb b/content/p5.ipynb
deleted file mode 100644
index 78be9cb..0000000
--- a/content/p5.ipynb
+++ /dev/null
@@ -1,150 +0,0 @@
-{
- "metadata":{
- "kernelspec":{
- "name":"p5js",
- "display_name":"p5.js",
- "language":"javascript"
- },
- "language_info":{
- "codemirror_mode":{
- "name":"javascript"
- },
- "file_extension":".js",
- "mimetype":"text/javascript",
- "name":"p5js",
- "nbconvert_exporter":"javascript",
- "pygments_lexer":"javascript",
- "version":"es2017"
- }
- },
- "nbformat_minor":4,
- "nbformat":4,
- "cells":[
- {
- "cell_type":"markdown",
- "source":"# p5 notebook\n\nA minimal Jupyter notebook UI for [p5.js](https://p5js.org) kernels.",
- "metadata":{
-
- }
- },
- {
- "cell_type":"markdown",
- "source":"First let's define a couple of variables:",
- "metadata":{
-
- }
- },
- {
- "cell_type":"code",
- "source":"var n = 4;\nvar speed = 1;",
- "metadata":{
- "trusted":true
- },
- "execution_count":null,
- "outputs":[
-
- ]
- },
- {
- "cell_type":"markdown",
- "source":"## The `setup` function\n\nThe usual p5 setup function, which creates the canvas.",
- "metadata":{
-
- }
- },
- {
- "cell_type":"code",
- "source":"function setup () {\n createCanvas(innerWidth, innerHeight);\n rectMode(CENTER);\n}",
- "metadata":{
- "trusted":true
- },
- "execution_count":null,
- "outputs":[
-
- ]
- },
- {
- "cell_type":"markdown",
- "source":"## The `draw` function\n\nFrom the [p5.js documentation](https://p5js.org/reference/#/p5/draw):\n\n> The `draw()` function continuously executes the lines of code contained inside its block until the program is stopped or `noLoop()` is called.",
- "metadata":{
-
- }
- },
- {
- "cell_type":"code",
- "source":"function draw() {\n background('#ddd');\n translate(innerWidth / 2, innerHeight / 2);\n for (let i = 0; i < n; i++) {\n push();\n rotate(frameCount * speed / 1000 * (i + 1));\n fill(i * 5, i * 100, i * 150);\n const s = 200 - i * 10;\n rect(0, 0, s, s);\n pop();\n }\n}",
- "metadata":{
- "trusted":true
- },
- "execution_count":null,
- "outputs":[
-
- ]
- },
- {
- "cell_type":"markdown",
- "source":"## Show the sketch\n\nNow let's show the sketch by using the `%show` magic:",
- "metadata":{
-
- }
- },
- {
- "cell_type":"code",
- "source":"%show",
- "metadata":{
- "trusted":true
- },
- "execution_count":null,
- "outputs":[
-
- ]
- },
- {
- "cell_type":"markdown",
- "source":"## Tweak the values\n\nWe can also tweak some values in real time:",
- "metadata":{
-
- }
- },
- {
- "cell_type":"code",
- "source":"speed = 3",
- "metadata":{
- "trusted":true
- },
- "execution_count":null,
- "outputs":[
-
- ]
- },
- {
- "cell_type":"code",
- "source":"n = 20",
- "metadata":{
- "trusted":true
- },
- "execution_count":null,
- "outputs":[
-
- ]
- },
- {
- "cell_type":"markdown",
- "source":"We can also show the sketch a second time taking into account the new values:",
- "metadata":{
-
- }
- },
- {
- "cell_type":"code",
- "source":"%show",
- "metadata":{
- "trusted":true
- },
- "execution_count":null,
- "outputs":[
-
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/content/pyodide/altair.ipynb b/content/pyodide/altair.ipynb
deleted file mode 100644
index d7b8780..0000000
--- a/content/pyodide/altair.ipynb
+++ /dev/null
@@ -1,231 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Altair in `JupyterLite`\n",
- "\n",
- "**Altair** is a declarative statistical visualization library for Python.\n",
- "\n",
- "Most of the examples below are from: https://altair-viz.github.io/gallery"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Import the dependencies:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "%pip install -q altair"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Simple Bar Chart"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "import altair as alt\n",
- "import pandas as pd\n",
- "\n",
- "source = pd.DataFrame({\n",
- " 'a': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'],\n",
- " 'b': [28, 55, 43, 91, 81, 53, 19, 87, 52]\n",
- "})\n",
- "\n",
- "alt.Chart(source).mark_bar().encode(\n",
- " x='a',\n",
- " y='b'\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Simple Heatmap"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "import altair as alt\n",
- "import numpy as np\n",
- "import pandas as pd\n",
- "\n",
- "# Compute x^2 + y^2 across a 2D grid\n",
- "x, y = np.meshgrid(range(-5, 5), range(-5, 5))\n",
- "z = x ** 2 + y ** 2\n",
- "\n",
- "# Convert this grid to columnar data expected by Altair\n",
- "source = pd.DataFrame({'x': x.ravel(),\n",
- " 'y': y.ravel(),\n",
- " 'z': z.ravel()})\n",
- "\n",
- "alt.Chart(source).mark_rect().encode(\n",
- " x='x:O',\n",
- " y='y:O',\n",
- " color='z:Q'\n",
- ")\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Install the Vega Dataset"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "%pip install -q vega_datasets"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Interactive Average"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "import altair as alt\n",
- "from vega_datasets import data\n",
- "\n",
- "source = data.seattle_weather()\n",
- "brush = alt.selection(type='interval', encodings=['x'])\n",
- "\n",
- "bars = alt.Chart().mark_bar().encode(\n",
- " x='month(date):O',\n",
- " y='mean(precipitation):Q',\n",
- " opacity=alt.condition(brush, alt.OpacityValue(1), alt.OpacityValue(0.7)),\n",
- ").add_selection(\n",
- " brush\n",
- ")\n",
- "\n",
- "line = alt.Chart().mark_rule(color='firebrick').encode(\n",
- " y='mean(precipitation):Q',\n",
- " size=alt.SizeValue(3)\n",
- ").transform_filter(\n",
- " brush\n",
- ")\n",
- "\n",
- "alt.layer(bars, line, data=source)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Locations of US Airports"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "import altair as alt\n",
- "from vega_datasets import data\n",
- "\n",
- "airports = data.airports.url\n",
- "states = alt.topo_feature(data.us_10m.url, feature='states')\n",
- "\n",
- "# US states background\n",
- "background = alt.Chart(states).mark_geoshape(\n",
- " fill='lightgray',\n",
- " stroke='white'\n",
- ").properties(\n",
- " width=500,\n",
- " height=300\n",
- ").project('albersUsa')\n",
- "\n",
- "# airport positions on background\n",
- "points = alt.Chart(airports).transform_aggregate(\n",
- " latitude='mean(latitude)',\n",
- " longitude='mean(longitude)',\n",
- " count='count()',\n",
- " groupby=['state']\n",
- ").mark_circle().encode(\n",
- " longitude='longitude:Q',\n",
- " latitude='latitude:Q',\n",
- " size=alt.Size('count:Q', title='Number of Airports'),\n",
- " color=alt.value('steelblue'),\n",
- " tooltip=['state:N','count:Q']\n",
- ").properties(\n",
- " title='Number of airports in US'\n",
- ")\n",
- "\n",
- "background + points\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python (Pyodide)",
- "language": "python",
- "name": "python"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "python",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/content/pyodide/folium.ipynb b/content/pyodide/folium.ipynb
deleted file mode 100644
index 2828de0..0000000
--- a/content/pyodide/folium.ipynb
+++ /dev/null
@@ -1,154 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "tags": []
- },
- "source": [
- "# `folium` Interactive Map Demo\n",
- "\n",
- "Simple demonstration of rendering a map in a `jupyterlite` notebook.\n",
- "\n",
- "Note that the `folium` package has several dependencies which themselves may have dependencies.\n",
- "\n",
- "The following code fragement, run in a fresh Python enviroment into which `folium` has already been installed, identifies the packages that are loaded in when `folium` is loaded:\n",
- "\n",
- "```python\n",
- "#https://stackoverflow.com/a/40381601/454773\n",
- "import sys\n",
- "before = [str(m) for m in sys.modules]\n",
- "import folium\n",
- "after = [str(m) for m in sys.modules]\n",
- "set([m.split('.')[0] for m in after if not m in before and not m.startswith('_')])\n",
- "```\n",
- "\n",
- "The loaded packages are:\n",
- "\n",
- "```\n",
- "{'branca',\n",
- " 'certifi',\n",
- " 'chardet',\n",
- " 'cmath',\n",
- " 'csv',\n",
- " 'dateutil',\n",
- " 'encodings',\n",
- " 'folium',\n",
- " 'gzip',\n",
- " 'http',\n",
- " 'idna',\n",
- " 'importlib',\n",
- " 'jinja2',\n",
- " 'markupsafe',\n",
- " 'mmap',\n",
- " 'numpy',\n",
- " 'pandas',\n",
- " 'pkg_resources',\n",
- " 'pytz',\n",
- " 'requests',\n",
- " 'secrets',\n",
- " 'stringprep',\n",
- " 'urllib3',\n",
- " 'zipfile'}\n",
- " ```\n",
- " "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The following packages seem to need installing in order load `folium`, along with folium itself:\n",
- "\n",
- "```\n",
- "chardet, certifi, idna, branca, urllib3, Jinja2, requests, Markupsafe\n",
- "```\n",
- "\n",
- "Universal wheels, with filenames of the form `PACKAGE-VERSION-py2.py3-none-any.whl` appearing in the *Download files* area of a PyPi package page ([example](https://pypi.org/project/requests/#files)] are required in order to install the package.\n",
- "\n",
- "One required package, [`Markupsafe`](https://pypi.org/project/Markupsafe/#files)) *did not* have a universal wheel available, so a wheel was manually built elsewhere (by hacking the [`setup.py` file](https://github.com/pallets/markupsafe/blob/main/setup.py) to force it to build the wheel in a platform and speedup free way) and pushed to a downloadable location in an [*ad hoc* wheelhouse](https://opencomputinglab.github.io/vce-wheelhouse/)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "# Install folium requirements\n",
- "%pip install -q folium"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Demo of `folium` Map\n",
- "\n",
- "Load in the `folium` package:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "import folium"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "And render a demo map:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "m = folium.Map(location=[50.693848, -1.304734], zoom_start=11)\n",
- "m"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python (Pyodide)",
- "language": "python",
- "name": "python"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "python",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8"
- },
- "orig_nbformat": 4,
- "toc-showcode": false
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/content/pyodide/interactive-widgets.ipynb b/content/pyodide/interactive-widgets.ipynb
deleted file mode 100644
index 267273d..0000000
--- a/content/pyodide/interactive-widgets.ipynb
+++ /dev/null
@@ -1,268 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "9ca234f7-84b7-4107-9bcd-74f5a4ffd07d",
- "metadata": {},
- "source": [
- "# `ipywidgets` Interactive Demo\n",
- "\n",
- "Simple demonstration of rendering Interactive widgets in a `jupyterlite` notebook.\n",
- "\n",
- "`ipywidgets` can be installed in this deployment (it provides the @jupyter-widgets/jupyterlab-manager federated extension), but you will need to make your own deployment to have access to other interactive widgets libraries."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "d62fba6e",
- "metadata": {},
- "outputs": [],
- "source": [
- "%pip install -q ipywidgets"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "3bab23f8-de91-43c9-9cec-84f4924425fc",
- "metadata": {},
- "outputs": [],
- "source": [
- "from ipywidgets import IntSlider"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a15c5acb-ee72-4005-8761-5693db853f22",
- "metadata": {},
- "outputs": [],
- "source": [
- "slider = IntSlider()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "8ba89682-e0d7-4bd2-961a-f9956850fd5a",
- "metadata": {},
- "outputs": [],
- "source": [
- "slider"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "50510ade-668f-4477-8cb2-41574609ac73",
- "metadata": {},
- "outputs": [],
- "source": [
- "slider"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "7bac1ed8-8c77-426b-a781-1c1a6cfad829",
- "metadata": {},
- "outputs": [],
- "source": [
- "slider.value"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "976a70a0-e99d-4c20-b005-f59bbba10f85",
- "metadata": {},
- "outputs": [],
- "source": [
- "slider.value = 5"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "3134c76e-cffb-4701-8230-e6c4bfbbfdb9",
- "metadata": {},
- "outputs": [],
- "source": [
- "from ipywidgets import IntText, link"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "f7b3fe0a-5695-4ef2-a573-40785e68fbae",
- "metadata": {},
- "outputs": [],
- "source": [
- "text = IntText()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "5e2fd50e-19e0-4e20-a1f7-ad65400ec636",
- "metadata": {},
- "outputs": [],
- "source": [
- "text"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "bb3bedce-7311-48c0-aeab-8fe3aa554b92",
- "metadata": {},
- "outputs": [],
- "source": [
- "link((slider, 'value'), (text, 'value'));"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "71b68c3e-184e-4320-9513-d0bc72800a85",
- "metadata": {},
- "source": [
- "# `bqplot` Interactive Demo\n",
- "\n",
- "Plotting in JupyterLite\n",
- "\n",
- "`bqplot` can be installed in this deployment (it provides the bqplot federated extension), but you will need to make your own deployment to have access to other interactive widgets libraries."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "119eb9a3-ac98-42c3-98d4-1ac460eb75d3",
- "metadata": {},
- "outputs": [],
- "source": [
- "%pip install -q bqplot"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "23b32857-2958-4083-b16a-ac26cd2408d4",
- "metadata": {},
- "outputs": [],
- "source": [
- "from bqplot import *\n",
- "\n",
- "import numpy as np\n",
- "import pandas as pd\n",
- "\n",
- "np.random.seed(0)\n",
- "\n",
- "n = 100\n",
- "\n",
- "x = list(range(n))\n",
- "y = np.cumsum(np.random.randn(n)) + 100.\n",
- "\n",
- "sc_x = LinearScale()\n",
- "sc_y = LinearScale()\n",
- "\n",
- "lines = Lines(\n",
- " x=x, y=y,\n",
- " scales={'x': sc_x, 'y': sc_y}\n",
- ")\n",
- "ax_x = Axis(scale=sc_x, label='Index')\n",
- "ax_y = Axis(scale=sc_y, orientation='vertical', label='lines')\n",
- "\n",
- "Figure(marks=[lines], axes=[ax_x, ax_y], title='Lines')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "ddb6b44e-06a0-4049-a79d-33ffc90d5a03",
- "metadata": {},
- "outputs": [],
- "source": [
- "lines.colors = ['green']"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "e367e7fb-b403-41aa-9629-224827ec3005",
- "metadata": {},
- "outputs": [],
- "source": [
- "lines.fill = 'bottom'"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "d4a167f3-07c4-4880-92f5-7fcdea0c61c6",
- "metadata": {},
- "outputs": [],
- "source": [
- "lines.marker = 'circle'"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "1d1342f7-ec08-4f53-84dc-d712226d9e46",
- "metadata": {},
- "outputs": [],
- "source": [
- "n = 100\n",
- "\n",
- "x = list(range(n))\n",
- "y = np.cumsum(np.random.randn(n))\n",
- "\n",
- "sc_x = LinearScale()\n",
- "sc_y = LinearScale()\n",
- "\n",
- "bars = Bars(\n",
- " x=x, y=y,\n",
- " scales={'x': sc_x, 'y': sc_y}\n",
- ")\n",
- "ax_x = Axis(scale=sc_x, label='Index')\n",
- "ax_y = Axis(scale=sc_y, orientation='vertical', label='bars')\n",
- "\n",
- "Figure(marks=[bars], axes=[ax_x, ax_y], title='Bars', animation_duration=1000)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "f86bbcfb-5b02-4700-b8d6-f90068893b55",
- "metadata": {},
- "outputs": [],
- "source": [
- "bars.y = np.cumsum(np.random.randn(n))"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python (Pyodide)",
- "language": "python",
- "name": "python"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "python",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8"
- },
- "orig_nbformat": 4,
- "toc-showcode": false
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/content/pyodide/ipycanvas.ipynb b/content/pyodide/ipycanvas.ipynb
deleted file mode 100644
index 33e1b9a..0000000
--- a/content/pyodide/ipycanvas.ipynb
+++ /dev/null
@@ -1,178 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# ipycanvas: John Conway's Game Of Life"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Some of the following code is adapted from https://jakevdp.github.io/blog/2013/08/07/conways-game-of-life/"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "%pip install -q ipycanvas"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "import asyncio\n",
- "\n",
- "import numpy as np\n",
- "\n",
- "from ipycanvas import RoughCanvas, hold_canvas"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "def life_step(x):\n",
- " \"\"\"Game of life step\"\"\"\n",
- " nbrs_count = sum(np.roll(np.roll(x, i, 0), j, 1)\n",
- " for i in (-1, 0, 1) for j in (-1, 0, 1)\n",
- " if (i != 0 or j != 0))\n",
- " return (nbrs_count == 3) | (x & (nbrs_count == 2))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "def draw(x, canvas, color='black'):\n",
- " with hold_canvas(canvas):\n",
- " canvas.clear()\n",
- " canvas.fill_style = '#FFF0C9'\n",
- " canvas.rough_fill_style = 'solid'\n",
- " canvas.fill_rect(-10, -10, canvas.width + 10, canvas.height + 10)\n",
- " canvas.rough_fill_style = 'cross-hatch'\n",
- "\n",
- " canvas.fill_style = color\n",
- " canvas.stroke_style = color\n",
- "\n",
- " living_cells = np.where(x)\n",
- " \n",
- " rects_x = living_cells[1] * n_pixels\n",
- " rects_y = living_cells[0] * n_pixels\n",
- "\n",
- " canvas.fill_rects(rects_x, rects_y, n_pixels)\n",
- " canvas.stroke_rects(rects_x, rects_y, n_pixels)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "glider_gun =\\\n",
- "[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],\n",
- " [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0],\n",
- " [0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1],\n",
- " [0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1],\n",
- " [1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\n",
- " [1,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0],\n",
- " [0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],\n",
- " [0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\n",
- " [0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]\n",
- "\n",
- "x = np.zeros((50, 70), dtype=bool)\n",
- "x[1:10,1:37] = glider_gun"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "n_pixels = 15\n",
- "\n",
- "canvas = RoughCanvas(width=x.shape[1]*n_pixels, height=x.shape[0]*n_pixels)\n",
- "canvas.fill_style = '#FFF0C9'\n",
- "canvas.rough_fill_style = 'solid'\n",
- "canvas.fill_rect(0, 0, canvas.width, canvas.height)\n",
- "\n",
- "canvas"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "draw(x, canvas, '#5770B3')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "for _ in range(300):\n",
- " x = life_step(x)\n",
- " draw(x, canvas, '#5770B3')\n",
- "\n",
- " await asyncio.sleep(0.1)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python (Pyodide)",
- "language": "python",
- "name": "python"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "python",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8"
- },
- "orig_nbformat": 4,
- "toc-showcode": false
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/content/pyodide/ipyleaflet.ipynb b/content/pyodide/ipyleaflet.ipynb
deleted file mode 100644
index 64dcff2..0000000
--- a/content/pyodide/ipyleaflet.ipynb
+++ /dev/null
@@ -1,259 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "%pip install -q bqplot ipyleaflet"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import os\n",
- "from urllib.request import urlopen\n",
- "import json\n",
- "from datetime import datetime\n",
- "\n",
- "import numpy as np\n",
- "import pandas as pd\n",
- "\n",
- "from js import fetch\n",
- "\n",
- "from ipywidgets import Dropdown\n",
- "\n",
- "from bqplot import Lines, Figure, LinearScale, DateScale, Axis\n",
- "\n",
- "from ipyleaflet import Map, GeoJSON, WidgetControl"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "URL = \"https://raw.githubusercontent.com/jupyter-widgets/ipyleaflet/master/examples/nations.json\"\n",
- "\n",
- "res = await fetch(URL)\n",
- "text = await res.text()\n",
- "\n",
- "data = pd.read_json(text)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def clean_data(data):\n",
- " for column in ['income', 'lifeExpectancy', 'population']:\n",
- " data = data.drop(data[data[column].apply(len) <= 4].index)\n",
- " return data\n",
- "\n",
- "def extrap_interp(data):\n",
- " data = np.array(data)\n",
- " x_range = np.arange(1800, 2009, 1.)\n",
- " y_range = np.interp(x_range, data[:, 0], data[:, 1])\n",
- " return y_range\n",
- "\n",
- "def extrap_data(data):\n",
- " for column in ['income', 'lifeExpectancy', 'population']:\n",
- " data[column] = data[column].apply(extrap_interp)\n",
- " return data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "data = clean_data(data)\n",
- "data = extrap_data(data)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "date_start = datetime(1800, 12, 31)\n",
- "date_end = datetime(2009, 12, 31)\n",
- "\n",
- "date_scale = DateScale(min=date_start, max=date_end)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "date_data = pd.date_range(start=date_start, end=date_end, freq='A', normalize=True)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "country_name = 'Angola'\n",
- "data_name = 'income'\n",
- "\n",
- "x_data = data[data.name == country_name][data_name].values[0]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "x_scale = LinearScale()\n",
- "\n",
- "lines = Lines(x=date_data, y=x_data, scales={'x': date_scale, 'y': x_scale})\n",
- "\n",
- "ax_x = Axis(label='Year', scale=date_scale, num_ticks=10, tick_format='%Y')\n",
- "ax_y = Axis(label=data_name.capitalize(), scale=x_scale, orientation='vertical', side='left')\n",
- "\n",
- "figure = Figure(axes=[ax_x, ax_y], title=country_name, marks=[lines], animation_duration=500,\n",
- " layout={'max_height': '250px', 'max_width': '400px'})"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def update_figure(country_name, data_name):\n",
- " try:\n",
- " lines.y = data[data.name == country_name][data_name].values[0]\n",
- " ax_y.label = data_name.capitalize()\n",
- " figure.title = country_name\n",
- " except IndexError:\n",
- " pass"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "URL = \"https://raw.githubusercontent.com/jupyter-widgets/ipyleaflet/master/examples/countries.geo.json\"\n",
- "\n",
- "res = await fetch(URL)\n",
- "text = await res.text()\n",
- "\n",
- "countries = json.loads(text)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "m = Map(zoom=3)\n",
- "\n",
- "geo = GeoJSON(data=countries, style={'fillColor': 'white', 'weight': 0.5}, hover_style={'fillColor': '#1f77b4'}, name='Countries')\n",
- "m.add_layer(geo)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "widget_control1 = WidgetControl(widget=figure, position='bottomright')\n",
- "\n",
- "m.add_control(widget_control1)\n",
- "\n",
- "def on_hover(event, feature, **kwargs):\n",
- " global country_name\n",
- "\n",
- " country_name = feature['properties']['name']\n",
- " update_figure(country_name, data_name)\n",
- "\n",
- "geo.on_hover(on_hover)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "dropdown = Dropdown(\n",
- " options=['income', 'population', 'lifeExpectancy'],\n",
- " value=data_name,\n",
- " description='Plotting:'\n",
- ")\n",
- "\n",
- "def on_click(change):\n",
- " global data_name\n",
- "\n",
- " data_name = change['new']\n",
- " update_figure(country_name, data_name)\n",
- "\n",
- "dropdown.observe(on_click, 'value')\n",
- "\n",
- "widget_control2 = WidgetControl(widget=dropdown, position='bottomleft')\n",
- "\n",
- "m.add_control(widget_control2)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "m"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python (Pyodide)",
- "language": "python",
- "name": "python"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "python",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8"
- },
- "orig_nbformat": 4,
- "toc-showcode": false
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/content/pyodide/matplotlib.ipynb b/content/pyodide/matplotlib.ipynb
deleted file mode 100644
index c2ec2be..0000000
--- a/content/pyodide/matplotlib.ipynb
+++ /dev/null
@@ -1,113 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "tags": []
- },
- "source": [
- "## Matplotlib"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "import numpy as np\n",
- "import matplotlib.pyplot as plt"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "x = np.linspace(0, 10, 1000)\n",
- "plt.plot(x, np.sin(x));"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "image/png": "iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Il7ecAAAACXBIWXMAAA9hAAAPYQGoP6dpAABmgklEQVR4nO3deXxTZdo+8OskaZKuKd1baKGFQtkLRUrZkQoILoy4oDgogzg6OqPCjCPvb9R3RmcYfd3GZWQYZXDfxhVUFMsOZWspe0sXoPveJt2X5Pz+yAIVKKVNcpKc6/v55A/DSXqnwsl1zvM89yOIoiiCiIiIiGRDIXUBRERERORcDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMsMASERERCQzDIBEREREMqOSugB3ZjKZUFpaCn9/fwiCIHU5RERE1AOiKKKhoQFRUVFQKOR5L4wBsA9KS0sRHR0tdRlERETUC0VFRRgwYIDUZUiCAbAP/P39AZj/AgUEBEhcDREREfWEwWBAdHS07XtcjhgA+8A67BsQEMAASERE5GbkPH1LngPfRERERDLGAEhEREQkMwyARERERDLDAEhEREQkMwyARERERDLDAEhEREQkMwyARERERDLDAEhEREQkMwyARERERDLjFgFw586duPHGGxEVFQVBEPDVV19d8TXbt2/H+PHjodFoMGTIEGzYsOGi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",
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "plt.show()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "tags": []
- },
- "source": [
- "## Matplotlib: support for widgets backend"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "%pip install -q ipympl"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "%matplotlib widget"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "x = np.linspace(0, 10, 1000)\n",
- "plt.plot(x, np.sin(x))"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python (Pyodide)",
- "language": "python",
- "name": "python"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "python",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8"
- },
- "orig_nbformat": 4,
- "toc-showcode": false
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/content/pyodide/plotly.ipynb b/content/pyodide/plotly.ipynb
deleted file mode 100644
index 9716bee..0000000
--- a/content/pyodide/plotly.ipynb
+++ /dev/null
@@ -1,158 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Plotly in JupyterLite\n",
- "\n",
- "`plotly.py` is an interactive, open-source, and browser-based graphing library for Python: https://plotly.com/python/"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "%pip install -q nbformat plotly"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "tags": []
- },
- "source": [
- "## Basic Figure"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import plotly.graph_objects as go\n",
- "fig = go.Figure()\n",
- "fig.add_trace(go.Scatter(y=[2, 1, 4, 3]))\n",
- "fig.add_trace(go.Bar(y=[1, 4, 3, 2]))\n",
- "fig.update_layout(title = 'Hello Figure')\n",
- "fig.show()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "tags": []
- },
- "source": [
- "## Basic Table with a Pandas DataFrame"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import plotly.graph_objects as go\n",
- "import pandas as pd\n",
- "\n",
- "from js import fetch\n",
- "\n",
- "URL = \"https://raw.githubusercontent.com/plotly/datasets/master/2014_usa_states.csv\"\n",
- "\n",
- "res = await fetch(URL)\n",
- "text = await res.text()\n",
- "\n",
- "filename = 'data.csv'\n",
- "\n",
- "with open(filename, 'w') as f:\n",
- " f.write(text)\n",
- "\n",
- "df = pd.read_csv(filename)\n",
- "\n",
- "fig = go.Figure(data=[go.Table(\n",
- " header=dict(values=list(df.columns),\n",
- " fill_color='paleturquoise',\n",
- " align='left'),\n",
- " cells=dict(values=[df.Rank, df.State, df.Postal, df.Population],\n",
- " fill_color='lavender',\n",
- " align='left'))\n",
- "])\n",
- "\n",
- "fig.show()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Quiver Plot with Points"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import plotly.figure_factory as ff\n",
- "import plotly.graph_objects as go\n",
- "\n",
- "import numpy as np\n",
- "\n",
- "x,y = np.meshgrid(np.arange(-2, 2, .2),\n",
- " np.arange(-2, 2, .25))\n",
- "z = x*np.exp(-x**2 - y**2)\n",
- "v, u = np.gradient(z, .2, .2)\n",
- "\n",
- "# Create quiver figure\n",
- "fig = ff.create_quiver(x, y, u, v,\n",
- " scale=.25,\n",
- " arrow_scale=.4,\n",
- " name='quiver',\n",
- " line_width=1)\n",
- "\n",
- "# Add points to figure\n",
- "fig.add_trace(go.Scatter(x=[-.7, .75], y=[0,0],\n",
- " mode='markers',\n",
- " marker_size=12,\n",
- " name='points'))\n",
- "\n",
- "fig.show()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python (Pyodide)",
- "language": "python",
- "name": "python"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "python",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8"
- },
- "orig_nbformat": 4,
- "toc-showcode": false
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/content/pyodide/pyb2d/0_tutorial.ipynb b/content/pyodide/pyb2d/0_tutorial.ipynb
deleted file mode 100644
index 3772314..0000000
--- a/content/pyodide/pyb2d/0_tutorial.ipynb
+++ /dev/null
@@ -1,649 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "b07a3b47-2262-4135-a1d2-52e8392b44eb",
- "metadata": {},
- "outputs": [],
- "source": [
- "import sys\n",
- "if \"pyodide\" in sys.modules:\n",
- " import piplite\n",
- " await piplite.install('pyb2d-jupyterlite-backend>=0.4.2')\n"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "49c3f9ea-23ce-4c5c-b3fe-44f1cecadf20",
- "metadata": {},
- "source": [
- "pyb2d is imported as b2d"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "dff93359-2c68-467a-9239-478a0e550a4b",
- "metadata": {},
- "outputs": [],
- "source": [
- "import b2d\n",
- "# import pyb2d_jupyterlite_backend\n",
- "from pyb2d_jupyterlite_backend.async_jupyter_gui import JupyterAsyncGui\n",
- "import numpy as np\n",
- "import matplotlib.pylab as plt"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "bc977c4e-75ee-4349-9408-650c3dcd01e0",
- "metadata": {},
- "source": [
- "# Tutorial 0: A free falling body\n",
- "The first step with Box2D is the creation of the world. The world is parametrized by a gravity vector."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "4ff914a6-eb18-45a1-b1ed-e8ad7ab0d298",
- "metadata": {},
- "outputs": [],
- "source": [
- "# the world\n",
- "gravity = (0, -10)\n",
- "world = b2d.World(gravity)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "3afdbb2a-e694-4779-b95e-73a5b38d34b6",
- "metadata": {},
- "source": [
- "Create a circle-shaped body"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "99837a63-4628-483c-8f2d-cc4aec9cb1d5",
- "metadata": {},
- "outputs": [],
- "source": [
- "# the body def\n",
- "body_def = b2d.BodyDef()\n",
- "body_def.type = b2d.BodyType.dynamic\n",
- "body_def.position = (0, 0)\n",
- "\n",
- "# the body\n",
- "body = world.create_body(body_def)\n",
- "\n",
- "# shape\n",
- "circle_shape = b2d.CircleShape()\n",
- "circle_shape.radius = 1.0\n",
- "\n",
- "# the fixture\n",
- "fixture_def = b2d.FixtureDef()\n",
- "fixture_def.shape = circle_shape\n",
- "fixture_def.density = 1.0\n",
- "\n",
- "# create and add the fixture to the body\n",
- "fixture = body.create_fixture(fixture_def)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "bf9758a6-fb6e-4f9c-b15f-783f9488cf7e",
- "metadata": {},
- "source": [
- "We can now have a look at the world: We render the world st. each meter in the Box2D world will be 100 pixels in the image:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "8b433892-3c82-43be-a085-eda3e4279b2c",
- "metadata": {},
- "outputs": [],
- "source": [
- "# from b2d.plot import render_world\n",
- "b2d.plot.plot_world(world, ppm=100)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "5e1db1f1-6e47-454c-9ea9-86262d7da309",
- "metadata": {},
- "source": [
- "Lets run the world for a total of 5 seconds. \n",
- "Usually one wants to run the world at a certain frame rate.\n",
- "With the frame rate and the total time we can compute the delta for each iteration and how many steps we need"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "41a232a9-a3c5-425d-9aed-d3adb90d6314",
- "metadata": {},
- "outputs": [],
- "source": [
- "t = 5\n",
- "fps = 40\n",
- "dt = 1.0 / fps\n",
- "n_steps = int(t / dt + 0.5)\n",
- "print(f\"t={t} fps={fps} dt={dt} n_steps={n_steps}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "4d458acb-6d5c-47ba-bcbf-d15ea2cf2537",
- "metadata": {},
- "source": [
- "in each step we query the bodies position and velocity and store then for later plotting"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "4e042c7b-07a7-445f-ba04-e38173b46c0f",
- "metadata": {},
- "outputs": [],
- "source": [
- "positions = np.zeros([n_steps, 2])\n",
- "velocites = np.zeros([n_steps, 2])\n",
- "timepoints = np.zeros([n_steps])\n",
- "\n",
- "t_elapsed = 0.0\n",
- "for i in range(n_steps):\n",
- "\n",
- " # get the bodies center of mass\n",
- " positions[i, :] = body.world_center\n",
- "\n",
- " # get the bodies velocity\n",
- " velocites[i, :] = body.linear_velocity\n",
- "\n",
- " timepoints[i] = t_elapsed\n",
- "\n",
- " world.step(time_step=dt, velocity_iterations=1, position_iterations=1)\n",
- " t_elapsed += dt"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "0ec7d66c-c979-40fa-8af3-9e99873ec105",
- "metadata": {},
- "source": [
- "plot the y-position against the time. We can see that the body is falling down in an accelerating way:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "434cb907-1b76-414e-bb5e-6ea32dd1f829",
- "metadata": {},
- "outputs": [],
- "source": [
- "plt.plot(timepoints, positions[:, 1])\n",
- "plt.ylabel('y-poistion [meter]')\n",
- "plt.xlabel('t [sec]')\n",
- "plt.show()"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b7f58954-4ea1-49f9-b0b0-7df38336860d",
- "metadata": {},
- "source": [
- "as expected the x position is not changing since the gravity vector is non-zero only in the x direction"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "39573eed-e6c8-45bf-8e35-4251b660ce3f",
- "metadata": {
- "tags": []
- },
- "outputs": [],
- "source": [
- "plt.plot(timepoints, positions[:, 0])\n",
- "plt.ylabel('x-poistion [meter]')\n",
- "plt.xlabel('t [sec]')\n",
- "plt.show()"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "cdb98dc5-3bc8-4933-91a0-a1db3afb9c34",
- "metadata": {},
- "source": [
- "# Tutorial 1: A falling body in a box, more pythonic\n",
- "Create a world, but in a more pythonic way, and animate the world"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "d58c2639-21da-490b-8dcd-205962f63dfc",
- "metadata": {},
- "outputs": [],
- "source": [
- "# the world\n",
- "world = b2d.world(gravity=(0, -10))\n",
- "\n",
- "# create the dynamic body\n",
- "body = world.create_dynamic_body(\n",
- " position=(5, 5),\n",
- " fixtures=b2d.fixture_def(shape=b2d.circle_shape(radius=1), density=1, restitution=0.75),\n",
- ")\n",
- "\n",
- "# create a box\n",
- "box_shape = b2d.ChainShape()\n",
- "box_shape.create_loop([(0, 0), (0, 10),(10,10),(10, 0)])\n",
- "box = world.create_static_body(\n",
- " position=(0, 0), fixtures=b2d.fixture_def(shape=box_shape, friction=0)\n",
- ")\n",
- "b2d.plot.animate_world(world, ppm=20, t=10)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "10dbb85a-84b0-4820-8fb3-6108d9c0fe00",
- "metadata": {},
- "source": [
- "note that when we animate that world again, the body has already been fallen"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "c919702c-7c62-4d87-bf1f-df5027d72a83",
- "metadata": {},
- "outputs": [],
- "source": [
- "b2d.plot.animate_world(world, ppm=20, t=2)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "7322e9c5-8608-4375-81ed-766cbb2af927",
- "metadata": {},
- "source": [
- "# Tutorial 2: Interactive worlds\n",
- "While animating the world already is already nice, interacting with the world is even better.\n",
- "pyb2d has a framwork to interact with the world for multiple backends.\n",
- "This framework is called `TestbedBase` since you can \"test\" your world in an interactive way"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "97cddd47-5a88-4cae-8543-cfcdf658255a",
- "metadata": {},
- "outputs": [],
- "source": [
- "from b2d.testbed import TestbedBase\n",
- "\n",
- "class InteractiveExample(TestbedBase):\n",
- " def __init__(self, settings=None):\n",
- " super(InteractiveExample, self).__init__(settings=settings)\n",
- " # create two balls\n",
- " body = self.world.create_dynamic_body(position=(5, 5),\n",
- " fixtures=b2d.fixture_def(shape=b2d.circle_shape(radius=1), density=1, restitution=0.5),\n",
- " )\n",
- " body = self.world.create_dynamic_body(position=(8, 5),\n",
- " fixtures=b2d.fixture_def(shape=b2d.circle_shape(radius=1), density=1, restitution=0.8),\n",
- " )\n",
- " # create a box\n",
- " box_shape = b2d.ChainShape()\n",
- " box_shape.create_loop([(0, 0), (0, 10),(10,10),(10, 0)])\n",
- " box = self.world.create_static_body(\n",
- " position=(0, 0), fixtures=b2d.fixture_def(shape=box_shape, friction=0)\n",
- " )\n",
- " \n",
- "s = JupyterAsyncGui.Settings()\n",
- "s.resolution = [300,300]\n",
- "b2d.testbed.run(InteractiveExample, backend=JupyterAsyncGui, gui_settings=s);"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "64bf55d1-4117-4af0-8f1f-65de33751743",
- "metadata": {
- "tags": []
- },
- "source": [
- "# Tutorial 3: Joints"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "07147cab-23be-4406-85f7-4b3d174e3954",
- "metadata": {
- "tags": []
- },
- "source": [
- "## Tutorial 3.1: Prismatic Joint"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "9a2d178d-33d7-4c51-b0ff-f66c98cac673",
- "metadata": {},
- "outputs": [],
- "source": [
- "world = b2d.world(gravity=(0, -10))\n",
- "anchor_body = world.create_static_body(position=(0, 0))\n",
- "b = world.create_dynamic_body(\n",
- " position=(10, 10),\n",
- " fixtures=b2d.fixture_def(shape=b2d.polygon_shape(box=[2, 0.5]), density=1),\n",
- " linear_damping=0.0,\n",
- " angular_damping=0.0,\n",
- ")\n",
- "world.create_prismatic_joint(anchor_body, b, local_axis_a=(1, 1))\n",
- "b2d.plot.animate_world(world, ppm=20, t=3, bounding_box=((0,0),(10,10)))"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "07971d69-b2ef-4d74-8c1c-48f38dcc708c",
- "metadata": {
- "tags": []
- },
- "source": [
- "## Tutorial 3.2: Pully Joint"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "39b7fef2-4b4b-4904-9899-1a34f1039693",
- "metadata": {},
- "outputs": [],
- "source": [
- "world = b2d.world(gravity=(0, -10))\n",
- "\n",
- "\n",
- "a = world.create_dynamic_body(\n",
- " position=(-5, 0),\n",
- " fixtures=b2d.fixture_def(shape=b2d.polygon_shape(box=[2, 0.8]), density=1),\n",
- " linear_damping=0.0,\n",
- " angular_damping=0.0,\n",
- ")\n",
- "b = world.create_dynamic_body(\n",
- " position=(5, 0),\n",
- " fixtures=b2d.fixture_def(shape=b2d.polygon_shape(box=[2, 0.5]), density=1),\n",
- " linear_damping=0.0,\n",
- " angular_damping=0.0,\n",
- ")\n",
- "world.create_pully_joint(\n",
- " a,\n",
- " b,\n",
- " length_a=10,\n",
- " length_b=10,\n",
- " ground_anchor_a=(-5, 10),\n",
- " ground_anchor_b=(5, 10),\n",
- " local_anchor_a=(0, 0),\n",
- " local_anchor_b=(0, 0),\n",
- ")\n",
- "b2d.plot.animate_world(world, ppm=20, t=5, bounding_box=((-10,-12),(10,12)))"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "7e8fb17d-1dda-45cb-98df-6b98be5b4e6c",
- "metadata": {},
- "source": [
- "## Tutorial 3.3: Revolute Joint"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a2686e5f-3fa2-412d-8a40-2e9a67123d43",
- "metadata": {},
- "outputs": [],
- "source": [
- "world = b2d.world(gravity=(0, -10))\n",
- "bodies = []\n",
- "b = world.create_static_body(position=(0, 15))\n",
- "bodies.append(b)\n",
- "for i in range(5):\n",
- " b = world.create_dynamic_body(\n",
- " position=(i * 4 + 2, 15),\n",
- " fixtures=b2d.fixture_def(shape=b2d.polygon_shape(box=[2, 0.5]), density=1),\n",
- " linear_damping=0.0,\n",
- " angular_damping=0.0,\n",
- " )\n",
- " bodies.append(b)\n",
- "world.create_revolute_joint(\n",
- " bodies[0], bodies[1], local_anchor_a=(0, 0), local_anchor_b=(-2, 0.0)\n",
- ")\n",
- "for i in range(1, len(bodies) - 1):\n",
- " a = bodies[i]\n",
- " b = bodies[i + 1]\n",
- " world.create_revolute_joint(a, b, local_anchor_a=(2, 0.0), local_anchor_b=(-2, 0.0))\n",
- "b2d.plot.animate_world(world, ppm=20, t=5, bounding_box=((-20,-10),(20,20)))"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "9caa18f0-4eb7-4e72-8445-8f6d096d9465",
- "metadata": {
- "tags": []
- },
- "source": [
- "## Tutorial 3.4: Weld Joint"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "3d74fb2f-7da5-4ad7-8f21-fba1f97acee2",
- "metadata": {},
- "outputs": [],
- "source": [
- "# the world\n",
- "world = b2d.world(gravity=(0, -10))\n",
- "\n",
- "\n",
- "bodies = []\n",
- "\n",
- "# create a static body as anchor\n",
- "b = world.create_static_body(\n",
- " position=(0, 4), fixtures=b2d.fixture_def(shape=b2d.polygon_shape(box=[0.3, 0.5]))\n",
- ")\n",
- "bodies.append(b)\n",
- "\n",
- "for i in range(4):\n",
- " b = world.create_dynamic_body(\n",
- " position=(i + 1.0, 4),\n",
- " fixtures=b2d.fixture_def(shape=b2d.polygon_shape(box=[0.3, 0.5]), density=0.1),\n",
- " linear_damping=2.5,\n",
- " angular_damping=2.5,\n",
- " )\n",
- " bodies.append(b)\n",
- "\n",
- "for i in range(len(bodies) - 1):\n",
- " a = bodies[i]\n",
- " b = bodies[i + 1]\n",
- " world.create_weld_joint(\n",
- " a,\n",
- " b,\n",
- " local_anchor_a=(0.5, 0.5),\n",
- " local_anchor_b=(-0.5, 0.5),\n",
- " damping=0.1,\n",
- " reference_angle=0,\n",
- " stiffness=20,\n",
- " )\n",
- " world.create_weld_joint(\n",
- " a,\n",
- " b,\n",
- " local_anchor_a=(0.5, -0.5),\n",
- " local_anchor_b=(-0.5, -0.5),\n",
- " damping=0.1,\n",
- " reference_angle=0,\n",
- " stiffness=20,\n",
- " )\n",
- "b2d.plot.animate_world(world, ppm=20, t=5, bounding_box=((0,-5),(5,5)))"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "7373461e-d1fa-4ad9-aeaa-048287839fd9",
- "metadata": {},
- "source": [
- "## Tutorial 3.5: Wheel Joint"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "1eb711e0-ae53-43ed-b0c1-c0a2fe42b407",
- "metadata": {},
- "outputs": [],
- "source": [
- "world = b2d.world(gravity=(0, -10))\n",
- "edge = world.create_static_body(\n",
- " position=(0, 0), fixtures=b2d.fixture_def(shape=b2d.edge_shape([(-20, 0), (5, 0)]))\n",
- ")\n",
- "\n",
- "# random slope\n",
- "x = np.linspace(5, 50, 10)\n",
- "y = np.random.rand(10) * 4 - 2\n",
- "y[0] = 0\n",
- "xy = np.stack([x, y]).T\n",
- "xy = np.flip(xy, axis=0)\n",
- "edge = world.create_static_body(\n",
- " position=(0, 0),\n",
- " fixtures=b2d.fixture_def(shape=b2d.chain_shape(xy, prev_vertex=(10, 0))),\n",
- ")\n",
- "# create car\n",
- "left_wheel = world.create_dynamic_body(\n",
- " position=(-3, 2),\n",
- " fixtures=b2d.fixture_def(shape=b2d.circle_shape(radius=2), density=1),\n",
- ")\n",
- "right_wheel = world.create_dynamic_body(\n",
- " position=(3, 2),\n",
- " fixtures=b2d.fixture_def(shape=b2d.circle_shape(radius=2), density=1),\n",
- ")\n",
- "\n",
- "chasis = world.create_dynamic_body(\n",
- " position=(0, 2),\n",
- " fixtures=b2d.fixture_def(shape=b2d.polygon_shape(box=[3, 0.5]), density=1),\n",
- ")\n",
- "\n",
- "wheel_joint_def = dict(\n",
- " stiffness=10,\n",
- " enable_motor=True,\n",
- " motor_speed=-100,\n",
- " max_motor_torque=100,\n",
- " collide_connected=False,\n",
- " enable_limit=True,\n",
- " lower_translation=-0.4,\n",
- " upper_translation=0.4,\n",
- " local_axis_a=(0, 1),\n",
- ")\n",
- "world.create_wheel_joint(chasis, left_wheel, local_anchor_a=(-3, 0), **wheel_joint_def)\n",
- "world.create_wheel_joint(chasis, right_wheel, local_anchor_a=(3, 0), **wheel_joint_def)\n",
- "\n",
- "\n",
- "b2d.plot.animate_world(world, ppm=20, t=15, bounding_box=((-10,-5),(20,5)))"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "d9a0fffc-ae40-47ec-b185-2a6fe0dde496",
- "metadata": {},
- "source": [
- "## Tutorial 3.6: Distance Joint"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "3c121398-f08f-4ea0-a875-de141ba53508",
- "metadata": {},
- "outputs": [],
- "source": [
- "world = b2d.world(gravity=(0, -10))\n",
- "\n",
- "for i in range(10):\n",
- "\n",
- " # create static anchor (does not need shape/fixture)\n",
- " anchor = world.create_static_body(position=(i, 0))\n",
- "\n",
- " # 5 below the anchor\n",
- " body = world.create_dynamic_body(\n",
- " position=(i, -10),\n",
- " fixtures=b2d.fixture_def(shape=b2d.circle_shape(radius=0.4), density=0.5),\n",
- " )\n",
- "\n",
- " # distance joints of various stiffness-es\n",
- " world.create_distance_joint(anchor, body, length=10, stiffness=0.5 * (i + 1))\n",
- "\n",
- "b2d.plot.animate_world(world, ppm=20, t=10, bounding_box=((-2,-20),(10,0)))"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "fb6afaff-8236-4206-85c7-3ba2de466ba9",
- "metadata": {},
- "source": [
- "# Tutorial 4: Particles"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "8f5c3b83-51d9-47cb-9b73-d5f8b0e03a76",
- "metadata": {},
- "outputs": [],
- "source": [
- "world = b2d.world(gravity=(0, -10))\n",
- "pdef = b2d.particle_system_def(radius=0.1)\n",
- "psystem = world.create_particle_system(pdef)\n",
- "\n",
- "emitter_pos = (0, 0)\n",
- "emitter_def = b2d.RandomizedLinearEmitterDef()\n",
- "emitter_def.emite_rate = 400\n",
- "emitter_def.lifetime = 5.1\n",
- "emitter_def.size = (2, 1)\n",
- "emitter_def.velocity = (6, 20)\n",
- "emitter = b2d.RandomizedLinearEmitter(psystem, emitter_def)\n",
- "b2d.plot.animate_world(world, ppm=20, t=10, bounding_box=((-10,-20),(20,5)), pre_step=emitter.step)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "ea9d7882-d3a4-45bb-b59b-cb1c9cd33990",
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.10.4"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/content/pyodide/pyb2d/color_mixing.ipynb b/content/pyodide/pyb2d/color_mixing.ipynb
deleted file mode 100644
index 9dc274c..0000000
--- a/content/pyodide/pyb2d/color_mixing.ipynb
+++ /dev/null
@@ -1,123 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import sys\n",
- "if \"pyodide\" in sys.modules:\n",
- " import piplite\n",
- " await piplite.install('pyb2d-jupyterlite-backend>=0.4.2')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "from b2d.testbed import TestbedBase\n",
- "import random\n",
- "import numpy\n",
- "import b2d\n",
- "\n",
- "class ColorMixing(TestbedBase):\n",
- "\n",
- " name = \"ColorMixing\"\n",
- "\n",
- " def __init__(self, settings=None):\n",
- " super(ColorMixing, self).__init__(settings=settings)\n",
- " dimensions = [30, 30]\n",
- "\n",
- " # the outer box\n",
- " box_shape = b2d.ChainShape()\n",
- " box_shape.create_loop(\n",
- " [\n",
- " (0, 0),\n",
- " (0, dimensions[1]),\n",
- " (dimensions[0], dimensions[1]),\n",
- " (dimensions[0], 0),\n",
- " ]\n",
- " )\n",
- " box = self.world.create_static_body(position=(0, 0), shape=box_shape)\n",
- "\n",
- " fixtureA = b2d.fixture_def(\n",
- " shape=b2d.circle_shape(1), density=2.2, friction=0.2, restitution=0.5\n",
- " )\n",
- " body = self.world.create_dynamic_body(position=(13, 10), fixtures=fixtureA)\n",
- "\n",
- " pdef = b2d.particle_system_def(\n",
- " viscous_strength=0.9,\n",
- " spring_strength=0.0,\n",
- " damping_strength=0.5,\n",
- " pressure_strength=0.5,\n",
- " color_mixing_strength=0.008,\n",
- " density=2,\n",
- " )\n",
- " psystem = self.world.create_particle_system(pdef)\n",
- " psystem.radius = 0.3\n",
- " psystem.damping = 1.0\n",
- "\n",
- " colors = [\n",
- " (255, 0, 0, 255),\n",
- " (0, 255, 0, 255),\n",
- " (0, 0, 255, 255),\n",
- " (255, 255, 0, 255),\n",
- " ]\n",
- " posiitons = [(6, 10), (20, 10), (20, 20), (6, 20)]\n",
- " for color, pos in zip(colors, posiitons):\n",
- "\n",
- " shape = b2d.polygon_shape(box=(5, 5), center=pos, angle=0)\n",
- " pgDef = b2d.particle_group_def(\n",
- " flags=b2d.ParticleFlag.waterParticle\n",
- " | b2d.ParticleFlag.colorMixingParticle,\n",
- " # group_flags=b2d.ParticleGroupFlag.solidParticleGroup,\n",
- " shape=shape,\n",
- " strength=1.0,\n",
- " color=color,\n",
- " )\n",
- " group = psystem.create_particle_group(pgDef)\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "from pyb2d_jupyterlite_backend.async_jupyter_gui import JupyterAsyncGui\n",
- "\n",
- "s = JupyterAsyncGui.Settings()\n",
- "s.resolution = [1000,500]\n",
- "s.scale = 8\n",
- "s.fps = 40\n",
- "\n",
- "tb = b2d.testbed.run(ColorMixing, backend=JupyterAsyncGui, gui_settings=s)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.10.4"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/content/pyodide/pyb2d/games/angry_shapes.ipynb b/content/pyodide/pyb2d/games/angry_shapes.ipynb
deleted file mode 100644
index cb199ef..0000000
--- a/content/pyodide/pyb2d/games/angry_shapes.ipynb
+++ /dev/null
@@ -1,419 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "2859de40-f927-4790-b192-c5b0531058f7",
- "metadata": {},
- "outputs": [],
- "source": [
- "import sys\n",
- "if \"pyodide\" in sys.modules:\n",
- " import piplite\n",
- " await piplite.install('pyb2d-jupyterlite-backend>=0.4.2')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "8ac3e93b-3e9e-4cd7-a183-0214b0dcb513",
- "metadata": {},
- "outputs": [],
- "source": [
- "from b2d.testbed import TestbedBase\n",
- "import math\n",
- "import numpy\n",
- "import b2d\n",
- "\n",
- "class AngryShapes(TestbedBase):\n",
- "\n",
- " name = \"AngryShapes\"\n",
- "\n",
- " class Settings(TestbedBase.Settings):\n",
- " substeps: int = 2\n",
- "\n",
- " def draw_segment(self, p1, p2, color, line_width=1):\n",
- " screen_p1 = self._point(self.world_to_screen(p1))\n",
- " screen_p2 = self._point(self.world_to_screen(p2))\n",
- " screen_color = self._uint8_color(color)\n",
- " screen_line_width = self._line_width(line_width)\n",
- "\n",
- " cv.line(self._image, screen_p1, screen_p2, screen_color, screen_line_width)\n",
- "\n",
- " def draw_polygon(self, vertices, color, line_width=1):\n",
- " # todo add C++ function for this\n",
- " screen_vertices = numpy.array(\n",
- " [self._point(self.world_to_screen(v)) for v in vertices], dtype=\"int32\"\n",
- " )\n",
- " screen_color = self._uint8_color(color)\n",
- " screen_line_width = self._line_width(line_width)\n",
- "\n",
- " cv.polylines(\n",
- " self._image, [screen_vertices], True, screen_color, screen_line_width, 8\n",
- " )\n",
- "\n",
- " def draw_solid_polygon(self, vertices, color):\n",
- " # todo add C++ function for this\n",
- " screen_vertices = numpy.array(\n",
- " [self._point(self.world_to_screen(v)) for v in vertices], dtype=\"int32\"\n",
- " )\n",
- " screen_color = self._uint8_color(color)\n",
- "\n",
- " cv.fillPoly(self._image, [screen_vertices], screen_color, 8)\n",
- "\n",
- " def __init__(self, settings=None):\n",
- " super(AngryShapes, self).__init__(settings=settings)\n",
- "\n",
- " self.targets = []\n",
- " self.projectiles = []\n",
- " self.marked_for_destruction = []\n",
- " self.emitter = None\n",
- "\n",
- " # particle system\n",
- " pdef = b2d.particle_system_def(\n",
- " viscous_strength=0.9,\n",
- " spring_strength=0.0,\n",
- " damping_strength=100.5,\n",
- " pressure_strength=1.0,\n",
- " color_mixing_strength=0.05,\n",
- " density=0.1,\n",
- " )\n",
- "\n",
- " self.psystem = self.world.create_particle_system(pdef)\n",
- " self.psystem.radius = 1\n",
- " self.psystem.damping = 0.5\n",
- "\n",
- " self.build_outer_box()\n",
- " self.build_castle()\n",
- " self.build_launcher()\n",
- " self.arm_launcher()\n",
- " self.build_explosives()\n",
- "\n",
- " def build_outer_box(self):\n",
- " # the outer box\n",
- "\n",
- " shape = b2d.edge_shape([(100, 0), (600, 0)])\n",
- " box = self.world.create_static_body(\n",
- " position=(0, 0), fixtures=b2d.fixture_def(shape=shape, friction=1)\n",
- " )\n",
- "\n",
- " def build_target(self, pos):\n",
- " t = self.world.create_dynamic_body(\n",
- " position=pos,\n",
- " fixtures=[\n",
- " b2d.fixture_def(shape=b2d.circle_shape(radius=4), density=1.0),\n",
- " b2d.fixture_def(\n",
- " shape=b2d.circle_shape(radius=2, pos=(3, 3)), density=1.0\n",
- " ),\n",
- " b2d.fixture_def(\n",
- " shape=b2d.circle_shape(radius=2, pos=(-3, 3)), density=1.0\n",
- " ),\n",
- " ],\n",
- " linear_damping=0,\n",
- " angular_damping=0,\n",
- " user_data=\"target\",\n",
- " )\n",
- " self.targets.append(t)\n",
- "\n",
- " def build_castle(self):\n",
- " def build_pyramid(offset, bar_shape, n):\n",
- " def build_brick(pos, size):\n",
- " hsize = [s / 2 for s in size]\n",
- " self.world.create_dynamic_body(\n",
- " position=(\n",
- " pos[0] + hsize[0] + offset[0],\n",
- " pos[1] + hsize[1] + offset[1],\n",
- " ),\n",
- " fixtures=b2d.fixture_def(\n",
- " shape=b2d.polygon_shape(box=hsize), density=8\n",
- " ),\n",
- " user_data=\"brick\",\n",
- " )\n",
- "\n",
- " bar_length = bar_shape[0]\n",
- " bar_width = bar_shape[1]\n",
- "\n",
- " nxm = n\n",
- " for y in range(nxm):\n",
- " py = y * (bar_length + bar_width)\n",
- " nx = nxm - y\n",
- " for x in range(nx):\n",
- " px = x * bar_length + y * (bar_length) / 2.0\n",
- " if y + 1 < nxm - 1:\n",
- " if x == 0:\n",
- " px += bar_width / 2\n",
- " if x + 1 == nx:\n",
- " px -= bar_width / 2\n",
- "\n",
- " build_brick((px, py), (bar_width, bar_length))\n",
- " if x < nx - 1:\n",
- " self.build_target(\n",
- " pos=(\n",
- " px + offset[0] + bar_length / 2,\n",
- " py + offset[1] + bar_width,\n",
- " )\n",
- " )\n",
- " build_brick(\n",
- " (px + bar_width / 2, py + bar_length),\n",
- " (bar_length, bar_width),\n",
- " )\n",
- "\n",
- " build_pyramid(offset=(100, 0), bar_shape=[40, 4], n=4)\n",
- " build_pyramid(offset=(400, 0), bar_shape=[30, 3], n=4)\n",
- "\n",
- " def build_launcher(self):\n",
- "\n",
- " self.launcher_anchor_pos = (30, 0)\n",
- " self.launcher_anchor = self.world.create_static_body(\n",
- " position=self.launcher_anchor_pos\n",
- " )\n",
- "\n",
- " def arm_launcher(self):\n",
- " self.reload_time = None\n",
- " self.is_armed = True\n",
- " self.projectile_radius = 3\n",
- " projectile_pos = (self.launcher_anchor_pos[0], self.launcher_anchor_pos[1] / 2)\n",
- "\n",
- " self.projectile = self.world.create_dynamic_body(\n",
- " position=projectile_pos,\n",
- " fixtures=b2d.fixture_def(\n",
- " shape=b2d.circle_shape(radius=self.projectile_radius), density=100.0\n",
- " ),\n",
- " linear_damping=0,\n",
- " angular_damping=0,\n",
- " user_data=\"projectile\",\n",
- " )\n",
- " self.projectiles.append(self.projectile)\n",
- " self.projectile_joint = self.world.create_distance_joint(\n",
- " self.launcher_anchor, self.projectile, length=1, stiffness=10000\n",
- " )\n",
- " self.mouse_joint = None\n",
- "\n",
- " def build_explosives(self):\n",
- " self.explosives = []\n",
- "\n",
- " def on_mouse_down(self, p):\n",
- " if self.is_armed:\n",
- " body = self.world.find_body(pos=p)\n",
- " if body is not None and body.user_data is not None:\n",
- " print(\"got body\")\n",
- " if body.user_data == \"projectile\":\n",
- " print(\"got projectile\")\n",
- " kwargs = dict(\n",
- " body_a=self.groundbody,\n",
- " body_b=body,\n",
- " target=p,\n",
- " max_force=50000.0 * body.mass,\n",
- " stiffness=10000.0,\n",
- " )\n",
- "\n",
- " self.mouse_joint = self.world.create_mouse_joint(**kwargs)\n",
- " body.awake = True\n",
- " return True\n",
- "\n",
- " return False\n",
- "\n",
- " def on_mouse_move(self, p):\n",
- " if self.is_armed:\n",
- " if self.mouse_joint is not None:\n",
- " self.mouse_joint.target = p\n",
- " return True\n",
- " return False\n",
- "\n",
- " def on_mouse_up(self, p):\n",
- " if self.is_armed:\n",
- " if self.mouse_joint is not None:\n",
- " self.world.destroy_joint(self.mouse_joint)\n",
- " if self.projectile_joint is not None:\n",
- " self.world.destroy_joint(self.projectile_joint)\n",
- " self.projectile_joint = None\n",
- " self.mouse_joint = None\n",
- " delta = self.launcher_anchor.position - b2d.vec2(p)\n",
- " scaled_delta = delta * 50000.0\n",
- " print(scaled_delta)\n",
- "\n",
- " self.projectile.apply_linear_impulse_to_center(scaled_delta, True)\n",
- " self.reload_time = self.elapsed_time + 1.0\n",
- " self.is_armed = False\n",
- " return False\n",
- "\n",
- " def begin_contact(self, contact):\n",
- " body_a = contact.body_a\n",
- " body_b = contact.body_b\n",
- " ud_a = body_a.user_data\n",
- " ud_b = body_b.user_data\n",
- " if ud_b == \"projectile\":\n",
- " body_a, body_b = body_b, body_a\n",
- " ud_a, ud_b = ud_b, ud_a\n",
- " if ud_a == \"projectile\":\n",
- "\n",
- " if ud_b == \"target\" or ud_b == \"brick\":\n",
- " self.marked_for_destruction.append(body_a)\n",
- " emitter_def = b2d.RandomizedRadialEmitterDef()\n",
- " emitter_def.emite_rate = 20000\n",
- " emitter_def.lifetime = 0.7\n",
- " emitter_def.enabled = True\n",
- " emitter_def.inner_radius = 0.0\n",
- " emitter_def.outer_radius = 1.0\n",
- " emitter_def.velocity_magnitude = 1000.0\n",
- " emitter_def.start_angle = 0\n",
- " emitter_def.stop_angle = math.pi\n",
- " emitter_def.transform = b2d.Transform(body_a.position, b2d.Rot(0))\n",
- " self.emitter = b2d.RandomizedRadialEmitter(self.psystem, emitter_def)\n",
- " self.emitter_die_time = self.elapsed_time + 0.02\n",
- "\n",
- " def pre_step(self, dt):\n",
- "\n",
- " if self.reload_time is not None:\n",
- " if self.elapsed_time >= self.reload_time:\n",
- " self.arm_launcher()\n",
- "\n",
- " # delete contact bodies\n",
- " for body in self.marked_for_destruction:\n",
- " if body in self.projectiles:\n",
- " self.projectiles.remove(body)\n",
- " self.world.destroy_body(body)\n",
- " if body == self.projectile:\n",
- " self.reload_time = self.elapsed_time + 1.0\n",
- " self.marked_for_destruction = []\n",
- "\n",
- " # delete bodies which have fallen down\n",
- " for body in self.world.bodies:\n",
- " if body.position.y < -100:\n",
- " if body.user_data == \"projectile\":\n",
- " self.projectiles.remove(body)\n",
- " if body.user_data == \"target\":\n",
- " self.targets.remove(body)\n",
- " self.world.destroy_body(body)\n",
- "\n",
- " # emmiter\n",
- " if self.emitter is not None:\n",
- " self.emitter.step(dt)\n",
- " if self.elapsed_time >= self.emitter_die_time:\n",
- " self.emitter = None\n",
- "\n",
- " def draw_target(self, target):\n",
- " center = target.position\n",
- " center_l = target.get_world_point((-3, 3))\n",
- " center_r = target.get_world_point((3, 3))\n",
- " eye_left = target.get_world_point((-1, 1))\n",
- " eye_right = target.get_world_point((1, 1))\n",
- " pink = [c / 255 for c in (248, 24, 148)]\n",
- "\n",
- " self.debug_draw.draw_solid_circle(\n",
- " center=center, radius=4, axis=None, color=pink\n",
- " )\n",
- " self.debug_draw.draw_solid_circle(\n",
- " center=center_l, radius=2, axis=None, color=pink\n",
- " )\n",
- " self.debug_draw.draw_solid_circle(\n",
- " center=center_r, radius=2, axis=None, color=pink\n",
- " )\n",
- "\n",
- " # schnautze\n",
- " nose_center = target.get_world_point((0, -1))\n",
- " nose_center_l = target.get_world_point((-0.3, -1))\n",
- " nose_center_r = target.get_world_point((0.3, -1))\n",
- "\n",
- " self.debug_draw.draw_circle(\n",
- " center=nose_center,\n",
- " radius=2,\n",
- " # axis=None,\n",
- " color=(1, 1, 1),\n",
- " line_width=0.2,\n",
- " )\n",
- " # eyes\n",
- " for nose_center in [nose_center_l, nose_center_r]:\n",
- " self.debug_draw.draw_solid_circle(\n",
- " center=nose_center, radius=0.6, axis=None, color=(1, 1, 1)\n",
- " )\n",
- " # eyes\n",
- " for eye_center in [eye_left, eye_right]:\n",
- " self.debug_draw.draw_solid_circle(\n",
- " center=eye_center, radius=1, axis=None, color=(1, 1, 1)\n",
- " )\n",
- " self.debug_draw.draw_solid_circle(\n",
- " center=eye_center, radius=0.7, axis=None, color=(0, 0, 0)\n",
- " )\n",
- "\n",
- " def draw_projectile(self, projectile):\n",
- "\n",
- " center = projectile.position\n",
- " # center_l = target.get_world_point((-3,3))\n",
- " # center_r = target.get_world_point(( 3,3))\n",
- " eye_left = projectile.get_world_point((-1, 1))\n",
- " eye_right = projectile.get_world_point((1, 1))\n",
- "\n",
- " self.debug_draw.draw_solid_circle(\n",
- " center=center,\n",
- " radius=self.projectile_radius * 1.1,\n",
- " axis=None,\n",
- " color=(1, 0, 0),\n",
- " )\n",
- "\n",
- " # eyes\n",
- " for eye_center in [eye_left, eye_right]:\n",
- " self.debug_draw.draw_solid_circle(\n",
- " center=eye_center, radius=1, axis=None, color=(1, 1, 1)\n",
- " )\n",
- " self.debug_draw.draw_solid_circle(\n",
- " center=eye_center, radius=0.7, axis=None, color=(0, 0, 0)\n",
- " )\n",
- "\n",
- " def post_debug_draw(self):\n",
- " for target in self.targets:\n",
- " self.draw_target(target)\n",
- "\n",
- " for projectile in self.projectiles:\n",
- " self.draw_projectile(projectile)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "6df7c8b9-216b-4fd2-8ee8-aeec294e149d",
- "metadata": {},
- "source": [
- "# Controlls\n",
- "* To play this game, click and drag the red ball and release it to shot it.\n",
- "* Use the mouse-wheel to zoom in/out, a\n",
- "* Click and drag in the empty space to translate the view."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "df412e76-7a9a-4e1d-8bc7-c02e222e10dc",
- "metadata": {},
- "outputs": [],
- "source": [
- "from pyb2d_jupyterlite_backend.async_jupyter_gui import JupyterAsyncGui\n",
- "s = JupyterAsyncGui.Settings()\n",
- "s.resolution = [1000,500]\n",
- "s.scale = 2\n",
- "s.translate = [100,100]\n",
- "tb = b2d.testbed.run(AngryShapes, backend=JupyterAsyncGui, gui_settings=s);"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.10.4"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/content/pyodide/pyb2d/games/billiard.ipynb b/content/pyodide/pyb2d/games/billiard.ipynb
deleted file mode 100644
index 13fff3a..0000000
--- a/content/pyodide/pyb2d/games/billiard.ipynb
+++ /dev/null
@@ -1,299 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import sys\n",
- "if \"pyodide\" in sys.modules:\n",
- " import piplite\n",
- " await piplite.install('pyb2d-jupyterlite-backend>=0.4.2')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import numpy\n",
- "import b2d\n",
- "import math\n",
- "import random\n",
- "\n",
- "from b2d.testbed import TestbedBase\n",
- "\n",
- "class Billiard(TestbedBase):\n",
- "\n",
- " name = \"Billiard\"\n",
- "\n",
- " def __init__(self, settings=None):\n",
- " super(Billiard, self).__init__(gravity=(0, 0), settings=settings)\n",
- " dimensions = [30, 50]\n",
- " self.dimensions = dimensions\n",
- "\n",
- " # the outer box\n",
- " box_shape = b2d.ChainShape()\n",
- " box_shape.create_loop(\n",
- " [\n",
- " (0, 0),\n",
- " (0, dimensions[1]),\n",
- " (dimensions[0], dimensions[1]),\n",
- " (dimensions[0], 0),\n",
- " ]\n",
- " )\n",
- " self.ball_radius = 1\n",
- " box = self.world.create_static_body(\n",
- " position=(0, 0), fixtures=b2d.fixture_def(shape=box_shape, friction=0)\n",
- " )\n",
- "\n",
- " self.place_balls()\n",
- " self.place_pockets()\n",
- "\n",
- " # mouse interaction\n",
- " self._selected_ball = None\n",
- " self._selected_ball_pos = None\n",
- " self._last_pos = None\n",
- "\n",
- " # balls to be destroyed in the next step\n",
- " # since they are in the pocket\n",
- " self._to_be_destroyed = []\n",
- "\n",
- " def place_pockets(self):\n",
- " pocket_radius = 1\n",
- " self.pockets = []\n",
- "\n",
- " def place_pocket(position):\n",
- " pocket_shape = b2d.circle_shape(radius=pocket_radius / 3)\n",
- " pocket = self.world.create_static_body(\n",
- " position=position,\n",
- " fixtures=b2d.fixture_def(shape=pocket_shape, is_sensor=True),\n",
- " user_data=(\"pocket\", None),\n",
- " )\n",
- " self.pockets.append(pocket)\n",
- "\n",
- " d = pocket_radius / 2\n",
- "\n",
- " place_pocket(position=(0 + d, 0 + d))\n",
- " place_pocket(position=(self.dimensions[0] - d, 0 + d))\n",
- "\n",
- " place_pocket(position=(0 + d, self.dimensions[1] / 2))\n",
- " place_pocket(position=(self.dimensions[0] - d, self.dimensions[1] / 2))\n",
- "\n",
- " place_pocket(position=(0 + d, self.dimensions[1] - d))\n",
- " place_pocket(position=(self.dimensions[0] - d, self.dimensions[1] - d))\n",
- "\n",
- " def place_balls(self):\n",
- " self.balls = []\n",
- "\n",
- " base_colors = [\n",
- " (1, 1, 0),\n",
- " (0, 0, 1),\n",
- " (1, 0, 0),\n",
- " (1, 0, 1),\n",
- " (1, 0.6, 0),\n",
- " (0, 1, 0),\n",
- " (0.7, 0.4, 0.4),\n",
- " ]\n",
- " colors = []\n",
- " for color in base_colors:\n",
- " # ``full`` ball\n",
- " colors.append((color, color))\n",
- " # ``half`` ball (half white)\n",
- " colors.append((color, (1, 1, 1)))\n",
- "\n",
- " random.shuffle(colors)\n",
- " colors.insert(4, ((0, 0, 0), (0, 0, 0))) # black\n",
- "\n",
- " n_y = 5\n",
- " c_x = self.dimensions[0] / 2\n",
- " diameter = (self.ball_radius * 2) * 1.01\n",
- "\n",
- " bi = 0\n",
- " for y in range(n_y):\n",
- "\n",
- " py = y * diameter * 0.5 * math.sqrt(3)\n",
- " n_x = y + 1\n",
- " ox = diameter * (n_y - y) / 2\n",
- " for x in range(y + 1):\n",
- " position = (x * diameter + 10 + ox, py + 30)\n",
- " self.create_billard_ball(position=position, color=colors[bi])\n",
- " bi += 1\n",
- "\n",
- " self.create_billard_ball(position=(c_x, 10), color=((1, 1, 1), (1, 1, 1)))\n",
- "\n",
- " def create_billard_ball(self, position, color):\n",
- "\n",
- " ball = self.world.create_dynamic_body(\n",
- " position=position,\n",
- " fixtures=b2d.fixture_def(\n",
- " shape=b2d.circle_shape(radius=self.ball_radius),\n",
- " density=1.0,\n",
- " restitution=0.8,\n",
- " ),\n",
- " linear_damping=0.8,\n",
- " user_data=(\"ball\", color),\n",
- " fixed_rotation=True,\n",
- " )\n",
- " self.balls.append(ball)\n",
- "\n",
- " def begin_contact(self, contact):\n",
- " body_a = contact.body_a\n",
- " body_b = contact.body_b\n",
- "\n",
- " ud_a = body_a.user_data\n",
- " ud_b = body_b.user_data\n",
- " if ud_a is None or ud_b is None:\n",
- " return\n",
- "\n",
- " if ud_b[0] == \"ball\":\n",
- " body_a, body_b = body_b, body_a\n",
- " ud_a, ud_b = ud_b, ud_a\n",
- "\n",
- " if ud_a[0] == \"ball\" and ud_b[0] == \"pocket\":\n",
- " self._to_be_destroyed.append(body_a)\n",
- "\n",
- " def pre_step(self, dt):\n",
- " for b in self._to_be_destroyed:\n",
- " self.balls.remove(b)\n",
- " self.world.destroy_body(b)\n",
- " self._to_be_destroyed = []\n",
- "\n",
- " def ball_at_position(self, pos):\n",
- " body = self.world.find_body(pos)\n",
- " if body is not None:\n",
- " user_data = body.user_data\n",
- " if user_data is not None and user_data[0] == \"ball\":\n",
- " return body\n",
- " return None\n",
- "\n",
- " def on_mouse_down(self, pos):\n",
- " body = self.ball_at_position(pos)\n",
- " if body is not None:\n",
- " self._selected_ball = body\n",
- " self._selected_ball_pos = pos\n",
- " return True\n",
- "\n",
- " return False\n",
- "\n",
- " def on_mouse_move(self, pos):\n",
- " if self._selected_ball is not None:\n",
- " self._last_pos = pos\n",
- " return True\n",
- " return False\n",
- "\n",
- " def on_mouse_up(self, pos):\n",
- " if self._selected_ball is not None:\n",
- " self._last_pos = pos\n",
- " # if the mouse is in the starting ball itself we do nothing\n",
- " if self.ball_at_position(pos) != self._selected_ball:\n",
- " delta = b2d.vec2(self._selected_ball_pos) - b2d.vec2(self._last_pos)\n",
- " delta *= 100.0\n",
- " self._selected_ball.apply_linear_impulse(\n",
- " delta, self._selected_ball_pos, True\n",
- " )\n",
- " self._selected_ball = None\n",
- " self._selected_ball_pos = None\n",
- " self._last_pos = None\n",
- " return False\n",
- "\n",
- " def post_debug_draw(self):\n",
- "\n",
- " for pocket in self.pockets:\n",
- " self.debug_draw.draw_solid_circle(\n",
- " pocket.position, self.ball_radius, (1, 0), (1, 1, 1)\n",
- " )\n",
- "\n",
- " for ball in self.balls:\n",
- " _, (color0, color1) = ball.user_data\n",
- "\n",
- " self.debug_draw.draw_solid_circle(\n",
- " ball.position, self.ball_radius, (1, 0), color0\n",
- " )\n",
- " self.debug_draw.draw_solid_circle(\n",
- " ball.position, self.ball_radius / 2, (1, 0), color1\n",
- " )\n",
- " self.debug_draw.draw_circle(\n",
- " ball.position, self.ball_radius, (1, 1, 1), line_width=0.1\n",
- " )\n",
- "\n",
- " if self._selected_ball is not None:\n",
- "\n",
- " # draw circle around selected ball\n",
- " self.debug_draw.draw_circle(\n",
- " self._selected_ball.position,\n",
- " self.ball_radius * 2,\n",
- " (1, 1, 1),\n",
- " line_width=0.2,\n",
- " )\n",
- "\n",
- " # mark position on selected ball with red dot\n",
- " self.debug_draw.draw_solid_circle(\n",
- " self._selected_ball_pos, self.ball_radius * 0.2, (1, 0), (1, 0, 0)\n",
- " )\n",
- "\n",
- " # draw the line between marked pos on ball and last pos\n",
- " if self._last_pos is not None:\n",
- " self.debug_draw.draw_segment(\n",
- " self._selected_ball_pos, self._last_pos, (1, 1, 1), line_width=0.2\n",
- " )"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Controlls\n",
- "* To play this game, click and hold inside a billiard ball, move and release the mouse to shoot the ball.\n",
- "* Use the mouse-wheel to zoom in/out, a\n",
- "* Click and drag in the empty space to translate the view."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "from pyb2d_jupyterlite_backend.async_jupyter_gui import JupyterAsyncGui\n",
- "backend = JupyterAsyncGui\n",
- "s = backend.Settings()\n",
- "s.resolution = [500,600]\n",
- "s.scale = 8\n",
- "s.fps = 40\n",
- "s.translate = [125,100]\n",
- "b2d.testbed.run(Billiard, backend=backend, gui_settings=s);"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.10.4"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/content/pyodide/pyb2d/games/goo.ipynb b/content/pyodide/pyb2d/games/goo.ipynb
deleted file mode 100644
index cbe018c..0000000
--- a/content/pyodide/pyb2d/games/goo.ipynb
+++ /dev/null
@@ -1,575 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import sys\n",
- "if \"pyodide\" in sys.modules:\n",
- " import piplite\n",
- " await piplite.install('networkx')\n",
- " await piplite.install('pyb2d-jupyterlite-backend>=0.4.2')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import b2d\n",
- "from b2d.testbed import TestbedBase\n",
- "import math\n",
- "import random\n",
- "import numpy\n",
- "from functools import partial\n",
- "import networkx\n",
- "from pyb2d_jupyterlite_backend.async_jupyter_gui import JupyterAsyncGui\n",
- "\n",
- "def best_pairwise_distance(data, f, distance):\n",
- " n = len(data)\n",
- " best = (None, None, float(\"inf\"))\n",
- " for i in range(n - 1):\n",
- " da = f(data[i])\n",
- " for j in range(i + 1, n):\n",
- " db = f(data[j])\n",
- "\n",
- " d = distance(da, db)\n",
- " if d < best[2]:\n",
- " best = (i, j, d)\n",
- " return best\n",
- "\n",
- "class Level(object):\n",
- " def __init__(self, testbed):\n",
- " self.testbed = testbed\n",
- " self.world = testbed.world\n",
- "\n",
- " self.gap_size = 15\n",
- " self.kill_sensors_height = 0.5\n",
- " self.usable_size = 20\n",
- " self.h = 10\n",
- " self.end_zone_height = 3\n",
- "\n",
- " self.outline_verts = [\n",
- " (0, self.h),\n",
- " (0, 2 * self.h),\n",
- " (0, self.h),\n",
- " (self.usable_size, self.h),\n",
- " (self.usable_size, 0),\n",
- " (self.usable_size + self.gap_size, 0),\n",
- " (self.usable_size + self.gap_size, self.h),\n",
- " (2 * self.usable_size + self.gap_size, self.h),\n",
- " (2 * self.usable_size + self.gap_size, 2 * self.h),\n",
- " ]\n",
- "\n",
- " # outline of the level\n",
- " shape = b2d.chain_shape(vertices=numpy.flip(self.outline_verts, axis=0))\n",
- " self.outline = self.world.create_static_body(position=(0, 0), shape=shape)\n",
- "\n",
- " # kill sensors\n",
- " self.kill_sensor_pos = (\n",
- " self.usable_size + self.gap_size / 2,\n",
- " self.kill_sensors_height / 2,\n",
- " )\n",
- "\n",
- " shape = b2d.polygon_shape(box=(self.gap_size / 2, self.kill_sensors_height / 2))\n",
- " self._kill_sensor = self.world.create_static_body(\n",
- " position=self.kill_sensor_pos,\n",
- " fixtures=b2d.fixture_def(shape=shape, is_sensor=True),\n",
- " )\n",
- " self._kill_sensor.user_data = \"destroyer\"\n",
- "\n",
- " # end sensor\n",
- " shape = b2d.polygon_shape(box=(self.usable_size / 2, self.end_zone_height / 2))\n",
- " self._end_sensor = self.world.create_static_body(\n",
- " position=(\n",
- " 1.5 * self.usable_size + self.gap_size,\n",
- " self.h + self.end_zone_height / 2,\n",
- " ),\n",
- " fixtures=b2d.fixture_def(shape=shape, is_sensor=True),\n",
- " )\n",
- " self._end_sensor.user_data = \"goal\"\n",
- "\n",
- " goo_radius = 1\n",
- " a = self.testbed.insert_goo(\n",
- " pos=(self.usable_size / 3, self.h + goo_radius), static=True\n",
- " )\n",
- " b = self.testbed.insert_goo(\n",
- " pos=(self.usable_size * 2 / 3, self.h + goo_radius), static=True\n",
- " )\n",
- " c = self.testbed.insert_goo(\n",
- " pos=(self.usable_size * 1 / 2, self.h + goo_radius + 4), static=False\n",
- " )\n",
- "\n",
- " self.testbed.connect_goos(a, b)\n",
- " self.testbed.connect_goos(a, c)\n",
- " self.testbed.connect_goos(b, c)\n",
- "\n",
- " def draw(self, debug_draw):\n",
- "\n",
- " # draw outline\n",
- " for i in range(len(self.outline_verts) - 1):\n",
- " debug_draw.draw_segment(\n",
- " self.outline_verts[i],\n",
- " self.outline_verts[i + 1],\n",
- " color=(1, 1, 0),\n",
- " line_width=0.3,\n",
- " )\n",
- "\n",
- " left = list(self.kill_sensor_pos)\n",
- " left[0] -= self.gap_size / 2\n",
- " left[1] += self.kill_sensors_height / 2\n",
- "\n",
- " right = list(self.kill_sensor_pos)\n",
- " right[0] += self.gap_size / 2\n",
- " right[1] += self.kill_sensors_height / 2\n",
- " debug_draw.draw_segment(left, right, (1, 0, 0), line_width=0.4)\n",
- "\n",
- "\n",
- "class FindGoos(b2d.QueryCallback):\n",
- " def __init__(self):\n",
- " super(FindGoos, self).__init__()\n",
- " self.goos = []\n",
- "\n",
- " def report_fixture(self, fixture):\n",
- " body = fixture.body\n",
- " if body.user_data == \"goo\":\n",
- " self.goos.append(body)\n",
- " return True\n",
- "\n",
- "\n",
- "class Goo(TestbedBase):\n",
- "\n",
- " name = \"Goo\"\n",
- "\n",
- " def __init__(self, settings=None):\n",
- " super(Goo, self).__init__(settings=settings)\n",
- "\n",
- " self.goo_graph = networkx.Graph()\n",
- " self.level = Level(testbed=self)\n",
- "\n",
- " # mouse related\n",
- " self.last_mouse_pos = None\n",
- " self.is_mouse_down = False\n",
- " self.could_place_goo_when_mouse_was_down = False\n",
- "\n",
- " # callback to draw tentative placement\n",
- " self.draw_callback = None\n",
- "\n",
- " # goos marked for destruction\n",
- " self.goo_to_destroy = []\n",
- "\n",
- " # joints marked for destruction\n",
- " self.joints_to_destroy = []\n",
- " self.gamma = 0.003\n",
- " self.break_threshold = 0.5\n",
- "\n",
- " # time point when goo can be inserted\n",
- " self.insert_time_point = 0\n",
- " self.insert_delay = 1.0\n",
- "\n",
- " # handle finishing of level\n",
- " self.with_goal_contact = dict()\n",
- "\n",
- " # amount of seconds one has to be in the finishing zone\n",
- " self.win_delay = 3.0\n",
- "\n",
- " # particle system will be defined an used on win!\n",
- " # this is then used for some kind of fireworks\n",
- " self.psystem = None\n",
- " self.emitter = None\n",
- " self.emitter_stop_time = None\n",
- " self.emitter_start_time = None\n",
- "\n",
- " # trigger some fireworks on win\n",
- " def on_win(self, win_body):\n",
- "\n",
- " if self.psystem is None:\n",
- " # particle system\n",
- " pdef = b2d.particle_system_def(\n",
- " viscous_strength=0.9,\n",
- " spring_strength=0.0,\n",
- " damping_strength=100.5,\n",
- " pressure_strength=1.0,\n",
- " color_mixing_strength=0.05,\n",
- " density=0.1,\n",
- " )\n",
- "\n",
- " self.psystem = self.world.create_particle_system(pdef)\n",
- " self.psystem.radius = 0.1\n",
- " self.psystem.damping = 0.5\n",
- "\n",
- " emitter_def = b2d.RandomizedRadialEmitterDef()\n",
- " emitter_def.emite_rate = 2000\n",
- " emitter_def.lifetime = 0.9\n",
- " emitter_def.enabled = True\n",
- " emitter_def.inner_radius = 0.0\n",
- " emitter_def.outer_radius = 0.1\n",
- " emitter_def.velocity_magnitude = 1000.0\n",
- " emitter_def.start_angle = 0\n",
- " emitter_def.stop_angle = 2 * math.pi\n",
- " emitter_def.transform = b2d.Transform(\n",
- " win_body.position + b2d.vec2(0, 20), b2d.Rot(0)\n",
- " )\n",
- " self.emitter = b2d.RandomizedRadialEmitter(self.psystem, emitter_def)\n",
- " self.emitter_stop_time = self.elapsed_time + 0.2\n",
- "\n",
- " def draw_goo(self, pos, angle, body=None):\n",
- " self.debug_draw.draw_solid_circle(pos, 1, axis=None, color=(1, 0, 1))\n",
- " self.debug_draw.draw_circle(pos, 1.1, (1, 1, 1), line_width=0.1)\n",
- "\n",
- " if body is not None:\n",
- " centers = [\n",
- " body.get_world_point((-0.3, 0.2)),\n",
- " body.get_world_point((0.3, 0.2)),\n",
- " ]\n",
- " for center in centers:\n",
- " self.debug_draw.draw_solid_circle(\n",
- " center, 0.4, axis=None, color=(1, 1, 1)\n",
- " )\n",
- " self.debug_draw.draw_solid_circle(\n",
- " center, 0.2, axis=None, color=(0, 0, 0)\n",
- " )\n",
- "\n",
- " def draw_edge(self, pos_a, pos_b, stress):\n",
- " no_stress = numpy.array([1, 1, 1])\n",
- " has_stress = numpy.array([1, 0, 0])\n",
- " color = (1.0 - stress) * no_stress + stress * has_stress\n",
- " color = tuple([float(c) for c in color])\n",
- " self.debug_draw.draw_segment(pos_a, pos_b, color=color, line_width=0.4)\n",
- "\n",
- " def insert_goo(self, pos, static=False):\n",
- " if static:\n",
- " f = self.world.create_static_body\n",
- " else:\n",
- " f = self.world.create_dynamic_body\n",
- "\n",
- " goo = f(\n",
- " position=pos,\n",
- " fixtures=b2d.fixture_def(shape=b2d.circle_shape(radius=1), density=1),\n",
- " user_data=\"goo\",\n",
- " )\n",
- " self.goo_graph.add_node(goo)\n",
- " return goo\n",
- "\n",
- " def connect_goos(self, goo_a, goo_b):\n",
- " length = (goo_a.position - goo_b.position).length\n",
- " joint = self.world.create_distance_joint(\n",
- " goo_a,\n",
- " goo_b,\n",
- " stiffness=500,\n",
- " damping=0.1,\n",
- " length=length,\n",
- " user_data=dict(length=length, stress=0),\n",
- " )\n",
- " self.goo_graph.add_edge(goo_a, goo_b, joint=joint)\n",
- "\n",
- " def query_placement(self, pos):\n",
- "\n",
- " radius = 8\n",
- "\n",
- " # find all goos in around pos\n",
- " pos = b2d.vec2(pos)\n",
- " box = b2d.aabb(\n",
- " lower_bound=pos - b2d.vec2(radius, radius),\n",
- " upper_bound=pos + b2d.vec2(radius, radius),\n",
- " )\n",
- " query = FindGoos()\n",
- " self.world.query_aabb(query, box)\n",
- " goos = query.goos\n",
- " n_goos = len(goos)\n",
- "\n",
- " if n_goos >= 2:\n",
- "\n",
- " # try to insert to goo as edge between\n",
- " # 2 existing goos\n",
- " def distance(a, b, p):\n",
- " if self.goo_graph.has_edge(a[0], b[0]):\n",
- " return float(\"inf\")\n",
- " return numpy.linalg.norm((a[1] + b[1]) / 2 - p)\n",
- "\n",
- " i, j, best_dist = best_pairwise_distance(\n",
- " goos,\n",
- " f=lambda goo: (goo, numpy.array(goo.position)),\n",
- " distance=partial(distance, p=pos),\n",
- " )\n",
- "\n",
- " if best_dist < 0.8:\n",
- "\n",
- " def draw_callback():\n",
- " self.draw_edge(goos[i].position, goos[j].position, stress=0)\n",
- "\n",
- " def insert_callack():\n",
- " self.connect_goos(goos[i], goos[j])\n",
- "\n",
- " return True, draw_callback, insert_callack\n",
- "\n",
- " # try to insert the goo as brand new\n",
- " # goo and connect it with 2 existing goos\n",
- " f = lambda goo: (goo, (goo.position - b2d.vec2(pos)).length)\n",
- "\n",
- " def distance(a, b):\n",
- " if not self.goo_graph.has_edge(a[0], b[0]):\n",
- " return float(\"inf\")\n",
- " return a[1] + b[1]\n",
- "\n",
- " i, j, best_dist = best_pairwise_distance(goos, f=f, distance=distance)\n",
- " if best_dist < float(\"inf\"):\n",
- "\n",
- " def draw_callback():\n",
- "\n",
- " self.draw_edge(pos, goos[i].position, stress=0)\n",
- " self.draw_edge(pos, goos[j].position, stress=0)\n",
- " self.draw_goo(pos, angle=None)\n",
- "\n",
- " def insert_callack():\n",
- " goo = self.insert_goo(pos=pos)\n",
- " self.connect_goos(goo, goos[i])\n",
- " self.connect_goos(goo, goos[j])\n",
- "\n",
- " return True, draw_callback, insert_callack\n",
- "\n",
- " return False, None, None\n",
- "\n",
- " def on_mouse_down(self, pos):\n",
- " self.last_mouse_pos = pos\n",
- " self.is_mouse_down = True\n",
- " can_be_placed, draw_callback, insert_callback = self.query_placement(pos)\n",
- " self.could_place_goo_when_mouse_was_down = can_be_placed\n",
- " if can_be_placed:\n",
- " if self.elapsed_time < self.insert_time_point:\n",
- " return True\n",
- " self.draw_callback = draw_callback\n",
- " return True\n",
- " return False\n",
- "\n",
- " def on_mouse_move(self, pos):\n",
- " self.last_mouse_pos = pos\n",
- " if self.is_mouse_down:\n",
- " can_be_placed, draw_callback, insert_callback = self.query_placement(pos)\n",
- " if can_be_placed:\n",
- " if self.elapsed_time < self.insert_time_point:\n",
- " return True\n",
- " self.draw_callback = draw_callback\n",
- " return True\n",
- " else:\n",
- " self.draw_callback = None\n",
- " return self.could_place_goo_when_mouse_was_down\n",
- "\n",
- " def on_mouse_up(self, pos):\n",
- " self.last_mouse_pos = pos\n",
- " self.is_mouse_down = False\n",
- " self.draw_callback = None\n",
- " can_be_placed, draw_callback, insert_callback = self.query_placement(pos)\n",
- " if can_be_placed:\n",
- " if self.elapsed_time < self.insert_time_point:\n",
- " return True\n",
- " # self.draw_callback = draw_callback\n",
- " insert_callback()\n",
- " self.insert_time_point = self.elapsed_time + self.insert_delay\n",
- " return True\n",
- " return False\n",
- "\n",
- " def begin_contact(self, contact):\n",
- " body_a = contact.body_a\n",
- " body_b = contact.body_b\n",
- " if body_b.user_data == \"goo\":\n",
- " body_a, body_b = body_b, body_a\n",
- "\n",
- " user_data_a = body_a.user_data\n",
- " user_data_b = body_b.user_data\n",
- " if body_a.user_data == \"goo\":\n",
- " if user_data_b == \"destroyer\":\n",
- " self.goo_to_destroy.append(body_a)\n",
- " elif user_data_b == \"goal\":\n",
- " self.with_goal_contact[body_a] = self.elapsed_time + self.win_delay\n",
- "\n",
- " def end_contact(self, contact):\n",
- " body_a = contact.body_a\n",
- " body_b = contact.body_b\n",
- " if body_b.user_data == \"goo\":\n",
- " body_a, body_b = body_b, body_a\n",
- "\n",
- " user_data_a = body_a.user_data\n",
- " user_data_b = body_b.user_data\n",
- " if body_a.user_data == \"goo\":\n",
- " if user_data_b == \"goal\":\n",
- " if body_a in self.with_goal_contact:\n",
- " del self.with_goal_contact[body_a]\n",
- "\n",
- " def pre_step(self, dt):\n",
- "\n",
- " # query if goo can be inserted\n",
- " if (\n",
- " self.is_mouse_down\n",
- " and self.last_mouse_pos is not None\n",
- " and self.draw_callback is None\n",
- " ):\n",
- " can_be_placed, draw_callback, insert_callback = self.query_placement(\n",
- " self.last_mouse_pos\n",
- " )\n",
- " if can_be_placed and self.elapsed_time >= self.insert_time_point:\n",
- " self.draw_callback = draw_callback\n",
- "\n",
- " # compute joint stress\n",
- " for goo_a, goo_b, joint in self.goo_graph.edges(data=\"joint\"):\n",
- " jd = joint.user_data\n",
- "\n",
- " # distance based stress\n",
- " insert_length = jd[\"length\"]\n",
- " length = (goo_a.position - goo_b.position).length\n",
- "\n",
- " d = length - insert_length\n",
- " if d > 0:\n",
- "\n",
- " # reaction force based stress\n",
- " rf = joint.get_reaction_force(30).length\n",
- "\n",
- " normalized_rf = 1.0 - math.exp(-rf * self.gamma)\n",
- "\n",
- " jd[\"stress\"] = normalized_rf / self.break_threshold\n",
- " if normalized_rf > self.break_threshold:\n",
- " self.joints_to_destroy.append((goo_a, goo_b, joint))\n",
- "\n",
- " else:\n",
- " jd[\"stress\"] = 0\n",
- "\n",
- " for goo_a, goo_b, joint in self.joints_to_destroy:\n",
- " self.goo_graph.remove_edge(u=goo_a, v=goo_b)\n",
- " self.world.destroy_joint(joint)\n",
- " self.joints_to_destroy = []\n",
- "\n",
- " # destroy goos\n",
- " for goo in self.goo_to_destroy:\n",
- " self.goo_graph.remove_node(goo)\n",
- " self.world.destroy_body(goo)\n",
- "\n",
- " # destroy all with wrong degree\n",
- " while True:\n",
- " destroyed_any = False\n",
- " to_remove = []\n",
- " for goo in self.goo_graph.nodes:\n",
- " if self.goo_graph.degree(goo) < 2:\n",
- " destroyed_any = True\n",
- " to_remove.append(goo)\n",
- " if not destroyed_any:\n",
- " break\n",
- " for goo in to_remove:\n",
- " self.goo_graph.remove_node(goo)\n",
- " self.world.destroy_body(goo)\n",
- " self.goo_to_destroy = []\n",
- "\n",
- " # check if we are done\n",
- " for goo, finish_time in self.with_goal_contact.items():\n",
- " if finish_time <= self.elapsed_time:\n",
- " self.on_win(goo)\n",
- "\n",
- " if self.emitter is not None:\n",
- " if self.emitter_stop_time is not None:\n",
- " if self.elapsed_time > self.emitter_stop_time:\n",
- " self.emitter.enabled = False\n",
- " self.emitter_start_time = self.elapsed_time + 0.4\n",
- " self.emitter_stop_time = None\n",
- " p = list(self.emitter.position)\n",
- " p[0] += (random.random() - 0.5) * 10.0\n",
- " p[1] += (random.random() - 0.5) * 2.0\n",
- " self.emitter.position = p\n",
- " if self.emitter_start_time is not None:\n",
- " if self.elapsed_time > self.emitter_start_time:\n",
- " self.emitter.enabled = True\n",
- " self.emitter_start_time = None\n",
- " self.emitter_stop_time = self.elapsed_time + 0.2\n",
- " self.emitter.step(dt)\n",
- "\n",
- " def post_debug_draw(self):\n",
- "\n",
- " self.level.draw(self.debug_draw)\n",
- "\n",
- " # draw mouse when mouse is down\n",
- " if (\n",
- " self.is_mouse_down\n",
- " and self.last_mouse_pos is not None\n",
- " and self.draw_callback is None\n",
- " ):\n",
- " d = (self.insert_time_point - self.elapsed_time) / self.insert_delay\n",
- " if d > 0:\n",
- " d = d * math.pi * 2\n",
- " x = math.sin(d)\n",
- " y = math.cos(d)\n",
- " p = self.last_mouse_pos[0] + x, self.last_mouse_pos[1] + y\n",
- " self.debug_draw.draw_segment(\n",
- " p, self.last_mouse_pos, color=(1, 0, 0), line_width=0.2\n",
- " )\n",
- " self.debug_draw.draw_circle(\n",
- " self.last_mouse_pos, 1, (1, 0, 0), line_width=0.2\n",
- " )\n",
- "\n",
- " # draw the tentative placement\n",
- " if self.draw_callback is not None:\n",
- " self.draw_callback()\n",
- "\n",
- " for goo_a, goo_b, joint in self.goo_graph.edges(data=\"joint\"):\n",
- " self.draw_edge(\n",
- " goo_a.position, goo_b.position, stress=joint.user_data[\"stress\"]\n",
- " )\n",
- "\n",
- " for goo in self.goo_graph:\n",
- " self.draw_goo(goo.position, goo.angle, body=goo)\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Controlls\n",
- "* To play this game, click and drag next to the existing \"goos\"\n",
- "* try to bridge the tiny gap\n",
- "* Use the mouse-wheel to zoom in/out, a\n",
- "* Click and drag in the empty space to translate the view."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "\n",
- "s = JupyterAsyncGui.Settings()\n",
- "s.resolution = [1000,500]\n",
- "s.scale = 8\n",
- "tb = b2d.testbed.run(Goo, backend=JupyterAsyncGui, gui_settings=s);"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.10.4"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/content/pyodide/pyb2d/games/rocket.ipynb b/content/pyodide/pyb2d/games/rocket.ipynb
deleted file mode 100644
index 316e9ce..0000000
--- a/content/pyodide/pyb2d/games/rocket.ipynb
+++ /dev/null
@@ -1,282 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "f7b8452d-61fe-4356-8084-cac603096fef",
- "metadata": {},
- "outputs": [],
- "source": [
- "import sys\n",
- "if \"pyodide\" in sys.modules:\n",
- " import piplite\n",
- " await piplite.install('pyb2d-jupyterlite-backend>=0.4.2')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "0bfa61e4-9817-4bea-aa66-6a660a423ae6",
- "metadata": {},
- "outputs": [],
- "source": [
- "from b2d.testbed import TestbedBase\n",
- "import random\n",
- "import numpy\n",
- "import b2d\n",
- "import math\n",
- "\n",
- "class Rocket(TestbedBase):\n",
- "\n",
- " name = \"Rocket\"\n",
- "\n",
- " def __init__(self, settings=None):\n",
- " super(Rocket, self).__init__(gravity=(0, 0), settings=settings)\n",
- "\n",
- " # gravitational constant\n",
- " self.gravitational_constant = 6.0\n",
- "\n",
- " self.planets = {}\n",
- "\n",
- " # home planet\n",
- " home_planet = self.world.create_kinematic_body(\n",
- " position=(10, 0),\n",
- " fixtures=b2d.fixture_def(shape=b2d.circle_shape(radius=20)),\n",
- " user_data=\"home_planet\",\n",
- " )\n",
- "\n",
- " # target planet\n",
- " target_planet = self.world.create_kinematic_body(\n",
- " position=(100, 100),\n",
- " fixtures=b2d.fixture_def(shape=b2d.circle_shape(radius=10)),\n",
- " user_data=\"target_planet\",\n",
- " )\n",
- "\n",
- " # black hole\n",
- " black_hole = self.world.create_kinematic_body(\n",
- " position=(0, 400),\n",
- " fixtures=b2d.fixture_def(shape=b2d.circle_shape(radius=1)),\n",
- " user_data=\"black_hole\",\n",
- " )\n",
- "\n",
- " self.planets = {\n",
- " home_planet: dict(radius=20, density=1, color=(0, 0.2, 1)),\n",
- " target_planet: dict(radius=10, density=1, color=(0.7, 0.7, 0.7)),\n",
- " black_hole: dict(radius=1, density=10000, color=(0.1, 0.1, 0.1)),\n",
- " }\n",
- "\n",
- " # a tiny rocket\n",
- " self.rocket = self.world.create_dynamic_body(\n",
- " position=(10, 10),\n",
- " fixtures=[\n",
- " b2d.fixture_def(shape=b2d.polygon_shape(box=[1, 1]), density=1),\n",
- " b2d.fixture_def(\n",
- " shape=b2d.polygon_shape(vertices=[(-1, 1), (0, 4), (1, 1)]),\n",
- " density=1,\n",
- " ),\n",
- " ],\n",
- " angular_damping=0.5,\n",
- " linear_damping=0.2,\n",
- " user_data=\"rocket\",\n",
- " )\n",
- " # check if the rocket is gone\n",
- " self.touched_black_hole = False\n",
- "\n",
- " # particle system\n",
- " pdef = b2d.particle_system_def(\n",
- " viscous_strength=0.9,\n",
- " spring_strength=0.0,\n",
- " damping_strength=100.5,\n",
- " pressure_strength=1.0,\n",
- " color_mixing_strength=0.05,\n",
- " density=0.1,\n",
- " )\n",
- "\n",
- " psystem = self.world.create_particle_system(pdef)\n",
- " psystem.radius = 0.1\n",
- " psystem.damping = 0.5\n",
- "\n",
- " self.emitters = []\n",
- " self.key_map = {\"w\": 0, \"a\": 1, \"d\": 2}\n",
- "\n",
- " angle_width = (math.pi * 2) / 16\n",
- " emitter_def = b2d.RandomizedRadialEmitterDef()\n",
- " emitter_def.emite_rate = 2000\n",
- " emitter_def.lifetime = 1.0\n",
- " emitter_def.enabled = False\n",
- " emitter_def.inner_radius = 1\n",
- " emitter_def.outer_radius = 1\n",
- " emitter_def.velocity_magnitude = 10.0\n",
- " emitter_def.start_angle = math.pi / 2 - angle_width / 2.0\n",
- " emitter_def.stop_angle = math.pi / 2 + angle_width / 2.0\n",
- " emitter_def.body = self.rocket\n",
- "\n",
- " delta = 0.2\n",
- " self.emitter_local_anchors = [\n",
- " (0, -delta), # main\n",
- " (-delta, -0.5), # left,\n",
- " (delta, -0.5), # right\n",
- " ]\n",
- " self.emitter_local_rot = [math.pi, math.pi / 2, -math.pi / 2] # main\n",
- "\n",
- " # main trust\n",
- " emitter_def.emite_rate = 2000\n",
- " world_anchor = self.rocket.get_world_point(self.emitter_local_anchors[0])\n",
- " emitter_def.transform = b2d.Transform(\n",
- " world_anchor, b2d.Rot(self.emitter_local_rot[0])\n",
- " )\n",
- " emitter = b2d.RandomizedRadialEmitter(psystem, emitter_def)\n",
- " self.emitters.append(emitter)\n",
- "\n",
- " # left\n",
- " emitter_def.emite_rate = 200\n",
- " world_anchor = self.rocket.get_world_point(self.emitter_local_anchors[1])\n",
- " emitter_def.transform = b2d.Transform(\n",
- " world_anchor, b2d.Rot(self.emitter_local_rot[1])\n",
- " )\n",
- " emitter = b2d.RandomizedRadialEmitter(psystem, emitter_def)\n",
- " self.emitters.append(emitter)\n",
- "\n",
- " # right\n",
- " emitter_def.emite_rate = 200\n",
- " world_anchor = self.rocket.get_world_point(self.emitter_local_anchors[1])\n",
- " emitter_def.transform = b2d.Transform(\n",
- " world_anchor, b2d.Rot(self.emitter_local_rot[1])\n",
- " )\n",
- " emitter = b2d.RandomizedRadialEmitter(psystem, emitter_def)\n",
- " self.emitters.append(emitter)\n",
- "\n",
- " def pre_step(self, dt):\n",
- "\n",
- " # check if the rocket has died\n",
- " if self.touched_black_hole:\n",
- " if self.rocket is not None:\n",
- " self.world.destroy_body(self.rocket)\n",
- " self.rocket = None\n",
- " else:\n",
- " rocket_center = self.rocket.world_center\n",
- " rocket_mass = self.rocket.mass\n",
- " # compute gravitational forces\n",
- " net_force = numpy.zeros([2])\n",
- " for planet, planet_def in self.planets.items():\n",
- " radius = planet_def[\"radius\"]\n",
- " planet_center = planet.position\n",
- " planet_mass = planet_def[\"density\"] * radius ** 2 * math.pi\n",
- " delta = rocket_center - planet_center\n",
- " distance = delta.normalize()\n",
- " f = (\n",
- " -self.gravitational_constant\n",
- " * rocket_mass\n",
- " * planet_mass\n",
- " / (distance * distance)\n",
- " )\n",
- " net_force += delta * f\n",
- " f = float(net_force[0]), float(net_force[1])\n",
- " self.rocket.apply_force_to_center(f)\n",
- "\n",
- " # run the rockets engines\n",
- " for emitter, local_anchor, local_rotation in zip(\n",
- " self.emitters, self.emitter_local_anchors, self.emitter_local_rot\n",
- " ):\n",
- " world_anchor = self.rocket.get_world_point(local_anchor)\n",
- " emitter.position = world_anchor\n",
- " emitter.angle = self.rocket.angle + local_rotation\n",
- " emitter.step(dt)\n",
- "\n",
- " def begin_contact(self, contact):\n",
- " body_a = contact.body_a\n",
- " body_b = contact.body_b\n",
- " if body_b.user_data == \"rocket\":\n",
- " body_a, body_b = body_b, body_a\n",
- "\n",
- " user_data_a = body_a.user_data\n",
- " user_data_b = body_b.user_data\n",
- " if body_a.user_data == \"rocket\":\n",
- " if user_data_b == \"black_hole\":\n",
- " self.touched_black_hole = True\n",
- "\n",
- " def on_keyboard_down(self, key):\n",
- " if key in self.key_map:\n",
- " self.emitters[self.key_map[key]].enabled = True\n",
- " return True\n",
- " return False\n",
- "\n",
- " def on_keyboard_up(self, key):\n",
- " if key in self.key_map:\n",
- " self.emitters[self.key_map[key]].enabled = False\n",
- " return False\n",
- " return False\n",
- "\n",
- " def pre_debug_draw(self):\n",
- " pass\n",
- "\n",
- " def post_debug_draw(self):\n",
- " for planet, planet_def in self.planets.items():\n",
- " pos = planet.position\n",
- " self.debug_draw.draw_solid_circle(\n",
- " pos, planet_def[\"radius\"] + 0.1, axis=None, color=planet_def[\"color\"]\n",
- " )\n",
- " if planet.user_data == \"black_hole\":\n",
- " self.debug_draw.draw_circle(\n",
- " pos, planet_def[\"radius\"] * 5, color=(1, 1, 1), line_width=0.1\n",
- " )"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "357866b3-876e-421f-8d2a-77d6697551d3",
- "metadata": {},
- "source": [
- "# Controlls\n",
- "* To play this game, use 'w','a','s','d' on your keyboard to steer the rocket\n",
- "* try to land on the other planet\n",
- "* avoid the black hole\n",
- "* Use the mouse-wheel to zoom in/out, a\n",
- "* Click and drag in the empty space to translate the view."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "7bab75b7-cec1-4348-b95d-9ffd282ded5c",
- "metadata": {},
- "outputs": [],
- "source": [
- "from pyb2d_jupyterlite_backend.async_jupyter_gui import JupyterAsyncGui\n",
- "s = JupyterAsyncGui.Settings()\n",
- "s.resolution = [1000,1000]\n",
- "s.scale = 3\n",
- "tb = b2d.testbed.run(Rocket, backend=JupyterAsyncGui, gui_settings=s);"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "674c57c8-b5b1-45a9-b75e-5ddc487f7d9b",
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.10.4"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/content/pyodide/pyb2d/gauss_machine.ipynb b/content/pyodide/pyb2d/gauss_machine.ipynb
deleted file mode 100644
index b7bb72c..0000000
--- a/content/pyodide/pyb2d/gauss_machine.ipynb
+++ /dev/null
@@ -1,128 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "39a80aae-a990-4ed8-b880-3db2e7f70f16",
- "metadata": {},
- "outputs": [],
- "source": [
- "import sys\n",
- "if \"pyodide\" in sys.modules:\n",
- " import piplite\n",
- " await piplite.install('pyb2d-jupyterlite-backend>=0.4.2')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "9d9f8d68-0f3b-49c1-9512-e3e8344e7342",
- "metadata": {},
- "outputs": [],
- "source": [
- "from b2d.testbed import TestbedBase\n",
- "import random\n",
- "import numpy\n",
- "import b2d\n",
- "\n",
- "\n",
- "class GaussMachine(TestbedBase):\n",
- "\n",
- " name = \"Gauss Machine\"\n",
- "\n",
- " def __init__(self, settings=None):\n",
- " super(GaussMachine, self).__init__(settings=settings)\n",
- "\n",
- " self.box_shape = 30, 20\n",
- " box_shape = self.box_shape\n",
- "\n",
- " # outer box\n",
- " verts = numpy.array(\n",
- " [(0, box_shape[1]), (0, 0), (box_shape[0], 0), (box_shape[0], box_shape[1])]\n",
- " )\n",
- " shape = b2d.chain_shape(vertices=numpy.flip(verts, axis=0))\n",
- " box = self.world.create_static_body(position=(0, 0), shape=shape)\n",
- "\n",
- " # \"bins\"\n",
- " bin_height = box_shape[1] / 3\n",
- " bin_width = 1\n",
- " for x in range(0, box_shape[0], bin_width):\n",
- " box = self.world.create_static_body(\n",
- " position=(0, 0), shape=b2d.two_sided_edge_shape((x, 0), (x, bin_height))\n",
- " )\n",
- "\n",
- " # reflectors\n",
- " ref_start_y = int(bin_height + box_shape[1] / 10.0)\n",
- " ref_stop_y = int(box_shape[1] * 0.9)\n",
- " for x in range(0, box_shape[0] + 1):\n",
- "\n",
- " for y in range(ref_start_y, ref_stop_y):\n",
- " s = [0.5, 0][y % 2 == 0]\n",
- " shape = b2d.circle_shape(radius=0.3)\n",
- " box = self.world.create_static_body(position=(x + s, y), shape=shape)\n",
- "\n",
- " # particle system\n",
- " pdef = b2d.particle_system_def(\n",
- " viscous_strength=0.9,\n",
- " spring_strength=0.0,\n",
- " damping_strength=100.5,\n",
- " pressure_strength=1.0,\n",
- " color_mixing_strength=0.05,\n",
- " density=2,\n",
- " )\n",
- "\n",
- " psystem = self.world.create_particle_system(pdef)\n",
- " psystem.radius = 0.1\n",
- " psystem.damping = 0.5\n",
- "\n",
- " # linear emitter\n",
- " emitter_pos = (self.box_shape[0] / 2, self.box_shape[1] + 10)\n",
- " emitter_def = b2d.RandomizedLinearEmitterDef()\n",
- " emitter_def.emite_rate = 400\n",
- " emitter_def.lifetime = 25\n",
- " emitter_def.size = (10, 1)\n",
- " emitter_def.transform = b2d.Transform(emitter_pos, b2d.Rot(0))\n",
- "\n",
- " self.emitter = b2d.RandomizedLinearEmitter(psystem, emitter_def)\n",
- "\n",
- " def pre_step(self, dt):\n",
- " self.emitter.step(dt)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "51e5df1b-f5e6-486a-a6c0-173597198e5d",
- "metadata": {},
- "outputs": [],
- "source": [
- "from pyb2d_jupyterlite_backend.async_jupyter_gui import JupyterAsyncGui\n",
- "s = JupyterAsyncGui.Settings()\n",
- "s.resolution = [350,400]\n",
- "s.scale = 11\n",
- "tb = b2d.testbed.run(GaussMachine, backend=JupyterAsyncGui, gui_settings=s);"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.10.4"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/content/pyodide/pyb2d/newtons_cradle.ipynb b/content/pyodide/pyb2d/newtons_cradle.ipynb
deleted file mode 100644
index 9ef3ff3..0000000
--- a/content/pyodide/pyb2d/newtons_cradle.ipynb
+++ /dev/null
@@ -1,130 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "82ae535a-a041-40f0-8c40-e689b894b0bc",
- "metadata": {},
- "outputs": [],
- "source": [
- "import sys\n",
- "if \"pyodide\" in sys.modules:\n",
- " import piplite\n",
- " await piplite.install('pyb2d-jupyterlite-backend>=0.4.0')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "7381ff66-7924-4216-b141-8f7b15ed038a",
- "metadata": {},
- "outputs": [],
- "source": [
- "from b2d.testbed import TestbedBase\n",
- "import b2d\n",
- "\n",
- "\n",
- "class NewtonsCradle(TestbedBase):\n",
- "\n",
- " name = \"newton's cradle\"\n",
- "\n",
- " def __init__(self, settings=None):\n",
- " super(NewtonsCradle, self).__init__(settings=settings)\n",
- "\n",
- " # radius of the circles\n",
- " r = 1.0\n",
- " # length of the rope\n",
- " l = 10.0\n",
- " # how many balls\n",
- " n = 10\n",
- "\n",
- " offset = (l + r, 2 * r)\n",
- " dynamic_circles = []\n",
- " static_bodies = []\n",
- " for i in range(n):\n",
- " if i + 1 == n:\n",
- " position = (offset[0] + i * 2 * r + l, offset[1] + l)\n",
- " else:\n",
- " position = (offset[0] + i * 2 * r, offset[1])\n",
- "\n",
- " circle = self.world.create_dynamic_body(\n",
- " position=position,\n",
- " fixtures=b2d.fixture_def(\n",
- " shape=b2d.circle_shape(radius=r * 0.90),\n",
- " density=1.0,\n",
- " restitution=1.0,\n",
- " friction=0.0,\n",
- " ),\n",
- " linear_damping=0.01,\n",
- " angular_damping=1.0,\n",
- " fixed_rotation=True,\n",
- " )\n",
- " dynamic_circles.append(circle)\n",
- "\n",
- " static_body = self.world.create_static_body(\n",
- " position=(offset[0] + i * 2 * r, offset[1] + l)\n",
- " )\n",
- "\n",
- " self.world.create_distance_joint(\n",
- " static_body,\n",
- " circle,\n",
- " local_anchor_a=(0, 0),\n",
- " local_anchor_b=(0, 0),\n",
- " max_length=l,\n",
- " stiffness=0,\n",
- " )\n",
- "\n",
- " static_bodies.append(static_body)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "6f694d32-8b23-40cf-91c3-0edd6abb658d",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "application/vnd.jupyter.widget-view+json": {
- "model_id": "b24ad88f6cfd4db19fc3145000e79635",
- "version_major": 2,
- "version_minor": 0
- },
- "text/plain": [
- "Output()"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "from pyb2d_jupyterlite_backend.async_jupyter_gui import JupyterAsyncGui\n",
- "s = JupyterAsyncGui.Settings()\n",
- "s.resolution = [1000,300]\n",
- "b2d.testbed.run(NewtonsCradle, backend=JupyterAsyncGui, gui_settings=s);"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.10.4"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/content/pyodide/renderers.ipynb b/content/pyodide/renderers.ipynb
deleted file mode 100644
index 286490f..0000000
--- a/content/pyodide/renderers.ipynb
+++ /dev/null
@@ -1 +0,0 @@
-{"metadata":{"language_info":{"codemirror_mode":{"name":"python","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.8"},"kernelspec":{"name":"python","display_name":"Pyolite","language":"python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# JupyterLab Renderers","metadata":{}},{"cell_type":"markdown","source":"## FASTA","metadata":{}},{"cell_type":"code","source":"def Fasta(data=''):\n bundle = {}\n bundle['application/vnd.fasta.fasta'] = data\n bundle['text/plain'] = data\n display(bundle, raw=True)\n\nFasta(\"\"\">SEQUENCE_1\nMTEITAAMVKELRESTGAGMMDCKNALSETNGDFDKAVQLLREKGLGKAAKKADRLAAEG\nLVSVKVSDDFTIAAMRPSYLSYEDLDMTFVENEYKALVAELEKENEERRRLKDPNKPEHK\nIPQFASRKQLSDAILKEAEEKIKEELKAQGKPEKIWDNIIPGKMNSFIADNSQLDSKLTL\nMGQFYVMDDKKTVEQVIAEKEKEFGGKIKIVEFICFEVGEGLEKKTEDFAAEVAAQL\n>SEQUENCE_2\nSATVSEINSETDFVAKNDQFIALTKDTTAHIQSNSLQSVEELHSSTINGVKFEEYLKSQI\nATIGENLVVRRFATLKAGANGVVNGYIHTNGRVGVVIAAACDSAEVASKSRDLLRQICMH\"\"\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## GeoJSON","metadata":{}},{"cell_type":"code","source":"def GeoJSON(data):\n bundle = {}\n bundle['application/geo+json'] = data\n bundle['text/plain'] = data\n display(bundle, raw=True)\n \nGeoJSON({\n \"type\": \"Feature\",\n \"geometry\": {\n \"type\": \"Point\",\n \"coordinates\": [125.6, 10.1]\n },\n \"properties\": {\n \"name\": \"Dinagat Islands\"\n }\n})","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}
\ No newline at end of file
diff --git a/content/python.ipynb b/content/python.ipynb
deleted file mode 100644
index 689e883..0000000
--- a/content/python.ipynb
+++ /dev/null
@@ -1,721 +0,0 @@
-{
- "cells": [
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {
- "tags": []
- },
- "source": [
- "# A Python kernel backed by Pyodide\n",
- "\n",
- "![](https://raw.githubusercontent.com/pyodide/pyodide/master/docs/_static/img/pyodide-logo.png)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "import pyodide_kernel\n",
- "pyodide_kernel.__version__"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Simple code execution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "a = 3"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "a"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "b = 89\n",
- "\n",
- "def sq(x):\n",
- " return x * x\n",
- "\n",
- "sq(b)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "print"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "tags": []
- },
- "source": [
- "# Redirected streams"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "import sys\n",
- "\n",
- "print(\"Error !!\", file=sys.stderr)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Error handling"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "scrolled": true,
- "trusted": true
- },
- "outputs": [],
- "source": [
- "\"Hello\"\n",
- "\n",
- "def dummy_function():\n",
- " import missing_module"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "dummy_function()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Code completion"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### press `tab` to see what is available in `sys` module"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "from sys import "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Code inspection"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### using the question mark"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "?print"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### by pressing `shift+tab`"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "print("
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Input support"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "name = await input('Enter your name: ')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "'Hello, ' + name"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Rich representation"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "from IPython.display import display, Markdown, HTML, JSON, Latex"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "tags": []
- },
- "source": [
- "## HTML"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "print('Before display')\n",
- "\n",
- "s = 'HTML Title
'\n",
- "display(HTML(s))\n",
- "\n",
- "print('After display')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Markdown"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "Markdown('''\n",
- "# Title\n",
- "\n",
- "**in bold**\n",
- "\n",
- "~~Strikthrough~~\n",
- "''')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Pandas DataFrame"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "import pandas as pd\n",
- "import numpy as np\n",
- "from string import ascii_uppercase as letters\n",
- "from IPython.display import display\n",
- "\n",
- "df = pd.DataFrame(np.random.randint(0, 100, size=(100, len(letters))), columns=list(letters))\n",
- "df"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Show the same DataFrame "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "df"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## IPython.display module"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "from IPython.display import clear_output, display, update_display\n",
- "from asyncio import sleep"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Update display"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "class Square:\n",
- " color = 'PeachPuff'\n",
- " def _repr_html_(self):\n",
- " return '''\n",
- " \n",
- "
''' % self.color\n",
- "square = Square()\n",
- "\n",
- "display(square, display_id='some-square')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "square.color = 'OliveDrab'\n",
- "update_display(square, display_id='some-square')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Clear output"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "print(\"hello\")\n",
- "await sleep(3)\n",
- "clear_output() # will flicker when replacing \"hello\" with \"goodbye\"\n",
- "print(\"goodbye\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "print(\"hello\")\n",
- "await sleep(3)\n",
- "clear_output(wait=True) # prevents flickering\n",
- "print(\"goodbye\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Display classes"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "from IPython.display import HTML\n",
- "HTML('''\n",
- " \n",
- "
''')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "from IPython.display import Math\n",
- "Math(r'F(k) = \\int_{-\\infty}^{\\infty} f(x) e^{2\\pi i k} dx')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "from IPython.display import Latex\n",
- "Latex(r\"\"\"\\begin{eqnarray}\n",
- "\\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\\\\n",
- "\\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\\n",
- "\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\\n",
- "\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \n",
- "\\end{eqnarray}\"\"\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "from IPython.display import ProgressBar\n",
- "\n",
- "for i in ProgressBar(10):\n",
- " await sleep(0.1)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "from IPython.display import JSON\n",
- "JSON(['foo', {'bar': ('baz', None, 1.0, 2)}], metadata={}, expanded=True, root='test')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "from IPython.display import GeoJSON\n",
- "GeoJSON(\n",
- " data={\n",
- " \"type\": \"Feature\",\n",
- " \"geometry\": {\n",
- " \"type\": \"Point\",\n",
- " \"coordinates\": [11.8, -45.04]\n",
- " }\n",
- " }, url_template=\"http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png\",\n",
- " layer_options={\n",
- " \"basemap_id\": \"celestia_mars-shaded-16k_global\",\n",
- " \"attribution\" : \"Celestia/praesepe\",\n",
- " \"tms\": True,\n",
- " \"minZoom\" : 0,\n",
- " \"maxZoom\" : 5\n",
- " }\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Network requests and JSON"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "import json\n",
- "from js import fetch"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "res = await fetch('https://httpbin.org/get')\n",
- "text = await res.text()\n",
- "obj = json.loads(text) \n",
- "JSON(obj)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Sympy"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "from sympy import Integral, sqrt, symbols, init_printing\n",
- "\n",
- "init_printing()\n",
- "\n",
- "x = symbols('x')\n",
- "\n",
- "Integral(sqrt(1 / x), x)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Magics"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "import os\n",
- "os.listdir()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "%cd /home"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "%pwd"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "current_path = %pwd\n",
- "print(current_path)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "%%writefile test.txt\n",
- "\n",
- "This will create a new file. \n",
- "With the text that you see here."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "%history"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "import time"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "trusted": true
- },
- "outputs": [],
- "source": [
- "%%timeit \n",
- "\n",
- "time.sleep(0.1)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python (Pyodide)",
- "language": "python",
- "name": "python"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "python",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8"
- },
- "orig_nbformat": 4
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}