summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ShowRunner.py17
-rwxr-xr-xbridge.py8
-rw-r--r--fish_show.py91
-rw-r--r--show.py18
-rw-r--r--sunrise.py138
-rw-r--r--webapp/app/__init__.py5
-rw-r--r--webapp/app/static/carousel.css144
-rw-r--r--webapp/app/templates/index.html206
-rw-r--r--webapp/app/views.py29
-rwxr-xr-xwebapp/run.py10
10 files changed, 536 insertions, 130 deletions
diff --git a/ShowRunner.py b/ShowRunner.py
new file mode 100644
index 0000000..05877ed
--- /dev/null
+++ b/ShowRunner.py
@@ -0,0 +1,17 @@
+from bridge import Bridge
+from fish_show import FishShow
+from sunrise import SunriseShow
+from constants import *
+
+import sys
+sys.path.append('webapp')
+from run import server_thread, send_queue, recv_queue
+
+def run_show():
+ server_thread.start()
+ bridge = Bridge()
+ SunriseShow(bridge, send_queue=recv_queue, recv_queue=send_queue).run(framerate=FRAME_RATE)
+ FishShow(bridge, send_queue=recv_queue, recv_queue=send_queue).run(framerate=FRAME_RATE)
+ print "done!"
+
+run_show() \ No newline at end of file
diff --git a/bridge.py b/bridge.py
index 26beb24..c5f832d 100755
--- a/bridge.py
+++ b/bridge.py
@@ -35,11 +35,13 @@ class Bridge(object):
for x in xrange(1, self.WIDTH+1)]
-
def main():
+ """Just for testing. Use ShowRunner when running the show for real."""
+ import Queue
bridge = Bridge()
- show = FishShow(bridge)
+ show = FishShow(bridge, send_queue=Queue.Queue(), recv_queue=Queue.Queue())
#show = SunriseShow(bridge)
show.run(framerate=FRAME_RATE)
-main()
+if __name__ == '__main__':
+ main()
diff --git a/fish_show.py b/fish_show.py
index 7cd8300..6677ddd 100644
--- a/fish_show.py
+++ b/fish_show.py
@@ -7,6 +7,8 @@ from constants import *
import math
from show import Show
+
+
class Fish(object):
"""Object displayed on the bridge, probably a fish."""
def __init__(self, x, y, width, height, color, step):
@@ -31,6 +33,9 @@ class Fish(object):
def update(self):
"""Swim gently to the other side of the bridge."""
+ if self.x > constants.BRIDGE_WIDTH or self.x < -self.width:
+ return False
+
self.speed += self.drag * self.speed
if abs(self.speed) < self.tailThreshold:
self.tailMoving = True
@@ -44,15 +49,8 @@ class Fish(object):
self.tailMoveCount += 1
self.x += self.speed.components[0]
self.y += self.speed.components[1]
- #
- # THIS NEEDS TO CHANGE
- #
- if self.x > constants.BRIDGE_WIDTH:
- self.x = -self.width
- if self.x < -self.width:
- self.x = constants.BRIDGE_WIDTH
- print(self.x, self.y)
+ return True
def fix_direction(self):
min_theta = -0.32
@@ -65,7 +63,29 @@ class Fish(object):
#theta = rand_in_range(min_theta, max_theta)
theta = random.gauss((max_theta+min_theta)/2, (max_theta - min_theta)/12)
self.tailAcceleration = Vector(self.tailAccelMag, 0).rot2d(theta)
-
+
+ @classmethod
+ def by_type(cls, fish_type):
+ if (fish_type == 'jellyfish'):
+ return cls(0, 0.5, 23, 1.5, color.black, 1)
+ if fish_type == 'dolphin':
+ return cls(0, 0.5, 23, 1.5, color.black, 1)
+ if fish_type == 'boat':
+ return cls(0, 0.5, 23, 1.5, color.black, 1)
+ if fish_type == 'dory':
+ return cls(0, 0.5, 23, 1.5, color.black, 1)
+ if fish_type == 'nemo':
+ return cls(0, 0.5, 23, 1.5, color.black, 1)
+ if fish_type == 'whale':
+ return cls(0, 0.5, 23, 1.5, color.black, 1)
+ if fish_type == 'stingray':
+ return cls(0, 0.5, 23, 1.5, color.black, 1)
+ if fish_type == 'eel':
+ return cls(0, 0.5, 23, 1.5, color.black, 1)
+ if fish_type == 'shark':
+ return cls(0, 0.5, 23, 1.5, color.black, 1)
+
+
class Light(object):
"""Represent a bridge light and its state."""
@@ -78,23 +98,21 @@ class Light(object):
self.x = x
self.y = y
- def update(self, fish):
+ def update(self, depth, fishes):
"""Change color depending on where the fish is."""
if abs(self.transitioning_to - self.color) <= self.step:
- self.transitioning_to = pick_HSV().to_RGB()
+ self.transitioning_to = pick_HSV(depth).to_RGB()
self.step = (random_change()
* abs(self.color - self.transitioning_to))
new_background = transition(
self.color, self.transitioning_to, self.step
)
- coverage = compute_coverage(fish, self.x, self.y)
- #coverage *= coverage
- new_color = coverage * fish.color + (1 - coverage) * new_background
- #new_color = new_background
+
self.color = new_background
- if self.x == 30 and self.y == 0:
- pass
- #print(self.color.r(), self.color.g(), self.color.b())
+ new_color = new_background
+ for fish in fishes:
+ coverage = compute_coverage(fish, self.x, self.y)
+ new_color = coverage * fish.color + (1 - coverage) * new_color
self.light.setRGBRaw(
*map(lambda x: clamp(0, x, 1), new_color.components)
)
@@ -113,10 +131,12 @@ def compute_coverage(fish, x, y):
return coverage * scale
-def pick_HSV():
+def pick_HSV(depth):
"""Return a random color in an appropriate interval."""
- hue = random.random() * (constants.MAX_HUE - constants.MIN_HUE) + constants.MIN_HUE
- saturation = random.random() * (MAX_SATURATION - MIN_SATURATION) + MIN_SATURATION
+ min_hue = constants.MIN_HUE + (10/360)*depth
+ min_saturation = constants.MIN_SATURATION + 0.05*depth
+ hue = random.random() * (constants.MAX_HUE - min_hue) + min_hue
+ saturation = random.random() * (MAX_SATURATION - min_saturation) + min_saturation
value = random.random() * (MAX_VALUE - MIN_VALUE) + MIN_VALUE
return color.HSV(hue, saturation, value)
@@ -143,17 +163,38 @@ def clamp(low, x, high):
return max(low, min(x, high))
class FishShow(Show):
+ # 1 minute show
+ TICKS_PER_DEPTH = FRAME_RATE * 10
+ MAX_DEPTH = 6
+
def init(self):
self.lights = [Light(x, y, self.bridge.get_light(x, y))
for y in xrange(self.bridge.HEIGHT)
for x in xrange(self.bridge.WIDTH)]
- self.fish = Fish(0, 0.5, 23, 1.5, color.black, 1)
-
+ self.fishes = set()
+ self.tick_count = 0
+ self.depth = 0
def update(self):
- self.fish.update()
+ event = self.recv_event()
+ if event and event[0] == 'spawn':
+ new_fish = Fish.by_type(event[1]['type'])
+ self.fishes.add(new_fish)
+
+ for fish in list(self.fishes):
+ if not fish.update():
+ self.fishes.remove(fish)
for light in self.lights:
- light.update(self.fish)
+ light.update(self.depth, self.fishes)
+
+ self.tick_count += 1
+ if self.tick_count % self.TICKS_PER_DEPTH == 0:
+ self.depth += 1
+ print("DEPTH: ", self.depth)
+
+ if self.depth > self.MAX_DEPTH:
+ self.stop()
+
def rand_in_range(low, high):
return random.random() * (high - low) + low
diff --git a/show.py b/show.py
index f8544c9..5749863 100644
--- a/show.py
+++ b/show.py
@@ -1,5 +1,6 @@
from __future__ import division, print_function
import time
+import Queue
class Clock(object):
def __init__(self):
@@ -18,10 +19,12 @@ class Clock(object):
class Show(object):
- def __init__(self, bridge):
+ def __init__(self, bridge, send_queue, recv_queue):
self.bridge = bridge
self.clock = Clock()
self.running = True
+ self.send_queue = send_queue
+ self.recv_queue = recv_queue
self.init()
def run(self, framerate=40):
@@ -30,6 +33,19 @@ class Show(object):
self.bridge.update()
self.clock.tick(framerate)
+ def stop(self):
+ self.running = False
+
+ def send_event(self, event_name, data=None):
+ self.send_queue.put((event_name, data))
+
+ def recv_event(self):
+ try:
+ msg = self.recv_queue.get_nowait()
+ return msg
+ except Queue.Empty:
+ return None
+
def init(self):
"""Override me"""
raise NotImplementedError
diff --git a/sunrise.py b/sunrise.py
index 7e11ee3..4956342 100644
--- a/sunrise.py
+++ b/sunrise.py
@@ -9,38 +9,46 @@ from show import Show
class Sun(object):
"""Object displayed on the bridge, probably a fish."""
- def __init__(self, x, y, width, height, c, step):
+ def __init__(self):
"""Create fish with default parameters."""
- self.x = x
- self.y = y
- self.x = 75
- self.y = -2
- self.width = width
- self.width = 75
- self.height = height
+ self.x = 25
+ self.y = 0 - 1
+ self.width = 150
self.height = 2
- self.color = c
- self.color = color.RGB(1, 0.5, 0)
- self.speed = step
- self.step = step
- self.wait = 0
+ self.base_color = color.HSV(27/360, 1, 0.5)
+ self.end_color = color.HSV(53/360, 1, 1)
+ self.color = self.base_color.to_RGB()
def update(self):
"""Swim gently to the other side of the bridge."""
- #self.x += self.step
- #if self.x > 200:
- # self.x = 0
- #self.y = 0.25 + 0.25 * math.sin(math.pi * self.x / 10)
- #theta = random.random() * math.pi / 2 - math.pi / 4
- #self.y += 0.01 * math.sin(theta)
- #print(self.y)
self.y += 0.005
- if self.y >= 1:
- self.y = 1
- self.wait += 1
- if self.wait >= 200:
- self.wait = 0
- self.y = -2
+ if self.y + self.height >= 3:
+ self.y = 3 - self.height
+ elif self.y + self.height > 1:
+ self.width += 0.2
+ self.x -= 0.1
+ color_range = (self.end_color - self.base_color).unit()
+ self.color = (self.y + self.height - 1) / 3 * color_range + self.base_color
+ self.color = self.color.to_RGB()
+
+ #print(self.y)
+
+ #def color_at(self, x, y):
+ #ball_width = 15
+ #if abs(x - self.center_x) < ball_width:
+ #return color.RGB(1, 1, 1)
+ #distance = abs(self.center_x - x)
+ #start_hue = 58 / 360
+ #end_hue = 41 / 360
+ #start_sat = 0
+ #end_sat = 0.92
+ #sat = scale_with_clamp(distance, ball_width, 60, start_sat, end_sat)
+ #hue = scale_with_clamp(distance, ball_width, 100, start_hue, end_hue)
+ #return color.HSV(hue, sat, 1).to_RGB()
+
+
+def scale_with_clamp(inp, min_inp, max_inp, min_out, max_out):
+ return (clamp(min_inp, inp, max_inp) - min_inp) / (max_inp - min_inp) * (max_out - min_out) + min_out
class Light(object):
@@ -53,18 +61,36 @@ class Light(object):
self.step = 0
self.x = x
self.y = y
+ self.fade_step = 0
- def update(self, fish):
+ def update(self, sky_brightness, sun):
"""Change color depending on where the fish is."""
- if self.y == 1:
- new_color = color.RGB(0.5, 0.5, 0.5)
+ if self.y == 0:
+ if abs(self.transitioning_to - self.color) <= self.step:
+ self.transitioning_to = pick_ocean_HSV(sky_brightness).to_RGB()
+ self.step = (random_change()
+ * abs(self.color - self.transitioning_to))
+ self.color = transition(
+ self.color, self.transitioning_to, self.step
+ )
+ self.light.setRGBRaw(
+ *map(lambda x: clamp(0, x, 1), self.color.components)
+ )
else:
- new_color = color.RGB(0.5, 0.5, 0)
- coverage = compute_coverage(fish, self.x, self.y)
- ccolor = new_color * (1 - coverage) + fish.color * coverage
- self.light.setRGBRaw(
- *map(lambda x: clamp(0, x, 1), ccolor.components)
- )
+ sky_color = color.HSV(250/360, 0.25, sky_brightness).to_RGB()
+ coverage = compute_coverage(sun, self.x, self.y)
+ self.color = coverage * sun.color + (1 - coverage) * sky_color
+ self.light.setRGBRaw(
+ *map(lambda x: clamp(0, x, 1), self.color.components)
+ )
+
+ def fade_to_black(self, steps):
+ hue = self.color.to_HSV().h()
+ sat = self.color.to_HSV().s()
+ value = self.color.to_HSV().v()
+ new_color = color.HSV(hue, sat, value - value / steps * self.fade_step).to_RGB()
+ self.fade_step += 1
+ self.light.setRGBRaw(*map(lambda x: clamp(0, x, 1), new_color.components))
class SunriseShow(Show):
@@ -72,13 +98,30 @@ class SunriseShow(Show):
self.lights = [Light(x, y, self.bridge.get_light(x, y))
for y in xrange(self.bridge.HEIGHT)
for x in xrange(self.bridge.WIDTH)]
- self.sun = Sun(0, 0, 23, 1.5, color.black, 1)
+ self.sun = Sun()
+ self.sky_brightness = 0
+ self.tick_count = 0
def update(self):
- self.sun.update()
- for light in self.lights:
- light.update(self.sun)
+ if self.tick_count > 1000:
+ fade_time = 3 # sec
+ if self.tick_count > 1000 + fade_time * FRAME_RATE:
+ self.stop()
+ return
+ for light in self.lights:
+ light.fade_to_black(fade_time * FRAME_RATE)
+ else:
+ if self.sky_brightness >= 1:
+ self.sun.update()
+ for light in self.lights:
+ light.update(self.sky_brightness, self.sun)
+
+ if self.sky_brightness < 1:
+ self.sky_brightness += 0.05 / FRAME_RATE
+
+ self.tick_count += 1
+ print self.tick_count
def compute_coverage(fish, x, y):
@@ -95,12 +138,15 @@ def compute_coverage(fish, x, y):
return coverage * scale
-def pick_HSV():
+def pick_ocean_HSV(sky_brightness):
"""Return a random color in an appropriate interval."""
- hue = random.random() * (constants.MAX_HUE - constants.MIN_HUE) + constants.MIN_HUE
- saturation = random.random() * (MAX_SATURATION - MIN_SATURATION) + MIN_SATURATION
- value = random.random() * (MAX_VALUE - MIN_VALUE) + MIN_VALUE
- return color.HSV(hue, saturation, value)
+ min_hue = 236 / 360
+ max_hue = 250 / 360
+ min_sat = 0.25
+ max_sat = 0.6
+ hue = random.random() * (max_hue - min_hue) + min_hue
+ saturation = random.random() * (max_sat - min_sat) + min_sat
+ return color.HSV(hue, saturation, sky_brightness)
def transition(start, end, step):
@@ -115,8 +161,8 @@ def transition(start, end, step):
def random_change():
"""Return a random amount that a light should change each frame."""
- min_time = int(constants.MIN_TIME * FRAME_RATE)
- max_time = int(MAX_TIME * FRAME_RATE)
+ min_time = int(2 * FRAME_RATE)
+ max_time = int(6 * FRAME_RATE)
return 1 / random.randint(min_time, max_time)
diff --git a/webapp/app/__init__.py b/webapp/app/__init__.py
index 29cd22b..3e188ec 100644
--- a/webapp/app/__init__.py
+++ b/webapp/app/__init__.py
@@ -1,6 +1,11 @@
from flask import Flask
import socketio
+import Queue
+
+recv_queue = Queue.Queue()
+send_queue = Queue.Queue()
+
sio = socketio.Server(async_mode='threading')
app = Flask(__name__)
app.wsgi_app = socketio.Middleware(sio, app.wsgi_app)
diff --git a/webapp/app/static/carousel.css b/webapp/app/static/carousel.css
new file mode 100644
index 0000000..4e49305
--- /dev/null
+++ b/webapp/app/static/carousel.css
@@ -0,0 +1,144 @@
+/* GLOBAL STYLES
+-------------------------------------------------- */
+/* Padding below the footer and lighter body text */
+
+body {
+ padding-bottom: 40px;
+ color: #5a5a5a;
+ /*background: lighten(#000, 93.5%);*/
+}
+
+
+/* CUSTOMIZE THE NAVBAR
+-------------------------------------------------- */
+
+/* Special class on .container surrounding .navbar, used for positioning it into place. */
+.navbar-wrapper {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: 20;
+}
+
+/* Flip around the padding for proper display in narrow viewports */
+.navbar-wrapper > .container {
+ padding-right: 0;
+ padding-left: 0;
+}
+.navbar-wrapper .navbar {
+ padding-right: 15px;
+ padding-left: 15px;
+}
+.navbar-wrapper .navbar .container {
+ width: auto;
+}
+
+
+/* CUSTOMIZE THE CAROUSEL
+-------------------------------------------------- */
+
+/* Carousel base class */
+.carousel {
+ height: 500px;
+ margin-bottom: 60px;
+}
+/* Since positioning the image, we need to help out the caption */
+.carousel-caption {
+ z-index: 10;
+}
+
+/* Declare heights because of positioning of img element */
+.carousel .item {
+ height: 500px;
+ background-color: #777;
+}
+.carousel-inner > .item > img {
+ position: absolute;
+ top: 0;
+ left: 0;
+ min-width: 100%;
+ height: 500px;
+}
+
+
+/* MARKETING CONTENT
+-------------------------------------------------- */
+
+/* Center align the text within the three columns below the carousel */
+.marketing .col-lg-4 {
+ margin-bottom: 20px;
+ text-align: center;
+}
+.marketing h2 {
+ font-weight: normal;
+}
+.marketing .col-lg-4 p {
+ margin-right: 10px;
+ margin-left: 10px;
+}
+
+
+/* Featurettes
+------------------------- */
+
+.featurette-divider {
+ margin: 80px 0; /* Space out the Bootstrap <hr> more */
+}
+
+/* Thin out the marketing headings */
+.featurette-heading {
+ font-weight: 300;
+ line-height: 1;
+ letter-spacing: -1px;
+}
+
+img.img-disable {
+ -webkit-filter: grayscale(100%);
+ -moz-filter: grayscale(100%);
+ -o-filter: grayscale(100%);
+ -ms-filter: grayscale(100%);
+ filter: grayscale(100%);
+ opacity: 0.5;
+}
+
+/* RESPONSIVE CSS
+-------------------------------------------------- */
+
+@media (min-width: 768px) {
+ /* Navbar positioning foo */
+ .navbar-wrapper {
+ margin-top: 20px;
+ }
+ .navbar-wrapper .container {
+ padding-right: 15px;
+ padding-left: 15px;
+ }
+ .navbar-wrapper .navbar {
+ padding-right: 0;
+ padding-left: 0;
+ }
+
+ /* The navbar becomes detached from the top, so we round the corners */
+ .navbar-wrapper .navbar {
+ border-radius: 4px;
+ }
+
+ /* Bump up size of carousel content */
+ .carousel-caption p {
+ margin-bottom: 20px;
+ font-size: 21px;
+ line-height: 1.4;
+ }
+
+ .featurette-heading {
+ font-size: 50px;
+ }
+}
+
+@media (min-width: 992px) {
+ .featurette-heading {
+ margin-top: 120px;
+ }
+
+}
diff --git a/webapp/app/templates/index.html b/webapp/app/templates/index.html
index b0d9bb3..4dadb7b 100644
--- a/webapp/app/templates/index.html
+++ b/webapp/app/templates/index.html
@@ -1,55 +1,171 @@
-<html>
- <head>
- <script type="text/javascript" src="//code.jquery.com/jquery-2.1.4.min.js"></script>
- <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js"></script>
-
- <script>
- var SPAWN_URL = "/spawn";
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
+ <!--<meta name="description" content="">
+ <meta name="author" content="">
+ <link rel="icon" href="../../favicon.ico">-->
+
+ <!-- Bootstrap core CSS -->
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
+
+ <!-- Custom styles for this template -->
+ <link href="static/carousel.css" rel="stylesheet">
+
+ </head>
+
+<div class = "page-header">
+<center>
+ <h1>
+ Welcome to the Underwater World <br>
+ <small>Select your favorite sea creature and relax!</small>
+ </h1>
+</center>
+
+</div>
+
+
+ <!-- Marketing messaging and featurettes
+ ================================================== -->
+ <!-- Wrap the rest of the page in another container to center all the content. -->
+
+ <div class="container marketing">
+
+ <!-- Three columns of text below the carousel -->
+ <div class="row">
+ <div class="col-lg-4">
+ <img id="jellyfish" onClick="clickImg(this)" class="img-circle" src="http://animalia-life.com/data_images/jellyfish/jellyfish7.jpg" alt="Generic placeholder image" width="140" height="140">
+ </div><!-- /.col-lg-4 -->
+ <div class="col-lg-4">
+ <img id="dolphin" onClick="clickImg(this)" class="img-circle" src="http://kids.nationalgeographic.com/content/dam/kids/photos/animals/Mammals/A-G/bottlenose-dolphin-jumping.jpg.adapt.945.1.jpg" alt="Generic placeholder image" width="140" height="140">
+ </div><!-- /.col-lg-4 -->
+ <div class="col-lg-4">
+ <img id="boat" onClick="clickImg(this)" class="img-circle" src="http://yachtpals.com/files/groupimages/woodduck.jpg" alt="Generic placeholder image" width="140" height="140">
+ </div><!-- /.col-lg-4 -->
+ </div><!-- /.row -->
+<br>
+ <div class="row">
+ <div class="col-lg-4">
+ <img id="dory" onClick="clickImg(this)" class="img-circle" src="https://www.sciencenewsforstudents.org/sites/default/files/2016/06/main/articles/wt_blue_tang_dory_free.jpg" alt="Generic placeholder image" width="150" height="150">
+ </div><!-- /.col-lg-4 -->
+ <div class="col-lg-4">
+ <img id="nemo" onClick="clickImg(this)" class="img-circle" src="http://img.wallpaperfolder.com/f/63DCF495B5BC/clown-fish-75948.jpg" alt="Generic placeholder image" width="140" height="140">
+ </div><!-- /.col-lg-4 -->
+ <div class="col-lg-4">
+ <img id="whale" onClick="clickImg(this)" class="img-circle" src="http://media-channel.nationalgeographic.com/media/uploads/photos/content/photo/2013/01/30/KillerWhale_01_BuiltForTheKillV.jpg" alt="Generic placeholder image" width="140" height="140">
+ </div><!-- /.col-lg-4 -->
+ </div><!-- /.row -->
+<br>
+ <div class="row">
+ <div class="col-lg-4">
+ <img id="stingray" onClick="clickImg(this)" class="img-circle" src="http://3.bp.blogspot.com/-enlwcfp2skQ/Tlxxws9FRlI/AAAAAAAAAmg/0-TEWn8Sokw/s1600/stingray_5.JPG" alt="Generic placeholder image" width="140" height="140">
+ </div><!-- /.col-lg-4 -->
+ <div class="col-lg-4">
+ <img id="eel" onClick="clickImg(this)" class="img-circle" src="http://blogs.courierpostonline.com/fishhead/files/2012/11/eel.jpg" alt="Generic placeholder image" width="140" height="140">
+ </div><!-- /.col-lg-4 -->
+ <div class="col-lg-4">
+ <img id="shark" onClick="clickImg(this)" class="img-circle" src="http://www.livescience.com/images/i/000/083/926/original/shark-attacks-increased-in-2015.jpg?interpolation=lanczos-none&downsize=*:1000" alt="Generic placeholder image" width="140" height="140">
+ </div><!-- /.col-lg-4 -->
+ </div><!-- /.row -->
+
+ </div><!-- /.container -->
- function creature(name, socket) {
- var self = this;
- self.name = name;
- self.socket = socket;
-
- self.render = function($parent) {
- var $a = $('<a>');
- $a.text(self.name);
- $a.click(self.click.bind(self));
- var $li = $('<li>');
- $li.append($a);
- $parent.append($li);
- };
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script type="text/javascript" src="//code.jquery.com/jquery-2.1.4.min.js"></script>
+ <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js"></script>
+ <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
- self.click = function() {
- self.socket.emit('spawn', {name:self.name});
- }
+ <script>
+ var SPAWN_URL = "/spawn";
+
+ function creature(name, socket) {
+ var self = this;
+ self.name = name;
+ self.socket = socket;
+
+ self.render = function($parent) {
+ var $a = $('<a>');
+ $a.text(self.name);
+ $a.click(self.click.bind(self));
+
+ var $li = $('<li>');
+ $li.append($a);
+ $parent.append($li);
+ };
+
+ self.click = function() {
+ self.socket.emit('spawn', {type:self.name});
}
+ }
- $(document).ready(function() {
- var namespace = '/spawn';
- var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
-
- socket.on('connect', function() {
- console.log('connected!');
- });
- socket.on('new creature', function(msg) {
- console.log('new!');
- var c = new creature(msg.name, socket);
- c.render($('#creatures'));
- });
+ $(document).ready(function() {
+ var namespace = '/spawn';
+ socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
+
+ socket.on('connect', function() {
+ console.log('connected!');
+ });
+
+ socket.on('new creature', function(msg) {
+ console.log('new!');
+ var c = new creature(msg.name, socket);
+ c.render($('#creatures'));
+ });
+
+ socket.on('enable image', function(msg) {
+ console.log('enable image');
+ // to be tested
+ var name = msg.name;
+ var myImgElement = $("#" + name);
+ myImgElement.removeClass('img-disable');
+
+ });
+
+ socket.on('disable image', function(msg) {
+ console.log('disable image');
+ // to be tested
+ var name = msg.name;
+ var myImgElement = $("#" + name);
+ myImgElement.addClass('img-disable');
+ });
- socket.on('disconnect', function() {
- console.log('disconnected');
- });
+ socket.on('disconnect', function() {
+ console.log('disconnected');
});
- </script>
- <title>{{ title }}</title>
- </head>
- <body>
- <h1>Ocean Fun Times</h1>
- <ul id="creatures"></ul>
- </body>
+ socket.emit('ready');
+ });
+
+ function clickImg(element) {
+ console.log(element.id);
+
+ //-----Testing------------------
+ //var name = element.id;
+ //var myImgElement = $("#" + name);
+ //myImgElement.addClass('img-disable');
+ // -----------------------------
+
+ var c = new creature(element.id, socket);
+ c.click()
+ }
+
+ </script>
+ <!-- Just to make our placeholder images work. Don't actually copy the next line! -->
+ <!--<script src="../../assets/js/vendor/holder.min.js"></script>-->
+ <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
+ <!--<script src="../../assets/js/ie10-viewport-bug-workaround.js"></script>-->
+ </body>
</html>
diff --git a/webapp/app/views.py b/webapp/app/views.py
index 4aa2c05..b03f6ab 100644
--- a/webapp/app/views.py
+++ b/webapp/app/views.py
@@ -1,17 +1,19 @@
-from flask import render_template, request
-from app import app, sio
+from flask import render_template, request, send_from_directory
+from app import app, sio, send_queue, recv_queue
import time
import threading
-thread = None
-
@app.route('/')
@app.route('/index')
def index():
return render_template('index.html',
title='F16 Group1')
+@app.route('/static/<path:path>')
+def send_static(path):
+ return send_from_directory('static', path)
+
connections = set()
@sio.on('connect', namespace='/spawn')
@@ -22,6 +24,14 @@ def connect(sid, environ):
@sio.on('spawn', namespace='/spawn')
def message(sid, data):
print("message ", data)
+ send_queue.put(('spawn', data))
+
+@sio.on('ready', namespace='/spawn')
+def ready(sid):
+ print("got ready message from ", sid)
+ for event in received_events:
+ print "sending missed event", event
+ sio.emit(event[0], event[1], room=sid, namespace='/spawn')
@sio.on('disconnect', namespace='/spawn')
def disconnect(sid):
@@ -29,9 +39,12 @@ def disconnect(sid):
connections.remove(sid)
+received_events = []
+def recv_events():
+ while True:
+ event = recv_queue.get()
+ sio.emit(event[0], event[1], namespace='/spawn')
+ received_events.append(event)
-def add_creature():
- time.sleep(5)
- sio.emit('new creature', {'name': 'fish1'}, namespace='/spawn')
-thread = sio.start_background_task(add_creature)
+thread = sio.start_background_task(recv_events)
diff --git a/webapp/run.py b/webapp/run.py
index b5c8c6f..2290f41 100755
--- a/webapp/run.py
+++ b/webapp/run.py
@@ -3,5 +3,11 @@
import sys
sys.path.append('packages')
-from app import app
-app.run(threaded=True, host='', port=8000)
+import threading
+
+from app import app, send_queue, recv_queue
+
+def run_server():
+ app.run(threaded=True, host='', port=8000)
+
+server_thread = threading.Thread(target=run_server)